align trace with geth trace

pull/7/head
yann300 9 years ago
parent 7a90aee4d4
commit 006dc9b32a
  1. 18
      src/asmCode.js
  2. 29
      src/basicPanel.js
  3. 23
      src/buttonNavigator.js
  4. 10
      src/calldataPanel.js
  5. 10
      src/callstackPanel.js
  6. 11
      src/codeResolver.js
  7. 2
      src/debugger.js
  8. 39
      src/memoryPanel.js
  9. 10
      src/stackPanel.js
  10. 2
      src/stepManager.js
  11. 25
      src/storagePanel.js
  12. 81
      src/traceAnalyser.js
  13. 19
      src/traceCache.js
  14. 165
      src/traceManager.js
  15. 38
      src/traceManagerUtil.js
  16. 22
      src/traceRetriever.js
  17. 34
      src/traceStepManager.js
  18. 47
      src/txBrowser.js
  19. 10
      src/vmDebugger.js
  20. 4
      src/web3Admin.js

@ -48,13 +48,17 @@ module.exports = React.createClass({
if (nextProps.currentStepIndex < 0) return if (nextProps.currentStepIndex < 0) return
codeResolver.setWeb3(this.context.web3) codeResolver.setWeb3(this.context.web3)
var self = this var self = this
this.context.traceManager.getCurrentCalledAddressAt(nextProps.currentStepIndex, function (error, address) { if (nextProps.currentStepIndex === 0) {
if (error) { self.ensureCodeLoaded(this.context.tx.to, nextProps.currentStepIndex)
console.log(error) } else {
} else { this.context.traceManager.getCurrentCalledAddressAt(nextProps.currentStepIndex, function (error, address) {
self.ensureCodeLoaded(address, nextProps.currentStepIndex) if (error) {
} console.log(error)
}) } else {
self.ensureCodeLoaded(address, nextProps.currentStepIndex)
}
})
}
}, },
ensureCodeLoaded: function (address, currentStep) { ensureCodeLoaded: function (address, currentStep) {

@ -6,8 +6,7 @@ module.exports = React.createClass({
getDefaultProps: function () { getDefaultProps: function () {
return { return {
data: null, data: null,
name: null, name: null
renderRow: null
} }
}, },
@ -18,33 +17,9 @@ module.exports = React.createClass({
{this.props.name} {this.props.name}
</div> </div>
<div style={style.panel.tableContainer}> <div style={style.panel.tableContainer}>
<table style={style.panel.table}> <pre style={Object.assign(style.panel.table, style.font)} >{this.props.data}</pre>
<tbody>
{this.renderItems()}
</tbody>
</table>
</div> </div>
</div> </div>
) )
},
renderItems: function () {
if (!this.props.data) {
return []
}
if (!this.props.renderRow) {
var ret = []
for (var key in this.props.data) {
ret.push(
<tr key={key}>
<td>
<pre style={style.font} >{this.props.data[key]}</pre>
</td>
</tr>)
}
return ret
} else {
return this.props.renderRow(this.props.data)
}
} }
}) })

@ -10,7 +10,8 @@ module.exports = React.createClass({
stepIntoBack: React.PropTypes.func.isRequired, stepIntoBack: React.PropTypes.func.isRequired,
stepIntoForward: React.PropTypes.func.isRequired, stepIntoForward: React.PropTypes.func.isRequired,
stepOverBack: 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 () { render: function () {
@ -28,15 +29,27 @@ module.exports = React.createClass({
<button onClick={this.props.stepIntoForward} disabled={this.checkButtonState(1)}> <button onClick={this.props.stepIntoForward} disabled={this.checkButtonState(1)}>
Step Into Forward Step Into Forward
</button> </button>
<button onClick={this.props.jumpNextCall} disabled={this.checkButtonState(1)}>
Jump Next Call
</button>
</div> </div>
) )
}, },
checkButtonState: function (incr) { checkButtonState: function (incr) {
if (incr === -1) { if (!this.context.traceManager) {
return this.props.step === 0 ? 'disabled' : '' return false
} else if (incr === 1) {
return this.props.step >= this.props.max - 1 ? 'disabled' : ''
} }
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' : ''
}
})
} }
}) })

