parent
cfe1903a05
commit
79ae4bf993
@ -0,0 +1,83 @@ |
|||||||
|
'use strict' |
||||||
|
var React = require('react') |
||||||
|
var style = require('./basicStyles') |
||||||
|
var codeResolver = require('./codeResolver') |
||||||
|
|
||||||
|
module.exports = React.createClass({ |
||||||
|
contextTypes: { |
||||||
|
traceManager: React.PropTypes.object, |
||||||
|
tx: React.PropTypes.object, |
||||||
|
web3: React.PropTypes.object |
||||||
|
}, |
||||||
|
|
||||||
|
getInitialState: function () { |
||||||
|
return { |
||||||
|
code: [], |
||||||
|
selected: -1, |
||||||
|
address: '' // selected instruction in the asm
|
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
getDefaultProps: function () { |
||||||
|
return { |
||||||
|
currentStepIndex: -1 |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
render: function () { |
||||||
|
return ( |
||||||
|
<select |
||||||
|
size='10' |
||||||
|
ref='itemsList' |
||||||
|
style={style.instructionsList} |
||||||
|
value={this.state.selected}> |
||||||
|
{this.renderAssemblyItems()} |
||||||
|
</select> |
||||||
|
) |
||||||
|
}, |
||||||
|
|
||||||
|
renderAssemblyItems: function () { |
||||||
|
if (this.state.code) { |
||||||
|
return this.state.code.map(function (item, i) { |
||||||
|
return <option key={i} value={i}>{item}</option> |
||||||
|
}) |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
componentWillReceiveProps: function (nextProps) { |
||||||
|
console.log('asm' + JSON.stringify(nextProps)) |
||||||
|
if (nextProps.currentStepIndex < 0) return |
||||||
|
codeResolver.setWeb3(this.context.web3) |
||||||
|
var self = this |
||||||
|
this.context.traceManager.getCurrentCalledAddressAt(nextProps.currentStepIndex, function (address) { |
||||||
|
self.ensureCodeLoaded(address, nextProps.currentStepIndex) |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
ensureCodeLoaded: function (address, currentStep) { |
||||||
|
if (address !== this.state.address) { |
||||||
|
this.setState({ |
||||||
|
code: ['loading...'] |
||||||
|
}) |
||||||
|
var self = this |
||||||
|
codeResolver.resolveCode(address, currentStep, this.context.tx, function (address, code) { |
||||||
|
self.setState({ |
||||||
|
code: code, |
||||||
|
address: address |
||||||
|
}) |
||||||
|
self.setInstructionIndex(address, currentStep) |
||||||
|
}) |
||||||
|
} else { |
||||||
|
this.setInstructionIndex(this.state.address, currentStep) |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
setInstructionIndex: function (address, step) { |
||||||
|
var self = this |
||||||
|
this.context.traceManager.getCurrentPC(step, function (instIndex) { |
||||||
|
self.setState({ |
||||||
|
selected: codeResolver.getInstructionIndex(address, instIndex) |
||||||
|
}) |
||||||
|
}) |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,67 @@ |
|||||||
|
'use strict' |
||||||
|
var codeUtils = require('./codeUtils') |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
web3: null, |
||||||
|
|
||||||
|
codes: {}, // assembly items instructions list by contract addesses
|
||||||
|
instructionsIndexByBytesOffset: {}, // mapping between bytes offset and instructions index.
|
||||||
|
|
||||||
|
setWeb3: function (web3) { |
||||||
|
this.web3 = web3 |
||||||
|
}, |
||||||
|
|
||||||
|
resolveCode: function (address, vmTraceIndex, transaction, callBack) { |
||||||
|
var cache = this.getExecutingCodeFromCache(address) |
||||||
|
if (cache) { |
||||||
|
callBack(address, cache.code) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if (vmTraceIndex === 0 && transaction.to === null) { // start of the trace
|
||||||
|
callBack(address, this.cacheExecutingCode(address, transaction.input).code) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
var self = this |
||||||
|
this.loadCode(address, function (code) { |
||||||
|
callBack(address, self.cacheExecutingCode(address, code).code) |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
loadCode: function (address, callback) { |
||||||
|
console.log('loading new code from web3 ' + address) |
||||||
|
this.web3.eth.getCode(address, function (error, result) { |
||||||
|
if (error) { |
||||||
|
console.log(error) |
||||||
|
} else { |
||||||
|
callback(result) |
||||||
|
} |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
cacheExecutingCode: function (address, 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] |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
getExecutingCodeFromCache: function (address) { |
||||||
|
if (this.codes[address]) { |
||||||
|
return { |
||||||
|
code: this.codes[address], |
||||||
|
instructionsIndexByBytesOffset: this.instructionsIndexByBytesOffset[address] |
||||||
|
} |
||||||
|
} else { |
||||||
|
return null |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
getInstructionIndex: function (address, pc) { |
||||||
|
return this.getExecutingCodeFromCache(address).instructionsIndexByBytesOffset[pc] |
||||||
|
} |
||||||
|
} |
@ -1,51 +1,70 @@ |
|||||||
'use strict' |
'use strict' |
||||||
var React = require('react') |
var React = require('react') |
||||||
var TxBrowser = require('./txBrowser') |
var TxBrowser = require('./txBrowser') |
||||||
|
var StepManager = require('./stepManager') |
||||||
var AssemblyItemsBrowser = require('./assemblyItemsBrowser') |
var AssemblyItemsBrowser = require('./assemblyItemsBrowser') |
||||||
|
var traceManager = require('./traceManager') |
||||||
var style = require('./basicStyles') |
var style = require('./basicStyles') |
||||||
|
|
||||||
module.exports = React.createClass({ |
module.exports = React.createClass({ |
||||||
getInitialState: function () { |
getInitialState: function () { |
||||||
return { |
return { |
||||||
vmTrace: null, |
tx: null, |
||||||
state: '', |
currentStepIndex: -1 // index of the selected item in the vmtrace
|
||||||
currentStep: -1 |
|
||||||
} |
} |
||||||
}, |
}, |
||||||
|
|
||||||
childContextTypes: { |
childContextTypes: { |
||||||
web3: React.PropTypes.object |
web3: React.PropTypes.object, |
||||||
|
traceManager: React.PropTypes.object, |
||||||
|
tx: React.PropTypes.object |
||||||
}, |
}, |
||||||
|
|
||||||
getChildContext: function () { |
getChildContext: function () { |
||||||
return { web3: this.props.web3 } |
return { |
||||||
|
web3: this.props.web3, |
||||||
|
traceManager: traceManager, |
||||||
|
tx: this.state.tx |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
componentDidMount: function () { |
||||||
|
traceManager.setWeb3(this.props.web3) |
||||||
}, |
}, |
||||||
|
|
||||||
render: function () { |
render: function () { |
||||||
return ( |
return ( |
||||||
<div style={style.wrapper}> |
<div style={style.wrapper}> |
||||||
<h1 style={style.container}>Eth Debugger</h1> |
<h1 style={style.container}>Eth Debugger</h1> |
||||||
<TxBrowser onNewTxRequested={this.retrieveVmTrace} /> |
<TxBrowser onNewTxRequested={this.startDebugging} /> |
||||||
<div style={style.container}> |
<StepManager ref='stepManager' onStepChanged={this.stepChanged} /> |
||||||
{this.state.state} |
<AssemblyItemsBrowser currentStepIndex={this.state.currentStepIndex} /> |
||||||
</div> |
|
||||||
<AssemblyItemsBrowser vmTrace={this.state.vmTrace} transaction={this.state.transaction} /> |
|
||||||
</div> |
</div> |
||||||
) |
) |
||||||
}, |
}, |
||||||
|
|
||||||
retrieveVmTrace: function (blockNumber, txNumber, tx) { |
stepChanged: function (stepIndex) { |
||||||
if (this.state.state !== '') return |
this.setState({ |
||||||
|
currentStepIndex: stepIndex |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
startDebugging: function (blockNumber, txIndex, tx) { |
||||||
|
if (traceManager.isLoading) { |
||||||
|
return |
||||||
|
} |
||||||
|
console.log('loading trace...') |
||||||
|
this.setState({ |
||||||
|
tx: tx |
||||||
|
}) |
||||||
|
traceManager.setTransaction(tx) |
||||||
var self = this |
var self = this |
||||||
this.setState({state: 'loading...'}) |
traceManager.resolveTrace(blockNumber, txIndex, function (success) { |
||||||
|
console.log('trace loaded ' + success) |
||||||
this.props.web3.debug.trace(blockNumber, parseInt(txNumber), function (error, result) { |
self.setState({ |
||||||
if (error) { |
currentStepIndex: 0 |
||||||
console.log(error) |
}) |
||||||
} else { |
self.refs.stepManager.newTraceAvailable() |
||||||
self.setState({vmTrace: result, transaction: tx, state: ''}) |
|
||||||
} |
|
||||||
}) |
}) |
||||||
} |
} |
||||||
}) |
}) |
||||||
|
@ -0,0 +1,98 @@ |
|||||||
|
'use strict' |
||||||
|
var React = require('react') |
||||||
|
var ButtonNavigator = require('./buttonNavigator') |
||||||
|
var Slider = require('./slider') |
||||||
|
var style = require('./basicStyles') |
||||||
|
|
||||||
|
module.exports = React.createClass({ |
||||||
|
propTypes: { |
||||||
|
onStepChanged: React.PropTypes.func.isRequired |
||||||
|
}, |
||||||
|
|
||||||
|
contextTypes: { |
||||||
|
traceManager: React.PropTypes.object |
||||||
|
}, |
||||||
|
|
||||||
|
getInitialState: function () { |
||||||
|
return { |
||||||
|
currentStepIndex: 0, |
||||||
|
traceLength: 0 |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
render: function () { |
||||||
|
return ( |
||||||
|
<div style={style.container}> |
||||||
|
<Slider |
||||||
|
ref='slider' |
||||||
|
onChange={this.sliderMoved} |
||||||
|
min='0' |
||||||
|
max={this.state.traceLength} /> |
||||||
|
<ButtonNavigator |
||||||
|
stepIntoBack={this.stepIntoBack} |
||||||
|
stepIntoForward={this.stepIntoForward} |
||||||
|
stepOverBack={this.stepOverBack} |
||||||
|
stepOverForward={this.stepOverForward} |
||||||
|
jumpToNextCall={this.jumpToNextCall} |
||||||
|
max={this.state.traceLength} /> |
||||||
|
</div> |
||||||
|
) |
||||||
|
}, |
||||||
|
|
||||||
|
init: function () { |
||||||
|
this.refs.slider.setValue(0) |
||||||
|
}, |
||||||
|
|
||||||
|
newTraceAvailable: function () { |
||||||
|
this.init() |
||||||
|
var self = this |
||||||
|
this.context.traceManager.getLength(function (length) { |
||||||
|
self.setState({ traceLength: length }) |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
sliderMoved: function (step) { |
||||||
|
this.props.onStepChanged(step) |
||||||
|
this.changeState(step) |
||||||
|
}, |
||||||
|
|
||||||
|
stepIntoForward: function () { |
||||||
|
var step = this.state.currentStepIndex + 1 |
||||||
|
this.props.onStepChanged(step) |
||||||
|
this.changeState(step) |
||||||
|
}, |
||||||
|
|
||||||
|
stepIntoBack: function () { |
||||||
|
var step = this.state.currentStepIndex - 1 |
||||||
|
this.props.onStepChanged(step) |
||||||
|
this.refs.slider.setValue(step) |
||||||
|
this.changeState(step) |
||||||
|
}, |
||||||
|
|
||||||
|
stepOverForward: function () { |
||||||
|
var step = this.context.traceManager.findStepOverForward(this.state.currentStepIndex) |
||||||
|
this.props.onStepChanged(step) |
||||||
|
this.refs.slider.setValue(step) |
||||||
|
this.changeState(step) |
||||||
|
}, |
||||||
|
|
||||||
|
stepOverBack: function () { |
||||||
|
var step = this.context.traceManager.findStepOverBack(this.state.currentStepIndex) |
||||||
|
this.props.onStepChanged(step) |
||||||
|
this.refs.slider.setValue(step) |
||||||
|
this.changeState(step) |
||||||
|
}, |
||||||
|
|
||||||
|
jumpToNextCall: function () { |
||||||
|
var step = this.context.traceManager.findNextCall(this.state.currentStepIndex) |
||||||
|
this.props.onStepChanged(step) |
||||||
|
this.refs.slider.setValue(step) |
||||||
|
this.changeState(step) |
||||||
|
}, |
||||||
|
|
||||||
|
changeState: function (step) { |
||||||
|
this.setState({ |
||||||
|
currentStepIndex: step |
||||||
|
}) |
||||||
|
} |
||||||
|
}) |
@ -1,113 +0,0 @@ |
|||||||
'use strict' |
|
||||||
var React = require('react') |
|
||||||
|
|
||||||
module.exports = React.createClass({ |
|
||||||
contextTypes: { |
|
||||||
web3: React.PropTypes.object |
|
||||||
}, |
|
||||||
|
|
||||||
getInitialState: function () { |
|
||||||
return { |
|
||||||
storage: {}, |
|
||||||
storageChanges: [], |
|
||||||
vmTraceIndexByStorageChange: {}, |
|
||||||
vmTraceChangesRef: [] |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
init: function () { |
|
||||||
var defaultState = this.getInitialState() |
|
||||||
this.state.storage = defaultState.storage |
|
||||||
this.state.storageChanges = defaultState.storageChanges |
|
||||||
this.state.vmTraceIndexByStorageChange = defaultState.vmTraceIndexByStorageChange |
|
||||||
this.state.vmTraceChangesRef = defaultState.vmTraceChangesRef |
|
||||||
}, |
|
||||||
|
|
||||||
render: function () { |
|
||||||
return null |
|
||||||
}, |
|
||||||
|
|
||||||
// retrieve the storage of an account just after the execution of txHash
|
|
||||||
retrieveStorage: function (address, transaction, callBack) { |
|
||||||
if (this.state.storage[address]) { |
|
||||||
callBack(this.state.storage[address]) |
|
||||||
} |
|
||||||
var self = this |
|
||||||
if (transaction) { |
|
||||||
this.context.web3.debug.storageAt(transaction.blockNumber.toString(), transaction.transactionIndex, address, function (error, result) { |
|
||||||
if (error) { |
|
||||||
console.log(error) |
|
||||||
} else { |
|
||||||
self.state.storage[address] = result |
|
||||||
callBack(result) |
|
||||||
} |
|
||||||
}) |
|
||||||
} else { |
|
||||||
console.log('transaction is not defined') |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
trackStorageChange: function (vmTraceIndex, trace) { |
|
||||||
var change = false |
|
||||||
if (trace.address) { |
|
||||||
// new context
|
|
||||||
this.state.storageChanges.push({ address: trace.address, changes: [] }) |
|
||||||
change = true |
|
||||||
} else if (trace.depth && !trace.address) { |
|
||||||
// returned from context
|
|
||||||
this.state.storageChanges.push({ address: this.state.storageChanges[this.state.storageChanges.length - 1].address, changes: [] }) |
|
||||||
change = true |
|
||||||
} else if (trace.inst === 'SSTORE') { |
|
||||||
this.state.storageChanges[this.state.storageChanges.length - 1].changes.push( |
|
||||||
{ |
|
||||||
'key': trace.stack[trace.stack.length - 1], |
|
||||||
'value': trace.stack[trace.stack.length - 2] |
|
||||||
}) |
|
||||||
change = true |
|
||||||
} |
|
||||||
|
|
||||||
if (change) { |
|
||||||
this.state.vmTraceIndexByStorageChange[vmTraceIndex] = { |
|
||||||
context: this.state.storageChanges.length - 1, |
|
||||||
changes: this.state.storageChanges[this.state.storageChanges.length - 1].changes.length - 1 |
|
||||||
} |
|
||||||
this.state.vmTraceChangesRef.push(vmTraceIndex) |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
rebuildStorageAt: function (vmTraceIndex, transaction, callBack) { |
|
||||||
var changesLocation = this.retrieveLastChange(vmTraceIndex) |
|
||||||
if (!changesLocation) { |
|
||||||
console.log('unable to build storage') |
|
||||||
callBack({}) |
|
||||||
} else { |
|
||||||
var address = this.state.storageChanges[changesLocation.context].address |
|
||||||
this.retrieveStorage(address, transaction, function (storage) { |
|
||||||
for (var k = 0; k < changesLocation.context; k++) { |
|
||||||
var context = this.state.storageChanges[k] |
|
||||||
if (context.address === address) { |
|
||||||
for (var i = 0; i < context.changes.length; i++) { |
|
||||||
if (i > changesLocation.changes) break |
|
||||||
var change = context.changes[i] |
|
||||||
storage[change.key] = change.value |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
callBack(storage) |
|
||||||
}) |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
retrieveLastChange: function (vmTraceIndex) { |
|
||||||
var change = this.state.vmTraceIndexByStorageChange[vmTraceIndex] |
|
||||||
if (change) { |
|
||||||
return change |
|
||||||
} else { |
|
||||||
for (var k in this.state.vmTraceChangesRef) { |
|
||||||
if (this.state.vmTraceChangesRef[k] > vmTraceIndex) { |
|
||||||
return this.state.vmTraceIndexByStorageChange[k - 1] |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}) |
|
@ -0,0 +1,340 @@ |
|||||||
|
'use strict' |
||||||
|
module.exports = { |
||||||
|
isLoading: false, |
||||||
|
web3: null, |
||||||
|
transaction: null, |
||||||
|
trace: null, |
||||||
|
|
||||||
|
// vmtrace changes section
|
||||||
|
depthChanges: [], |
||||||
|
callStack: {}, |
||||||
|
memoryChanges: [], |
||||||
|
callDataChanges: [], |
||||||
|
|
||||||
|
// storage section
|
||||||
|
storageChanges: [], |
||||||
|
vmTraceIndexByStorageChange: {}, |
||||||
|
vmTraceChangesRef: [], |
||||||
|
storages: {}, |
||||||
|
|
||||||
|
// init section
|
||||||
|
setWeb3: function (web3) { |
||||||
|
this.web3 = web3 |
||||||
|
}, |
||||||
|
|
||||||
|
setTransaction: function (tx) { |
||||||
|
this.transaction = tx |
||||||
|
}, |
||||||
|
|
||||||
|
resolveTrace: function (blockNumber, txNumber, callback) { |
||||||
|
this.isLoading = true |
||||||
|
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 { |
||||||
|
console.log(error) |
||||||
|
callback(false) |
||||||
|
} |
||||||
|
this.isLoading = false |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
init: function () { |
||||||
|
this.trace = null |
||||||
|
this.depthChanges = [] |
||||||
|
this.memoryChanges = [] |
||||||
|
this.callDataChanges = [] |
||||||
|
this.storageChanges = [] |
||||||
|
this.vmTraceIndexByStorageChange = {} |
||||||
|
this.vmTraceChangesRef = [] |
||||||
|
this.callStack = {} |
||||||
|
}, |
||||||
|
|
||||||
|
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.calldata(k, step) |
||||||
|
this.memory(k, step) |
||||||
|
currentStorageAddress = this.storage(k, step, currentStorageAddress) |
||||||
|
var depth = this.depth(k, step, currentDepth, callStack) |
||||||
|
if (depth) { |
||||||
|
currentDepth = depth |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
// compute trace section
|
||||||
|
calldata: function (index, step) { |
||||||
|
if (step.calldata) { |
||||||
|
this.callDataChanges.push(index) |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
memory: function (index, step) { |
||||||
|
if (step.memory) { |
||||||
|
this.memoryChanges.push(index) |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
storage: 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 |
||||||
|
}, |
||||||
|
|
||||||
|
depth: 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 |
||||||
|
}, |
||||||
|
|
||||||
|
// API section
|
||||||
|
getLength: function (callback) { |
||||||
|
if (!this.trace) callback(0) |
||||||
|
callback(this.trace.length) |
||||||
|
}, |
||||||
|
|
||||||
|
getStorageAt: function (stepIndex, callback) { |
||||||
|
var stoChange = this.lastPropertyChange(stepIndex, this.vmTraceChangesRef) |
||||||
|
if (!stoChange) { |
||||||
|
return {} |
||||||
|
} |
||||||
|
|
||||||
|
var changeRefs = this.vmTraceIndexByStorageChange[stoChange] |
||||||
|
var address = this.storageChanges[changeRefs.context].address |
||||||
|
var self = this |
||||||
|
this.retrieveStorage(address, 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 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
callback(storage) |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
getCallDataAt: function (stepIndex, callback) { |
||||||
|
var callDataChange = this.lastPropertyChange(stepIndex, this.callDataChanges) |
||||||
|
if (!callDataChange) return [''] |
||||||
|
callback([this.trace[callDataChange].calldata]) |
||||||
|
}, |
||||||
|
|
||||||
|
getCallStackAt: function (stepIndex, callback) { |
||||||
|
var callStackChange = this.lastPropertyChange(stepIndex, this.depthChanges) |
||||||
|
if (!callStackChange) return '' |
||||||
|
callback(this.callStack[callStackChange].stack) |
||||||
|
}, |
||||||
|
|
||||||
|
getStackAt: function (stepIndex, callback) { |
||||||
|
var stack |
||||||
|
if (this.trace[stepIndex].stack) { // there's always a stack
|
||||||
|
stack = this.trace[stepIndex].stack.slice(0) |
||||||
|
stack.reverse() |
||||||
|
callback(stack) |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
getLastDepthIndexChangeSince: function (stepIndex, callback) { |
||||||
|
var depthIndex = this.lastPropertyChange(stepIndex, this.depthChanges) |
||||||
|
callback(depthIndex) |
||||||
|
}, |
||||||
|
|
||||||
|
getCurrentCalledAddressAt: function (stepIndex, callback) { |
||||||
|
var self = this |
||||||
|
this.getLastDepthIndexChangeSince(stepIndex, function (addressIndex) { |
||||||
|
callback(self.resolveAddress(addressIndex)) |
||||||
|
}) |
||||||
|
}, |
||||||
|
|
||||||
|
getMemoryAt: function (stepIndex, callback) { |
||||||
|
var lastChanges = this.lastPropertyChange(stepIndex, this.memoryChanges) |
||||||
|
if (!lastChanges) return '' |
||||||
|
callback(this.trace[lastChanges].memory) |
||||||
|
}, |
||||||
|
|
||||||
|
getCurrentPC: function (stepIndex, callback) { |
||||||
|
callback(this.trace[stepIndex].pc) |
||||||
|
}, |
||||||
|
|
||||||
|
getCurrentStep: function (stepIndex, callback) { |
||||||
|
callback(this.trace[stepIndex].steps) |
||||||
|
}, |
||||||
|
|
||||||
|
getMemExpand: function (stepIndex, callback) { |
||||||
|
callback(this.trace[stepIndex].memexpand ? this.trace[stepIndex].memexpand : '') |
||||||
|
}, |
||||||
|
|
||||||
|
getStepCost: function (stepIndex, callback) { |
||||||
|
callback(this.trace[stepIndex].gascost) |
||||||
|
}, |
||||||
|
|
||||||
|
getRemainingGas: function (stepIndex, callback) { |
||||||
|
callback(this.trace[stepIndex].gas) |
||||||
|
}, |
||||||
|
|
||||||
|
// step section
|
||||||
|
isCallInstruction: function (index) { |
||||||
|
var state = this.trace[index] |
||||||
|
return state.instname === 'CALL' || state.instname === 'CALLCODE' || state.instname === 'CREATE' || state.instname === 'DELEGATECALL' |
||||||
|
}, |
||||||
|
|
||||||
|
isReturnInstruction: function (index) { |
||||||
|
var state = this.trace[index] |
||||||
|
return state.instname === 'RETURN' |
||||||
|
}, |
||||||
|
|
||||||
|
findStepOverBack: function (currentStep) { |
||||||
|
if (this.isReturnInstruction(currentStep - 1)) { |
||||||
|
return this.findStepOutBack(currentStep) |
||||||
|
} else { |
||||||
|
return currentStep - 1 |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
findStepOverForward: function (currentStep) { |
||||||
|
if (this.isCallInstruction(currentStep)) { |
||||||
|
return this.findStepOutForward(currentStep) |
||||||
|
} else { |
||||||
|
return currentStep + 1 |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
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 |
||||||
|
}, |
||||||
|
|
||||||
|
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
|
||||||
|
lastPropertyChange: 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.lastPropertyChange(target, changes.slice(0, middle)) |
||||||
|
} else if (changes[middle] < target) { |
||||||
|
return this.lastPropertyChange(target, changes.slice(middle, changes.length)) |
||||||
|
} else { |
||||||
|
return changes[middle] |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
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
|
||||||
|
retrieveStorage: function (address, callBack) { |
||||||
|
if (this.storages[address]) { |
||||||
|
callBack(this.storages[address]) |
||||||
|
} |
||||||
|
var self = this |
||||||
|
if (this.transaction) { |
||||||
|
this.web3.debug.storageAt(this.transaction.blockNumber.toString(), this.transaction.transactionIndex, address, function (error, result) { |
||||||
|
if (error) { |
||||||
|
console.log(error) |
||||||
|
} else { |
||||||
|
self.storages[address] = result |
||||||
|
callBack(result) |
||||||
|
} |
||||||
|
}) |
||||||
|
} else { |
||||||
|
console.log('transaction is not defined') |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue