From 006dc9b32ab15b2d2cae5da54c7d3c0b0a4ccd10 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 24 May 2016 12:52:14 +0200 Subject: [PATCH] align trace with geth trace --- src/asmCode.js | 18 +++-- src/basicPanel.js | 29 +------ src/buttonNavigator.js | 23 ++++-- src/calldataPanel.js | 10 ++- src/callstackPanel.js | 10 ++- src/codeResolver.js | 11 ++- src/debugger.js | 2 +- src/memoryPanel.js | 39 +++------- src/stackPanel.js | 10 ++- src/stepManager.js | 2 +- src/storagePanel.js | 25 ++---- src/traceAnalyser.js | 81 +++++++++++--------- src/traceCache.js | 19 ++++- src/traceManager.js | 165 +++++++++++++++++++++++++++++++--------- src/traceManagerUtil.js | 38 +++++++-- src/traceRetriever.js | 22 ++++-- src/traceStepManager.js | 34 +++++---- src/txBrowser.js | 47 +++++++++--- src/vmDebugger.js | 10 +-- src/web3Admin.js | 4 +- 20 files changed, 387 insertions(+), 212 deletions(-) diff --git a/src/asmCode.js b/src/asmCode.js index 765c64e89d..4e7098504c 100644 --- a/src/asmCode.js +++ b/src/asmCode.js @@ -48,13 +48,17 @@ module.exports = React.createClass({ if (nextProps.currentStepIndex < 0) return codeResolver.setWeb3(this.context.web3) var self = this - this.context.traceManager.getCurrentCalledAddressAt(nextProps.currentStepIndex, function (error, address) { - if (error) { - console.log(error) - } else { - self.ensureCodeLoaded(address, nextProps.currentStepIndex) - } - }) + if (nextProps.currentStepIndex === 0) { + self.ensureCodeLoaded(this.context.tx.to, nextProps.currentStepIndex) + } else { + this.context.traceManager.getCurrentCalledAddressAt(nextProps.currentStepIndex, function (error, address) { + if (error) { + console.log(error) + } else { + self.ensureCodeLoaded(address, nextProps.currentStepIndex) + } + }) + } }, ensureCodeLoaded: function (address, currentStep) { diff --git a/src/basicPanel.js b/src/basicPanel.js index f5780d7022..cda0818765 100644 --- a/src/basicPanel.js +++ b/src/basicPanel.js @@ -6,8 +6,7 @@ module.exports = React.createClass({ getDefaultProps: function () { return { data: null, - name: null, - renderRow: null + name: null } }, @@ -18,33 +17,9 @@ module.exports = React.createClass({ {this.props.name}
- - - {this.renderItems()} - -
+
{this.props.data}
) - }, - - renderItems: function () { - if (!this.props.data) { - return [] - } - if (!this.props.renderRow) { - var ret = [] - for (var key in this.props.data) { - ret.push( - - -
{this.props.data[key]}
- - ) - } - return ret - } else { - return this.props.renderRow(this.props.data) - } } }) diff --git a/src/buttonNavigator.js b/src/buttonNavigator.js index 1b23e343d9..e834ad35b9 100644 --- a/src/buttonNavigator.js +++ b/src/buttonNavigator.js @@ -10,7 +10,8 @@ module.exports = React.createClass({ stepIntoBack: React.PropTypes.func.isRequired, stepIntoForward: React.PropTypes.func.isRequired, stepOverBack: React.PropTypes.func.isRequired, - stepOverForward: React.PropTypes.func.isRequired + stepOverForward: React.PropTypes.func.isRequired, + jumpNextCall: React.PropTypes.func.isRequired }, render: function () { @@ -28,15 +29,27 @@ module.exports = React.createClass({ + ) }, checkButtonState: function (incr) { - if (incr === -1) { - return this.props.step === 0 ? 'disabled' : '' - } else if (incr === 1) { - return this.props.step >= this.props.max - 1 ? 'disabled' : '' + if (!this.context.traceManager) { + return false } + var self = this + this.context.traceManager.getLength(function (error, length) { + if (error) { + return false + } + if (incr === -1) { + return self.props.step === 0 ? 'disabled' : '' + } else if (incr === 1) { + return self.props.step >= self.props.max - 1 ? 'disabled' : '' + } + }) } }) diff --git a/src/calldataPanel.js b/src/calldataPanel.js index 752a2bf15e..7bc9bc0baa 100644 --- a/src/calldataPanel.js +++ b/src/calldataPanel.js @@ -35,9 +35,17 @@ module.exports = React.createClass({ console.log(error) } else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) { self.setState({ - data: calldata + data: self.format(calldata) }) } }) + }, + + format: function (calldata) { + var ret = '' + for (var key in calldata) { + ret += calldata[key] + '\n' + } + return ret } }) diff --git a/src/callstackPanel.js b/src/callstackPanel.js index 83ec5ce94a..e223b8d411 100644 --- a/src/callstackPanel.js +++ b/src/callstackPanel.js @@ -35,9 +35,17 @@ module.exports = React.createClass({ console.log(error) } else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) { self.setState({ - data: callstack + data: self.format(callstack) }) } }) + }, + + format: function (callstack) { + var ret = '' + for (var key in callstack) { + ret += callstack[key] + '\n' + } + return ret } }) diff --git a/src/codeResolver.js b/src/codeResolver.js index 69250b6d74..77d3fec33c 100644 --- a/src/codeResolver.js +++ b/src/codeResolver.js @@ -18,7 +18,7 @@ module.exports = { return } - if (vmTraceIndex === 0 && transaction.to === null) { // start of the trace + if (address === '(Contract Creation Code)') { // start of the trace callBack(address, this.cacheExecutingCode(address, transaction.input).code) return } @@ -41,9 +41,14 @@ module.exports = { }, cacheExecutingCode: function (address, hexCode) { + var codes = this.formatCode(hexCode) + this.codes[address] = codes[0] + this.instructionsIndexByBytesOffset[address] = codes[1] + return codes + }, + + formatCode: function (hexCode) { var code = codeUtils.nameOpCodes(new Buffer(hexCode.substring(2), 'hex')) - this.codes[address] = code[0] - this.instructionsIndexByBytesOffset[address] = code[1] return { code: code[0], instructionsIndexByBytesOffset: code[1] diff --git a/src/debugger.js b/src/debugger.js index f7e1e9a7a0..87816aa1b1 100644 --- a/src/debugger.js +++ b/src/debugger.js @@ -61,7 +61,7 @@ module.exports = React.createClass({ tx: tx }) var self = this - this.state.traceManager.resolveTrace(blockNumber, txIndex, function (success) { + this.state.traceManager.resolveTrace(tx, function (success) { console.log('trace loaded ' + success) self.setState({ currentStepIndex: 0 diff --git a/src/memoryPanel.js b/src/memoryPanel.js index 2a5f6f10d1..c972989f33 100644 --- a/src/memoryPanel.js +++ b/src/memoryPanel.js @@ -1,7 +1,6 @@ 'use strict' var React = require('react') var BasicPanel = require('./basicPanel') -var style = require('./basicStyles') module.exports = React.createClass({ contextTypes: { @@ -23,7 +22,7 @@ module.exports = React.createClass({ render: function () { return ( - + ) }, @@ -43,36 +42,20 @@ module.exports = React.createClass({ }) }, - renderMemoryRow: function (data) { - var ret = [] - if (data) { - for (var key in data) { - var memSlot = data[key] - ret.push( - - -
{memSlot.address}
- - -
{memSlot.content.raw}
- - -
{memSlot.content.ascii}
- - ) - } + formatMemory: function (mem, width) { + var ret = '' + if (!mem) { + return ret + } + + if (!mem.substr) { + mem = mem.join('') // geth returns an array, eth return raw string } - return ret - }, - formatMemory: function (mem, width) { - var ret = [] for (var k = 0; k < mem.length; k += (width * 2)) { var memory = mem.substr(k, width * 2) - ret.push({ - address: this.context.web3.toHex(k), - content: this.tryAsciiFormat(memory) - }) + var content = this.tryAsciiFormat(memory) + ret += this.context.web3.toHex(k) + ' ' + content.raw + ' ' + content.ascii + '\n' } return ret }, diff --git a/src/stackPanel.js b/src/stackPanel.js index a4ff3c8472..0d5e117662 100644 --- a/src/stackPanel.js +++ b/src/stackPanel.js @@ -35,9 +35,17 @@ module.exports = React.createClass({ console.log(error) } else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) { self.setState({ - data: stack + data: self.format(stack) }) } }) + }, + + format: function (stack) { + var ret = '' + for (var key in stack) { + ret += stack[key] + '\n' + } + return ret } }) diff --git a/src/stepManager.js b/src/stepManager.js index 9967318309..2b5000f82f 100644 --- a/src/stepManager.js +++ b/src/stepManager.js @@ -33,7 +33,7 @@ module.exports = React.createClass({ stepIntoForward={this.stepIntoForward} stepOverBack={this.stepOverBack} stepOverForward={this.stepOverForward} - jumpToNextCall={this.jumpToNextCall} + jumpNextCall={this.jumpToNextCall} max={this.state.traceLength} /> ) diff --git a/src/storagePanel.js b/src/storagePanel.js index 96356cbe70..71b1bc8d62 100644 --- a/src/storagePanel.js +++ b/src/storagePanel.js @@ -1,7 +1,6 @@ 'use strict' var React = require('react') var BasicPanel = require('./basicPanel') -var style = require('./basicStyles') module.exports = React.createClass({ contextTypes: { @@ -23,7 +22,7 @@ module.exports = React.createClass({ render: function () { return ( - + ) }, @@ -32,31 +31,21 @@ module.exports = React.createClass({ if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return var self = this - this.context.traceManager.getStorageAt(nextProps.currentStepIndex, this.context.tx.blockNumber.toString(), this.context.tx.transactionIndex, function (error, storage) { + this.context.traceManager.getStorageAt(nextProps.currentStepIndex, this.context.tx, function (error, storage) { if (error) { console.log(error) } else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) { self.setState({ - data: storage + data: self.formatStorage(storage) }) } }) }, - renderStorageRow: function (data) { - var ret = [] - if (data) { - for (var key in data) { - ret.push( - - -
{key}
- - -
{data[key]}
- - ) - } + formatStorage: function (storage) { + var ret = '' + for (var key in storage) { + ret += key + ' ' + storage[key] + '\n' } return ret } diff --git a/src/traceAnalyser.js b/src/traceAnalyser.js index d79368c1ba..873b874412 100644 --- a/src/traceAnalyser.js +++ b/src/traceAnalyser.js @@ -1,25 +1,29 @@ 'use strict' +var traceManagerUtil = require('./traceManagerUtil') + function TraceAnalyser (_cache) { this.traceCache = _cache this.trace = null } -TraceAnalyser.prototype.analyse = function (trace, callback) { +TraceAnalyser.prototype.analyse = function (trace, root, callback) { this.trace = trace - var currentDepth = 0 + + this.traceCache.pushStoreChanges(0, root) var context = { - currentStorageAddress: trace[0].address, - previousStorageAddress: trace[0].address + currentStorageAddress: root, + previousStorageAddress: root } - var callStack = [] - for (var k in this.trace) { + var callStack = [root] + this.traceCache.pushCallStack(0, { + callStack: callStack.slice(0) + }) + + for (var k = 0; k < this.trace.length; k++) { var step = this.trace[k] this.buildCalldata(k, step) this.buildMemory(k, step) - var depth = this.buildDepth(k, step, currentDepth, callStack) - if (depth) { - currentDepth = depth - } + this.buildDepth(k, step, callStack) context = this.buildStorage(k, step, context) } callback(null, true) @@ -38,40 +42,45 @@ TraceAnalyser.prototype.buildMemory = function (index, step) { } TraceAnalyser.prototype.buildStorage = function (index, step, context) { - if (step.address) { - // new context - context.currentStorageAddress = step.address - this.traceCache.pushStoreChanges(index, context.currentStorageAddress) - } else if (step.inst === 'SSTORE') { - this.traceCache.pushStoreChanges(index, context.currentStorageAddress, step.stack[step.stack.length - 1], step.stack[step.stack.length - 2]) - } else if (!step.address && step.depth) { - // returned from context + if (traceManagerUtil.newContextStorage(step)) { + var calledAddress = traceManagerUtil.resolveCalledAddress(index, this.trace) + if (calledAddress) { + context.currentStorageAddress = calledAddress + } else { + console.log('unable to build storage changes. ' + index + ' does not match with a CALL. storage changes will be corrupted') + } + this.traceCache.pushStoreChanges(index + 1, context.currentStorageAddress) + } else if (step.op === 'SSTORE') { + this.traceCache.pushStoreChanges(index + 1, context.currentStorageAddress, step.stack[step.stack.length - 1], step.stack[step.stack.length - 2]) + } else if (!step.op === 'RETURN') { context.currentStorageAddress = context.previousStorageAddress - this.traceCache.pushStoreChanges(index, context.currentStorageAddress) + this.traceCache.pushStoreChanges(index + 1, context.currentStorageAddress) } return context } -TraceAnalyser.prototype.buildDepth = function (index, step, currentDepth, callStack) { - if (step.depth === undefined) return - if (step.depth > currentDepth) { - if (index === 0) { - callStack.push('0x' + step.address) // new context +TraceAnalyser.prototype.buildDepth = function (index, step, callStack) { + if (traceManagerUtil.isCallInstruction(step) && !traceManagerUtil.isCallToPrecompiledContract(index, this.trace)) { + var newAddress = traceManagerUtil.resolveCalledAddress(index, this.trace) + if (newAddress) { + callStack.push(newAddress) } else { - // getting the address from the stack - var callTrace = this.trace[index - 1] - var address = callTrace.stack[callTrace.stack.length - 2] - callStack.push(address) // new context + console.log('unable to build depth changes. ' + index + ' does not match with a CALL. depth changes will be corrupted') } - } else if (step.depth < currentDepth) { - callStack.pop() // returning from context + this.traceCache.pushCallChanges(step, index + 1) + this.traceCache.pushCallStack(index + 1, { + callStack: callStack.slice(0) + }) + } else if (traceManagerUtil.isReturnInstruction(step)) { + this.traceCache.pushCallChanges(step, index) + this.traceCache.pushCallStack(index, { + callStack: callStack.slice(0) + }) + callStack.pop() } - this.traceCache.pushCallStack(index, { - stack: callStack.slice(0), - depth: step.depth - }) - this.traceCache.pushDepthChanges(index) - return step.depth } +// 0x90a99e9dbfc38ce0fd6330f97a192a9ef27b8329b57309e8b2abe47a6fe74574 +// 0xc0e95f27e1482ba09dea8162c4b4090e3d89e214416df2ce9d5517ff2107de19 + module.exports = TraceAnalyser diff --git a/src/traceCache.js b/src/traceCache.js index d7cb3e9a3a..9fc4ac5a79 100644 --- a/src/traceCache.js +++ b/src/traceCache.js @@ -5,9 +5,13 @@ function TraceCache () { TraceCache.prototype.init = function () { // ...Changes contains index in the vmtrace of the corresponding changes - this.depthChanges = [] - this.memoryChanges = [] + + this.callChanges = [] + this.returnChanges = [] + this.calls = {} + this.callDataChanges = [] + this.memoryChanges = [] this.storageChanges = [] this.sstore = {} // all sstore occurence in the trace this.callStack = {} // contains all callStack by vmtrace index (we need to rebuild it, callstack is not included in the vmtrace) @@ -21,8 +25,15 @@ TraceCache.prototype.pushMemoryChanges = function (value) { this.memoryChanges.push(value) } -TraceCache.prototype.pushDepthChanges = function (value) { - this.depthChanges.push(value) +TraceCache.prototype.pushCallChanges = function (step, value) { + this.callChanges.push(value) + this.calls[value] = { + op: step.op + } +} + +TraceCache.prototype.pushReturnChanges = function (value) { + this.returnChanges.push(value) } TraceCache.prototype.pushCallStack = function (index, callStack) { diff --git a/src/traceManager.js b/src/traceManager.js index c53e66b81e..0ed6f4ba36 100644 --- a/src/traceManager.js +++ b/src/traceManager.js @@ -13,27 +13,36 @@ function TraceManager (_web3) { this.traceAnalyser = new TraceAnalyser(this.traceCache) this.traceRetriever = new TraceRetriever(_web3) this.traceStepManager = new TraceStepManager(this.traceAnalyser) + this.tx } // init section -TraceManager.prototype.resolveTrace = function (blockNumber, txNumber, callback) { - this.isLoading = true +TraceManager.prototype.resolveTrace = function (tx, callback) { + this.tx = tx this.init() if (!this.web3) callback(false) + this.isLoading = true var self = this - this.traceRetriever.getTrace(blockNumber, parseInt(txNumber), function (error, result) { - self.trace = result + this.traceRetriever.getTrace(tx.hash, function (error, result) { if (error) { console.log(error) + self.isLoading = false } else { - self.traceAnalyser.analyse(result, function (error, result) { - if (error) { - console.log(error) - callback(false) - } else { - callback(true) - } - }) + if (result.structLogs.length > 0) { + self.trace = result.structLogs + self.traceAnalyser.analyse(result.structLogs, tx.to, function (error, result) { + if (error) { + console.log(error) + callback(false) + } else { + callback(true) + } + self.isLoading = false + }) + } else { + console.log(tx.hash + ' is not a contract invokation or contract creation.') + self.isLoading = false + } } }) } @@ -45,39 +54,62 @@ TraceManager.prototype.init = function () { // API section TraceManager.prototype.getLength = function (callback) { - if (!this.trace) callback('no trace available', null) - callback(null, this.trace.length) + if (!this.trace) { + callback('no trace available', null) + } else { + callback(null, this.trace.length) + } } -TraceManager.prototype.getStorageAt = function (stepIndex, blockNumber, txIndex, callback) { +TraceManager.prototype.getStorageAt = function (stepIndex, tx, callback) { + if (stepIndex >= this.trace.length) { + callback('trace smaller than requested', null) + return + } var stoChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.storageChanges) - - var address = this.traceCache.sstore[stoChange].address + if (stoChange === undefined) return callback('no storage found', null) var self = this - this.traceRetriever.getStorage(blockNumber, txIndex, address, function (error, result) { - if (error) { - console.log(error) - callback(error, null) - } else { - var storage = self.traceCache.rebuildStorage(address, result, stepIndex) - callback(null, storage) - } - }) + if (this.traceRetriever.debugStorageAtAvailable()) { + var address = this.traceCache.sstore[stoChange].address + this.traceRetriever.getStorage(tx, address, function (error, result) { + if (error) { + console.log(error) + callback(error, null) + } else { + var storage = self.traceCache.rebuildStorage(address, result, stepIndex) + callback(null, storage) + } + }) + } else { + callback(null, this.trace[stoChange].storage) + } } TraceManager.prototype.getCallDataAt = function (stepIndex, callback) { + if (stepIndex >= this.trace.length) { + callback('trace smaller than requested', null) + return + } var callDataChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.callDataChanges) - if (!callDataChange) return callback('no calldata found', null) + if (callDataChange === undefined) return callback('no calldata found', null) callback(null, [this.trace[callDataChange].calldata]) } TraceManager.prototype.getCallStackAt = function (stepIndex, callback) { - var callStackChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.depthChanges) - if (!callStackChange) return callback('no callstack found', null) - callback(null, this.traceCache.callStack[callStackChange].stack) + if (stepIndex >= this.trace.length) { + callback('trace smaller than requested', null) + return + } + var callStackChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.callChanges) + if (callStackChange === undefined) return callback('no callstack found', null) + callback(null, this.traceCache.callStack[callStackChange].callStack) } TraceManager.prototype.getStackAt = function (stepIndex, callback) { + if (stepIndex >= this.trace.length) { + callback('trace smaller than requested', null) + return + } var stack if (this.trace[stepIndex].stack) { // there's always a stack stack = this.trace[stepIndex].stack.slice(0) @@ -88,48 +120,103 @@ TraceManager.prototype.getStackAt = function (stepIndex, callback) { } } -TraceManager.prototype.getLastDepthIndexChangeSince = function (stepIndex, callback) { - var depthIndex = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.depthChanges) - callback(null, depthIndex) +TraceManager.prototype.getLastCallChangeSince = function (stepIndex, callback) { + if (stepIndex >= this.trace.length) { + callback('trace smaller than requested', null) + return + } + var callChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.callChanges) + if (callChange === undefined) { + callback(null, 0) + } else { + callback(null, callChange) + } } TraceManager.prototype.getCurrentCalledAddressAt = function (stepIndex, callback) { + if (stepIndex > this.trace.length) { + callback('trace smaller than requested', null) + return + } var self = this - this.getLastDepthIndexChangeSince(stepIndex, function (error, addressIndex) { + this.getLastCallChangeSince(stepIndex, function (error, addressIndex) { if (error) { callback(error, null) } else { - callback(null, traceManagerUtil.resolveCalledAddress(addressIndex, self.trace)) + if (addressIndex === 0) { + callback(null, self.tx.to) + } else { + var step = this.trace[addressIndex] + if (traceManagerUtil.isCreateInstruction(step)) { + callback(null, '(Contract Creation Code)') + } else { + var callStack = self.traceCache.callStack[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) + } + } + } } }) } TraceManager.prototype.getMemoryAt = function (stepIndex, callback) { + if (stepIndex >= this.trace.length) { + callback('trace smaller than requested', null) + return + } var lastChanges = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.memoryChanges) - if (!lastChanges) return callback('no memory found', null) + if (lastChanges === undefined) return callback('no memory found', null) callback(null, this.trace[lastChanges].memory) } TraceManager.prototype.getCurrentPC = function (stepIndex, callback) { + if (stepIndex >= this.trace.length) { + callback('trace smaller than requested', null) + return + } callback(null, this.trace[stepIndex].pc) } TraceManager.prototype.getCurrentStep = function (stepIndex, callback) { + if (stepIndex >= this.trace.length) { + callback('trace smaller than requested', null) + return + } callback(null, this.trace[stepIndex].steps) } TraceManager.prototype.getMemExpand = function (stepIndex, callback) { + if (stepIndex >= this.trace.length) { + callback('trace smaller than requested', null) + return + } callback(null, this.trace[stepIndex].memexpand ? this.trace[stepIndex].memexpand : '') } TraceManager.prototype.getStepCost = function (stepIndex, callback) { - callback(null, this.trace[stepIndex].gascost) + if (stepIndex >= this.trace.length) { + callback('trace smaller than requested', null) + return + } + callback(null, this.trace[stepIndex].gasCost) } TraceManager.prototype.getRemainingGas = function (stepIndex, callback) { + if (stepIndex >= this.trace.length) { + callback('trace smaller than requested', null) + return + } callback(null, this.trace[stepIndex].gas) } +TraceManager.prototype.isCreationStep = function (stepIndex) { + return traceManagerUtil.isCreateInstruction(stepIndex, this.trace) +} + // step section TraceManager.prototype.findStepOverBack = function (currentStep) { return this.traceStepManager.findStepOverBack(currentStep) @@ -147,4 +234,8 @@ TraceManager.prototype.findStepOutForward = function (currentStep) { return this.traceStepManager.findStepOutForward(currentStep) } +TraceManager.prototype.findNextCall = function (currentStep) { + return this.traceStepManager.findNextCall(currentStep) +} + module.exports = TraceManager diff --git a/src/traceManagerUtil.js b/src/traceManagerUtil.js index 3231efa5af..61f3f1f7cb 100644 --- a/src/traceManagerUtil.js +++ b/src/traceManagerUtil.js @@ -24,13 +24,39 @@ module.exports = { } }, + // vmTraceIndex has to point to a CALL, CODECALL, ... resolveCalledAddress: function (vmTraceIndex, trace) { - var address = trace[vmTraceIndex].address - if (vmTraceIndex > 0) { - var stack = trace[vmTraceIndex - 1].stack // callcode, delegatecall, ... - address = stack[stack.length - 2] + var step = trace[vmTraceIndex] + if (this.isCallInstruction(step)) { + var stack = step.stack // callcode, delegatecall, ... + return stack[stack.length - 2] } - return address - } + return undefined + }, + + isCallInstruction: function (step) { + return step.op === 'CALL' || step.op === 'CALLCODE' || step.op === 'CREATE' || step.op === 'DELEGATECALL' + }, + + isCreateInstruction: function (step) { + return step.op === 'CREATE' + }, + + isReturnInstruction: function (step) { + return step.op === 'RETURN' + }, + + newContextStorage: function (step) { + return step.op === 'CREATE' || step.op === 'CALL' + }, + isCallToPrecompiledContract: function (index, trace) { + // if stack empty => this is not a precompiled contract + var step = trace[index] + if (this.isCallInstruction(step)) { + return trace[index + 1].stack.length !== 0 + } else { + return false + } + } } diff --git a/src/traceRetriever.js b/src/traceRetriever.js index 238510b66b..7f2343ba90 100644 --- a/src/traceRetriever.js +++ b/src/traceRetriever.js @@ -4,22 +4,34 @@ function TraceRetriever (_web3) { this.storages = {} // contains all intial storage (by addresses) } -TraceRetriever.prototype.getTrace = function (blockNumber, txNumber, callback) { - this.web3.debug.trace(blockNumber, parseInt(txNumber), function (error, result) { +TraceRetriever.prototype.getTrace = function (txHash, callback) { + var options = { + disableStorage: this.debugStorageAtAvailable(), + disableMemory: false, + disableStack: false, + fullStorage: !this.debugStorageAtAvailable() + } + this.web3.debug.traceTransaction(txHash, options, function (error, result) { callback(error, result) }) } -TraceRetriever.prototype.getStorage = function (blockNumber, txIndex, address, callback) { - if (this.storages[address]) { +TraceRetriever.prototype.getStorage = function (tx, address, callback) { + if (tx.to === '(Contract Creation Code)') { + callback(null, {}) + } else if (this.storages[address]) { callback(null, this.storages[address]) } else { var self = this - this.web3.debug.storageAt(blockNumber, txIndex, address, function (error, result) { + this.web3.debug.storageAt(tx.blockNumber.toString(), tx.transactionIndex, address, function (error, result) { self.storages[address] = result callback(error, result) }) } } +TraceRetriever.prototype.debugStorageAtAvailable = function () { + return true // storageAt not available if using geth +} + module.exports = TraceRetriever diff --git a/src/traceStepManager.js b/src/traceStepManager.js index 40d9afc098..38e074fd39 100644 --- a/src/traceStepManager.js +++ b/src/traceStepManager.js @@ -1,32 +1,28 @@ 'use strict' +var traceManagerUtil = require('./traceManagerUtil') + function TraceStepManager (_traceAnalyser) { this.traceAnalyser = _traceAnalyser } TraceStepManager.prototype.isCallInstruction = function (index) { var state = this.traceAnalyser.trace[index] - return state.instname === 'CALL' || state.instname === 'CALLCODE' || state.instname === 'CREATE' || state.instname === 'DELEGATECALL' + return traceManagerUtil.isCallInstruction(state) } TraceStepManager.prototype.isReturnInstruction = function (index) { var state = this.traceAnalyser.trace[index] - return state.instname === 'RETURN' + return traceManagerUtil.isReturnInstruction(state) } TraceStepManager.prototype.findStepOverBack = function (currentStep) { - if (this.isReturnInstruction(currentStep - 1)) { - return this.findStepOutBack(currentStep) - } else { - return currentStep - 1 - } + if (currentStep === 0) return 0 + return this.findStepOutBack(currentStep) } TraceStepManager.prototype.findStepOverForward = function (currentStep) { - if (this.isCallInstruction(currentStep)) { - return this.findStepOutForward(currentStep) - } else { - return currentStep + 1 - } + if (currentStep === this.traceAnalyser.trace.length - 1) return currentStep + return this.findStepOutForward(currentStep) } TraceStepManager.prototype.findStepOutBack = function (currentStep) { @@ -49,7 +45,7 @@ TraceStepManager.prototype.findStepOutBack = function (currentStep) { TraceStepManager.prototype.findStepOutForward = function (currentStep) { var i = currentStep var depth = 0 - while (++i < this.traceAnalyser.length) { + while (++i < this.traceAnalyser.trace.length) { if (this.isReturnInstruction(i)) { if (depth === 0) { break @@ -60,7 +56,17 @@ TraceStepManager.prototype.findStepOutForward = function (currentStep) { depth++ } } - return i + 1 + return i +} + +TraceStepManager.prototype.findNextCall = function (currentStep) { + var i = currentStep + while (++i < this.traceAnalyser.trace.length) { + if (this.isCallInstruction(i)) { + return i + } + } + return currentStep } module.exports = TraceStepManager diff --git a/src/txBrowser.js b/src/txBrowser.js index 61dad1c78e..a3717f4f68 100644 --- a/src/txBrowser.js +++ b/src/txBrowser.js @@ -12,12 +12,23 @@ module.exports = React.createClass({ }, getInitialState: function () { - return {blockNumber: '1382256', txNumber: '1', from: '', to: '', hash: ''} + return {blockNumber: '1000110', txNumber: '0x71a6d583d16d142c5c3e8903060e8a4ee5a5016348a9448df6c3e63b68076ec4', from: '', to: '', hash: ''} }, + // creation 0xa9619e1d0a35b2c1d686f5b661b3abd87f998d2844e8e9cc905edb57fc9ce349 + // invokation 0x71a6d583d16d142c5c3e8903060e8a4ee5a5016348a9448df6c3e63b68076ec4 + submit: function () { - var tx = this.context.web3.eth.getTransactionFromBlock(this.state.blockNumber, this.state.txNumber) + var tx + if (this.state.txNumber.indexOf('0x') !== -1) { + tx = this.context.web3.eth.getTransaction(this.state.txNumber) + } else { + tx = this.context.web3.eth.getTransactionFromBlock(this.state.blockNumber, this.state.txNumber) + } if (tx) { + if (!tx.to) { + tx.to = '(Contract Creation Code)' + } this.setState({from: tx.from, to: tx.to, hash: tx.hash}) this.props.onNewTxRequested(this.state.blockNumber, parseInt(this.state.txNumber), tx) } else { @@ -25,6 +36,10 @@ module.exports = React.createClass({ } }, + updateTxhash: function (ev) { + this.state.hash = ev.target.value + }, + updateBlockN: function (ev) { this.state.blockNumber = ev.target.value }, @@ -36,8 +51,8 @@ module.exports = React.createClass({ render: function () { return (
- - + + @@ -45,16 +60,28 @@ module.exports = React.createClass({ - - + + - - + + - - + +
Hash: {this.state.hash} + Hash: + + {this.state.hash} +
From: {this.state.from} + From: + + {this.state.from} +
To: {this.state.to} + To: + + {this.state.to} +
diff --git a/src/vmDebugger.js b/src/vmDebugger.js index e340b5c74e..9303e05aa6 100644 --- a/src/vmDebugger.js +++ b/src/vmDebugger.js @@ -43,23 +43,23 @@ module.exports = React.createClass({
- + - + - + - + - + diff --git a/src/web3Admin.js b/src/web3Admin.js index a3a37661c9..492f139337 100644 --- a/src/web3Admin.js +++ b/src/web3Admin.js @@ -96,8 +96,8 @@ module.exports = { params: 3 }), new web3._extend.Method({ - name: 'trace', - call: 'debug_trace', + name: 'traceTransaction', + call: 'debug_traceTransaction', inputFormatter: [null, null], params: 2 }),