@ -35,9 +35,17 @@ module.exports = React.createClass({
console.log(error) console.log(error)
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) { } else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) {
self.setState({ self.setState({
data: calldata data: self.format(calldata)
}) })
} }
}) })
},
format: function (calldata) {
var ret = ''
for (var key in calldata) {
ret += calldata[key] + '\n'
}
return ret
} }
}) })

@ -35,9 +35,17 @@ module.exports = React.createClass({
console.log(error) console.log(error)
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) { } else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) {
self.setState({ self.setState({
data: callstack data: self.format(callstack)
}) })
} }
}) })
},
format: function (callstack) {
var ret = ''
for (var key in callstack) {
ret += callstack[key] + '\n'
}
return ret
} }
}) })

@ -18,7 +18,7 @@ module.exports = {
return 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) callBack(address, this.cacheExecutingCode(address, transaction.input).code)
return return
} }
@ -41,9 +41,14 @@ module.exports = {
}, },
cacheExecutingCode: function (address, hexCode) { 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')) var code = codeUtils.nameOpCodes(new Buffer(hexCode.substring(2), 'hex'))
this.codes[address] = code[0]
this.instructionsIndexByBytesOffset[address] = code[1]
return { return {
code: code[0], code: code[0],
instructionsIndexByBytesOffset: code[1] instructionsIndexByBytesOffset: code[1]

@ -61,7 +61,7 @@ module.exports = React.createClass({
tx: tx tx: tx
}) })
var self = this var self = this
this.state.traceManager.resolveTrace(blockNumber, txIndex, function (success) { this.state.traceManager.resolveTrace(tx, function (success) {
console.log('trace loaded ' + success) console.log('trace loaded ' + success)
self.setState({ self.setState({
currentStepIndex: 0 currentStepIndex: 0

@ -1,7 +1,6 @@
'use strict' 'use strict'
var React = require('react') var React = require('react')
var BasicPanel = require('./basicPanel') var BasicPanel = require('./basicPanel')
var style = require('./basicStyles')
module.exports = React.createClass({ module.exports = React.createClass({
contextTypes: { contextTypes: {
@ -23,7 +22,7 @@ module.exports = React.createClass({
render: function () { render: function () {
return ( return (
<BasicPanel name='Memory' data={this.state.data} renderRow={this.renderMemoryRow} /> <BasicPanel name='Memory' data={this.state.data} />
) )
}, },
@ -43,36 +42,20 @@ module.exports = React.createClass({
}) })
}, },
renderMemoryRow: function (data) { formatMemory: function (mem, width) {
var ret = [] var ret = ''
if (data) { if (!mem) {
for (var key in data) { return ret
var memSlot = data[key] }
ret.push(
<tr key={key}> if (!mem.substr) {
<td> mem = mem.join('') // geth returns an array, eth return raw string
<pre style={style.font}>{memSlot.address}</pre>
</td>
<td>
<pre style={style.font}>{memSlot.content.raw}</pre>
</td>
<td>
<pre style={style.font}>{memSlot.content.ascii}</pre>
</td>
</tr>)
}
} }
return ret
},
formatMemory: function (mem, width) {
var ret = []
for (var k = 0; k < mem.length; k += (width * 2)) { for (var k = 0; k < mem.length; k += (width * 2)) {
var memory = mem.substr(k, width * 2) var memory = mem.substr(k, width * 2)
ret.push({ var content = this.tryAsciiFormat(memory)
address: this.context.web3.toHex(k), ret += this.context.web3.toHex(k) + ' ' + content.raw + ' ' + content.ascii + '\n'
content: this.tryAsciiFormat(memory)
})
} }
return ret return ret
}, },

@ -35,9 +35,17 @@ module.exports = React.createClass({
console.log(error) console.log(error)
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) { } else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) {
self.setState({ self.setState({
data: stack data: self.format(stack)
}) })
} }
}) })
},
format: function (stack) {
var ret = ''
for (var key in stack) {
ret += stack[key] + '\n'
}
return ret
} }
}) })

@ -33,7 +33,7 @@ module.exports = React.createClass({
stepIntoForward={this.stepIntoForward} stepIntoForward={this.stepIntoForward}
stepOverBack={this.stepOverBack} stepOverBack={this.stepOverBack}
stepOverForward={this.stepOverForward} stepOverForward={this.stepOverForward}
jumpToNextCall={this.jumpToNextCall} jumpNextCall={this.jumpToNextCall}
max={this.state.traceLength} /> max={this.state.traceLength} />
</div> </div>
) )

@ -1,7 +1,6 @@
'use strict' 'use strict'
var React = require('react') var React = require('react')
var BasicPanel = require('./basicPanel') var BasicPanel = require('./basicPanel')
var style = require('./basicStyles')
module.exports = React.createClass({ module.exports = React.createClass({
contextTypes: { contextTypes: {
@ -23,7 +22,7 @@ module.exports = React.createClass({
render: function () { render: function () {
return ( return (
<BasicPanel name='Storage' data={this.state.data} renderRow={this.renderStorageRow} /> <BasicPanel name='Storage' data={this.state.data} />
) )
}, },
@ -32,31 +31,21 @@ module.exports = React.createClass({
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return
var self = this 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) { if (error) {
console.log(error) console.log(error)
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) { } else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) {
self.setState({ self.setState({
data: storage data: self.formatStorage(storage)
}) })
} }
}) })
}, },
renderStorageRow: function (data) { formatStorage: function (storage) {
var ret = [] var ret = ''
if (data) { for (var key in storage) {
for (var key in data) { ret += key + ' ' + storage[key] + '\n'
ret.push(
<tr key={key}>
<td>
<pre style={style.font} >{key}</pre>
</td>
<td>
<pre style={style.font}>{data[key]}</pre>
</td>
</tr>)
}
} }
return ret return ret
} }

@ -1,25 +1,29 @@
'use strict' 'use strict'
var traceManagerUtil = require('./traceManagerUtil')
function TraceAnalyser (_cache) { function TraceAnalyser (_cache) {
this.traceCache = _cache this.traceCache = _cache
this.trace = null this.trace = null
} }
TraceAnalyser.prototype.analyse = function (trace, callback) { TraceAnalyser.prototype.analyse = function (trace, root, callback) {
this.trace = trace this.trace = trace
var currentDepth = 0
this.traceCache.pushStoreChanges(0, root)
var context = { var context = {
currentStorageAddress: trace[0].address, currentStorageAddress: root,
previousStorageAddress: trace[0].address previousStorageAddress: root
} }
var callStack = [] var callStack = [root]
for (var k in this.trace) { this.traceCache.pushCallStack(0, {
callStack: callStack.slice(0)
})
for (var k = 0; k < this.trace.length; k++) {
var step = this.trace[k] var step = this.trace[k]
this.buildCalldata(k, step) this.buildCalldata(k, step)
this.buildMemory(k, step) this.buildMemory(k, step)
var depth = this.buildDepth(k, step, currentDepth, callStack) this.buildDepth(k, step, callStack)
if (depth) {
currentDepth = depth
}
context = this.buildStorage(k, step, context) context = this.buildStorage(k, step, context)
} }
callback(null, true) callback(null, true)
@ -38,40 +42,45 @@ TraceAnalyser.prototype.buildMemory = function (index, step) {
} }
TraceAnalyser.prototype.buildStorage = function (index, step, context) { TraceAnalyser.prototype.buildStorage = function (index, step, context) {
if (step.address) { if (traceManagerUtil.newContextStorage(step)) {
// new context var calledAddress = traceManagerUtil.resolveCalledAddress(index, this.trace)
context.currentStorageAddress = step.address if (calledAddress) {
this.traceCache.pushStoreChanges(index, context.currentStorageAddress) context.currentStorageAddress = calledAddress
} else if (step.inst === 'SSTORE') { } else {
this.traceCache.pushStoreChanges(index, context.currentStorageAddress, step.stack[step.stack.length - 1], step.stack[step.stack.length - 2]) console.log('unable to build storage changes. ' + index + ' does not match with a CALL. storage changes will be corrupted')
} else if (!step.address && step.depth) { }
// returned from context 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 context.currentStorageAddress = context.previousStorageAddress
this.traceCache.pushStoreChanges(index, context.currentStorageAddress) this.traceCache.pushStoreChanges(index + 1, context.currentStorageAddress)
} }
return context return context
} }
TraceAnalyser.prototype.buildDepth = function (index, step, currentDepth, callStack) { TraceAnalyser.prototype.buildDepth = function (index, step, callStack) {
if (step.depth === undefined) return if (traceManagerUtil.isCallInstruction(step) && !traceManagerUtil.isCallToPrecompiledContract(index, this.trace)) {
if (step.depth > currentDepth) { var newAddress = traceManagerUtil.resolveCalledAddress(index, this.trace)
if (index === 0) { if (newAddress) {
callStack.push('0x' + step.address) // new context callStack.push(newAddress)
} else { } else {
// getting the address from the stack console.log('unable to build depth changes. ' + index + ' does not match with a CALL. depth changes will be corrupted')
var callTrace = this.trace[index - 1]
var address = callTrace.stack[callTrace.stack.length - 2]
callStack.push(address) // new context
} }
} else if (step.depth < currentDepth) { this.traceCache.pushCallChanges(step, index + 1)
callStack.pop() // returning from context 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 module.exports = TraceAnalyser

@ -5,9 +5,13 @@ function TraceCache () {
TraceCache.prototype.init = function () { TraceCache.prototype.init = function () {
// ...Changes contains index in the vmtrace of the corresponding changes // ...Changes contains index in the vmtrace of the corresponding changes
this.depthChanges = []
this.memoryChanges = [] this.callChanges = []
this.returnChanges = []
this.calls = {}
this.callDataChanges = [] this.callDataChanges = []
this.memoryChanges = []
this.storageChanges = [] this.storageChanges = []
this.sstore = {} // all sstore occurence in the trace 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) 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) this.memoryChanges.push(value)
} }
TraceCache.prototype.pushDepthChanges = function (value) { TraceCache.prototype.pushCallChanges = function (step, value) {
this.depthChanges.push(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) { TraceCache.prototype.pushCallStack = function (index, callStack) {

@ -13,27 +13,36 @@ function TraceManager (_web3) {
this.traceAnalyser = new TraceAnalyser(this.traceCache) this.traceAnalyser = new TraceAnalyser(this.traceCache)
this.traceRetriever = new TraceRetriever(_web3) this.traceRetriever = new TraceRetriever(_web3)
this.traceStepManager = new TraceStepManager(this.traceAnalyser) this.traceStepManager = new TraceStepManager(this.traceAnalyser)
this.tx
} }
// init section // init section
TraceManager.prototype.resolveTrace = function (blockNumber, txNumber, callback) { TraceManager.prototype.resolveTrace = function (tx, callback) {
this.isLoading = true this.tx = tx
this.init() this.init()
if (!this.web3) callback(false) if (!this.web3) callback(false)
this.isLoading = true
var self = this var self = this
this.traceRetriever.getTrace(blockNumber, parseInt(txNumber), function (error, result) { this.traceRetriever.getTrace(tx.hash, function (error, result) {
self.trace = result
if (error) { if (error) {
console.log(error) console.log(error)
self.isLoading = false
} else { } else {
self.traceAnalyser.analyse(result, function (error, result) { if (result.structLogs.length > 0) {
if (error) { self.trace = result.structLogs
console.log(error) self.traceAnalyser.analyse(result.structLogs, tx.to, function (error, result) {
callback(false) if (error) {
} else { console.log(error)
callback(true) 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 // API section
TraceManager.prototype.getLength = function (callback) { TraceManager.prototype.getLength = function (callback) {
if (!this.trace) callback('no trace available', null) if (!this.trace) {
callback(null, this.trace.length) 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 stoChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.storageChanges)
if (stoChange === undefined) return callback('no storage found', null)
var address = this.traceCache.sstore[stoChange].address
var self = this var self = this
this.traceRetriever.getStorage(blockNumber, txIndex, address, function (error, result) { if (this.traceRetriever.debugStorageAtAvailable()) {
if (error) { var address = this.traceCache.sstore[stoChange].address
console.log(error) this.traceRetriever.getStorage(tx, address, function (error, result) {
callback(error, null) if (error) {
} else { console.log(error)
var storage = self.traceCache.rebuildStorage(address, result, stepIndex) callback(error, null)
callback(null, storage) } 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) { 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) 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]) callback(null, [this.trace[callDataChange].calldata])
} }
TraceManager.prototype.getCallStackAt = function (stepIndex, callback) { TraceManager.prototype.getCallStackAt = function (stepIndex, callback) {
var callStackChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.depthChanges) if (stepIndex >= this.trace.length) {
if (!callStackChange) return callback('no callstack found', null) callback('trace smaller than requested', null)
callback(null, this.traceCache.callStack[callStackChange].stack) 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) { TraceManager.prototype.getStackAt = function (stepIndex, callback) {
if (stepIndex >= this.trace.length) {
callback('trace smaller than requested', null)
return
}
var stack var stack
if (this.trace[stepIndex].stack) { // there's always a stack if (this.trace[stepIndex].stack) { // there's always a stack
stack = this.trace[stepIndex].stack.slice(0) stack = this.trace[stepIndex].stack.slice(0)
@ -88,48 +120,103 @@ TraceManager.prototype.getStackAt = function (stepIndex, callback) {
} }
} }
TraceManager.prototype.getLastDepthIndexChangeSince = function (stepIndex, callback) { TraceManager.prototype.getLastCallChangeSince = function (stepIndex, callback) {
var depthIndex = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.depthChanges) if (stepIndex >= this.trace.length) {
callback(null, depthIndex) 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) { TraceManager.prototype.getCurrentCalledAddressAt = function (stepIndex, callback) {
if (stepIndex > this.trace.length) {
callback('trace smaller than requested', null)
return
}
var self = this var self = this
this.getLastDepthIndexChangeSince(stepIndex, function (error, addressIndex) { this.getLastCallChangeSince(stepIndex, function (error, addressIndex) {
if (error) { if (error) {
callback(error, null) callback(error, null)
} else { } 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) { 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) 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) callback(null, this.trace[lastChanges].memory)
} }
TraceManager.prototype.getCurrentPC = function (stepIndex, callback) { TraceManager.prototype.getCurrentPC = function (stepIndex, callback) {
if (stepIndex >= this.trace.length) {
callback('trace smaller than requested', null)
return
}
callback(null, this.trace[stepIndex].pc) callback(null, this.trace[stepIndex].pc)
} }
TraceManager.prototype.getCurrentStep = function (stepIndex, callback) { TraceManager.prototype.getCurrentStep = function (stepIndex, callback) {
if (stepIndex >= this.trace.length) {
callback('trace smaller than requested', null)
return
}
callback(null, this.trace[stepIndex].steps) callback(null, this.trace[stepIndex].steps)
} }
TraceManager.prototype.getMemExpand = function (stepIndex, callback) { 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 : '') callback(null, this.trace[stepIndex].memexpand ? this.trace[stepIndex].memexpand : '')
} }
TraceManager.prototype.getStepCost = function (stepIndex, callback) { 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) { TraceManager.prototype.getRemainingGas = function (stepIndex, callback) {
if (stepIndex >= this.trace.length) {
callback('trace smaller than requested', null)
return
}
callback(null, this.trace[stepIndex].gas) callback(null, this.trace[stepIndex].gas)
} }
TraceManager.prototype.isCreationStep = function (stepIndex) {
return traceManagerUtil.isCreateInstruction(stepIndex, this.trace)
}
// step section // step section
TraceManager.prototype.findStepOverBack = function (currentStep) { TraceManager.prototype.findStepOverBack = function (currentStep) {
return this.traceStepManager.findStepOverBack(currentStep) return this.traceStepManager.findStepOverBack(currentStep)
@ -147,4 +234,8 @@ TraceManager.prototype.findStepOutForward = function (currentStep) {
return this.traceStepManager.findStepOutForward(currentStep) return this.traceStepManager.findStepOutForward(currentStep)
} }
TraceManager.prototype.findNextCall = function (currentStep) {
return this.traceStepManager.findNextCall(currentStep)
}
module.exports = TraceManager module.exports = TraceManager

@ -24,13 +24,39 @@ module.exports = {
} }
}, },
// vmTraceIndex has to point to a CALL, CODECALL, ...
resolveCalledAddress: function (vmTraceIndex, trace) { resolveCalledAddress: function (vmTraceIndex, trace) {
var address = trace[vmTraceIndex].address var step = trace[vmTraceIndex]
if (vmTraceIndex > 0) { if (this.isCallInstruction(step)) {
var stack = trace[vmTraceIndex - 1].stack // callcode, delegatecall, ... var stack = step.stack // callcode, delegatecall, ...
address = stack[stack.length - 2] 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
}
}
} }

@ -4,22 +4,34 @@ function TraceRetriever (_web3) {
this.storages = {} // contains all intial storage (by addresses) this.storages = {} // contains all intial storage (by addresses)
} }
TraceRetriever.prototype.getTrace = function (blockNumber, txNumber, callback) { TraceRetriever.prototype.getTrace = function (txHash, callback) {
this.web3.debug.trace(blockNumber, parseInt(txNumber), function (error, result) { var options = {
disableStorage: this.debugStorageAtAvailable(),
disableMemory: false,
disableStack: false,
fullStorage: !this.debugStorageAtAvailable()
}
this.web3.debug.traceTransaction(txHash, options, function (error, result) {
callback(error, result) callback(error, result)
}) })
} }
TraceRetriever.prototype.getStorage = function (blockNumber, txIndex, address, callback) { TraceRetriever.prototype.getStorage = function (tx, address, callback) {
if (this.storages[address]) { if (tx.to === '(Contract Creation Code)') {
callback(null, {})
} else if (this.storages[address]) {
callback(null, this.storages[address]) callback(null, this.storages[address])
} else { } else {
var self = this 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 self.storages[address] = result
callback(error, result) callback(error, result)
}) })
} }
} }
TraceRetriever.prototype.debugStorageAtAvailable = function () {
return true // storageAt not available if using geth
}
module.exports = TraceRetriever module.exports = TraceRetriever

@ -1,32 +1,28 @@
'use strict' 'use strict'
var traceManagerUtil = require('./traceManagerUtil')
function TraceStepManager (_traceAnalyser) { function TraceStepManager (_traceAnalyser) {
this.traceAnalyser = _traceAnalyser this.traceAnalyser = _traceAnalyser
} }
TraceStepManager.prototype.isCallInstruction = function (index) { TraceStepManager.prototype.isCallInstruction = function (index) {
var state = this.traceAnalyser.trace[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) { TraceStepManager.prototype.isReturnInstruction = function (index) {
var state = this.traceAnalyser.trace[index] var state = this.traceAnalyser.trace[index]
return state.instname === 'RETURN' return traceManagerUtil.isReturnInstruction(state)
} }
TraceStepManager.prototype.findStepOverBack = function (currentStep) { TraceStepManager.prototype.findStepOverBack = function (currentStep) {
if (this.isReturnInstruction(currentStep - 1)) { if (currentStep === 0) return 0
return this.findStepOutBack(currentStep) return this.findStepOutBack(currentStep)
} else {
return currentStep - 1
}
} }
TraceStepManager.prototype.findStepOverForward = function (currentStep) { TraceStepManager.prototype.findStepOverForward = function (currentStep) {
if (this.isCallInstruction(currentStep)) { if (currentStep === this.traceAnalyser.trace.length - 1) return currentStep
return this.findStepOutForward(currentStep) return this.findStepOutForward(currentStep)
} else {
return currentStep + 1
}
} }
TraceStepManager.prototype.findStepOutBack = function (currentStep) { TraceStepManager.prototype.findStepOutBack = function (currentStep) {
@ -49,7 +45,7 @@ TraceStepManager.prototype.findStepOutBack = function (currentStep) {
TraceStepManager.prototype.findStepOutForward = function (currentStep) { TraceStepManager.prototype.findStepOutForward = function (currentStep) {
var i = currentStep var i = currentStep
var depth = 0 var depth = 0
while (++i < this.traceAnalyser.length) { while (++i < this.traceAnalyser.trace.length) {
if (this.isReturnInstruction(i)) { if (this.isReturnInstruction(i)) {
if (depth === 0) { if (depth === 0) {
break break
@ -60,7 +56,17 @@ TraceStepManager.prototype.findStepOutForward = function (currentStep) {
depth++ 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 module.exports = TraceStepManager

@ -12,12 +12,23 @@ module.exports = React.createClass({
}, },
getInitialState: function () { getInitialState: function () {
return {blockNumber: '1382256', txNumber: '1', from: '', to: '', hash: ''} return {blockNumber: '1000110', txNumber: '0x71a6d583d16d142c5c3e8903060e8a4ee5a5016348a9448df6c3e63b68076ec4', from: '', to: '', hash: ''}
}, },
// creation 0xa9619e1d0a35b2c1d686f5b661b3abd87f998d2844e8e9cc905edb57fc9ce349
// invokation 0x71a6d583d16d142c5c3e8903060e8a4ee5a5016348a9448df6c3e63b68076ec4
submit: function () { 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) {
if (!tx.to) {
tx.to = '(Contract Creation Code)'
}
this.setState({from: tx.from, to: tx.to, hash: tx.hash}) this.setState({from: tx.from, to: tx.to, hash: tx.hash})
this.props.onNewTxRequested(this.state.blockNumber, parseInt(this.state.txNumber), tx) this.props.onNewTxRequested(this.state.blockNumber, parseInt(this.state.txNumber), tx)
} else { } else {
@ -25,6 +36,10 @@ module.exports = React.createClass({
} }
}, },
updateTxhash: function (ev) {
this.state.hash = ev.target.value
},
updateBlockN: function (ev) { updateBlockN: function (ev) {
this.state.blockNumber = ev.target.value this.state.blockNumber = ev.target.value
}, },
@ -36,8 +51,8 @@ module.exports = React.createClass({
render: function () { render: function () {
return ( return (
<div style={style.container}> <div style={style.container}>
<input onChange={this.updateBlockN} type='text' placeholder={'Block number or hash (default 1382256)' + this.state.blockNumber}></input> <input onChange={this.updateBlockN} type='text' placeholder={'Block number (default 1000110)' + this.state.blockNumber}></input>
<input onChange={this.updateTxN} type='text' placeholder={'Transaction Number (default 1) ' + this.state.txNumber}></input> <input onChange={this.updateTxN} type='text' placeholder={'Transaction Number or hash (default 2) ' + this.state.txNumber}></input>
<button onClick={this.submit}> <button onClick={this.submit}>
Get Get
</button> </button>
@ -45,16 +60,28 @@ module.exports = React.createClass({
<table> <table>
<tbody> <tbody>
<tr> <tr>
<td>Hash: </td> <td>
<td>{this.state.hash}</td> Hash:
</td>
<td>
{this.state.hash}
</td>
</tr> </tr>
<tr> <tr>
<td>From: </td> <td>
<td>{this.state.from}</td> From:
</td>
<td>
{this.state.from}
</td>
</tr> </tr>
<tr> <tr>
<td>To: </td> <td>
<td>{this.state.to}</td> To:
</td>
<td>
{this.state.to}
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

@ -43,23 +43,23 @@ module.exports = React.createClass({
</div> </div>
</td> </td>
<td> <td>
<CalldataPanel currentStepIndex={this.props.currentStepIndex} /> <StackPanel currentStepIndex={this.props.currentStepIndex} />
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<StackPanel currentStepIndex={this.props.currentStepIndex} /> <StoragePanel currentStepIndex={this.props.currentStepIndex} />
</td> </td>
<td> <td>
<CallstackPanel currentStepIndex={this.props.currentStepIndex} /> <MemoryPanel currentStepIndex={this.props.currentStepIndex} />
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<StoragePanel currentStepIndex={this.props.currentStepIndex} /> <CalldataPanel currentStepIndex={this.props.currentStepIndex} />
</td> </td>
<td> <td>
<MemoryPanel currentStepIndex={this.props.currentStepIndex} /> <CallstackPanel currentStepIndex={this.props.currentStepIndex} />
</td> </td>
</tr> </tr>
</tbody> </tbody>

@ -96,8 +96,8 @@ module.exports = {
params: 3 params: 3
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'trace', name: 'traceTransaction',
call: 'debug_trace', call: 'debug_traceTransaction',
inputFormatter: [null, null], inputFormatter: [null, null],
params: 2 params: 2
}), }),

Loading…
Cancel
Save