Merge pull request #159 from ethereum/decodeLocals

Add DecodeFromMemory to Solidity Types
pull/7/head
yann300 8 years ago committed by GitHub
commit 7dd80d338c
  1. 154
      src/solidity/decodeInfo.js
  2. 12
      src/solidity/localDecoder.js
  3. 7
      src/solidity/solidityProxy.js
  4. 2
      src/solidity/stateDecoder.js
  5. 20
      src/solidity/types/Address.js
  6. 101
      src/solidity/types/ArrayType.js
  7. 21
      src/solidity/types/Bool.js
  8. 63
      src/solidity/types/DynamicByteArray.js
  9. 40
      src/solidity/types/Enum.js
  10. 17
      src/solidity/types/FixedByteArray.js
  11. 16
      src/solidity/types/Int.js
  12. 23
      src/solidity/types/Mapping.js
  13. 71
      src/solidity/types/RefType.js
  14. 30
      src/solidity/types/StringType.js
  15. 44
      src/solidity/types/Struct.js
  16. 16
      src/solidity/types/Uint.js
  17. 53
      src/solidity/types/ValueType.js
  18. 18
      src/solidity/types/util.js
  19. 3
      src/ui/Ethdebugger.js
  20. 19
      src/util/internalCallTree.js
  21. 2
      test/solidity/contracts/intLocal.js
  22. 39
      test/solidity/contracts/miscLocal.js
  23. 84
      test/solidity/contracts/structArrayLocal.js
  24. 139
      test/solidity/localDecoder.js
  25. 28
      test/solidity/localsTests/helper.js
  26. 105
      test/solidity/localsTests/int.js
  27. 93
      test/solidity/localsTests/misc.js
  28. 63
      test/solidity/localsTests/misc2.js
  29. 121
      test/solidity/localsTests/structArray.js
  30. 30
      test/solidity/localsTests/vmCall.js
  31. 58
      test/solidity/storageLocation.js

@ -11,6 +11,7 @@ var StructType = require('./types/Struct')
var IntType = require('./types/Int')
var UintType = require('./types/Uint')
var MappingType = require('./types/Mapping')
var util = require('./types/util')
/**
* mapping decode the given @arg type
@ -18,7 +19,7 @@ var MappingType = require('./types/Mapping')
* @param {String} type - type given by the AST
* @return {Object} returns decoded info about the current type: { storageBytes, typeName}
*/
function Mapping (type) {
function mapping (type) {
return new MappingType()
}
@ -28,7 +29,7 @@ function Mapping (type) {
* @param {String} type - type given by the AST (e.g uint256, uint32)
* @return {Object} returns decoded info about the current type: { storageBytes, typeName}
*/
function Uint (type) {
function uint (type) {
type === 'uint' ? 'uint256' : type
var storageBytes = parseInt(type.replace('uint', '')) / 8
return new UintType(storageBytes)
@ -40,7 +41,7 @@ function Uint (type) {
* @param {String} type - type given by the AST (e.g int256, int32)
* @return {Object} returns decoded info about the current type: { storageBytes, typeName}
*/
function Int (type) {
function int (type) {
type === 'int' ? 'int256' : type
var storageBytes = parseInt(type.replace('int', '')) / 8
return new IntType(storageBytes)
@ -52,7 +53,7 @@ function Int (type) {
* @param {String} type - type given by the AST (e.g address)
* @return {Object} returns decoded info about the current type: { storageBytes, typeName}
*/
function Address (type) {
function address (type) {
return new AddressType()
}
@ -62,7 +63,7 @@ function Address (type) {
* @param {String} type - type given by the AST (e.g bool)
* @return {Object} returns decoded info about the current type: { storageBytes, typeName}
*/
function Bool (type) {
function bool (type) {
return new BoolType()
}
@ -70,10 +71,20 @@ function Bool (type) {
* DynamicByteArray decode the given @arg type
*
* @param {String} type - type given by the AST (e.g bytes storage ref)
* @param {null} stateDefinitions - all state definitions given by the AST (including struct and enum type declaration) for all contracts
* @param {null} contractName - contract the @args typeName belongs to
* @param {String} location - location of the data (storage ref| storage pointer| memory| calldata)
* @return {Object} returns decoded info about the current type: { storageBytes, typeName}
*/
function DynamicByteArray (type) {
return new BytesType()
function dynamicByteArray (type, stateDefinitions, contractName, location) {
if (!location) {
location = util.extractLocation(type)
}
if (location) {
return new BytesType(location)
} else {
return null
}
}
/**
@ -82,7 +93,7 @@ function DynamicByteArray (type) {
* @param {String} type - type given by the AST (e.g bytes16)
* @return {Object} returns decoded info about the current type: { storageBytes, typeName}
*/
function FixedByteArray (type) {
function fixedByteArray (type) {
var storageBytes = parseInt(type.replace('bytes', ''))
return new BytesXType(storageBytes)
}
@ -91,10 +102,20 @@ function FixedByteArray (type) {
* StringType decode the given @arg type
*
* @param {String} type - type given by the AST (e.g string storage ref)
* @param {null} stateDefinitions - all state definitions given by the AST (including struct and enum type declaration) for all contracts
* @param {null} contractName - contract the @args typeName belongs to
* @param {String} location - location of the data (storage ref| storage pointer| memory| calldata)
* @return {Object} returns decoded info about the current type: { storageBytes, typeName}
*/
function String (type) {
return new StringType()
function stringType (type, stateDefinitions, contractName, location) {
if (!location) {
location = util.extractLocation(type)
}
if (location) {
return new StringType(location)
} else {
return null
}
}
/**
@ -103,22 +124,26 @@ function String (type) {
* @param {String} type - type given by the AST (e.g int256[] storage ref, int256[] storage ref[] storage ref)
* @param {Object} stateDefinitions - all state definitions given by the AST (including struct and enum type declaration) for all contracts
* @param {String} contractName - contract the @args typeName belongs to
* @param {String} location - location of the data (storage ref| storage pointer| memory| calldata)
* @return {Object} returns decoded info about the current type: { storageBytes, typeName, arraySize, subArray}
*/
function Array (type, stateDefinitions, contractName) {
function array (type, stateDefinitions, contractName, location) {
var arraySize
var match = type.match(/(.*)\[(.*?)\]( storage ref| storage pointer| memory| calldata)?$/)
if (!match || match.length < 3) {
if (!match) {
console.log('unable to parse type ' + type)
return null
}
if (!location) {
location = match[3].trim()
}
arraySize = match[2] === '' ? 'dynamic' : parseInt(match[2])
var underlyingType = parseType(match[1], stateDefinitions, contractName)
var underlyingType = parseType(match[1], stateDefinitions, contractName, location)
if (underlyingType === null) {
console.log('unable to parse type ' + type)
return null
}
return new ArrayType(underlyingType, arraySize)
return new ArrayType(underlyingType, arraySize, location)
}
/**
@ -129,7 +154,7 @@ function Array (type, stateDefinitions, contractName) {
* @param {String} contractName - contract the @args typeName belongs to
* @return {Object} returns decoded info about the current type: { storageBytes, typeName, enum}
*/
function Enum (type, stateDefinitions, contractName) {
function enumType (type, stateDefinitions, contractName) {
var match = type.match(/enum (.*)/)
var enumDef = getEnum(match[1], stateDefinitions, contractName)
if (enumDef === null) {
@ -145,16 +170,21 @@ function Enum (type, stateDefinitions, contractName) {
* @param {String} type - type given by the AST (e.g struct structDef storage ref)
* @param {Object} stateDefinitions - all state definitions given by the AST (including struct and enum type declaration) for all contracts
* @param {String} contractName - contract the @args typeName belongs to
* @param {String} location - location of the data (storage ref| storage pointer| memory| calldata)
* @return {Object} returns decoded info about the current type: { storageBytes, typeName, members}
*/
function Struct (type, stateDefinitions, contractName) {
var match = type.match(/struct (.*?)( storage ref| storage pointer| memory| calldata)?$/)
if (!match) {
function struct (type, stateDefinitions, contractName, location) {
var match = type.match(/struct (\S*?)( storage ref| storage pointer| memory| calldata)?$/)
if (match) {
if (!location) {
location = match[2].trim()
}
var memberDetails = getStructMembers(match[1], stateDefinitions, contractName, location) // type is used to extract the ast struct definition
if (!memberDetails) return null
return new StructType(memberDetails, location)
} else {
return null
}
var memberDetails = getStructMembers(match[1], stateDefinitions, contractName) // type is used to extract the ast struct definition
if (!memberDetails) return null
return new StructType(memberDetails)
}
/**
@ -189,9 +219,10 @@ function getEnum (type, stateDefinitions, contractName) {
* @param {String} typeName - name of the struct type (e.g struct <name>)
* @param {Object} stateDefinitions - all state definition given by the AST (including struct and enum type declaration) for all contracts
* @param {String} contractName - contract the @args typeName belongs to
* @param {String} location - location of the data (storage ref| storage pointer| memory| calldata)
* @return {Array} containing all members of the current struct type
*/
function getStructMembers (type, stateDefinitions, contractName) {
function getStructMembers (type, stateDefinitions, contractName, location) {
var split = type.split('.')
if (!split.length) {
type = contractName + '.' + type
@ -242,22 +273,22 @@ function typeClass (fullType) {
* @param {Object} type - type name given by the ast node
* @param {Object} stateDefinitions - all state stateDefinitions given by the AST (including struct and enum type declaration) for all contracts
* @param {String} contractName - contract the @args typeName belongs to
* @param {String} location - location of the data (storage ref| storage pointer| memory| calldata)
* @return {Object} - return the corresponding decoder or null on error
*/
function parseType (type, stateDefinitions, contractName) {
function parseType (type, stateDefinitions, contractName, location) {
var decodeInfos = {
'address': Address,
'contract': Address,
'array': Array,
'bool': Bool,
'bytes': DynamicByteArray,
'bytesX': FixedByteArray,
'enum': Enum,
'string': String,
'struct': Struct,
'int': Int,
'uint': Uint,
'mapping': Mapping
'address': address,
'array': array,
'bool': bool,
'bytes': dynamicByteArray,
'bytesX': fixedByteArray,
'enum': enumType,
'string': stringType,
'struct': struct,
'int': int,
'uint': uint,
'mapping': mapping
}
var currentType = typeClass(type)
if (currentType === null) {
@ -277,60 +308,61 @@ function parseType (type, stateDefinitions, contractName) {
* @param {Array} types - list of types
* @param {Object} stateDefinitions - all state definitions given by the AST (including struct and enum type declaration) for all contracts
* @param {String} contractName - contract the @args typeName belongs to
* @param {String} location - location of the data (storage ref| storage pointer| memory| calldata)
* @return {Array} - return an array of types item: {name, type, location}. location defines the byte offset and slot offset
*/
function computeOffsets (types, stateDefinitions, contractName) {
function computeOffsets (types, stateDefinitions, contractName, location) {
var ret = []
var location = {
var storagelocation = {
offset: 0,
slot: 0
}
for (var i in types) {
var variable = types[i]
var type = parseType(variable.attributes.type, stateDefinitions, contractName)
var type = parseType(variable.attributes.type, stateDefinitions, contractName, location)
if (!type) {
console.log('unable to retrieve decode info of ' + variable.attributes.type)
return null
}
if (location.offset + type.storageBytes > 32) {
location.slot++
location.offset = 0
if (storagelocation.offset + type.storageBytes > 32) {
storagelocation.slot++
storagelocation.offset = 0
}
ret.push({
name: variable.attributes.name,
type: type,
location: {
offset: location.offset,
slot: location.slot
storagelocation: {
offset: storagelocation.offset,
slot: storagelocation.slot
}
})
if (type.storageSlots === 1 && location.offset + type.storageBytes <= 32) {
location.offset += type.storageBytes
if (type.storageSlots === 1 && storagelocation.offset + type.storageBytes <= 32) {
storagelocation.offset += type.storageBytes
} else {
location.slot += type.storageSlots
location.offset = 0
storagelocation.slot += type.storageSlots
storagelocation.offset = 0
}
}
if (location.offset > 0) {
location.slot++
if (storagelocation.offset > 0) {
storagelocation.slot++
}
return {
typesOffsets: ret,
endLocation: location
endLocation: storagelocation
}
}
module.exports = {
parseType: parseType,
computeOffsets: computeOffsets,
Uint: Uint,
Address: Address,
Bool: Bool,
DynamicByteArray: DynamicByteArray,
FixedByteArray: FixedByteArray,
Int: Int,
String: String,
Array: Array,
Enum: Enum,
Struct: Struct
Uint: uint,
Address: address,
Bool: bool,
DynamicByteArray: dynamicByteArray,
FixedByteArray: fixedByteArray,
Int: int,
String: stringType,
Array: array,
Enum: enumType,
Struct: struct
}

@ -6,10 +6,11 @@ function solidityLocals (vmtraceIndex, internalTreeCall, stack, memory) {
return { 'error': 'Can\'t display locals. reason: compilation result might not have been provided' }
}
var locals = {}
memory = formatMemory(memory)
for (var local in scope.locals) {
let variable = scope.locals[local]
if (variable.type.decodeLocals) {
locals[variable.name] = variable.type.decodeLocals(variable.stackHeight, stack, memory)
if (variable.type.decodeFromStack) {
locals[variable.name] = variable.type.decodeFromStack(variable.stackDepth, stack, memory)
} else {
locals[variable.name] = ''
}
@ -17,6 +18,13 @@ function solidityLocals (vmtraceIndex, internalTreeCall, stack, memory) {
return locals
}
function formatMemory (memory) {
if (memory instanceof Array) {
memory = memory.join('').replace(/0x/g, '')
}
return memory
}
module.exports = {
solidityLocals: solidityLocals
}

@ -113,7 +113,12 @@ class SolidityProxy {
*/
ast (sourceLocation) {
var file = this.sourceList[sourceLocation.file]
return this.sources[file].AST
if (this.sources[file]) {
return this.sources[file].AST
} else {
console.log('AST not found for file id ' + sourceLocation.file)
return null
}
}
}

@ -12,7 +12,7 @@ function decodeState (stateVars, storageContent) {
var ret = {}
for (var k in stateVars) {
var stateVar = stateVars[k]
ret[stateVar.name] = stateVar.type.decodeFromStorage(stateVar.location, storageContent)
ret[stateVar.name] = stateVar.type.decodeFromStorage(stateVar.storagelocation, storageContent)
}
return ret
}

@ -1,15 +1,19 @@
'use strict'
var util = require('./util')
var ValueType = require('./ValueType')
function Address () {
this.storageSlots = 1
this.storageBytes = 20
this.typeName = 'address'
}
class Address extends ValueType {
constructor () {
super(1, 20, 'address')
}
Address.prototype.decodeFromStorage = function (location, storageContent) {
var value = util.extractHexValue(location, storageContent, this.storageBytes)
return '0x' + value.toUpperCase()
decodeValue (value) {
if (!value) {
return '0x0000000000000000000000000000000000000000'
} else {
return '0x' + util.extractHexByteSlice(value, this.storageBytes, 0).toUpperCase()
}
}
}
module.exports = Address

@ -1,56 +1,75 @@
'use strict'
var util = require('./util')
var BN = require('ethereumjs-util').BN
var RefType = require('./RefType')
function ArrayType (underlyingType, arraySize) {
this.typeName = 'array'
this.storageBytes = 32
this.underlyingType = underlyingType
this.arraySize = arraySize
this.storageSlots = null
if (arraySize === 'dynamic') {
this.storageSlots = 1
} else {
if (underlyingType.storageBytes < 32) {
var itemPerSlot = Math.floor(32 / underlyingType.storageBytes)
this.storageSlots = Math.ceil(arraySize / itemPerSlot)
class ArrayType extends RefType {
constructor (underlyingType, arraySize, location) {
var storageSlots = null
if (arraySize === 'dynamic') {
storageSlots = 1
} else {
this.storageSlots = arraySize * underlyingType.storageSlots
if (underlyingType.storageBytes < 32) {
var itemPerSlot = Math.floor(32 / underlyingType.storageBytes)
storageSlots = Math.ceil(arraySize / itemPerSlot)
} else {
storageSlots = arraySize * underlyingType.storageSlots
}
}
super(storageSlots, 32, 'array', location)
this.underlyingType = underlyingType
this.arraySize = arraySize
}
}
ArrayType.prototype.decodeFromStorage = function (location, storageContent) {
var ret = []
var size = null
var slotValue = util.extractHexValue(location, storageContent, this.storageBytes)
var currentLocation = {
offset: 0,
slot: location.slot
}
if (this.arraySize === 'dynamic') {
size = util.toBN('0x' + slotValue)
currentLocation.slot = util.sha3(location.slot)
} else {
size = new BN(this.arraySize)
}
var k = util.toBN(0)
for (; k.lt(size) && k.ltn(300); k.iaddn(1)) {
ret.push(this.underlyingType.decodeFromStorage(currentLocation, storageContent))
if (this.underlyingType.storageSlots === 1 && location.offset + this.underlyingType.storageBytes <= 32) {
currentLocation.offset += this.underlyingType.storageBytes
if (currentLocation.offset + this.underlyingType.storageBytes > 32) {
decodeFromStorage (location, storageContent) {
var ret = []
var size = null
var slotValue = util.extractHexValue(location, storageContent, this.storageBytes)
var currentLocation = {
offset: 0,
slot: location.slot
}
if (this.arraySize === 'dynamic') {
size = util.toBN('0x' + slotValue)
currentLocation.slot = util.sha3(location.slot)
} else {
size = new BN(this.arraySize)
}
var k = util.toBN(0)
for (; k.lt(size) && k.ltn(300); k.iaddn(1)) {
ret.push(this.underlyingType.decodeFromStorage(currentLocation, storageContent))
if (this.underlyingType.storageSlots === 1 && location.offset + this.underlyingType.storageBytes <= 32) {
currentLocation.offset += this.underlyingType.storageBytes
if (currentLocation.offset + this.underlyingType.storageBytes > 32) {
currentLocation.offset = 0
currentLocation.slot = '0x' + util.add(currentLocation.slot, 1).toString(16)
}
} else {
currentLocation.slot = '0x' + util.add(currentLocation.slot, this.underlyingType.storageSlots).toString(16)
currentLocation.offset = 0
currentLocation.slot = '0x' + util.add(currentLocation.slot, 1).toString(16)
}
} else {
currentLocation.slot = '0x' + util.add(currentLocation.slot, this.underlyingType.storageSlots).toString(16)
currentLocation.offset = 0
}
return {
value: ret,
length: '0x' + size.toString(16)
}
}
return {
value: ret,
length: '0x' + size.toString(16)
decodeFromMemoryInternal (offset, memory) {
var ret = []
var length = this.arraySize
if (this.arraySize === 'dynamic') {
length = memory.substr(2 * offset, 64)
length = parseInt(length, 16)
offset = offset + 32
}
for (var k = 0; k < length; k++) {
var contentOffset = offset
ret.push(this.underlyingType.decodeFromMemory(contentOffset, memory))
offset += 32
}
return ret
}
}

@ -1,15 +1,20 @@
'use strict'
var ValueType = require('./ValueType')
var util = require('./util')
function Bool () {
this.storageSlots = 1
this.storageBytes = 1
this.typeName = 'bool'
}
class Bool extends ValueType {
constructor () {
super(1, 1, 'bool')
}
Bool.prototype.decodeFromStorage = function (location, storageContent) {
var value = util.extractHexValue(location, storageContent, this.storageBytes)
return value !== '00'
decodeValue (value) {
if (!value) {
return false
} else {
value = util.extractHexByteSlice(value, this.storageBytes, 0)
return value !== '00'
}
}
}
module.exports = Bool

@ -1,36 +1,47 @@
'use strict'
var util = require('./util')
var BN = require('ethereumjs-util').BN
var RefType = require('./RefType')
function DynamicByteArray () {
this.storageSlots = 1
this.storageBytes = 32
this.typeName = 'bytes'
}
class DynamicByteArray extends RefType {
constructor (location) {
super(1, 32, 'bytes', location)
}
DynamicByteArray.prototype.decodeFromStorage = function (location, storageContent) {
var value = util.extractHexValue(location, storageContent, this.storageBytes)
var bn = new BN(value, 16)
if (bn.testn(0)) {
var length = bn.div(new BN(2))
var dataPos = new BN(util.sha3(location.slot).replace('0x', ''), 16)
var ret = ''
var currentSlot = util.readFromStorage(dataPos, storageContent)
while (length.gt(ret.length) && ret.length < 32000) {
currentSlot = currentSlot.replace('0x', '')
ret += currentSlot
dataPos = dataPos.add(new BN(1))
currentSlot = util.readFromStorage(dataPos, storageContent)
}
return {
value: '0x' + ret.replace(/(00)+$/, ''),
length: '0x' + length.toString(16)
decodeFromStorage (location, storageContent) {
var value = util.extractHexValue(location, storageContent, this.storageBytes)
var bn = new BN(value, 16)
if (bn.testn(0)) {
var length = bn.div(new BN(2))
var dataPos = new BN(util.sha3(location.slot).replace('0x', ''), 16)
var ret = ''
var currentSlot = util.readFromStorage(dataPos, storageContent)
while (length.gt(ret.length) && ret.length < 32000) {
currentSlot = currentSlot.replace('0x', '')
ret += currentSlot
dataPos = dataPos.add(new BN(1))
currentSlot = util.readFromStorage(dataPos, storageContent)
}
return {
value: '0x' + ret.replace(/(00)+$/, ''),
length: '0x' + length.toString(16)
}
} else {
var size = parseInt(value.substr(value.length - 2, 2), 16) / 2
return {
value: '0x' + value.substr(0, size * 2),
length: '0x' + size.toString(16)
}
}
} else {
var size = parseInt(value.substr(value.length - 2, 2), 16) / 2
}
decodeFromMemoryInternal (offset, memory) {
offset = 2 * offset
var length = memory.substr(offset, 64)
length = 2 * parseInt(length, 16)
return {
value: '0x' + value.substr(0, size * 2),
length: '0x' + size.toString(16)
length: '0x' + length.toString(16),
value: '0x' + memory.substr(offset + 64, length)
}
}
}

@ -1,25 +1,29 @@
'use strict'
var util = require('./util')
var ValueType = require('./ValueType')
function Enum (enumDef) {
this.enumDef = enumDef
this.typeName = 'enum'
this.storageSlots = 1
var length = enumDef.children.length
this.storageBytes = 0
while (length > 1) {
length = length / 256
this.storageBytes++
class Enum extends ValueType {
constructor (enumDef) {
var storageBytes = 0
var length = enumDef.children.length
while (length > 1) {
length = length / 256
storageBytes++
}
super(1, storageBytes, 'enum')
this.enumDef = enumDef
}
}
Enum.prototype.decodeFromStorage = function (location, storageContent) {
var value = util.extractHexValue(location, storageContent, this.storageBytes)
value = parseInt(value, 16)
if (this.enumDef.children.length > value) {
return this.enumDef.children[value].attributes.name
} else {
return 'INVALID_ENUM<' + value + '>'
decodeValue (value) {
if (!value) {
return this.enumDef.children[0].attributes.name
} else {
value = parseInt(value, 16)
if (this.enumDef.children.length > value) {
return this.enumDef.children[value].attributes.name
} else {
return 'INVALID_ENUM<' + value + '>'
}
}
}
}

@ -1,15 +1,14 @@
'use strict'
var util = require('./util')
var ValueType = require('./ValueType')
function FixedByteArray (storageBytes) {
this.storageSlots = 1
this.storageBytes = storageBytes
this.typeName = 'bytesX'
}
class FixedByteArray extends ValueType {
constructor (storageBytes) {
super(1, storageBytes, 'bytesX')
}
FixedByteArray.prototype.decodeFromStorage = function (location, storageContent) {
var value = util.extractHexValue(location, storageContent, this.storageBytes)
return '0x' + value.toUpperCase()
decodeValue (value) {
return '0x' + value.substr(0, 2 * this.storageBytes).toUpperCase()
}
}
module.exports = FixedByteArray

@ -1,14 +1,16 @@
'use strict'
var util = require('./util')
var ValueType = require('./ValueType')
function Int (storageBytes) {
this.storageSlots = 1
this.storageBytes = storageBytes
this.typeName = 'int'
}
class Int extends ValueType {
constructor (storageBytes) {
super(1, storageBytes, 'int')
}
Int.prototype.decodeFromStorage = function (location, storageContent) {
return util.decodeInt(location, storageContent, this.storageBytes, true)
decodeValue (value) {
value = util.extractHexByteSlice(value, this.storageBytes, 0)
return util.decodeIntFromHex(value, this.storageBytes, true)
}
}
module.exports = Int

@ -1,13 +1,22 @@
'use strict'
var RefType = require('./RefType')
function Mapping () {
this.storageSlots = 1
this.storageBytes = 32
this.typeName = 'mapping'
}
class Mapping extends RefType {
constructor () {
super(1, 32, 'mapping')
}
decodeValue (value) {
return '<not implemented>'
}
decodeFromStorage (location, storageContent) {
return '<not implemented>'
}
Mapping.prototype.decodeFromStorage = function (location, storageContent) {
return '<not implemented>'
decodeFromMemoryInternal (offset, memory) {
return '<not implemented>'
}
}
module.exports = Mapping

@ -0,0 +1,71 @@
'use strict'
class RefType {
constructor (storageSlots, storageBytes, typeName, location) {
this.location = location
this.storageSlots = storageSlots
this.storageBytes = storageBytes
this.typeName = typeName
this.basicType = 'RefType'
}
/**
* decode the type from the stack
*
* @param {Int} stackDepth - position of the type in the stack
* @param {Array} stack - stack
* @param {String} - memory
* @param {Object} - storage
* @return {Object} decoded value
*/
decodeFromStack (stackDepth, stack, memory, storage) {
if (stack.length - 1 < stackDepth) {
return { error: '<decoding failed - stack underflow ' + stackDepth + '>' }
}
if (!storage) {
storage = {} // TODO this is a fallback, should manage properly locals store in storage
}
var offset = stack[stack.length - 1 - stackDepth]
offset = parseInt(offset, 16)
if (this.isInStorage()) {
return this.decodeFromStorage({ offset: 0, slot: offset }, storage)
} else if (this.isInMemory()) {
return this.decodeFromMemoryInternal(offset, memory)
} else {
return { error: '<decoding failed - no decoder for ' + this.location + '>' }
}
}
/**
* decode the type from the memory
*
* @param {Int} offset - position of the ref of the type in memory
* @param {String} memory - memory
* @return {Object} decoded value
*/
decodeFromMemory (offset, memory) {
offset = memory.substr(2 * offset, 64)
offset = parseInt(offset, 16)
return this.decodeFromMemoryInternal(offset, memory)
}
/**
* current type defined in storage
*
* @return {Bool} - return true if the type is defined in the storage
*/
isInStorage () {
return this.location.indexOf('storage') === 0
}
/**
* current type defined in memory
*
* @return {Bool} - return true if the type is defined in the memory
*/
isInMemory () {
return this.location.indexOf('memory') === 0
}
}
module.exports = RefType

@ -1,15 +1,31 @@
'use strict'
var DynamicBytes = require('./DynamicByteArray')
function StringType () {
this.storageSlots = 1
this.storageBytes = 32
this.typeName = 'string'
this.dynamicBytes = new DynamicBytes()
class StringType extends DynamicBytes {
constructor (location) {
super(location)
this.typeName = 'string'
}
decodeFromStorage (location, storageContent) {
var decoded = super.decodeFromStorage(location, storageContent)
return format(decoded)
}
decodeFromStack (stackDepth, stack, memory) {
return super.decodeFromStack(stackDepth, stack, memory)
}
decodeFromMemoryInternal (offset, memory) {
var decoded = super.decodeFromMemoryInternal(offset, memory)
return format(decoded)
}
}
StringType.prototype.decodeFromStorage = function (location, storageContent) {
var decoded = this.dynamicBytes.decodeFromStorage(location, storageContent)
function format (decoded) {
if (decoded.error) {
return decoded
}
var value = decoded.value
value = value.replace('0x', '').replace(/(..)/g, '%$1')
var ret = {

@ -1,23 +1,35 @@
'use strict'
var util = require('./util')
var RefType = require('./RefType')
function Struct (memberDetails) {
this.storageSlots = memberDetails.storageSlots
this.storageBytes = 32
this.members = memberDetails.members
this.typeName = 'struct'
}
class Struct extends RefType {
constructor (memberDetails, location) {
super(memberDetails.storageSlots, 32, 'struct', location)
this.members = memberDetails.members
}
decodeFromStorage (location, storageContent) {
var ret = {}
this.members.map(function (item, i) {
var globalLocation = {
offset: location.offset + item.storagelocation.offset,
slot: util.add(location.slot, item.storagelocation.slot)
}
ret[item.name] = item.type.decodeFromStorage(globalLocation, storageContent)
})
return ret
}
Struct.prototype.decodeFromStorage = function (location, storageContent) {
var ret = {}
this.members.map(function (item, i) {
var globalLocation = {
offset: location.offset + item.location.offset,
slot: util.add(location.slot, item.location.slot)
}
ret[item.name] = item.type.decodeFromStorage(globalLocation, storageContent)
})
return ret
decodeFromMemoryInternal (offset, memory) {
var ret = {}
this.members.map((item, i) => {
var contentOffset = offset
var member = item.type.decodeFromMemory(contentOffset, memory)
ret[item.name] = member
offset += 32
})
return ret
}
}
module.exports = Struct

@ -1,14 +1,16 @@
'use strict'
var util = require('./util')
var ValueType = require('./ValueType')
function Uint (storageBytes) {
this.storageSlots = 1
this.storageBytes = storageBytes
this.typeName = 'uint'
}
class Uint extends ValueType {
constructor (storageBytes) {
super(1, storageBytes, 'uint')
}
Uint.prototype.decodeFromStorage = function (location, storageContent) {
return util.decodeInt(location, storageContent, this.storageBytes, false)
decodeValue (value) {
value = util.extractHexByteSlice(value, this.storageBytes, 0)
return util.decodeIntFromHex(value, this.storageBytes, false)
}
}
module.exports = Uint

@ -0,0 +1,53 @@
'use strict'
var util = require('./util')
class ValueType {
constructor (storageSlots, storageBytes, typeName) {
this.storageSlots = storageSlots
this.storageBytes = storageBytes
this.typeName = typeName
this.basicType = 'ValueType'
}
/**
* decode the type with the @arg location from the storage
*
* @param {Object} location - containing offset and slot
* @param {Object} storageContent - storageContent (storage)
* @return {Object} - decoded value
*/
decodeFromStorage (location, storageContent) {
var value = util.extractHexValue(location, storageContent, this.storageBytes)
return this.decodeValue(value)
}
/**
* decode the type from the stack
*
* @param {Int} stackDepth - position of the type in the stack
* @param {Array} stack - stack
* @param {String} - memory
* @return {Object} - decoded value
*/
decodeFromStack (stackDepth, stack, memory) {
if (stackDepth >= stack.length) {
return this.decodeValue('')
} else {
return this.decodeValue(stack[stack.length - 1 - stackDepth].replace('0x', ''))
}
}
/**
* decode the type with the @arg offset location from the memory
*
* @param {Int} stackDepth - position of the type in the stack
* @return {String} - memory
* @return {Object} - decoded value
*/
decodeFromMemory (offset, memory) {
var value = memory.substr(2 * offset, 64)
return this.decodeValue(util.extractHexByteSlice(value, this.storageBytes, 0))
}
}
module.exports = ValueType

@ -5,15 +5,22 @@ var BN = require('ethereumjs-util').BN
module.exports = {
readFromStorage: readFromStorage,
decodeInt: decodeInt,
decodeIntFromHex: decodeIntFromHex,
extractHexValue: extractHexValue,
extractHexByteSlice: extractHexByteSlice,
sha3: sha3,
toBN: toBN,
add: add
add: add,
extractLocation: extractLocation
}
function decodeInt (location, storageContent, byteLength, signed) {
var slotvalue = readFromStorage(location.slot, storageContent)
var value = extractHexByteSlice(slotvalue, byteLength, location.offset)
return decodeIntFromHex(value, byteLength, signed)
}
function decodeIntFromHex (value, byteLength, signed) {
var bigNumber = new BN(value, 16)
if (signed) {
bigNumber = bigNumber.fromTwos(8 * byteLength)
@ -85,3 +92,12 @@ function toBN (value) {
function add (value1, value2) {
return toBN(value1).add(toBN(value2))
}
function extractLocation (type) {
var match = type.match(/( storage ref| storage pointer| memory| calldata)?$/)
if (match[1] !== '') {
return match[1].trim()
} else {
return null
}
}

@ -31,7 +31,8 @@ function Ethdebugger () {
this.solidityProxy = new SolidityProxy(this.traceManager, this.codeManager)
var callTree = new InternalCallTree(this.event, this.traceManager, this.solidityProxy, this.codeManager, { includeLocalVariables: true })
this.callTree = callTree
this.callTree = callTree // TODO: currently used by browser solidity, we should improve the API
this.event.register('indexChanged', this, function (index) {
self.codeManager.resolveStep(index, self.tx)
})

@ -91,7 +91,7 @@ async function buildTree (tree, step, scopeId) {
try {
sourceLocation = await extractSourceLocation(tree, step)
} catch (e) {
return { outStep: step, error: 'InternalCallTree - Error resolving source location. ' + step + ' ' + e.messager }
return { outStep: step, error: 'InternalCallTree - Error resolving source location. ' + step + ' ' + e.message }
}
if (!sourceLocation) {
return { outStep: step, error: 'InternalCallTree - No source Location. ' + step }
@ -132,7 +132,7 @@ function includeVariableDeclaration (tree, step, sourceLocation, scopeId) {
tree.scopes[scopeId].locals[variableDeclaration.attributes.name] = {
name: variableDeclaration.attributes.name,
type: decodeInfo.parseType(variableDeclaration.attributes.type, states, contractName),
stackHeight: stack.length
stackDepth: stack.length
}
}
})
@ -149,11 +149,11 @@ function extractSourceLocation (tree, step) {
if (!error) {
return resolve(sourceLocation)
} else {
return reject('InternalCallTree - Cannot retrieve sourcelocation for step ' + step)
return reject('InternalCallTree - Cannot retrieve sourcelocation for step ' + step + ' ' + error)
}
})
} else {
return reject('InternalCallTree - Cannot retrieve address for step ' + step)
return reject('InternalCallTree - Cannot retrieve address for step ' + step + ' ' + error)
}
})
})
@ -161,10 +161,15 @@ function extractSourceLocation (tree, step) {
function resolveVariableDeclaration (tree, step, sourceLocation) {
if (!tree.variableDeclarationByFile[sourceLocation.file]) {
tree.variableDeclarationByFile[sourceLocation.file] = extractVariableDeclarations(tree.solidityProxy.ast(sourceLocation), tree.astWalker)
var ast = tree.solidityProxy.ast(sourceLocation)
if (ast) {
tree.variableDeclarationByFile[sourceLocation.file] = extractVariableDeclarations(ast, tree.astWalker)
} else {
console.log('Ast not found for step ' + step + '. file ' + sourceLocation.file)
return null
}
}
var variableDeclarations = tree.variableDeclarationByFile[sourceLocation.file]
return variableDeclarations[sourceLocation.start + ':' + sourceLocation.length + ':' + sourceLocation.file]
return tree.variableDeclarationByFile[sourceLocation.file][sourceLocation.start + ':' + sourceLocation.length + ':' + sourceLocation.file]
}
function extractVariableDeclarations (ast, astWalker) {

@ -2,7 +2,7 @@
module.exports = {
contract: `
contract proxy {
contract proxy {
struct testStruct {
int one;
uint two;

@ -0,0 +1,39 @@
'use strict'
module.exports = {
contract: `
contract miscLocal {
enum enumDef {
one,
two,
three,
four
}
function miscLocal () {
bool boolFalse = false;
bool boolTrue = true;
enumDef testEnum;
testEnum = enumDef.three;
address sender = msg.sender;
byte _bytes1 = hex"99";
bytes1 __bytes1 = hex"99";
bytes2 __bytes2 = hex"99AB";
bytes4 __bytes4 = hex"99FA";
bytes6 __bytes6 = hex"99";
bytes7 __bytes7 = hex"993567";
bytes8 __bytes8 = hex"99ABD417";
bytes9 __bytes9 = hex"99156744AF";
bytes13 __bytes13 = hex"991234234253";
bytes16 __bytes16 = hex"99AFAD234324";
bytes24 __bytes24 = hex"99AFAD234324";
bytes32 __bytes32 = hex"9999ABD41799ABD417";
}
}
contract miscLocal2 {
function miscLocal2 () {
bytes memory dynbytes = "dynamicbytes";
string memory smallstring = "test_test_test";
}
}
`}

@ -0,0 +1,84 @@
'use strict'
module.exports = {
contract: `
contract structArrayLocal {
struct teststruct {
string a;
int b;
string c;
int d;
bool e;
}
enum enumdef
{
one,
two,
three
}
struct teststructArray {
string[] a;
int8[3] b;
enumdef c;
}
function structArrayLocal () {
bytes memory bytesSimple = "test_super";
teststruct memory e;
e.a = "test";
e.b = 5;
string memory f = "test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_";
e.c = "test_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_test";
e.d = 3;
e.e = true;
int[5] memory simpleArray;
simpleArray[0] = 45;
simpleArray[1] = 324324;
simpleArray[2] = -333;
simpleArray[3] = 5656;
simpleArray[4] = -1111;
string[3] memory stringArray;
stringArray[0] = "long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_";
stringArray[1] = "two";
stringArray[2] = "three";
int[][3] memory dynArray;
dynArray[0] = new int[](1);
dynArray[1] = new int[](2);
dynArray[2] = new int[](3);
dynArray[0][0] = 3423423532;
dynArray[1][0] = -342343323532;
dynArray[1][1] = 23432;
dynArray[2][0] = -432432;
dynArray[2][1] = 3423423532;
dynArray[2][2] = -432432;
teststruct[3] memory structArray;
structArray[0] = e;
structArray[1].a = "item1 a";
structArray[1].b = 20;
structArray[1].c = "item1 c";
structArray[1].d = -45;
structArray[1].e = false;
structArray[2].a = "item2 a";
structArray[2].b = 200;
structArray[2].c = "item2 c";
structArray[2].d = -450;
structArray[2].e = true;
teststructArray memory arrayStruct;
arrayStruct.a = new string[](1);
arrayStruct.a[0] = "string";
arrayStruct.b[0] = 34;
arrayStruct.b[1] = -23;
arrayStruct.b[2] = -3;
arrayStruct.c = enumdef.three;
}
}
`}

@ -2,120 +2,26 @@
var tape = require('tape')
var compiler = require('solc')
var intLocal = require('./contracts/intLocal')
var TraceManager = require('../../babelify-src/trace/traceManager')
var CodeManager = require('../../babelify-src/code/codeManager')
var miscLocal = require('./contracts/miscLocal')
var structArrayLocal = require('./contracts/structArrayLocal')
var VM = require('ethereumjs-vm')
var Tx = require('ethereumjs-tx')
var Block = require('ethereumjs-block')
var BN = require('ethereumjs-util').BN
var utileth = require('ethereumjs-util')
var Web3Providers = require('../../babelify-src/web3Provider/web3Providers')
var traceHelper = require('../../babelify-src/helpers/traceHelper')
var util = require('../../babelify-src/helpers/global')
var SolidityProxy = require('../../babelify-src/solidity/solidityProxy')
var InternalCallTree = require('../../babelify-src/util/internalCallTree')
var EventManager = require('../../babelify-src/lib/eventManager')
var localDecoder = require('../../babelify-src/solidity/localDecoder')
var intLocalTest = require('./localsTests/int')
var miscLocalTest = require('./localsTests/misc')
var misc2LocalTest = require('./localsTests/misc2')
var structArrayLocalTest = require('./localsTests/structArray')
tape('solidity', function (t) {
t.test('local decoder', function (st) {
var privateKey = new Buffer('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a', 'hex')
var address = utileth.privateToAddress(privateKey)
var vm = initVM(st, address)
var output = compiler.compile(intLocal.contract, 0)
sendTx(vm, {nonce: 0, privateKey: privateKey}, null, 0, output.contracts['intLocal'].bytecode, function (error, txHash) {
if (error) {
st.fail(error)
} else {
util.web3.getTransaction(txHash, function (error, tx) {
if (error) {
st.fail(error)
} else {
tx.to = traceHelper.contractCreationToken('0')
var traceManager = new TraceManager()
var codeManager = new CodeManager(traceManager)
var solidityProxy = new SolidityProxy(traceManager, codeManager)
solidityProxy.reset(output)
var debuggerEvent = new EventManager()
var callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true })
callTree.event.register('callTreeBuildFailed', (error) => {
st.fail(JSON.stringify(error))
})
callTree.event.register('callTreeReady', (scopes, scopeStarts) => {
st.equals(scopeStarts[0], '')
st.equals(scopeStarts[97], '1')
st.equals(scopeStarts[112], '1.1')
st.equals(scopeStarts[135], '2')
st.equals(scopeStarts[154], '3')
st.equals(scopeStarts[169], '3.1')
st.equals(scopes[''].locals['ui8'].type.typeName, 'uint')
st.equals(scopes[''].locals['ui16'].type.typeName, 'uint')
st.equals(scopes[''].locals['ui32'].type.typeName, 'uint')
st.equals(scopes[''].locals['ui64'].type.typeName, 'uint')
st.equals(scopes[''].locals['ui128'].type.typeName, 'uint')
st.equals(scopes[''].locals['ui256'].type.typeName, 'uint')
st.equals(scopes[''].locals['ui'].type.typeName, 'uint')
st.equals(scopes[''].locals['i8'].type.typeName, 'int')
st.equals(scopes[''].locals['i16'].type.typeName, 'int')
st.equals(scopes[''].locals['i32'].type.typeName, 'int')
st.equals(scopes[''].locals['i64'].type.typeName, 'int')
st.equals(scopes[''].locals['i128'].type.typeName, 'int')
st.equals(scopes[''].locals['i256'].type.typeName, 'int')
st.equals(scopes[''].locals['i'].type.typeName, 'int')
st.equals(scopes[''].locals['ishrink'].type.typeName, 'int')
st.equals(scopes['1'].locals['ui8'].type.typeName, 'uint')
st.equals(scopes['1.1'].locals['ui81'].type.typeName, 'uint')
st.equals(scopes['2'].locals['ui81'].type.typeName, 'uint')
st.equals(scopes['3'].locals['ui8'].type.typeName, 'uint')
st.equals(scopes['3.1'].locals['ui81'].type.typeName, 'uint')
decodeLocal(st, 125, traceManager, callTree, function (locals) {
st.equals(Object.keys(locals).length, 16)
})
decodeLocal(st, 177, traceManager, callTree, function (locals) {
try {
st.equals(locals['ui8'], '')
st.equals(Object.keys(locals).length, 1)
} catch (e) {
st.fail(e.message)
}
})
})
traceManager.resolveTrace(tx, (error, result) => {
if (error) {
st.fail(error)
} else {
debuggerEvent.trigger('newTraceLoaded', [traceManager.trace])
}
})
}
})
}
})
test(st, vm, privateKey)
})
})
/*
Decode local variable
*/
function decodeLocal (st, index, traceManager, callTree, verifier) {
traceManager.waterfall([
traceManager.getStackAt,
traceManager.getMemoryAt],
index,
function (error, result) {
if (!error) {
var locals = localDecoder.solidityLocals(index, callTree, result[0].value, result[1].value)
verifier(locals)
} else {
st.fail(error)
}
})
}
/*
Init VM / Send Transaction
*/
@ -140,25 +46,16 @@ function initVM (st, address) {
return vm
}
function sendTx (vm, from, to, value, data, cb) {
var tx = new Tx({
nonce: new BN(from.nonce++),
gasPrice: new BN(1),
gasLimit: new BN(3000000, 10),
to: to,
value: new BN(value, 10),
data: new Buffer(data, 'hex')
})
tx.sign(from.privateKey)
var block = new Block({
header: {
timestamp: new Date().getTime() / 1000 | 0,
number: 0
},
transactions: [],
uncleHeaders: []
})
vm.runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, function (error, result) {
cb(error, utileth.bufferToHex(tx.hash()))
function test (st, vm, privateKey) {
var output = compiler.compile(intLocal.contract, 0)
intLocalTest(st, vm, privateKey, output.contracts['intLocal'].bytecode, output, function () {
output = compiler.compile(miscLocal.contract, 0)
miscLocalTest(st, vm, privateKey, output.contracts['miscLocal'].bytecode, output, function () {
output = compiler.compile(miscLocal.contract, 0)
misc2LocalTest(st, vm, privateKey, output.contracts['miscLocal2'].bytecode, output, function () {
output = compiler.compile(structArrayLocal.contract, 0)
structArrayLocalTest(st, vm, privateKey, output.contracts['structArrayLocal'].bytecode, output, function () {})
})
})
})
}

@ -0,0 +1,28 @@
'use strict'
var localDecoder = require('../../../babelify-src/solidity/localDecoder')
/*
Decode local variable
*/
function decodeLocal (st, index, traceManager, callTree, verifier) {
try {
traceManager.waterfall([
traceManager.getStackAt,
traceManager.getMemoryAt],
index,
function (error, result) {
if (!error) {
var locals = localDecoder.solidityLocals(index, callTree, result[0].value, result[1].value)
verifier(locals)
} else {
st.fail(error)
}
})
} catch (e) {
st.fail(e.message)
}
}
module.exports = {
decodeLocals: decodeLocal
}

@ -0,0 +1,105 @@
'use strict'
var TraceManager = require('../../../babelify-src/trace/traceManager')
var CodeManager = require('../../../babelify-src/code/codeManager')
var vmSendTx = require('./vmCall')
var traceHelper = require('../../../babelify-src/helpers/traceHelper')
var util = require('../../../babelify-src/helpers/global')
var SolidityProxy = require('../../../babelify-src/solidity/solidityProxy')
var InternalCallTree = require('../../../babelify-src/util/internalCallTree')
var EventManager = require('../../../babelify-src/lib/eventManager')
var helper = require('./helper')
module.exports = function (st, vm, privateKey, contractBytecode, compilationResult, cb) {
vmSendTx(vm, {nonce: 0, privateKey: privateKey}, null, 0, contractBytecode, function (error, txHash) {
if (error) {
st.fail(error)
} else {
util.web3.getTransaction(txHash, function (error, tx) {
if (error) {
st.fail(error)
} else {
tx.to = traceHelper.contractCreationToken('0')
var traceManager = new TraceManager()
var codeManager = new CodeManager(traceManager)
codeManager.clear()
var solidityProxy = new SolidityProxy(traceManager, codeManager)
solidityProxy.reset(compilationResult)
var debuggerEvent = new EventManager()
var callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true })
callTree.event.register('callTreeBuildFailed', (error) => {
st.fail(error)
})
callTree.event.register('callTreeReady', (scopes, scopeStarts) => {
try {
st.equals(scopeStarts[0], '')
st.equals(scopeStarts[97], '1')
st.equals(scopeStarts[112], '1.1')
st.equals(scopeStarts[135], '2')
st.equals(scopeStarts[154], '3')
st.equals(scopeStarts[169], '3.1')
st.equals(scopes[''].locals['ui8'].type.typeName, 'uint')
st.equals(scopes[''].locals['ui16'].type.typeName, 'uint')
st.equals(scopes[''].locals['ui32'].type.typeName, 'uint')
st.equals(scopes[''].locals['ui64'].type.typeName, 'uint')
st.equals(scopes[''].locals['ui128'].type.typeName, 'uint')
st.equals(scopes[''].locals['ui256'].type.typeName, 'uint')
st.equals(scopes[''].locals['ui'].type.typeName, 'uint')
st.equals(scopes[''].locals['i8'].type.typeName, 'int')
st.equals(scopes[''].locals['i16'].type.typeName, 'int')
st.equals(scopes[''].locals['i32'].type.typeName, 'int')
st.equals(scopes[''].locals['i64'].type.typeName, 'int')
st.equals(scopes[''].locals['i128'].type.typeName, 'int')
st.equals(scopes[''].locals['i256'].type.typeName, 'int')
st.equals(scopes[''].locals['i'].type.typeName, 'int')
st.equals(scopes[''].locals['ishrink'].type.typeName, 'int')
st.equals(scopes['1'].locals['ui8'].type.typeName, 'uint')
st.equals(scopes['1.1'].locals['ui81'].type.typeName, 'uint')
st.equals(scopes['2'].locals['ui81'].type.typeName, 'uint')
st.equals(scopes['3'].locals['ui8'].type.typeName, 'uint')
st.equals(scopes['3.1'].locals['ui81'].type.typeName, 'uint')
} catch (e) {
st.fail(e.message)
}
helper.decodeLocals(st, 125, traceManager, callTree, function (locals) {
st.equals(Object.keys(locals).length, 16)
st.equals(locals['ui8'], '130')
st.equals(locals['ui16'], '456')
st.equals(locals['ui32'], '4356')
st.equals(locals['ui64'], '3543543543')
st.equals(locals['ui128'], '234567')
st.equals(locals['ui256'], '115792089237316195423570985008687907853269984665640564039457584007880697216513')
st.equals(locals['ui'], '123545666')
st.equals(locals['i8'], '-45')
st.equals(locals['i16'], '-1234')
st.equals(locals['i32'], '3455')
st.equals(locals['i64'], '-35566')
st.equals(locals['i128'], '-444444')
st.equals(locals['i256'], '3434343')
st.equals(locals['i'], '-32432423423')
st.equals(locals['ishrink'], '2')
})
helper.decodeLocals(st, 177, traceManager, callTree, function (locals) {
try {
st.equals(locals['ui8'], '123')
st.equals(Object.keys(locals).length, 1)
} catch (e) {
st.fail(e.message)
}
cb()
})
})
traceManager.resolveTrace(tx, (error, result) => {
if (error) {
st.fail(error)
} else {
debuggerEvent.trigger('newTraceLoaded', [traceManager.trace])
}
})
}
})
}
})
}

@ -0,0 +1,93 @@
'use strict'
var TraceManager = require('../../../babelify-src/trace/traceManager')
var CodeManager = require('../../../babelify-src/code/codeManager')
var vmSendTx = require('./vmCall')
var traceHelper = require('../../../babelify-src/helpers/traceHelper')
var util = require('../../../babelify-src/helpers/global')
var SolidityProxy = require('../../../babelify-src/solidity/solidityProxy')
var InternalCallTree = require('../../../babelify-src/util/internalCallTree')
var EventManager = require('../../../babelify-src/lib/eventManager')
var helper = require('./helper')
module.exports = function (st, vm, privateKey, contractBytecode, compilationResult, cb) {
vmSendTx(vm, {nonce: 0, privateKey: privateKey}, null, 0, contractBytecode, function (error, txHash) {
if (error) {
st.fail(error)
} else {
util.web3.getTransaction(txHash, function (error, tx) {
if (error) {
st.fail(error)
} else {
tx.to = traceHelper.contractCreationToken('0')
var traceManager = new TraceManager()
var codeManager = new CodeManager(traceManager)
codeManager.clear()
var solidityProxy = new SolidityProxy(traceManager, codeManager)
solidityProxy.reset(compilationResult)
var debuggerEvent = new EventManager()
var callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true })
callTree.event.register('callTreeBuildFailed', (error) => {
st.fail(error)
})
callTree.event.register('callTreeReady', (scopes, scopeStarts) => {
helper.decodeLocals(st, 70, traceManager, callTree, function (locals) {
try {
st.equals(locals['boolFalse'], false)
st.equals(locals['boolTrue'], true)
st.equals(locals['testEnum'], 'three')
st.equals(locals['sender'], '0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB')
st.equals(locals['_bytes1'], '0x99')
st.equals(locals['__bytes1'], '0x99')
st.equals(locals['__bytes2'], '0x99AB')
st.equals(locals['__bytes4'], '0x99FA0000')
st.equals(locals['__bytes6'], '0x990000000000')
st.equals(locals['__bytes7'], '0x99356700000000')
st.equals(locals['__bytes8'], '0x99ABD41700000000')
st.equals(locals['__bytes9'], '0x99156744AF00000000')
st.equals(locals['__bytes13'], '0x99123423425300000000000000')
st.equals(locals['__bytes16'], '0x99AFAD23432400000000000000000000')
st.equals(locals['__bytes24'], '0x99AFAD234324000000000000000000000000000000000000')
st.equals(locals['__bytes32'], '0x9999ABD41799ABD4170000000000000000000000000000000000000000000000')
st.equals(Object.keys(locals).length, 16)
} catch (e) {
st.fail(e.message)
}
})
helper.decodeLocals(st, 7, traceManager, callTree, function (locals) {
try {
st.equals(locals['boolFalse'], false)
st.equals(locals['boolTrue'], false)
st.equals(locals['testEnum'], 'one')
st.equals(locals['sender'], '0x0000000000000000000000000000000000000000')
st.equals(locals['_bytes1'], '0x')
st.equals(locals['__bytes1'], '0x')
st.equals(locals['__bytes2'], '0x')
st.equals(locals['__bytes4'], '0x')
st.equals(locals['__bytes6'], '0x')
st.equals(locals['__bytes7'], '0x')
st.equals(locals['__bytes8'], '0x')
st.equals(locals['__bytes9'], '0x')
st.equals(locals['__bytes13'], '0x')
st.equals(locals['__bytes16'], '0x')
st.equals(locals['__bytes24'], '0x')
st.equals(locals['__bytes32'], '0x')
st.equals(Object.keys(locals).length, 16)
} catch (e) {
st.fail(e.message)
}
cb()
})
})
traceManager.resolveTrace(tx, (error, result) => {
if (error) {
st.fail(error)
} else {
debuggerEvent.trigger('newTraceLoaded', [traceManager.trace])
}
})
}
})
}
})
}

@ -0,0 +1,63 @@
'use strict'
var TraceManager = require('../../../babelify-src/trace/traceManager')
var CodeManager = require('../../../babelify-src/code/codeManager')
var vmSendTx = require('./vmCall')
var traceHelper = require('../../../babelify-src/helpers/traceHelper')
var util = require('../../../babelify-src/helpers/global')
var SolidityProxy = require('../../../babelify-src/solidity/solidityProxy')
var InternalCallTree = require('../../../babelify-src/util/internalCallTree')
var EventManager = require('../../../babelify-src/lib/eventManager')
var helper = require('./helper')
module.exports = function (st, vm, privateKey, contractBytecode, compilationResult, cb) {
vmSendTx(vm, {nonce: 0, privateKey: privateKey}, null, 0, contractBytecode, function (error, txHash) {
if (error) {
st.fail(error)
} else {
util.web3.getTransaction(txHash, function (error, tx) {
if (error) {
st.fail(error)
} else {
tx.to = traceHelper.contractCreationToken('0')
var traceManager = new TraceManager()
var codeManager = new CodeManager(traceManager)
codeManager.clear()
var solidityProxy = new SolidityProxy(traceManager, codeManager)
solidityProxy.reset(compilationResult)
var debuggerEvent = new EventManager()
var callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true })
callTree.event.register('callTreeBuildFailed', (error) => {
st.fail(error)
})
callTree.event.register('callTreeReady', (scopes, scopeStarts) => {
helper.decodeLocals(st, 72, traceManager, callTree, function (locals) {
try {
st.equals(locals['dynbytes'].value, '0x64796e616d69636279746573')
st.equals(locals['smallstring'].value, 'test_test_test')
st.equals(Object.keys(locals).length, 2)
} catch (e) {
st.fail(e.message)
}
})
helper.decodeLocals(st, 7, traceManager, callTree, function (locals) {
try {
st.equals(Object.keys(locals).length, 2)
} catch (e) {
st.fail(e.message)
}
cb()
})
})
traceManager.resolveTrace(tx, (error, result) => {
if (error) {
st.fail(error)
} else {
debuggerEvent.trigger('newTraceLoaded', [traceManager.trace])
}
})
}
})
}
})
}

@ -0,0 +1,121 @@
'use strict'
var TraceManager = require('../../../babelify-src/trace/traceManager')
var CodeManager = require('../../../babelify-src/code/codeManager')
var vmSendTx = require('./vmCall')
var traceHelper = require('../../../babelify-src/helpers/traceHelper')
var util = require('../../../babelify-src/helpers/global')
var SolidityProxy = require('../../../babelify-src/solidity/solidityProxy')
var InternalCallTree = require('../../../babelify-src/util/internalCallTree')
var EventManager = require('../../../babelify-src/lib/eventManager')
var helper = require('./helper')
module.exports = function (st, vm, privateKey, contractBytecode, compilationResult, cb) {
vmSendTx(vm, {nonce: 0, privateKey: privateKey}, null, 0, contractBytecode, function (error, txHash) {
if (error) {
st.fail(error)
} else {
util.web3.getTransaction(txHash, function (error, tx) {
if (error) {
st.fail(error)
} else {
tx.to = traceHelper.contractCreationToken('0')
var traceManager = new TraceManager()
var codeManager = new CodeManager(traceManager)
codeManager.clear()
var solidityProxy = new SolidityProxy(traceManager, codeManager)
solidityProxy.reset(compilationResult)
var debuggerEvent = new EventManager()
var callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalVariables: true })
callTree.event.register('callTreeBuildFailed', (error) => {
st.fail(error)
})
callTree.event.register('callTreeReady', (scopes, scopeStarts) => {
helper.decodeLocals(st, 1814, traceManager, callTree, function (locals) {
try {
st.equals(locals['bytesSimple'].length, '0x14')
st.equals(locals['bytesSimple'].value, '0x746573745f7375706572')
st.equals(locals['e']['a'].value, 'test')
st.equals(locals['e']['a'].length, '0x8')
st.equals(locals['e']['a'].raw, '0x74657374')
st.equals(locals['e']['b'], '5')
st.equals(locals['e']['c'].length, '0x220')
st.equals(locals['e']['c'].raw, '0x746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374')
st.equals(locals['e']['c'].value, 'test_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_test')
st.equals(locals['e']['d'], '3')
st.equals(locals['f'].length, '0x1b8')
st.equals(locals['f'].raw, '0x746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f')
st.equals(locals['f'].value, 'test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_test_long_')
st.equals(locals['e']['e'], true)
st.equals(locals['simpleArray'][0], '45')
st.equals(locals['simpleArray'][1], '324324')
st.equals(locals['simpleArray'][2], '-333')
st.equals(locals['simpleArray'][3], '5656')
st.equals(locals['simpleArray'][4], '-1111')
st.equals(locals['stringArray'][0].value, 'long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_long_one_')
st.equals(locals['stringArray'][1].value, 'two')
st.equals(locals['stringArray'][2].value, 'three')
st.equals(locals['dynArray'][0][0], '3423423532')
st.equals(locals['dynArray'][1][0], '-342343323532')
st.equals(locals['dynArray'][1][1], '23432')
st.equals(locals['dynArray'][2][0], '-432432')
st.equals(locals['dynArray'][2][1], '3423423532')
st.equals(locals['dynArray'][2][2], '-432432')
st.equals(locals['structArray'][0]['a'].value, 'test')
st.equals(locals['structArray'][0]['a'].length, '0x8')
st.equals(locals['structArray'][0]['a'].raw, '0x74657374')
st.equals(locals['structArray'][0]['b'], '5')
st.equals(locals['structArray'][0]['c'].length, '0x220')
st.equals(locals['structArray'][0]['c'].raw, '0x746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374746573745f6c6f6e675f746573745f6c6f6e675f746573745f6c6f6e675f74657374')
st.equals(locals['structArray'][0]['c'].value, 'test_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_testtest_long_test_long_test_long_test')
st.equals(locals['structArray'][0]['d'], '3')
st.equals(locals['structArray'][0]['e'], true)
st.equals(locals['structArray'][1]['a'].value, 'item1 a')
st.equals(locals['structArray'][1]['b'], '20')
st.equals(locals['structArray'][1]['c'].value, 'item1 c')
st.equals(locals['structArray'][1]['d'], '-45')
st.equals(locals['structArray'][1]['e'], false)
st.equals(locals['structArray'][2]['a'].value, 'item2 a')
st.equals(locals['structArray'][2]['b'], '200')
st.equals(locals['structArray'][2]['c'].value, 'item2 c')
st.equals(locals['structArray'][2]['d'], '-450')
st.equals(locals['structArray'][2]['e'], true)
st.equals(locals['arrayStruct'].a[0].value, 'string')
st.equals(locals['arrayStruct'].b[0], '34')
st.equals(locals['arrayStruct'].b[1], '-23')
st.equals(locals['arrayStruct'].b[2], '-3')
st.equals(locals['arrayStruct'].c, 'three')
st.equals(Object.keys(locals).length, 8)
} catch (e) {
st.fail(e.message)
}
})
helper.decodeLocals(st, 7, traceManager, callTree, function (locals) {
try {
st.equals(Object.keys(locals).length, 8)
} catch (e) {
st.fail(e.message)
}
cb()
})
})
traceManager.resolveTrace(tx, (error, result) => {
if (error) {
st.fail(error)
} else {
debuggerEvent.trigger('newTraceLoaded', [traceManager.trace])
}
})
}
})
}
})
}

@ -0,0 +1,30 @@
'use strict'
var utileth = require('ethereumjs-util')
var Tx = require('ethereumjs-tx')
var Block = require('ethereumjs-block')
var BN = require('ethereumjs-util').BN
function sendTx (vm, from, to, value, data, cb) {
var tx = new Tx({
nonce: new BN(from.nonce++),
gasPrice: new BN(1),
gasLimit: new BN(3000000, 10),
to: to,
value: new BN(value, 10),
data: new Buffer(data, 'hex')
})
tx.sign(from.privateKey)
var block = new Block({
header: {
timestamp: new Date().getTime() / 1000 | 0,
number: 0
},
transactions: [],
uncleHeaders: []
})
vm.runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, function (error, result) {
cb(error, utileth.bufferToHex(tx.hash()))
})
}
module.exports = sendTx

@ -8,43 +8,43 @@ tape('solidity', function (t) {
t.test('storage location', function (st) {
var output = compiler.compile(contracts, 0)
var stateDec = index.solidity.stateDecoder.extractStateVariables('contractUint', output.sources)
checkLocation(st, stateDec[0].location, 0, 0)
checkLocation(st, stateDec[1].location, 1, 0)
checkLocation(st, stateDec[2].location, 2, 0)
checkLocation(st, stateDec[3].location, 3, 0)
checkLocation(st, stateDec[0].storagelocation, 0, 0)
checkLocation(st, stateDec[1].storagelocation, 1, 0)
checkLocation(st, stateDec[2].storagelocation, 2, 0)
checkLocation(st, stateDec[3].storagelocation, 3, 0)
stateDec = index.solidity.stateDecoder.extractStateVariables('contractStructAndArray', output.sources)
checkLocation(st, stateDec[0].location, 0, 0)
checkLocation(st, stateDec[1].location, 2, 0)
checkLocation(st, stateDec[2].location, 8, 0)
checkLocation(st, stateDec[0].storagelocation, 0, 0)
checkLocation(st, stateDec[1].storagelocation, 2, 0)
checkLocation(st, stateDec[2].storagelocation, 8, 0)
stateDec = index.solidity.stateDecoder.extractStateVariables('contractArray', output.sources)
checkLocation(st, stateDec[0].location, 0, 0)
checkLocation(st, stateDec[1].location, 1, 0)
checkLocation(st, stateDec[2].location, 2, 0)
checkLocation(st, stateDec[0].storagelocation, 0, 0)
checkLocation(st, stateDec[1].storagelocation, 1, 0)
checkLocation(st, stateDec[2].storagelocation, 2, 0)
stateDec = index.solidity.stateDecoder.extractStateVariables('contractSmallVariable', output.sources)
checkLocation(st, stateDec[0].location, 0, 0)
checkLocation(st, stateDec[1].location, 0, 1)
checkLocation(st, stateDec[2].location, 0, 2)
checkLocation(st, stateDec[3].location, 0, 4)
checkLocation(st, stateDec[4].location, 1, 0)
checkLocation(st, stateDec[5].location, 2, 0)
checkLocation(st, stateDec[0].storagelocation, 0, 0)
checkLocation(st, stateDec[1].storagelocation, 0, 1)
checkLocation(st, stateDec[2].storagelocation, 0, 2)
checkLocation(st, stateDec[3].storagelocation, 0, 4)
checkLocation(st, stateDec[4].storagelocation, 1, 0)
checkLocation(st, stateDec[5].storagelocation, 2, 0)
stateDec = index.solidity.stateDecoder.extractStateVariables('testSimpleStorage', output.sources)
checkLocation(st, stateDec[0].location, 0, 0)
checkLocation(st, stateDec[1].location, 1, 0)
checkLocation(st, stateDec[2].location, 2, 0)
checkLocation(st, stateDec[3].location, 3, 0)
checkLocation(st, stateDec[4].location, 4, 0)
checkLocation(st, stateDec[5].location, 8, 0)
checkLocation(st, stateDec[6].location, 9, 0)
checkLocation(st, stateDec[8].location, 17, 0)
checkLocation(st, stateDec[9].location, 17, 4)
checkLocation(st, stateDec[10].location, 17, 6)
checkLocation(st, stateDec[11].location, 17, 7)
checkLocation(st, stateDec[12].location, 18, 0)
checkLocation(st, stateDec[13].location, 21, 0)
checkLocation(st, stateDec[0].storagelocation, 0, 0)
checkLocation(st, stateDec[1].storagelocation, 1, 0)
checkLocation(st, stateDec[2].storagelocation, 2, 0)
checkLocation(st, stateDec[3].storagelocation, 3, 0)
checkLocation(st, stateDec[4].storagelocation, 4, 0)
checkLocation(st, stateDec[5].storagelocation, 8, 0)
checkLocation(st, stateDec[6].storagelocation, 9, 0)
checkLocation(st, stateDec[8].storagelocation, 17, 0)
checkLocation(st, stateDec[9].storagelocation, 17, 4)
checkLocation(st, stateDec[10].storagelocation, 17, 6)
checkLocation(st, stateDec[11].storagelocation, 17, 7)
checkLocation(st, stateDec[12].storagelocation, 18, 0)
checkLocation(st, stateDec[13].storagelocation, 21, 0)
st.end()
})

Loading…
Cancel
Save