From 350786a35e9eab8ed5edd229807655151226686e Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 12 Dec 2016 15:32:39 +0100 Subject: [PATCH 01/21] decode int, uint, address --- src/solidity/types/Address.js | 8 ++++++++ src/solidity/types/Int.js | 8 ++++++++ src/solidity/types/Uint.js | 8 ++++++++ src/solidity/types/util.js | 6 ++++++ 4 files changed, 30 insertions(+) diff --git a/src/solidity/types/Address.js b/src/solidity/types/Address.js index 9ff6d84cf6..3492010489 100644 --- a/src/solidity/types/Address.js +++ b/src/solidity/types/Address.js @@ -12,4 +12,12 @@ Address.prototype.decodeFromStorage = function (location, storageContent) { return '0x' + value.toUpperCase() } +Address.prototype.decodeLocals = function (stackHeight, stack, memory) { + if (stack.length < stackHeight) { + return '0x0000000000000000000000000000000000000000' + } else { + return '0x' + util.extractHexByteSlice(stack[stack.length - 1 - stackHeight], this.storageBytes, 0) + } +} + module.exports = Address diff --git a/src/solidity/types/Int.js b/src/solidity/types/Int.js index feee4f0fc8..3d4c1e6a09 100644 --- a/src/solidity/types/Int.js +++ b/src/solidity/types/Int.js @@ -11,4 +11,12 @@ Int.prototype.decodeFromStorage = function (location, storageContent) { return util.decodeInt(location, storageContent, this.storageBytes, true) } +Int.prototype.decodeLocals = function (stackHeight, stack, memory) { + if (stack.length < stackHeight) { + return '0' + } else { + return util.decodeIntFromHex(stack[stack.length - 1 - stackHeight].replace('0x', ''), 32, true) + } +} + module.exports = Int diff --git a/src/solidity/types/Uint.js b/src/solidity/types/Uint.js index eef4d5f302..95fa2880a2 100644 --- a/src/solidity/types/Uint.js +++ b/src/solidity/types/Uint.js @@ -11,4 +11,12 @@ Uint.prototype.decodeFromStorage = function (location, storageContent) { return util.decodeInt(location, storageContent, this.storageBytes, false) } +Uint.prototype.decodeLocals = function (stackHeight, stack, memory) { + if (stack.length < stackHeight) { + return '0' + } else { + return util.decodeIntFromHex(stack[stack.length - 1 - stackHeight].replace('0x', ''), this.storageBytes, false) + } +} + module.exports = Uint diff --git a/src/solidity/types/util.js b/src/solidity/types/util.js index 518e75c7da..aea200d5ed 100644 --- a/src/solidity/types/util.js +++ b/src/solidity/types/util.js @@ -5,7 +5,9 @@ var BN = require('ethereumjs-util').BN module.exports = { readFromStorage: readFromStorage, decodeInt: decodeInt, + decodeIntFromHex: decodeIntFromHex, extractHexValue: extractHexValue, + extractHexByteSlice: extractHexByteSlice, sha3: sha3, toBN: toBN, add: add @@ -14,6 +16,10 @@ module.exports = { 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) From 367a6d508ac4f26e3b55ce26af8341d9b9b8904a Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 12 Dec 2016 15:33:07 +0100 Subject: [PATCH 02/21] reorganise and add tests --- test/solidity/contracts/addressLocal.js | 10 ++ test/solidity/contracts/intLocal.js | 2 +- test/solidity/localDecoder.js | 127 ++---------------------- test/solidity/localsTests/address.js | 60 +++++++++++ test/solidity/localsTests/helper.js | 24 +++++ test/solidity/localsTests/int.js | 98 ++++++++++++++++++ test/solidity/localsTests/vmCall.js | 30 ++++++ 7 files changed, 233 insertions(+), 118 deletions(-) create mode 100644 test/solidity/contracts/addressLocal.js create mode 100644 test/solidity/localsTests/address.js create mode 100644 test/solidity/localsTests/helper.js create mode 100644 test/solidity/localsTests/int.js create mode 100644 test/solidity/localsTests/vmCall.js diff --git a/test/solidity/contracts/addressLocal.js b/test/solidity/contracts/addressLocal.js new file mode 100644 index 0000000000..e67de7ee8c --- /dev/null +++ b/test/solidity/contracts/addressLocal.js @@ -0,0 +1,10 @@ +'use strict' + +module.exports = { + contract: ` +contract addressLocal { + function addressLocal () { + address sender = msg.sender; + } + } +`} diff --git a/test/solidity/contracts/intLocal.js b/test/solidity/contracts/intLocal.js index 5fab1e92b3..1bfaf7c5ec 100644 --- a/test/solidity/contracts/intLocal.js +++ b/test/solidity/contracts/intLocal.js @@ -2,7 +2,7 @@ module.exports = { contract: ` - contract proxy { +contract proxy { struct testStruct { int one; uint two; diff --git a/test/solidity/localDecoder.js b/test/solidity/localDecoder.js index b5d87f62d8..77441820b6 100644 --- a/test/solidity/localDecoder.js +++ b/test/solidity/localDecoder.js @@ -2,116 +2,24 @@ var tape = require('tape') var compiler = require('solc') var intLocal = require('./contracts/intLocal') -var TraceManager = require('../../src/trace/traceManager') -var CodeManager = require('../../src/code/codeManager') +var addressLocal = require('./contracts/addressLocal') 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('../../src/web3Provider/web3Providers') -var traceHelper = require('../../src/helpers/traceHelper') var util = require('../../src/helpers/global') -var SolidityProxy = require('../../src/solidity/solidityProxy') -var InternalCallTree = require('../../src/util/internalCallTree') -var EventManager = require('../../src/lib/eventManager') -var localDecoder = require('../../src/solidity/localDecoder') +var intLocalTest = require('./localsTests/int') +var addressLocalTest = require('./localsTests/address') +// var sync = require('sync') 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, { includeLocalsVariables: true }) - 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 */ @@ -136,25 +44,10 @@ 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(addressLocal.contract, 0) + addressLocalTest(st, vm, privateKey, output.contracts['addressLocal'].bytecode, output, function () {}) }) } diff --git a/test/solidity/localsTests/address.js b/test/solidity/localsTests/address.js new file mode 100644 index 0000000000..91a54365a2 --- /dev/null +++ b/test/solidity/localsTests/address.js @@ -0,0 +1,60 @@ +'use strict' +var TraceManager = require('../../../src/trace/traceManager') +var CodeManager = require('../../../src/code/codeManager') +var vmSendTx = require('./vmCall') +var traceHelper = require('../../../src/helpers/traceHelper') +var util = require('../../../src/helpers/global') +var SolidityProxy = require('../../../src/solidity/solidityProxy') +var InternalCallTree = require('../../../src/util/internalCallTree') +var EventManager = require('../../../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, { includeLocalsVariables: true }) + callTree.event.register('callTreeReady', (scopes, scopeStarts) => { + helper.decodeLocals(st, 10, traceManager, callTree, function (locals) { + try { + st.equals(locals['sender'], '0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db') + st.equals(Object.keys(locals).length, 1) + } catch (e) { + st.fail(e.message) + } + }) + + helper.decodeLocals(st, 4, traceManager, callTree, function (locals) { + try { + st.equals(locals['sender'], '0x0000000000000000000000000000000000000000') + 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]) + } + }) + } + }) + } + }) +} diff --git a/test/solidity/localsTests/helper.js b/test/solidity/localsTests/helper.js new file mode 100644 index 0000000000..dcd24a7b99 --- /dev/null +++ b/test/solidity/localsTests/helper.js @@ -0,0 +1,24 @@ +'use strict' +var localDecoder = require('../../../src/solidity/localDecoder') + +/* + 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) + } + }) +} + +module.exports = { + decodeLocals: decodeLocal +} diff --git a/test/solidity/localsTests/int.js b/test/solidity/localsTests/int.js new file mode 100644 index 0000000000..969040dc9b --- /dev/null +++ b/test/solidity/localsTests/int.js @@ -0,0 +1,98 @@ +'use strict' +var TraceManager = require('../../../src/trace/traceManager') +var CodeManager = require('../../../src/code/codeManager') +var vmSendTx = require('./vmCall') +var traceHelper = require('../../../src/helpers/traceHelper') +var util = require('../../../src/helpers/global') +var SolidityProxy = require('../../../src/solidity/solidityProxy') +var InternalCallTree = require('../../../src/util/internalCallTree') +var EventManager = require('../../../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, { includeLocalsVariables: true }) + 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') + + 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]) + } + }) + } + }) + } + }) +} + diff --git a/test/solidity/localsTests/vmCall.js b/test/solidity/localsTests/vmCall.js new file mode 100644 index 0000000000..6f9fb83f06 --- /dev/null +++ b/test/solidity/localsTests/vmCall.js @@ -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 From 4761e967264fd804b2f96413440a2598a8e72c39 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 12 Dec 2016 16:39:02 +0100 Subject: [PATCH 03/21] fix decode add, int, uint --- src/solidity/types/Address.js | 2 +- src/solidity/types/Int.js | 2 +- src/solidity/types/Uint.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/solidity/types/Address.js b/src/solidity/types/Address.js index 3492010489..edea15e5f4 100644 --- a/src/solidity/types/Address.js +++ b/src/solidity/types/Address.js @@ -13,7 +13,7 @@ Address.prototype.decodeFromStorage = function (location, storageContent) { } Address.prototype.decodeLocals = function (stackHeight, stack, memory) { - if (stack.length < stackHeight) { + if (stack.length - 1 < stackHeight) { return '0x0000000000000000000000000000000000000000' } else { return '0x' + util.extractHexByteSlice(stack[stack.length - 1 - stackHeight], this.storageBytes, 0) diff --git a/src/solidity/types/Int.js b/src/solidity/types/Int.js index 3d4c1e6a09..36839868ee 100644 --- a/src/solidity/types/Int.js +++ b/src/solidity/types/Int.js @@ -12,7 +12,7 @@ Int.prototype.decodeFromStorage = function (location, storageContent) { } Int.prototype.decodeLocals = function (stackHeight, stack, memory) { - if (stack.length < stackHeight) { + if (stack.length - 1 < stackHeight) { return '0' } else { return util.decodeIntFromHex(stack[stack.length - 1 - stackHeight].replace('0x', ''), 32, true) diff --git a/src/solidity/types/Uint.js b/src/solidity/types/Uint.js index 95fa2880a2..c5d65bf6a7 100644 --- a/src/solidity/types/Uint.js +++ b/src/solidity/types/Uint.js @@ -12,7 +12,7 @@ Uint.prototype.decodeFromStorage = function (location, storageContent) { } Uint.prototype.decodeLocals = function (stackHeight, stack, memory) { - if (stack.length < stackHeight) { + if (stack.length - 1 < stackHeight) { return '0' } else { return util.decodeIntFromHex(stack[stack.length - 1 - stackHeight].replace('0x', ''), this.storageBytes, false) From 8798eeef8fd7d4325bdafa408c6b32e5e173f134 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 12 Dec 2016 16:39:13 +0100 Subject: [PATCH 04/21] decode bool --- src/solidity/types/Bool.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/solidity/types/Bool.js b/src/solidity/types/Bool.js index 6889b909da..5df3e29fa2 100644 --- a/src/solidity/types/Bool.js +++ b/src/solidity/types/Bool.js @@ -12,4 +12,12 @@ Bool.prototype.decodeFromStorage = function (location, storageContent) { return value !== '00' } +Bool.prototype.decodeLocals = function (stackHeight, stack, memory) { + if (stack.length - 1 < stackHeight) { + return false + } else { + return util.extractHexByteSlice(stack[stack.length - 1 - stackHeight], this.storageBytes, 0) !== '00' + } +} + module.exports = Bool From f574ee65de9b45295e3aec7e6afe10ee9bed3a93 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 12 Dec 2016 16:39:26 +0100 Subject: [PATCH 05/21] decode enum --- src/solidity/types/Enum.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/solidity/types/Enum.js b/src/solidity/types/Enum.js index e8aca6a9cd..f8950209b5 100644 --- a/src/solidity/types/Enum.js +++ b/src/solidity/types/Enum.js @@ -16,8 +16,23 @@ function Enum (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 + return output(value, this.enumDef) +} + +Enum.prototype.decodeLocals = function (stackHeight, stack, memory) { + var defaultValue = 0 + if (stack.length - 1 < stackHeight) { + defaultValue = 0 + } else { + defaultValue = util.extractHexByteSlice(stack[stack.length - 1 - stackHeight], this.storageBytes, 0) + defaultValue = parseInt(defaultValue, 16) + } + return output(defaultValue, this.enumDef) +} + +function output (value, enumDef) { + if (enumDef.children.length > value) { + return enumDef.children[value].attributes.name } else { return 'INVALID_ENUM<' + value + '>' } From 2e3d94f370b8650824db6bf2a0f54eba8a5477d0 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 12 Dec 2016 16:39:43 +0100 Subject: [PATCH 06/21] decode fixedbytearray --- src/solidity/types/FixedByteArray.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/solidity/types/FixedByteArray.js b/src/solidity/types/FixedByteArray.js index 36c9cdd7d1..434dc1852f 100644 --- a/src/solidity/types/FixedByteArray.js +++ b/src/solidity/types/FixedByteArray.js @@ -12,4 +12,13 @@ FixedByteArray.prototype.decodeFromStorage = function (location, storageContent) return '0x' + value.toUpperCase() } +FixedByteArray.prototype.decodeLocals = function (stackHeight, stack, memory) { + if (stack.length - 1 < stackHeight) { + return '0x' + } else { + var value = stack[stack.length - 1 - stackHeight] + return '0x' + value.substr(2, 2 * this.storageBytes).toUpperCase() // util.extractHexByteSlice(stack[stack.length - 1 - stackHeight], this.storageBytes, this.storageBytes) + } +} + module.exports = FixedByteArray From 72ab547de5bc63bec764b7f6371588d291869d16 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 12 Dec 2016 16:40:06 +0100 Subject: [PATCH 07/21] fix and add tests --- test/solidity/contracts/addressLocal.js | 10 ----- test/solidity/contracts/miscLocal.js | 32 ++++++++++++++++ test/solidity/localDecoder.js | 9 ++--- .../localsTests/{address.js => misc.js} | 38 +++++++++++++++++-- 4 files changed, 70 insertions(+), 19 deletions(-) delete mode 100644 test/solidity/contracts/addressLocal.js create mode 100644 test/solidity/contracts/miscLocal.js rename test/solidity/localsTests/{address.js => misc.js} (51%) diff --git a/test/solidity/contracts/addressLocal.js b/test/solidity/contracts/addressLocal.js deleted file mode 100644 index e67de7ee8c..0000000000 --- a/test/solidity/contracts/addressLocal.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict' - -module.exports = { - contract: ` -contract addressLocal { - function addressLocal () { - address sender = msg.sender; - } - } -`} diff --git a/test/solidity/contracts/miscLocal.js b/test/solidity/contracts/miscLocal.js new file mode 100644 index 0000000000..a53fd226a3 --- /dev/null +++ b/test/solidity/contracts/miscLocal.js @@ -0,0 +1,32 @@ +'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"; + } + } +`} diff --git a/test/solidity/localDecoder.js b/test/solidity/localDecoder.js index 77441820b6..6950b08aa9 100644 --- a/test/solidity/localDecoder.js +++ b/test/solidity/localDecoder.js @@ -2,14 +2,13 @@ var tape = require('tape') var compiler = require('solc') var intLocal = require('./contracts/intLocal') -var addressLocal = require('./contracts/addressLocal') +var miscLocal = require('./contracts/miscLocal') var VM = require('ethereumjs-vm') var utileth = require('ethereumjs-util') var Web3Providers = require('../../src/web3Provider/web3Providers') var util = require('../../src/helpers/global') var intLocalTest = require('./localsTests/int') -var addressLocalTest = require('./localsTests/address') -// var sync = require('sync') +var miscLocalTest = require('./localsTests/misc') tape('solidity', function (t) { t.test('local decoder', function (st) { @@ -47,7 +46,7 @@ function initVM (st, address) { 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(addressLocal.contract, 0) - addressLocalTest(st, vm, privateKey, output.contracts['addressLocal'].bytecode, output, function () {}) + output = compiler.compile(miscLocal.contract, 0) + miscLocalTest(st, vm, privateKey, output.contracts['miscLocal'].bytecode, output, function () {}) }) } diff --git a/test/solidity/localsTests/address.js b/test/solidity/localsTests/misc.js similarity index 51% rename from test/solidity/localsTests/address.js rename to test/solidity/localsTests/misc.js index 91a54365a2..20f042e4bb 100644 --- a/test/solidity/localsTests/address.js +++ b/test/solidity/localsTests/misc.js @@ -27,19 +27,49 @@ module.exports = function (st, vm, privateKey, contractBytecode, compilationResu var debuggerEvent = new EventManager() var callTree = new InternalCallTree(debuggerEvent, traceManager, solidityProxy, codeManager, { includeLocalsVariables: true }) callTree.event.register('callTreeReady', (scopes, scopeStarts) => { - helper.decodeLocals(st, 10, traceManager, callTree, function (locals) { + 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(Object.keys(locals).length, 1) + 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, 4, traceManager, callTree, function (locals) { + 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(Object.keys(locals).length, 1) + 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) } From b1937883fd7e5b032438a16cbb657a97edc3a47a Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 13 Dec 2016 10:41:03 +0100 Subject: [PATCH 08/21] format memory before decoding --- src/solidity/localDecoder.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/solidity/localDecoder.js b/src/solidity/localDecoder.js index 6d91d1f9ca..66713a18b4 100644 --- a/src/solidity/localDecoder.js +++ b/src/solidity/localDecoder.js @@ -6,6 +6,7 @@ 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) { @@ -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 } From fbe4b47a132aeb2f2ed6bbb177b61cf08be669ad Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 13 Dec 2016 10:41:30 +0100 Subject: [PATCH 09/21] dyn byte array decoding --- src/solidity/types/DynamicByteArray.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/solidity/types/DynamicByteArray.js b/src/solidity/types/DynamicByteArray.js index c09b41b189..4441e9f7bc 100644 --- a/src/solidity/types/DynamicByteArray.js +++ b/src/solidity/types/DynamicByteArray.js @@ -35,4 +35,22 @@ DynamicByteArray.prototype.decodeFromStorage = function (location, storageConten } } +DynamicByteArray.prototype.decodeLocals = function (stackHeight, stack, memory) { + if (stack.length - 1 < stackHeight) { + return { + value: '0x', + length: '0x' + } + } else { + var offset = stack[stack.length - 1 - stackHeight] + offset = 2 * parseInt(offset, 16) + var length = memory.substr(offset, 64) + length = parseInt(length, 16) + return { + length: 2 * length, + value: '0x' + memory.substr(offset + 64, 2 * length) + } + } +} + module.exports = DynamicByteArray From c08508a0bb1ce6e80948d02461270198716c1275 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 13 Dec 2016 10:41:48 +0100 Subject: [PATCH 10/21] string decoding --- src/solidity/types/StringType.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/solidity/types/StringType.js b/src/solidity/types/StringType.js index b914056d5b..3ce6d3a982 100644 --- a/src/solidity/types/StringType.js +++ b/src/solidity/types/StringType.js @@ -10,6 +10,15 @@ function StringType () { StringType.prototype.decodeFromStorage = function (location, storageContent) { var decoded = this.dynamicBytes.decodeFromStorage(location, storageContent) + return format(decoded) +} + +StringType.prototype.decodeLocals = function (stackHeight, stack, memory) { + var decoded = this.dynamicBytes.decodeLocals(stackHeight, stack, memory) + return format(decoded) +} + +function format (decoded) { var value = decoded.value value = value.replace('0x', '').replace(/(..)/g, '%$1') var ret = { From 6b29bbbbb563a72877ea05641634d40ec119fae4 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 13 Dec 2016 10:42:05 +0100 Subject: [PATCH 11/21] Fix for browser solidity --- src/ui/Ethdebugger.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/Ethdebugger.js b/src/ui/Ethdebugger.js index 6bfa55b629..9c92ba2913 100644 --- a/src/ui/Ethdebugger.js +++ b/src/ui/Ethdebugger.js @@ -31,6 +31,7 @@ function Ethdebugger () { this.solidityProxy = new SolidityProxy(this.traceManager, this.codeManager) var callTree = new InternalCallTree(this.event, this.traceManager, this.solidityProxy, this.codeManager, { includeLocalsVariables: true }) + 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) From dcdf20d9884f2bb0b55c2d23b31cf5f10267a4ae Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 13 Dec 2016 10:42:57 +0100 Subject: [PATCH 12/21] fix internal call tree --- src/util/internalCallTree.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/util/internalCallTree.js b/src/util/internalCallTree.js index d238308306..7a45b7769b 100644 --- a/src/util/internalCallTree.js +++ b/src/util/internalCallTree.js @@ -55,7 +55,7 @@ class InternalCallTree { function buildTree (tree, step, scopeId, trace) { let subScope = 1 tree.scopeStarts[step] = scopeId - tree.scopes[scopeId] = { firstStep: step } + tree.scopes[scopeId] = { firstStep: step, locals: {} } while (step < trace.length) { var sourceLocation extractSourceLocation(tree, step, (error, src) => { @@ -74,10 +74,7 @@ function buildTree (tree, step, scopeId, trace) { } else { if (tree.includeLocalsVariables) { var variableDeclaration = resolveVariableDeclaration(tree, step, sourceLocation) - if (variableDeclaration) { - if (!tree.scopes[scopeId].locals) { - tree.scopes[scopeId].locals = {} - } + if (variableDeclaration && !tree.scopes[scopeId].locals[variableDeclaration.attributes.name]) { tree.traceManager.getStackAt(step, (error, stack) => { if (!error) { tree.solidityProxy.contractNameAt(step, (error, contractName) => { // cached From 80fcf8af139594ed830e682fbfeba02e6d191ea5 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 13 Dec 2016 10:43:08 +0100 Subject: [PATCH 13/21] tests --- test/solidity/contracts/miscLocal.js | 7 ++++ test/solidity/localDecoder.js | 6 ++- test/solidity/localsTests/misc2.js | 60 ++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 test/solidity/localsTests/misc2.js diff --git a/test/solidity/contracts/miscLocal.js b/test/solidity/contracts/miscLocal.js index a53fd226a3..750b8e63e6 100644 --- a/test/solidity/contracts/miscLocal.js +++ b/test/solidity/contracts/miscLocal.js @@ -29,4 +29,11 @@ contract miscLocal { bytes32 __bytes32 = hex"9999ABD41799ABD417"; } } + + contract miscLocal2 { + function miscLocal2 () { + bytes memory dynbytes = "dynamicbytes"; + string memory smallstring = "test_test_test"; + } + } `} diff --git a/test/solidity/localDecoder.js b/test/solidity/localDecoder.js index 6950b08aa9..24a5aa1ce7 100644 --- a/test/solidity/localDecoder.js +++ b/test/solidity/localDecoder.js @@ -9,6 +9,7 @@ var Web3Providers = require('../../src/web3Provider/web3Providers') var util = require('../../src/helpers/global') var intLocalTest = require('./localsTests/int') var miscLocalTest = require('./localsTests/misc') +var misc2LocalTest = require('./localsTests/misc2') tape('solidity', function (t) { t.test('local decoder', function (st) { @@ -47,6 +48,9 @@ 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 () {}) + 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 () {}) + }) }) } diff --git a/test/solidity/localsTests/misc2.js b/test/solidity/localsTests/misc2.js new file mode 100644 index 0000000000..aa2d7f9756 --- /dev/null +++ b/test/solidity/localsTests/misc2.js @@ -0,0 +1,60 @@ +'use strict' +var TraceManager = require('../../../src/trace/traceManager') +var CodeManager = require('../../../src/code/codeManager') +var vmSendTx = require('./vmCall') +var traceHelper = require('../../../src/helpers/traceHelper') +var util = require('../../../src/helpers/global') +var SolidityProxy = require('../../../src/solidity/solidityProxy') +var InternalCallTree = require('../../../src/util/internalCallTree') +var EventManager = require('../../../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, { includeLocalsVariables: true }) + callTree.event.register('callTreeReady', (scopes, scopeStarts) => { + helper.decodeLocals(st, 82, 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]) + } + }) + } + }) + } + }) +} From d4daac1fc16306ab93c2292695801697052f87ea Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 14 Dec 2016 13:22:29 +0100 Subject: [PATCH 14/21] decodeFromMemory Address, Bool --- src/solidity/types/Address.js | 6 ++++++ src/solidity/types/Bool.js | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/src/solidity/types/Address.js b/src/solidity/types/Address.js index edea15e5f4..cf657a81fd 100644 --- a/src/solidity/types/Address.js +++ b/src/solidity/types/Address.js @@ -20,4 +20,10 @@ Address.prototype.decodeLocals = function (stackHeight, stack, memory) { } } +Address.prototype.decodeFromMemory = function (offset, memory) { + var value = memory.substr(offset, 64) + value = util.extractHexByteSlice(value, this.storageBytes, 0) + return value +} + module.exports = Address diff --git a/src/solidity/types/Bool.js b/src/solidity/types/Bool.js index 5df3e29fa2..0bf57f7272 100644 --- a/src/solidity/types/Bool.js +++ b/src/solidity/types/Bool.js @@ -20,4 +20,9 @@ Bool.prototype.decodeLocals = function (stackHeight, stack, memory) { } } +Bool.prototype.decodeFromMemory = function (offset, memory) { + var value = memory.substr(offset, 64) + return util.extractHexByteSlice(value, this.storageBytes, 0) !== '00' +} + module.exports = Bool From b2bfec66b8ff683125e8c5084ec2d943db7d4d12 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 14 Dec 2016 13:22:54 +0100 Subject: [PATCH 15/21] decodeFromMemory enum, int, uint --- src/solidity/types/Enum.js | 7 +++++++ src/solidity/types/Int.js | 5 +++++ src/solidity/types/Uint.js | 5 +++++ 3 files changed, 17 insertions(+) diff --git a/src/solidity/types/Enum.js b/src/solidity/types/Enum.js index f8950209b5..b2aad9e05e 100644 --- a/src/solidity/types/Enum.js +++ b/src/solidity/types/Enum.js @@ -30,6 +30,13 @@ Enum.prototype.decodeLocals = function (stackHeight, stack, memory) { return output(defaultValue, this.enumDef) } +Enum.prototype.decodeFromMemory = function (offset, memory) { + var value = memory.substr(offset, 64) + value = util.extractHexByteSlice(value, this.storageBytes, 0) + value = parseInt(value, 16) + return output(value, this.enumDef) +} + function output (value, enumDef) { if (enumDef.children.length > value) { return enumDef.children[value].attributes.name diff --git a/src/solidity/types/Int.js b/src/solidity/types/Int.js index 36839868ee..87934a2cb6 100644 --- a/src/solidity/types/Int.js +++ b/src/solidity/types/Int.js @@ -19,4 +19,9 @@ Int.prototype.decodeLocals = function (stackHeight, stack, memory) { } } +Int.prototype.decodeFromMemory = function (offset, memory) { + var value = memory.substr(offset, 64) + return util.decodeIntFromHex(value, 32, true) +} + module.exports = Int diff --git a/src/solidity/types/Uint.js b/src/solidity/types/Uint.js index c5d65bf6a7..815b2c8e6a 100644 --- a/src/solidity/types/Uint.js +++ b/src/solidity/types/Uint.js @@ -19,4 +19,9 @@ Uint.prototype.decodeLocals = function (stackHeight, stack, memory) { } } +Uint.prototype.decodeFromMemory = function (offset, memory) { + var value = memory.substr(offset, 64) + return util.decodeIntFromHex(value, this.storageBytes, false) +} + module.exports = Uint From 3a9a7672333aff279271bd2f05a4019070de84a2 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 14 Dec 2016 13:23:21 +0100 Subject: [PATCH 16/21] decodeFromMemory String --- src/solidity/types/StringType.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/solidity/types/StringType.js b/src/solidity/types/StringType.js index 3ce6d3a982..daf217f947 100644 --- a/src/solidity/types/StringType.js +++ b/src/solidity/types/StringType.js @@ -18,6 +18,11 @@ StringType.prototype.decodeLocals = function (stackHeight, stack, memory) { return format(decoded) } +StringType.prototype.decodeFromMemory = function (offset, memory) { + var decoded = this.dynamicBytes.decodeFromMemory(offset, memory) + return format(decoded) +} + function format (decoded) { var value = decoded.value value = value.replace('0x', '').replace(/(..)/g, '%$1') From 64bdcff5e888f61e952f3f7032ccc9d4d452778d Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 14 Dec 2016 13:23:52 +0100 Subject: [PATCH 17/21] decodeLocals, decodeFromMemory Array --- src/solidity/types/ArrayType.js | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/solidity/types/ArrayType.js b/src/solidity/types/ArrayType.js index ea924f4cfb..ecd7bdfb90 100644 --- a/src/solidity/types/ArrayType.js +++ b/src/solidity/types/ArrayType.js @@ -54,4 +54,41 @@ ArrayType.prototype.decodeFromStorage = function (location, storageContent) { } } +ArrayType.prototype.decodeLocals = function (stackHeight, stack, memory) { + if (stack.length - 1 < stackHeight) { + return [] + } else { // TODO manage decoding locals from storage + var offset = stack[stack.length - 1 - stackHeight] + offset = 2 * parseInt(offset, 16) + return this.decodeFromMemory(offset, memory) + } +} + +ArrayType.prototype.decodeFromMemory = function (offset, memory) { + var ret = [] + var length = extractLength(this, offset, memory) + if (this.arraySize === 'dynamic') { + offset = offset + 64 + } + for (var k = 0; k < length; k++) { + var contentOffset = offset + if (this.underlyingType.typeName === 'bytes' || this.underlyingType.typeName === 'string' || this.underlyingType.typeName === 'array' || this.underlyingType.typeName === 'struct') { + contentOffset = memory.substr(offset, 64) + contentOffset = 2 * parseInt(contentOffset, 16) + } + ret.push(this.underlyingType.decodeFromMemory(contentOffset, memory)) + offset += 64 + } + return ret +} + +function extractLength (type, offset, memory) { + var length = type.arraySize + if (length === 'dynamic') { + length = memory.substr(offset, 64) + length = parseInt(length, 16) + } + return length +} + module.exports = ArrayType From 3a214acfad1a52a38d02505a9dfcca3ef8012280 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 14 Dec 2016 13:24:16 +0100 Subject: [PATCH 18/21] decodeFromMemory DynamicByteArray --- src/solidity/types/DynamicByteArray.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/solidity/types/DynamicByteArray.js b/src/solidity/types/DynamicByteArray.js index 4441e9f7bc..270ce25006 100644 --- a/src/solidity/types/DynamicByteArray.js +++ b/src/solidity/types/DynamicByteArray.js @@ -39,17 +39,21 @@ DynamicByteArray.prototype.decodeLocals = function (stackHeight, stack, memory) if (stack.length - 1 < stackHeight) { return { value: '0x', - length: '0x' + length: '0x0' } } else { var offset = stack[stack.length - 1 - stackHeight] offset = 2 * parseInt(offset, 16) - var length = memory.substr(offset, 64) - length = parseInt(length, 16) - return { - length: 2 * length, - value: '0x' + memory.substr(offset + 64, 2 * length) - } + return this.decodeFromMemory(offset, memory) + } +} + +DynamicByteArray.prototype.decodeFromMemory = function (offset, memory) { + var length = memory.substr(offset, 64) + length = 2 * parseInt(length, 16) + return { + length: '0x' + length.toString(16), + value: '0x' + memory.substr(offset + 64, length) } } From 790a56725084f85b904ec8ad0ccd0ff5912674c9 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 14 Dec 2016 13:24:35 +0100 Subject: [PATCH 19/21] decodeFromMemory FixedByteArray --- src/solidity/types/FixedByteArray.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/solidity/types/FixedByteArray.js b/src/solidity/types/FixedByteArray.js index 434dc1852f..275838d5c9 100644 --- a/src/solidity/types/FixedByteArray.js +++ b/src/solidity/types/FixedByteArray.js @@ -17,8 +17,13 @@ FixedByteArray.prototype.decodeLocals = function (stackHeight, stack, memory) { return '0x' } else { var value = stack[stack.length - 1 - stackHeight] - return '0x' + value.substr(2, 2 * this.storageBytes).toUpperCase() // util.extractHexByteSlice(stack[stack.length - 1 - stackHeight], this.storageBytes, this.storageBytes) + return '0x' + value.substr(2, 2 * this.storageBytes).toUpperCase() } } +FixedByteArray.prototype.decodeFromMemory = function (offset, memory) { + var value = memory.substr(offset, 64) + return util.extractHexByteSlice(value, this.storageBytes, 0).toUpperCase() +} + module.exports = FixedByteArray From aa469daeffb11fee379c7a00411a7a33887a7e88 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 14 Dec 2016 13:25:17 +0100 Subject: [PATCH 20/21] decodeLocals, decodeFromMemory Struct --- src/solidity/types/Struct.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/solidity/types/Struct.js b/src/solidity/types/Struct.js index 172d919d01..666c45a02e 100644 --- a/src/solidity/types/Struct.js +++ b/src/solidity/types/Struct.js @@ -20,4 +20,29 @@ Struct.prototype.decodeFromStorage = function (location, storageContent) { return ret } +Struct.prototype.decodeLocals = function (stackHeight, stack, memory) { + if (stack.length - 1 < stackHeight) { + return {} + } else { // TODO manage decoding locals from storage + var offset = stack[stack.length - 1 - stackHeight] + offset = 2 * parseInt(offset, 16) + return this.decodeFromMemory(offset, memory) + } +} + +Struct.prototype.decodeFromMemory = function (offset, memory) { + var ret = {} + this.members.map(function (item, i) { + var contentOffset = offset + if (item.type.typeName === 'bytes' || item.type.typeName === 'string' || item.type.typeName === 'array' || item.type.typeName === 'struct') { + contentOffset = memory.substr(offset, 64) + contentOffset = 2 * parseInt(contentOffset, 16) + } + var member = item.type.decodeFromMemory(contentOffset, memory) + ret[item.name] = member + offset += 64 + }) + return ret +} + module.exports = Struct From 00251a7bfb954fed2d2a62e3a2c5827abf91c1f9 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 14 Dec 2016 13:25:27 +0100 Subject: [PATCH 21/21] add tests --- test/solidity/contracts/structArrayLocal.js | 84 ++++++++++++++ test/solidity/localDecoder.js | 7 +- test/solidity/localsTests/structArray.js | 118 ++++++++++++++++++++ 3 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 test/solidity/contracts/structArrayLocal.js create mode 100644 test/solidity/localsTests/structArray.js diff --git a/test/solidity/contracts/structArrayLocal.js b/test/solidity/contracts/structArrayLocal.js new file mode 100644 index 0000000000..35bf48ea69 --- /dev/null +++ b/test/solidity/contracts/structArrayLocal.js @@ -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; + } +} +`} diff --git a/test/solidity/localDecoder.js b/test/solidity/localDecoder.js index 24a5aa1ce7..46dad4cb49 100644 --- a/test/solidity/localDecoder.js +++ b/test/solidity/localDecoder.js @@ -3,6 +3,7 @@ var tape = require('tape') var compiler = require('solc') var intLocal = require('./contracts/intLocal') var miscLocal = require('./contracts/miscLocal') +var structArrayLocal = require('./contracts/structArrayLocal') var VM = require('ethereumjs-vm') var utileth = require('ethereumjs-util') var Web3Providers = require('../../src/web3Provider/web3Providers') @@ -10,6 +11,7 @@ var util = require('../../src/helpers/global') 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) { @@ -50,7 +52,10 @@ function test (st, vm, privateKey) { 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 () {}) + 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 () {}) + }) }) }) } diff --git a/test/solidity/localsTests/structArray.js b/test/solidity/localsTests/structArray.js new file mode 100644 index 0000000000..0581ffc88d --- /dev/null +++ b/test/solidity/localsTests/structArray.js @@ -0,0 +1,118 @@ +'use strict' +var TraceManager = require('../../../src/trace/traceManager') +var CodeManager = require('../../../src/code/codeManager') +var vmSendTx = require('./vmCall') +var traceHelper = require('../../../src/helpers/traceHelper') +var util = require('../../../src/helpers/global') +var SolidityProxy = require('../../../src/solidity/solidityProxy') +var InternalCallTree = require('../../../src/util/internalCallTree') +var EventManager = require('../../../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, { includeLocalsVariables: true }) + callTree.event.register('callTreeReady', (scopes, scopeStarts) => { + helper.decodeLocals(st, 1899, 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]) + } + }) + } + }) + } + }) +}