diff --git a/src/basicPanel.js b/src/basicPanel.js index 7ce113cacc..a1e02a6189 100644 --- a/src/basicPanel.js +++ b/src/basicPanel.js @@ -38,7 +38,7 @@ module.exports = React.createClass({ ret.push( - {this.props.data[key]} +
{this.props.data[key]}
) } diff --git a/src/memoryPanel.js b/src/memoryPanel.js index 8dbfb066b3..1724b32a07 100644 --- a/src/memoryPanel.js +++ b/src/memoryPanel.js @@ -50,13 +50,13 @@ module.exports = React.createClass({ ret.push( - {memSlot.address} +
{memSlot.address}
- {memSlot.content.raw} +
{memSlot.content.raw}
- {memSlot.content.ascii} +
{memSlot.content.ascii}
) } diff --git a/src/storagePanel.js b/src/storagePanel.js index d6799b72ec..b5eac32de7 100644 --- a/src/storagePanel.js +++ b/src/storagePanel.js @@ -49,10 +49,10 @@ module.exports = React.createClass({ ret.push( - {key} +
{key}
- {data[key]} +
{data[key]}
) } diff --git a/src/traceAnalyser.js b/src/traceAnalyser.js new file mode 100644 index 0000000000..d79368c1ba --- /dev/null +++ b/src/traceAnalyser.js @@ -0,0 +1,77 @@ +'use strict' +function TraceAnalyser (_cache) { + this.traceCache = _cache + this.trace = null +} + +TraceAnalyser.prototype.analyse = function (trace, callback) { + this.trace = trace + var currentDepth = 0 + var context = { + currentStorageAddress: trace[0].address, + previousStorageAddress: trace[0].address + } + var callStack = [] + for (var k in this.trace) { + 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 + } + context = this.buildStorage(k, step, context) + } + callback(null, true) +} + +TraceAnalyser.prototype.buildCalldata = function (index, step) { + if (step.calldata) { + this.traceCache.pushCallDataChanges(index) + } +} + +TraceAnalyser.prototype.buildMemory = function (index, step) { + if (step.memory) { + this.traceCache.pushMemoryChanges(index) + } +} + +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 + context.currentStorageAddress = context.previousStorageAddress + this.traceCache.pushStoreChanges(index, 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 + } 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 + } + } else if (step.depth < currentDepth) { + callStack.pop() // returning from context + } + this.traceCache.pushCallStack(index, { + stack: callStack.slice(0), + depth: step.depth + }) + this.traceCache.pushDepthChanges(index) + return step.depth +} + +module.exports = TraceAnalyser diff --git a/src/traceCache.js b/src/traceCache.js new file mode 100644 index 0000000000..d7cb3e9a3a --- /dev/null +++ b/src/traceCache.js @@ -0,0 +1,55 @@ +'use strict' +function TraceCache () { + this.init() +} + +TraceCache.prototype.init = function () { + // ...Changes contains index in the vmtrace of the corresponding changes + this.depthChanges = [] + this.memoryChanges = [] + this.callDataChanges = [] + 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) +} + +TraceCache.prototype.pushCallDataChanges = function (value) { + this.callDataChanges.push(value) +} + +TraceCache.prototype.pushMemoryChanges = function (value) { + this.memoryChanges.push(value) +} + +TraceCache.prototype.pushDepthChanges = function (value) { + this.depthChanges.push(value) +} + +TraceCache.prototype.pushCallStack = function (index, callStack) { + this.callStack[index] = callStack +} + +TraceCache.prototype.pushStoreChanges = function (index, address, key, value) { + this.sstore[index] = { + 'address': address, + 'key': key, + 'value': value + } + this.storageChanges.push(index) +} + +TraceCache.prototype.rebuildStorage = function (address, storage, index) { + for (var k in this.storageChanges) { + var changesIndex = this.storageChanges[k] + if (changesIndex > index) { + return storage + } + var sstore = this.sstore[changesIndex] + if (sstore.address === address && sstore.key) { + storage[sstore.key] = sstore.value + } + } + return storage +} + +module.exports = TraceCache diff --git a/src/traceManager.js b/src/traceManager.js index ab0f4cc843..c53e66b81e 100644 --- a/src/traceManager.js +++ b/src/traceManager.js @@ -1,21 +1,18 @@ 'use strict' +var TraceAnalyser = require('./traceAnalyser') +var TraceRetriever = require('./traceRetriever') +var TraceCache = require('./traceCache') +var TraceStepManager = require('./traceStepManager') +var traceManagerUtil = require('./traceManagerUtil') + function TraceManager (_web3) { this.web3 = _web3 - this.isLoading = false this.trace = null - - // vmtrace changes section - this.depthChanges = [] - this.callStack = {} - this.memoryChanges = [] - this.callDataChanges = [] - - // storage section - this.storageChanges = [] - this.vmTraceIndexByStorageChange = {} - this.vmTraceChangesRef = [] - this.storages = {} + this.traceCache = new TraceCache() + this.traceAnalyser = new TraceAnalyser(this.traceCache) + this.traceRetriever = new TraceRetriever(_web3) + this.traceStepManager = new TraceStepManager(this.traceAnalyser) } // init section @@ -24,111 +21,26 @@ TraceManager.prototype.resolveTrace = function (blockNumber, txNumber, callback) this.init() if (!this.web3) callback(false) var self = this - this.web3.debug.trace(blockNumber, parseInt(txNumber), function (error, result) { - if (!error) { - self.computeTrace(result) - callback(true) - } else { + this.traceRetriever.getTrace(blockNumber, parseInt(txNumber), function (error, result) { + self.trace = result + if (error) { console.log(error) - callback(false) + } else { + self.traceAnalyser.analyse(result, function (error, result) { + if (error) { + console.log(error) + callback(false) + } else { + callback(true) + } + }) } - this.isLoading = false }) } TraceManager.prototype.init = function () { this.trace = null - this.depthChanges = [] - this.memoryChanges = [] - this.callDataChanges = [] - this.storageChanges = [] - this.vmTraceIndexByStorageChange = {} - this.vmTraceChangesRef = [] - this.callStack = {} -} - -TraceManager.prototype.computeTrace = function (trace) { - this.trace = trace - var currentDepth = 0 - var currentStorageAddress - var callStack = [] - for (var k in this.trace) { - var step = this.trace[k] - - this.buildCalldata(k, step) - this.buildMemory(k, step) - currentStorageAddress = this.buildStorage(k, step, currentStorageAddress) - var depth = this.buildDepth(k, step, currentDepth, callStack) - if (depth) { - currentDepth = depth - } - } -} - -// compute trace section -TraceManager.prototype.buildCalldata = function (index, step) { - if (step.calldata) { - this.callDataChanges.push(index) - } -} - -TraceManager.prototype.buildMemory = function (index, step) { - if (step.memory) { - this.memoryChanges.push(index) - } -} - -TraceManager.prototype.buildStorage = function (index, step, currentAddress) { - var change = false - if (step.address) { - // new context - this.storageChanges.push({ address: step.address, changes: [] }) - change = true - } else if (step.inst === 'SSTORE') { - this.storageChanges[this.storageChanges.length - 1].changes.push( - { - 'key': step.stack[step.stack.length - 1], - 'value': step.stack[step.stack.length - 2] - }) - change = true - } else if (!step.address && step.depth) { - // returned from context - var address = this.storageChanges[this.storageChanges.length - 2].address - this.storageChanges.push({ address: address, changes: [] }) - change = true - } - - if (change) { - this.vmTraceIndexByStorageChange[index] = { - context: this.storageChanges.length - 1, - changes: this.storageChanges[this.storageChanges.length - 1].changes.length - 1 - } - this.vmTraceChangesRef.push(index) - } - return currentAddress -} - -TraceManager.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 - } 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 - } - } else if (step.depth < currentDepth) { - callStack.pop() // returning from context - } - this.callStack[index] = { - stack: callStack.slice(0), - depth: step.depth, - address: step.address - } - this.depthChanges.push(index) - return step.depth + this.traceCache.init() } // API section @@ -138,39 +50,31 @@ TraceManager.prototype.getLength = function (callback) { } TraceManager.prototype.getStorageAt = function (stepIndex, blockNumber, txIndex, callback) { - var stoChange = this.findLowerBound(stepIndex, this.vmTraceChangesRef) - if (!stoChange) { - callback('cannot rebuild storage', null) - } + var stoChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.storageChanges) - var changeRefs = this.vmTraceIndexByStorageChange[stoChange] - var address = this.storageChanges[changeRefs.context].address + var address = this.traceCache.sstore[stoChange].address var self = this - this.retrieveStorage(address, blockNumber, txIndex, function (storage) { - for (var k = 0; k < changeRefs.context; k++) { - var context = self.storageChanges[k] - if (context.address === address) { - for (var i = 0; i < context.changes.length; i++) { - if (i > changeRefs.changes) break - var change = context.changes[i] - storage[change.key] = change.value - } - } + 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) } - callback(null, storage) }) } TraceManager.prototype.getCallDataAt = function (stepIndex, callback) { - var callDataChange = this.findLowerBound(stepIndex, this.callDataChanges) + var callDataChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.callDataChanges) if (!callDataChange) return callback('no calldata found', null) callback(null, [this.trace[callDataChange].calldata]) } TraceManager.prototype.getCallStackAt = function (stepIndex, callback) { - var callStackChange = this.findLowerBound(stepIndex, this.depthChanges) + var callStackChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.depthChanges) if (!callStackChange) return callback('no callstack found', null) - callback(null, this.callStack[callStackChange].stack) + callback(null, this.traceCache.callStack[callStackChange].stack) } TraceManager.prototype.getStackAt = function (stepIndex, callback) { @@ -185,7 +89,7 @@ TraceManager.prototype.getStackAt = function (stepIndex, callback) { } TraceManager.prototype.getLastDepthIndexChangeSince = function (stepIndex, callback) { - var depthIndex = this.findLowerBound(stepIndex, this.depthChanges) + var depthIndex = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.depthChanges) callback(null, depthIndex) } @@ -195,13 +99,13 @@ TraceManager.prototype.getCurrentCalledAddressAt = function (stepIndex, callback if (error) { callback(error, null) } else { - callback(null, self.resolveAddress(addressIndex)) + callback(null, traceManagerUtil.resolveCalledAddress(addressIndex, self.trace)) } }) } TraceManager.prototype.getMemoryAt = function (stepIndex, callback) { - var lastChanges = this.findLowerBound(stepIndex, this.memoryChanges) + var lastChanges = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.memoryChanges) if (!lastChanges) return callback('no memory found', null) callback(null, this.trace[lastChanges].memory) } @@ -227,114 +131,20 @@ TraceManager.prototype.getRemainingGas = function (stepIndex, callback) { } // step section -TraceManager.prototype.isCallInstruction = function (index) { - var state = this.trace[index] - return state.instname === 'CALL' || state.instname === 'CALLCODE' || state.instname === 'CREATE' || state.instname === 'DELEGATECALL' -} - -TraceManager.prototype.isReturnInstruction = function (index) { - var state = this.trace[index] - return state.instname === 'RETURN' -} - TraceManager.prototype.findStepOverBack = function (currentStep) { - if (this.isReturnInstruction(currentStep - 1)) { - return this.findStepOutBack(currentStep) - } else { - return currentStep - 1 - } + return this.traceStepManager.findStepOverBack(currentStep) } TraceManager.prototype.findStepOverForward = function (currentStep) { - if (this.isCallInstruction(currentStep)) { - return this.findStepOutForward(currentStep) - } else { - return currentStep + 1 - } + return this.traceStepManager.findStepOverForward(currentStep) } TraceManager.prototype.findStepOutBack = function (currentStep) { - var i = currentStep - 1 - var depth = 0 - while (--i >= 0) { - if (this.isCallInstruction(i)) { - if (depth === 0) { - break - } else { - depth-- - } - } else if (this.isReturnInstruction(i)) { - depth++ - } - } - return i + return this.traceStepManager.findStepOutBack(currentStep) } TraceManager.prototype.findStepOutForward = function (currentStep) { - var i = currentStep - var depth = 0 - while (++i < this.trace.length) { - if (this.isReturnInstruction(i)) { - if (depth === 0) { - break - } else { - depth-- - } - } else if (this.isCallInstruction(i)) { - depth++ - } - } - return i + 1 -} - -// util section -TraceManager.prototype.findLowerBound = function (target, changes) { - if (changes.length === 1) { - if (changes[0] > target) { - // we only a closest maximum, returning 0 - return null - } else { - return changes[0] - } - } - - var middle = Math.floor(changes.length / 2) - if (changes[middle] > target) { - return this.findLowerBound(target, changes.slice(0, middle)) - } else if (changes[middle] < target) { - return this.findLowerBound(target, changes.slice(middle, changes.length)) - } else { - return changes[middle] - } -} - -TraceManager.prototype.resolveAddress = function (vmTraceIndex) { - var address = this.trace[vmTraceIndex].address - if (vmTraceIndex > 0) { - var stack = this.trace[vmTraceIndex - 1].stack // callcode, delegatecall, ... - address = stack[stack.length - 2] - } - return address -} - -// retrieve the storage of an account just after the execution of tx -TraceManager.prototype.retrieveStorage = function (address, blockNumber, txIndex, callBack) { - if (this.storages[address]) { - callBack(this.storages[address]) - } - var self = this - if (blockNumber !== null && txIndex !== null) { - this.web3.debug.storageAt(blockNumber, txIndex, address, function (error, result) { - if (error) { - console.log(error) - } else { - self.storages[address] = result - callBack(result) - } - }) - } else { - console.log('blockNumber/txIndex are not defined') - } + return this.traceStepManager.findStepOutForward(currentStep) } module.exports = TraceManager diff --git a/src/traceManagerUtil.js b/src/traceManagerUtil.js new file mode 100644 index 0000000000..3231efa5af --- /dev/null +++ b/src/traceManagerUtil.js @@ -0,0 +1,36 @@ +module.exports = { + // util section + findLowerBound: function (target, changes) { + if (changes.length === 0) { + return undefined + } + + if (changes.length === 1) { + if (changes[0] > target) { + // we only a closest maximum, returning O + return 0 + } else { + return changes[0] + } + } + + var middle = Math.floor(changes.length / 2) + if (changes[middle] > target) { + return this.findLowerBound(target, changes.slice(0, middle)) + } else if (changes[middle] < target) { + return this.findLowerBound(target, changes.slice(middle, changes.length)) + } else { + return changes[middle] + } + }, + + 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] + } + return address + } + +} diff --git a/src/traceRetriever.js b/src/traceRetriever.js new file mode 100644 index 0000000000..238510b66b --- /dev/null +++ b/src/traceRetriever.js @@ -0,0 +1,25 @@ +'use strict' +function TraceRetriever (_web3) { + this.web3 = _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) { + callback(error, result) + }) +} + +TraceRetriever.prototype.getStorage = function (blockNumber, txIndex, address, callback) { + if (this.storages[address]) { + callback(null, this.storages[address]) + } else { + var self = this + this.web3.debug.storageAt(blockNumber, txIndex, address, function (error, result) { + self.storages[address] = result + callback(error, result) + }) + } +} + +module.exports = TraceRetriever diff --git a/src/traceStepManager.js b/src/traceStepManager.js new file mode 100644 index 0000000000..40d9afc098 --- /dev/null +++ b/src/traceStepManager.js @@ -0,0 +1,66 @@ +'use strict' +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' +} + +TraceStepManager.prototype.isReturnInstruction = function (index) { + var state = this.traceAnalyser.trace[index] + return state.instname === 'RETURN' +} + +TraceStepManager.prototype.findStepOverBack = function (currentStep) { + if (this.isReturnInstruction(currentStep - 1)) { + return this.findStepOutBack(currentStep) + } else { + return currentStep - 1 + } +} + +TraceStepManager.prototype.findStepOverForward = function (currentStep) { + if (this.isCallInstruction(currentStep)) { + return this.findStepOutForward(currentStep) + } else { + return currentStep + 1 + } +} + +TraceStepManager.prototype.findStepOutBack = function (currentStep) { + var i = currentStep - 1 + var depth = 0 + while (--i >= 0) { + if (this.isCallInstruction(i)) { + if (depth === 0) { + break + } else { + depth-- + } + } else if (this.isReturnInstruction(i)) { + depth++ + } + } + return i +} + +TraceStepManager.prototype.findStepOutForward = function (currentStep) { + var i = currentStep + var depth = 0 + while (++i < this.traceAnalyser.length) { + if (this.isReturnInstruction(i)) { + if (depth === 0) { + break + } else { + depth-- + } + } else if (this.isCallInstruction(i)) { + depth++ + } + } + return i + 1 +} + +module.exports = TraceStepManager