From e0005aa2deb5da5d5097c8652c21c724d2598078 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 3 Jan 2017 12:49:50 +0100 Subject: [PATCH 01/28] jump out --- src/trace/traceManager.js | 4 ++++ src/trace/traceStepManager.js | 11 +++++++++++ src/ui/ButtonNavigator.js | 8 +++++++- src/ui/StepManager.js | 12 ++++++++++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/trace/traceManager.js b/src/trace/traceManager.js index 57aca56aa2..8e34b11dd7 100644 --- a/src/trace/traceManager.js +++ b/src/trace/traceManager.js @@ -271,6 +271,10 @@ TraceManager.prototype.findNextCall = function (currentStep) { return this.traceStepManager.findNextCall(currentStep) } +TraceManager.prototype.findStepOut = function (currentStep) { + return this.traceStepManager.findStepOut(currentStep) +} + // util TraceManager.prototype.checkRequestedStep = function (stepIndex) { if (!this.trace) { diff --git a/src/trace/traceStepManager.js b/src/trace/traceStepManager.js index 1a1b641126..c6b6fdaa77 100644 --- a/src/trace/traceStepManager.js +++ b/src/trace/traceStepManager.js @@ -43,4 +43,15 @@ TraceStepManager.prototype.findNextCall = function (currentStep) { } } +TraceStepManager.prototype.findStepOut = function (currentStep) { + var callChanges = this.traceAnalyser.traceCache.callChanges + var stepIndex = util.findLowerBound(currentStep, callChanges) + var call = this.traceAnalyser.traceCache.calls[callChanges[stepIndex]] + if (call && call.return) { + return call.return + } else { + return currentStep + } +} + module.exports = TraceStepManager diff --git a/src/ui/ButtonNavigator.js b/src/ui/ButtonNavigator.js index 434e46bd0e..ee3916a334 100644 --- a/src/ui/ButtonNavigator.js +++ b/src/ui/ButtonNavigator.js @@ -11,6 +11,7 @@ function ButtonNavigator (_traceManager) { this.intoForwardDisabled = true this.overForwardDisabled = true this.nextCallDisabled = true + this.jumpOutDisabled = true this.traceManager = _traceManager @@ -29,9 +30,11 @@ ButtonNavigator.prototype.render = function () { + + ` if (!this.view) { this.view = view @@ -45,11 +48,13 @@ ButtonNavigator.prototype.reset = function () { this.intoForwardDisabled = true this.overForwardDisabled = true this.nextCallDisabled = true + this.jumpOutDisabled = true } ButtonNavigator.prototype.stepChanged = function (step) { this.intoBackDisabled = step <= 0 this.overBackDisabled = step <= 0 + this.jumpOutDisabled = step <= 0 if (!this.traceManager) { this.intoForwardDisabled = true this.overForwardDisabled = true @@ -77,6 +82,7 @@ ButtonNavigator.prototype.updateAll = function () { this.updateDisabled('overforward', this.overForwardDisabled) this.updateDisabled('intoforward', this.intoForwardDisabled) this.updateDisabled('nextcall', this.nextCallDisabled) + this.updateDisabled('jumpout', this.jumpOutDisabled) } ButtonNavigator.prototype.updateDisabled = function (id, disabled) { diff --git a/src/ui/StepManager.js b/src/ui/StepManager.js index 4f16ac5e82..0c03547b72 100644 --- a/src/ui/StepManager.js +++ b/src/ui/StepManager.js @@ -42,6 +42,9 @@ function StepManager (_parent, _traceManager) { this.buttonNavigator.event.register('jumpNextCall', this, function () { self.jumpNextCall() }) + this.buttonNavigator.event.register('jumpOut', this, function () { + self.jumpOut() + }) } StepManager.prototype.render = function () { @@ -126,6 +129,15 @@ StepManager.prototype.jumpNextCall = function () { this.changeState(step) } +StepManager.prototype.jumpOut = function () { + if (!this.traceManager.isLoaded()) { + return + } + var step = this.traceManager.findStepOut(this.currentStepIndex) + this.slider.setValue(step) + this.changeState(step) +} + StepManager.prototype.changeState = function (step) { this.currentStepIndex = step this.buttonNavigator.stepChanged(step) From d3f25cf53d0cdd2ff88f1886138570d87d841b02 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 3 Jan 2017 14:02:53 +0100 Subject: [PATCH 02/28] disable button nextCall, jumpOut if applicable --- src/ui/ButtonNavigator.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ui/ButtonNavigator.js b/src/ui/ButtonNavigator.js index ee3916a334..5d2dc3284b 100644 --- a/src/ui/ButtonNavigator.js +++ b/src/ui/ButtonNavigator.js @@ -54,11 +54,9 @@ ButtonNavigator.prototype.reset = function () { ButtonNavigator.prototype.stepChanged = function (step) { this.intoBackDisabled = step <= 0 this.overBackDisabled = step <= 0 - this.jumpOutDisabled = step <= 0 if (!this.traceManager) { this.intoForwardDisabled = true this.overForwardDisabled = true - this.nextCallDisabled = true } else { var self = this this.traceManager.getLength(function (error, length) { @@ -68,7 +66,10 @@ ButtonNavigator.prototype.stepChanged = function (step) { } else { self.intoForwardDisabled = step >= length - 1 self.overForwardDisabled = step >= length - 1 - self.nextCallDisabled = step >= length - 1 + var nextCall = self.traceManager.findNextCall(step) + self.nextCallDisabled = nextCall === step + var stepOut = self.traceManager.findStepOut(step) + self.jumpOutDisabled = stepOut === step } self.updateAll() }) From 035150e4a491de50e65efb79209f930e1efb64b7 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 3 Jan 2017 18:31:27 +0100 Subject: [PATCH 03/28] calculate cost of SSTORE & store empty storage if creation --- src/web3Provider/web3VmProvider.js | 42 +++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/web3Provider/web3VmProvider.js b/src/web3Provider/web3VmProvider.js index c84d6e2fe3..9cc4bb9631 100644 --- a/src/web3Provider/web3VmProvider.js +++ b/src/web3Provider/web3VmProvider.js @@ -1,6 +1,9 @@ var util = require('../helpers/util') +var uiutil = require('../helpers/ui') var traceHelper = require('../helpers/traceHelper') var Web3 = require('web3') +var fees = require('ethereum-common') +var ethutil = require('ethereumjs-util') function web3VmProvider () { var self = this @@ -9,6 +12,8 @@ function web3VmProvider () { this.vmTraces = {} this.txs = {} this.processingHash + this.processingAddress + this.processingIndex this.incr = 0 this.eth = {} this.debug = {} @@ -43,7 +48,7 @@ web3VmProvider.prototype.releaseCurrentHash = function () { return ret } -web3VmProvider.prototype.txWillProcess = function (self, data) { +web3VmProvider.prototype.txWillProcess = function (self, data) { self.incr++ self.processingHash = util.hexConvert(data.hash()) self.vmTraces[self.processingHash] = { @@ -57,6 +62,7 @@ web3VmProvider.prototype.txWillProcess = function (self, data) { if (data.to && data.to.length) { tx.to = util.hexConvert(data.to) } + this.processingAddress = tx.to tx.data = util.hexConvert(data.data) tx.input = util.hexConvert(data.input) tx.gas = util.hexConvert(data.gas) @@ -70,6 +76,7 @@ web3VmProvider.prototype.txWillProcess = function (self, data) { self.storageCache[self.processingHash][tx.to] = storage }) } + this.processingIndex = 0 } web3VmProvider.prototype.txProcessed = function (self, data) { @@ -79,6 +86,8 @@ web3VmProvider.prototype.txProcessed = function (self, data) { } else { self.vmTraces[self.processingHash].return = util.hexConvert(data.vm.return) } + this.processingIndex = null + this.processingAddress = null } web3VmProvider.prototype.pushTrace = function (self, data) { @@ -95,15 +104,36 @@ web3VmProvider.prototype.pushTrace = function (self, data) { gasCost: data.opcode.fee.toString(), gas: data.gasLeft.toString() } + if (data.opcode.name === 'SSTORE') { + var currentStorage = this.storageCache[this.processingHash][this.processingAddress] + var key = step.stack[step.stack.length - 1] + var value = step.stack[step.stack.length - 2] + value = util.hexToIntArray(value) + value = ethutil.unpad(value) + if (value.length === 0) { + data.opcode.fee = fees.sstoreResetGas.v + } else if (currentStorage[key] === undefined) { + data.opcode.fee = fees.sstoreSetGas.v + } else { + data.opcode.fee = fees.sstoreResetGas.v + } + step.gasCost = data.opcode.fee + } self.vmTraces[self.processingHash].structLogs.push(step) if (traceHelper.newContextStorage(step)) { - if (!self.storageCache[self.processingHash][address]) { - var address = step.stack[step.stack.length - 2] - self.vm.stateManager.dumpStorage(address, function (storage) { - self.storageCache[self.processingHash][address] = storage - }) + if (step.op === 'CREATE') { + this.processingAddress = traceHelper.contractCreationToken(this.processingIndex) + this.storageCache[this.processingHash][this.processingAddress] = {} + } else { + this.processingAddress = uiutil.normalizeHex(step.stack[step.stack.length - 2]) + if (!self.storageCache[self.processingHash][this.processingAddress]) { + self.vm.stateManager.dumpStorage(this.processingAddress, function (storage) { + self.storageCache[self.processingHash][this.processingAddress] = storage + }) + } } } + this.processingIndex++ } web3VmProvider.prototype.getCode = function (address, cb) { From e8d628f9df6709ae37f6633d9b9ff2329fa6dc84 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 3 Jan 2017 18:31:39 +0100 Subject: [PATCH 04/28] dep --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 644af4ad6e..e473df41b9 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "babel-plugin-transform-regenerator": "^6.16.1", "babelify": "^7.3.0", "browserify": "^13.0.1", + "ethereum-common": "0.0.18", "ethereumjs-util": "^4.5.0", "fast-async": "^6.1.2", "http-server": "^0.9.0", From 6a02df530c3e8fd1072e501cddd0316b8d483459 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 3 Jan 2017 18:31:50 +0100 Subject: [PATCH 05/28] check if run out of gas --- src/trace/traceAnalyser.js | 9 +++++++-- src/trace/traceCache.js | 7 ++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/trace/traceAnalyser.js b/src/trace/traceAnalyser.js index 8acf27a17f..2e119253c2 100644 --- a/src/trace/traceAnalyser.js +++ b/src/trace/traceAnalyser.js @@ -90,6 +90,7 @@ TraceAnalyser.prototype.buildStorage = function (index, step, context) { } TraceAnalyser.prototype.buildDepth = function (index, step, tx, callStack, context) { + var outOfGas = runOutOfGas(step) if (traceHelper.isCallInstruction(step) && !traceHelper.isCallToPrecompiledContract(index, this.trace)) { var newAddress if (traceHelper.isCreateInstruction(step)) { @@ -110,10 +111,10 @@ TraceAnalyser.prototype.buildDepth = function (index, step, tx, callStack, conte this.traceCache.pushSteps(index, context.currentCallIndex) context.lastCallIndex = context.currentCallIndex context.currentCallIndex = 0 - } else if (traceHelper.isReturnInstruction(step) || traceHelper.isStopInstruction(step)) { + } else if (traceHelper.isReturnInstruction(step) || traceHelper.isStopInstruction(step) || outOfGas) { if (index + 1 < this.trace.length) { callStack.pop() - this.traceCache.pushCall(step, index + 1, null, callStack.slice(0)) + this.traceCache.pushCall(step, index + 1, null, callStack.slice(0), outOfGas) this.buildCalldata(index, step, tx, false) this.traceCache.pushSteps(index, context.currentCallIndex) context.currentCallIndex = context.lastCallIndex + 1 @@ -125,4 +126,8 @@ TraceAnalyser.prototype.buildDepth = function (index, step, tx, callStack, conte return context } +function runOutOfGas (step) { + return parseInt(step.gas) - parseInt(step.gasCost) < 0 +} + module.exports = TraceAnalyser diff --git a/src/trace/traceCache.js b/src/trace/traceCache.js index e6f7459c09..9480c6f600 100644 --- a/src/trace/traceCache.js +++ b/src/trace/traceCache.js @@ -34,14 +34,15 @@ TraceCache.prototype.pushMemoryChanges = function (value) { this.memoryChanges.push(value) } -TraceCache.prototype.pushCall = function (step, index, address, callStack) { +TraceCache.prototype.pushCall = function (step, index, address, callStack, outofGas) { this.callChanges.push(index) this.calls[index] = { op: step.op, address: address, - callStack: callStack + callStack: callStack, + outofGas: outofGas } - if (step.op === 'RETURN' || step.op === 'STOP') { + if (step.op === 'RETURN' || step.op === 'STOP' || outofGas) { var call = this.callsRef.pop() this.calls[index].call = call this.calls[call].return = index From 88b92d5c037d1e8c903898c70953890923585c6f Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 4 Jan 2017 15:59:51 +0100 Subject: [PATCH 06/28] do not crash if no storage --- src/trace/traceRetriever.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/trace/traceRetriever.js b/src/trace/traceRetriever.js index 7629e0b7c5..94e86d58b3 100644 --- a/src/trace/traceRetriever.js +++ b/src/trace/traceRetriever.js @@ -27,7 +27,13 @@ TraceRetriever.prototype.getStorage = function (tx, address, callback) { // The VM gives only a tx hash // TODO: get rid of that and use the range parameters util.web3.debug.storageRangeAt(tx.blockHash, tx.transactionIndex === undefined ? tx.hash : tx.transactionIndex, address, '0x0', '0x' + end, maxSize, function (error, result) { - callback(error, result.storage) + if (error) { + callback(error) + } else if (result.storage) { + callback(null, result.storage) + } else { + callback('storage has not been provided') + } }) } else { callback('no storageRangeAt endpoint found') From ba9c610ad6c7a4f51ca32c83e3c4f0caa57ca1c6 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 4 Jan 2017 16:01:09 +0100 Subject: [PATCH 07/28] only take care of the first outofgas --- src/trace/traceCache.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/trace/traceCache.js b/src/trace/traceCache.js index 9480c6f600..d53a4339f8 100644 --- a/src/trace/traceCache.js +++ b/src/trace/traceCache.js @@ -9,7 +9,7 @@ TraceCache.prototype.init = function () { this.returnValues = {} this.callChanges = [] this.calls = {} - this.callsRef = [0] // track of calls during the vm trace analysis + this.callsRef = [] // track of calls during the vm trace analysis this.callsData = {} this.contractCreation = {} this.steps = {} @@ -35,18 +35,23 @@ TraceCache.prototype.pushMemoryChanges = function (value) { } TraceCache.prototype.pushCall = function (step, index, address, callStack, outofGas) { - this.callChanges.push(index) - this.calls[index] = { + var call = { op: step.op, address: address, callStack: callStack, outofGas: outofGas } if (step.op === 'RETURN' || step.op === 'STOP' || outofGas) { - var call = this.callsRef.pop() - this.calls[index].call = call - this.calls[call].return = index + if (this.callsRef.length > 1) { // we are returning from an internal call + this.callChanges.push(index) + this.calls[index] = call + var callIndex = this.callsRef.pop() + this.calls[index].call = callIndex + this.calls[callIndex].return = index + } } else { + this.callChanges.push(index) + this.calls[index] = call this.callsRef.push(index) } } From 92666bf9dd1425b981fc1178cb2e74cde62abe0d Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 4 Jan 2017 16:01:43 +0100 Subject: [PATCH 08/28] use depth change to check for bas return (needed byt the js vm) --- src/trace/traceAnalyser.js | 2 +- src/web3Provider/web3VmProvider.js | 33 +++++++++++++----------------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/trace/traceAnalyser.js b/src/trace/traceAnalyser.js index 2e119253c2..ad346ea4bc 100644 --- a/src/trace/traceAnalyser.js +++ b/src/trace/traceAnalyser.js @@ -111,7 +111,7 @@ TraceAnalyser.prototype.buildDepth = function (index, step, tx, callStack, conte this.traceCache.pushSteps(index, context.currentCallIndex) context.lastCallIndex = context.currentCallIndex context.currentCallIndex = 0 - } else if (traceHelper.isReturnInstruction(step) || traceHelper.isStopInstruction(step) || outOfGas) { + } else if (traceHelper.isReturnInstruction(step) || traceHelper.isStopInstruction(step) || outOfGas || step.error || step.invalidDepthChange) { if (index + 1 < this.trace.length) { callStack.pop() this.traceCache.pushCall(step, index + 1, null, callStack.slice(0), outOfGas) diff --git a/src/web3Provider/web3VmProvider.js b/src/web3Provider/web3VmProvider.js index 9cc4bb9631..3d409a4e2b 100644 --- a/src/web3Provider/web3VmProvider.js +++ b/src/web3Provider/web3VmProvider.js @@ -2,8 +2,6 @@ var util = require('../helpers/util') var uiutil = require('../helpers/ui') var traceHelper = require('../helpers/traceHelper') var Web3 = require('web3') -var fees = require('ethereum-common') -var ethutil = require('ethereumjs-util') function web3VmProvider () { var self = this @@ -14,6 +12,7 @@ function web3VmProvider () { this.processingHash this.processingAddress this.processingIndex + this.previousDepth = 0 this.incr = 0 this.eth = {} this.debug = {} @@ -48,7 +47,7 @@ web3VmProvider.prototype.releaseCurrentHash = function () { return ret } -web3VmProvider.prototype.txWillProcess = function (self, data) { +web3VmProvider.prototype.txWillProcess = function (self, data) { self.incr++ self.processingHash = util.hexConvert(data.hash()) self.vmTraces[self.processingHash] = { @@ -88,13 +87,20 @@ web3VmProvider.prototype.txProcessed = function (self, data) { } this.processingIndex = null this.processingAddress = null + this.previousDepth = 0 } web3VmProvider.prototype.pushTrace = function (self, data) { + var depth = data.depth + 1 // geth starts the depth from 1 if (!self.processingHash) { console.log('no tx processing') return } + if (this.previousDepth > depth) { + // returning from context, set error it is not STOP, RETURN + var previousopcode = self.vmTraces[self.processingHash].structLogs[this.processingIndex - 1] + previousopcode.invalidDepthChange = previousopcode.op !== 'RETURN' && previousopcode.op !== 'STOP' + } var step = { stack: util.hexListConvert(data.stack), memory: util.formatMemory(data.memory), @@ -102,23 +108,11 @@ web3VmProvider.prototype.pushTrace = function (self, data) { op: data.opcode.name, pc: data.pc, gasCost: data.opcode.fee.toString(), - gas: data.gasLeft.toString() - } - if (data.opcode.name === 'SSTORE') { - var currentStorage = this.storageCache[this.processingHash][this.processingAddress] - var key = step.stack[step.stack.length - 1] - var value = step.stack[step.stack.length - 2] - value = util.hexToIntArray(value) - value = ethutil.unpad(value) - if (value.length === 0) { - data.opcode.fee = fees.sstoreResetGas.v - } else if (currentStorage[key] === undefined) { - data.opcode.fee = fees.sstoreSetGas.v - } else { - data.opcode.fee = fees.sstoreResetGas.v - } - step.gasCost = data.opcode.fee + gas: data.gasLeft.toString(), + depth: depth, + error: data.error === false ? undefined : data.error } + console.log(JSON.stringify(step)) self.vmTraces[self.processingHash].structLogs.push(step) if (traceHelper.newContextStorage(step)) { if (step.op === 'CREATE') { @@ -134,6 +128,7 @@ web3VmProvider.prototype.pushTrace = function (self, data) { } } this.processingIndex++ + this.previousDepth = depth } web3VmProvider.prototype.getCode = function (address, cb) { From 1e5e4e8f2cbc62f30788cae3874ff4437e94d110 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 5 Jan 2017 13:51:22 +0100 Subject: [PATCH 09/28] add findCall --- src/helpers/util.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/helpers/util.js b/src/helpers/util.js index 756ac4fe5b..261037eeff 100644 --- a/src/helpers/util.js +++ b/src/helpers/util.js @@ -83,5 +83,27 @@ module.exports = { findLowerBoundValue: function (target, array) { var index = this.findLowerBound(target, array) return index >= 0 ? array[index] : null + }, + + findCall: findCall +} + +/** + * Find the call from @args rootCall which contains @args index (recursive) + * + * @param {Int} index - index of the vmtrace + * @param {Object} rootCall - call tree, built by the trace analyser + * @return {Object} - return the call which include the @args index + */ +function findCall (index, rootCall) { + var calls = Object.keys(rootCall.calls) + var ret = rootCall + for (var k in calls) { + var subCall = rootCall.calls[calls[k]] + if (index >= subCall.start && index <= subCall.return) { + ret = findCall(index, subCall) + break + } } + return ret } From 858f17d264d9b50e2ef7137776a02488ed0f5e8c Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 5 Jan 2017 13:52:18 +0100 Subject: [PATCH 10/28] change way of bulding the calltree --- src/trace/traceCache.js | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/trace/traceCache.js b/src/trace/traceCache.js index d53a4339f8..d2fa4664a4 100644 --- a/src/trace/traceCache.js +++ b/src/trace/traceCache.js @@ -7,9 +7,8 @@ TraceCache.prototype.init = function () { // ...Changes contains index in the vmtrace of the corresponding changes this.returnValues = {} - this.callChanges = [] - this.calls = {} - this.callsRef = [] // track of calls during the vm trace analysis + this.currentCall = null + this.callsTree = null this.callsData = {} this.contractCreation = {} this.steps = {} @@ -35,24 +34,26 @@ TraceCache.prototype.pushMemoryChanges = function (value) { } TraceCache.prototype.pushCall = function (step, index, address, callStack, outofGas) { - var call = { - op: step.op, - address: address, - callStack: callStack, - outofGas: outofGas - } if (step.op === 'RETURN' || step.op === 'STOP' || outofGas) { - if (this.callsRef.length > 1) { // we are returning from an internal call - this.callChanges.push(index) - this.calls[index] = call - var callIndex = this.callsRef.pop() - this.calls[index].call = callIndex - this.calls[callIndex].return = index - } + this.currentCall.call.return = index + var parent = this.currentCall.parent + this.currentCall = { call: parent.call, parent: parent.parent } } else { - this.callChanges.push(index) - this.calls[index] = call - this.callsRef.push(index) + var call = { + op: step.op, + address: address, + callStack: callStack, + outofGas: outofGas, + calls: {}, + start: index + } + this.addresses.push(address) + if (this.currentCall) { + this.currentCall.call.calls[index] = call + } else { + this.callsTree = { call: call } + } + this.currentCall = { call: call, parent: this.currentCall } } } From 3df99cd3199f05be6fb9f66644494f87eabde6cb Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 5 Jan 2017 13:52:53 +0100 Subject: [PATCH 11/28] Update TraceManager to use the new callstree --- src/trace/traceManager.js | 32 +++++++++----------------------- src/trace/traceStepManager.js | 19 ++++++------------- 2 files changed, 15 insertions(+), 36 deletions(-) diff --git a/src/trace/traceManager.js b/src/trace/traceManager.js index 8e34b11dd7..412c520f3f 100644 --- a/src/trace/traceManager.js +++ b/src/trace/traceManager.js @@ -101,14 +101,7 @@ TraceManager.prototype.getStorageAt = function (stepIndex, tx, callback, address } TraceManager.prototype.getAddresses = function (callback) { - var addresses = [ this.tx.to ] - for (var k in this.traceCache.calls) { - var address = this.traceCache.calls[k].address - if (address && addresses.join('').indexOf(address) === -1) { - addresses.push(address) - } - } - callback(null, addresses) + callback(null, this.traceCache.addresses) } TraceManager.prototype.getCallDataAt = function (stepIndex, callback) { @@ -126,9 +119,9 @@ TraceManager.prototype.getCallStackAt = function (stepIndex, callback) { if (check) { return callback(check, null) } - var callStackChange = util.findLowerBoundValue(stepIndex, this.traceCache.callChanges) - if (callStackChange === null) return callback('no callstack found', null) - callback(null, this.traceCache.calls[callStackChange].callStack) + var call = util.findCall(stepIndex, this.traceCache.callsTree.call) + if (call === null) return callback('no callstack found', null) + callback(null, call.callStack) } TraceManager.prototype.getStackAt = function (stepIndex, callback) { @@ -151,7 +144,7 @@ TraceManager.prototype.getLastCallChangeSince = function (stepIndex, callback) { if (check) { return callback(check, null) } - var callChange = util.findLowerBoundValue(stepIndex, this.traceCache.callChanges) + var callChange = util.findCall(stepIndex, this.traceCache.callsTree.call) if (callChange === null) { callback(null, 0) } else { @@ -164,21 +157,14 @@ TraceManager.prototype.getCurrentCalledAddressAt = function (stepIndex, callback if (check) { return callback(check, null) } - var self = this - this.getLastCallChangeSince(stepIndex, function (error, addressIndex) { + this.getLastCallChangeSince(stepIndex, function (error, resp) { if (error) { callback(error, null) } else { - if (addressIndex === 0) { - callback(null, self.tx.to) + if (resp) { + callback(null, resp.address) } else { - var callStack = self.traceCache.calls[addressIndex].callStack - var calledAddress = callStack[callStack.length - 1] - if (calledAddress) { - callback(null, calledAddress) - } else { - callback('unable to get current called address. ' + stepIndex + ' does not match with a CALL', null) - } + callback('unable to get current called address. ' + stepIndex + ' does not match with a CALL') } } }) diff --git a/src/trace/traceStepManager.js b/src/trace/traceStepManager.js index c6b6fdaa77..a95d5050de 100644 --- a/src/trace/traceStepManager.js +++ b/src/trace/traceStepManager.js @@ -33,25 +33,18 @@ TraceStepManager.prototype.findStepOverForward = function (currentStep) { } TraceStepManager.prototype.findNextCall = function (currentStep) { - var callChanges = this.traceAnalyser.traceCache.callChanges - var stepIndex = util.findLowerBound(currentStep, callChanges) - var callchange = callChanges[stepIndex + 1] - if (callchange && this.isCallInstruction(callchange - 1)) { - return callchange - 1 + var call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call) + var subCalls = Object.keys(call.calls) + if (subCalls.length) { + return call.calls[subCalls[0]].start - 1 } else { return currentStep } } TraceStepManager.prototype.findStepOut = function (currentStep) { - var callChanges = this.traceAnalyser.traceCache.callChanges - var stepIndex = util.findLowerBound(currentStep, callChanges) - var call = this.traceAnalyser.traceCache.calls[callChanges[stepIndex]] - if (call && call.return) { - return call.return - } else { - return currentStep - } + var call = util.findCall(currentStep, this.traceAnalyser.traceCache.callsTree.call) + return call.return } module.exports = TraceStepManager From 246ba2c48a8ed49abd74da120959857f206ad85e Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 5 Jan 2017 13:53:05 +0100 Subject: [PATCH 12/28] fix tests --- test/traceManager.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/traceManager.js b/test/traceManager.js index cdc8946156..d35a4a5608 100644 --- a/test/traceManager.js +++ b/test/traceManager.js @@ -125,7 +125,7 @@ tape('TraceManager', function (t) { if (error) { st.fail(error) } else { - st.ok(result === 0) + st.ok(result.start === 0) } }) @@ -134,7 +134,7 @@ tape('TraceManager', function (t) { if (error) { st.fail(error) } else { - st.ok(result === 64) + st.ok(result.start === 64) } }) @@ -143,7 +143,9 @@ tape('TraceManager', function (t) { if (error) { st.fail(error) } else { - st.ok(result === 109) + st.ok(result.start === 0) + // this was 109 before: 111 is targeting the root call (starting index 0) + // this test make more sense as it is now (109 is the index of RETURN). } }) }) From 9f7bf14a68c6711a7a6209ad491e17df29433d0a Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 5 Jan 2017 13:53:14 +0100 Subject: [PATCH 13/28] remove log --- src/web3Provider/web3VmProvider.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/web3Provider/web3VmProvider.js b/src/web3Provider/web3VmProvider.js index 3d409a4e2b..4656c50a1a 100644 --- a/src/web3Provider/web3VmProvider.js +++ b/src/web3Provider/web3VmProvider.js @@ -112,7 +112,6 @@ web3VmProvider.prototype.pushTrace = function (self, data) { depth: depth, error: data.error === false ? undefined : data.error } - console.log(JSON.stringify(step)) self.vmTraces[self.processingHash].structLogs.push(step) if (traceHelper.newContextStorage(step)) { if (step.op === 'CREATE') { From bdad659a74c59c49928d0647e0beebe799c15ed2 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 5 Jan 2017 17:49:03 +0100 Subject: [PATCH 14/28] add buildCallsPath to Util --- src/helpers/util.js | 24 ++++++++++++++++++++++-- src/trace/traceManager.js | 10 ++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/helpers/util.js b/src/helpers/util.js index 261037eeff..2a84846cd4 100644 --- a/src/helpers/util.js +++ b/src/helpers/util.js @@ -85,7 +85,21 @@ module.exports = { return index >= 0 ? array[index] : null }, - findCall: findCall + findCall: findCall, + buildCallsPath: buildCallsPath +} + +/** + * Find calls path from @args rootCall which leads to @args index (recursive) + * + * @param {Int} index - index of the vmtrace + * @param {Object} rootCall - call tree, built by the trace analyser + * @return {Array} - return the calls path to @args index + */ +function buildCallsPath (index, rootCall) { + var ret = [] + findCallInternal(index, rootCall, ret) + return ret } /** @@ -96,12 +110,18 @@ module.exports = { * @return {Object} - return the call which include the @args index */ function findCall (index, rootCall) { + var ret = buildCallsPath(index, rootCall) + return ret[ret.length - 1] +} + +function findCallInternal (index, rootCall, callsPath) { var calls = Object.keys(rootCall.calls) var ret = rootCall + callsPath.push(rootCall) for (var k in calls) { var subCall = rootCall.calls[calls[k]] if (index >= subCall.start && index <= subCall.return) { - ret = findCall(index, subCall) + findCallInternal(index, subCall, callsPath) break } } diff --git a/src/trace/traceManager.js b/src/trace/traceManager.js index 412c520f3f..a8f323827b 100644 --- a/src/trace/traceManager.js +++ b/src/trace/traceManager.js @@ -114,6 +114,16 @@ TraceManager.prototype.getCallDataAt = function (stepIndex, callback) { callback(null, [this.traceCache.callsData[callDataChange]]) } +TraceManager.prototype.buildCallsPath = function (stepIndex, callback) { + var check = this.checkRequestedStep(stepIndex) + if (check) { + return callback(check, null) + } + var callsPath = util.buildCallsPath(stepIndex, this.traceCache.callsTree.call) + if (callsPath === null) return callback('no call path built', null) + callback(null, callsPath) +} + TraceManager.prototype.getCallStackAt = function (stepIndex, callback) { var check = this.checkRequestedStep(stepIndex) if (check) { From df0dcbd9f7306302980634887c3f315fb20bdc3a Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 5 Jan 2017 17:49:39 +0100 Subject: [PATCH 15/28] fix pushCall --- src/trace/traceAnalyser.js | 4 ++-- src/trace/traceCache.js | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/trace/traceAnalyser.js b/src/trace/traceAnalyser.js index ad346ea4bc..b87850610b 100644 --- a/src/trace/traceAnalyser.js +++ b/src/trace/traceAnalyser.js @@ -112,9 +112,9 @@ TraceAnalyser.prototype.buildDepth = function (index, step, tx, callStack, conte context.lastCallIndex = context.currentCallIndex context.currentCallIndex = 0 } else if (traceHelper.isReturnInstruction(step) || traceHelper.isStopInstruction(step) || outOfGas || step.error || step.invalidDepthChange) { - if (index + 1 < this.trace.length) { + if (index < this.trace.length) { callStack.pop() - this.traceCache.pushCall(step, index + 1, null, callStack.slice(0), outOfGas) + this.traceCache.pushCall(step, index + 1, null, callStack.slice(0), outOfGas || step.error || step.invalidDepthChange, outOfGas) this.buildCalldata(index, step, tx, false) this.traceCache.pushSteps(index, context.currentCallIndex) context.currentCallIndex = context.lastCallIndex + 1 diff --git a/src/trace/traceCache.js b/src/trace/traceCache.js index d2fa4664a4..000b9bee7c 100644 --- a/src/trace/traceCache.js +++ b/src/trace/traceCache.js @@ -33,17 +33,18 @@ TraceCache.prototype.pushMemoryChanges = function (value) { this.memoryChanges.push(value) } -TraceCache.prototype.pushCall = function (step, index, address, callStack, outofGas) { - if (step.op === 'RETURN' || step.op === 'STOP' || outofGas) { - this.currentCall.call.return = index +TraceCache.prototype.pushCall = function (step, index, address, callStack, reverted, outOfGas) { + if (step.op === 'RETURN' || step.op === 'STOP' || reverted) { + this.currentCall.call.return = index - 1 + this.currentCall.call.reverted = reverted + this.currentCall.call.outOfGas = outOfGas var parent = this.currentCall.parent - this.currentCall = { call: parent.call, parent: parent.parent } + this.currentCall = parent ? { call: parent.call, parent: parent.parent } : null } else { var call = { op: step.op, address: address, callStack: callStack, - outofGas: outofGas, calls: {}, start: index } From 5a563ab7943261d3a72d54c684077b3d42641dd8 Mon Sep 17 00:00:00 2001 From: yann300 Date: Thu, 5 Jan 2017 17:50:00 +0100 Subject: [PATCH 16/28] add jump to exception action --- src/ui/ButtonNavigator.js | 53 ++++++++++++++++++++++++++++++++++++++- src/ui/StepManager.js | 13 +++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/ui/ButtonNavigator.js b/src/ui/ButtonNavigator.js index 5d2dc3284b..55ddb7e3c6 100644 --- a/src/ui/ButtonNavigator.js +++ b/src/ui/ButtonNavigator.js @@ -4,7 +4,7 @@ var style = require('./styles/basicStyles') var ui = require('../helpers/ui') var yo = require('yo-yo') -function ButtonNavigator (_traceManager) { +function ButtonNavigator (_parent, _traceManager) { this.event = new EventManager() this.intoBackDisabled = true this.overBackDisabled = true @@ -14,6 +14,42 @@ function ButtonNavigator (_traceManager) { this.jumpOutDisabled = true this.traceManager = _traceManager + this.currentCall = null + this.revertionPoint = null + + _parent.event.register('indexChanged', this, (index) => { + if (index < 0) return + if (_parent.currentStepIndex !== index) return + + this.traceManager.buildCallsPath(index, (error, callsPath) => { + if (error) { + console.log(error) + resetWarning(this) + } else { + this.currentCall = callsPath[callsPath.length - 1] + if (this.currentCall.reverted) { + this.revertionPoint = this.currentCall.return + this.view.querySelector('#reverted').style.display = 'block' + this.view.querySelector('#reverted #outofgas').style.display = this.currentCall.outOfGas ? 'block' : 'none' + this.view.querySelector('#reverted #parenthasthrown').style.display = 'none' + } else { + var k = callsPath.length - 2 + while (k > 0) { + var parent = callsPath[k] + if (parent.reverted) { + this.revertionPoint = parent.return + this.view.querySelector('#reverted').style.display = 'block' + this.view.querySelector('#reverted #parenthasthrown').style.display = parent ? 'block' : 'none' + this.view.querySelector('#reverted #outofgas').style.display = 'none' + return + } + k-- + } + resetWarning(this) + } + } + }) + }) this.view } @@ -35,6 +71,13 @@ ButtonNavigator.prototype.render = function () { + ` if (!this.view) { this.view = view @@ -49,6 +92,7 @@ ButtonNavigator.prototype.reset = function () { this.overForwardDisabled = true this.nextCallDisabled = true this.jumpOutDisabled = true + resetWarning(this) } ButtonNavigator.prototype.stepChanged = function (step) { @@ -84,6 +128,7 @@ ButtonNavigator.prototype.updateAll = function () { this.updateDisabled('intoforward', this.intoForwardDisabled) this.updateDisabled('nextcall', this.nextCallDisabled) this.updateDisabled('jumpout', this.jumpOutDisabled) + this.updateDisabled('jumptoexception', this.jumpOutDisabled) } ButtonNavigator.prototype.updateDisabled = function (id, disabled) { @@ -94,4 +139,10 @@ ButtonNavigator.prototype.updateDisabled = function (id, disabled) { } } +function resetWarning (self) { + self.view.querySelector('#reverted #outofgas').style.display = 'none' + self.view.querySelector('#reverted #parenthasthrown').style.display = 'none' + self.view.querySelector('#reverted').style.display = 'none' +} + module.exports = ButtonNavigator diff --git a/src/ui/StepManager.js b/src/ui/StepManager.js index 0c03547b72..c1a380ff24 100644 --- a/src/ui/StepManager.js +++ b/src/ui/StepManager.js @@ -26,7 +26,7 @@ function StepManager (_parent, _traceManager) { self.sliderMoved(step) }) - this.buttonNavigator = new ButtonNavigator(this.traceManager) + this.buttonNavigator = new ButtonNavigator(_parent, this.traceManager) this.buttonNavigator.event.register('stepIntoBack', this, function () { self.stepIntoBack() }) @@ -45,6 +45,9 @@ function StepManager (_parent, _traceManager) { this.buttonNavigator.event.register('jumpOut', this, function () { self.jumpOut() }) + this.buttonNavigator.event.register('jumpToException', this, function (exceptionIndex) { + self.jumpTo(exceptionIndex) + }) } StepManager.prototype.render = function () { @@ -71,6 +74,14 @@ StepManager.prototype.newTraceAvailable = function () { this.init() } +StepManager.prototype.jumpTo = function (step) { + if (!this.traceManager.inRange(step)) { + return + } + this.slider.setValue(step) + this.changeState(step) +} + StepManager.prototype.sliderMoved = function (step) { if (!this.traceManager.inRange(step)) { return From ca2fff8fc602ad9e3f328315d488a468394e35cc Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 9 Jan 2017 10:33:59 +0100 Subject: [PATCH 17/28] bug fix --- src/trace/traceCache.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/trace/traceCache.js b/src/trace/traceCache.js index 000b9bee7c..10482ea1e1 100644 --- a/src/trace/traceCache.js +++ b/src/trace/traceCache.js @@ -35,11 +35,13 @@ TraceCache.prototype.pushMemoryChanges = function (value) { TraceCache.prototype.pushCall = function (step, index, address, callStack, reverted, outOfGas) { if (step.op === 'RETURN' || step.op === 'STOP' || reverted) { - this.currentCall.call.return = index - 1 - this.currentCall.call.reverted = reverted - this.currentCall.call.outOfGas = outOfGas - var parent = this.currentCall.parent - this.currentCall = parent ? { call: parent.call, parent: parent.parent } : null + if (this.currentCall) { + this.currentCall.call.return = index - 1 + this.currentCall.call.reverted = reverted + this.currentCall.call.outOfGas = outOfGas + var parent = this.currentCall.parent + this.currentCall = parent ? { call: parent.call, parent: parent.parent } : null + } } else { var call = { op: step.op, From 574cf9586497730ec2dcae2d6a257e655a3e3206 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 9 Jan 2017 10:34:21 +0100 Subject: [PATCH 18/28] label --- src/ui/ButtonNavigator.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ui/ButtonNavigator.js b/src/ui/ButtonNavigator.js index 55ddb7e3c6..8b438d215e 100644 --- a/src/ui/ButtonNavigator.js +++ b/src/ui/ButtonNavigator.js @@ -30,7 +30,7 @@ function ButtonNavigator (_parent, _traceManager) { if (this.currentCall.reverted) { this.revertionPoint = this.currentCall.return this.view.querySelector('#reverted').style.display = 'block' - this.view.querySelector('#reverted #outofgas').style.display = this.currentCall.outOfGas ? 'block' : 'none' + this.view.querySelector('#reverted #outofgas').style.display = this.currentCall.outOfGas ? 'inline' : 'none' this.view.querySelector('#reverted #parenthasthrown').style.display = 'none' } else { var k = callsPath.length - 2 @@ -39,7 +39,7 @@ function ButtonNavigator (_parent, _traceManager) { if (parent.reverted) { this.revertionPoint = parent.return this.view.querySelector('#reverted').style.display = 'block' - this.view.querySelector('#reverted #parenthasthrown').style.display = parent ? 'block' : 'none' + this.view.querySelector('#reverted #parenthasthrown').style.display = parent ? 'inline' : 'none' this.view.querySelector('#reverted #outofgas').style.display = 'none' return } @@ -72,11 +72,11 @@ ButtonNavigator.prototype.render = function () { ` if (!this.view) { From e6fea60b0c9abf47631f53b730ac9c7d59e57c17 Mon Sep 17 00:00:00 2001 From: yann300 Date: Mon, 9 Jan 2017 12:04:30 +0100 Subject: [PATCH 19/28] typo + comment --- src/helpers/util.js | 21 +++++++++++---------- src/trace/traceManager.js | 4 ++-- src/ui/ButtonNavigator.js | 4 ++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/helpers/util.js b/src/helpers/util.js index 2a84846cd4..60ee806f15 100644 --- a/src/helpers/util.js +++ b/src/helpers/util.js @@ -85,8 +85,16 @@ module.exports = { return index >= 0 ? array[index] : null }, + /** + * Find the call from @args rootCall which contains @args index (recursive) + * + * @param {Int} index - index of the vmtrace + * @param {Object} rootCall - call tree, built by the trace analyser + * @return {Object} - return the call which include the @args index + */ findCall: findCall, - buildCallsPath: buildCallsPath + + buildCallPath: buildCallPath } /** @@ -96,21 +104,14 @@ module.exports = { * @param {Object} rootCall - call tree, built by the trace analyser * @return {Array} - return the calls path to @args index */ -function buildCallsPath (index, rootCall) { +function buildCallPath (index, rootCall) { var ret = [] findCallInternal(index, rootCall, ret) return ret } -/** - * Find the call from @args rootCall which contains @args index (recursive) - * - * @param {Int} index - index of the vmtrace - * @param {Object} rootCall - call tree, built by the trace analyser - * @return {Object} - return the call which include the @args index - */ function findCall (index, rootCall) { - var ret = buildCallsPath(index, rootCall) + var ret = buildCallPath(index, rootCall) return ret[ret.length - 1] } diff --git a/src/trace/traceManager.js b/src/trace/traceManager.js index a8f323827b..8208844b70 100644 --- a/src/trace/traceManager.js +++ b/src/trace/traceManager.js @@ -114,12 +114,12 @@ TraceManager.prototype.getCallDataAt = function (stepIndex, callback) { callback(null, [this.traceCache.callsData[callDataChange]]) } -TraceManager.prototype.buildCallsPath = function (stepIndex, callback) { +TraceManager.prototype.buildCallPath = function (stepIndex, callback) { var check = this.checkRequestedStep(stepIndex) if (check) { return callback(check, null) } - var callsPath = util.buildCallsPath(stepIndex, this.traceCache.callsTree.call) + var callsPath = util.buildCallPath(stepIndex, this.traceCache.callsTree.call) if (callsPath === null) return callback('no call path built', null) callback(null, callsPath) } diff --git a/src/ui/ButtonNavigator.js b/src/ui/ButtonNavigator.js index 8b438d215e..161d82fb79 100644 --- a/src/ui/ButtonNavigator.js +++ b/src/ui/ButtonNavigator.js @@ -21,7 +21,7 @@ function ButtonNavigator (_parent, _traceManager) { if (index < 0) return if (_parent.currentStepIndex !== index) return - this.traceManager.buildCallsPath(index, (error, callsPath) => { + this.traceManager.buildCallPath(index, (error, callsPath) => { if (error) { console.log(error) resetWarning(this) @@ -66,7 +66,7 @@ ButtonNavigator.prototype.render = function () { +