load storage async

pull/7/head
yann300 9 years ago
parent 71cd5801aa
commit cfe1903a05
  1. 178
      src/assemblyItemsBrowser.js
  2. 19
      src/debugger.js
  3. 113
      src/storageResolver.js
  4. 4
      src/txBrowser.js
  5. 13
      src/vmTraceBrowser.js
  6. 8
      src/vmTraceManager.js
  7. 6
      src/web3Admin.js

@ -6,12 +6,13 @@ var ButtonNavigator = require('./vmTraceButtonNavigator')
var codeUtils = require('./codeUtils') var codeUtils = require('./codeUtils')
var style = require('./basicStyles') var style = require('./basicStyles')
var Slider = require('./slider') var Slider = require('./slider')
var StorageResolver = require('./storageResolver.js')
module.exports = React.createClass({ module.exports = React.createClass({
contextTypes: { contextTypes: {
web3: React.PropTypes.object web3: React.PropTypes.object
}, },
getInitialState: function () { getInitialState: function () {
return { return {
currentSelected: -1, // current selected item in the vmTrace currentSelected: -1, // current selected item in the vmTrace
@ -24,14 +25,17 @@ module.exports = React.createClass({
currentCallData: null, currentCallData: null,
currentStepInfo: null, currentStepInfo: null,
codes: {}, // assembly items instructions list by contract addesses codes: {}, // assembly items instructions list by contract addesses
executingCode: [], // code currently loaded in the debugger
instructionsIndexByBytesOffset: {}, // mapping between bytes offset and instructions index. instructionsIndexByBytesOffset: {}, // mapping between bytes offset and instructions index.
callStack: {} callStack: {},
storageStates: {}
} }
}, },
getDefaultProps: function () { getDefaultProps: function () {
return { return {
vmTrace: null vmTrace: null,
transaction: null
} }
}, },
@ -53,8 +57,10 @@ module.exports = React.createClass({
stepIntoBack={this.stepIntoBack} stepIntoBack={this.stepIntoBack}
stepIntoForward={this.stepIntoForward} stepIntoForward={this.stepIntoForward}
stepOverBack={this.stepOverBack} stepOverBack={this.stepOverBack}
stepOverForward={this.stepOverForward} /> stepOverForward={this.stepOverForward}
jumpToNextCall={this.jumpToNextCall} />
</div> </div>
<StorageResolver ref='storageResolver' transaction={this.props.transaction} />
<div style={style.container}> <div style={style.container}>
<table> <table>
<tbody> <tbody>
@ -138,84 +144,131 @@ module.exports = React.createClass({
return ret return ret
}, },
resolveAddress: function (address) { loadCode: function (address, callback) {
if (!this.state.codes[address]) { console.log('loading new code from web3 ' + address)
var hexCode = this.context.web3.eth.getCode(address) this.context.web3.eth.getCode(address, function (error, result) {
var code = codeUtils.nameOpCodes(new Buffer(hexCode.substring(2), 'hex')) if (error) {
this.state.codes[address] = code[0] console.log(error)
this.state.instructionsIndexByBytesOffset[address] = code[1] } else {
callback(result)
}
})
},
cacheExecutingCode: function (address, hexCode) {
var code = codeUtils.nameOpCodes(new Buffer(hexCode.substring(2), 'hex'))
this.state.codes[address] = code[0]
this.state.instructionsIndexByBytesOffset[address] = code[1]
return {
code: code[0],
instructionsIndexByBytesOffset: code[1]
}
},
getExecutingCodeFromCache: function (address) {
if (this.state.codes[address]) {
return {
code: this.state.codes[address],
instructionsIndexByBytesOffset: this.state.instructionsIndexByBytesOffset[address]
}
} else {
return null
} }
}, },
renderAssemblyItems: function () { renderAssemblyItems: function () {
if (this.props.vmTrace) { if (this.props.vmTrace && this.state.executingCode) {
return this.state.codes[this.state.currentAddress].map(function (item, i) { return this.state.executingCode.map(function (item, i) {
return <option key={i} value={i}>{item}</option> return <option key={i} value={i}>{item}</option>
}) })
} }
}, },
componentWillReceiveProps: function (nextProps) { componentWillReceiveProps: function (nextProps) {
this.setState(this.getInitialState())
if (!nextProps.vmTrace) { if (!nextProps.vmTrace) {
return return
} }
this.buildCallStack(nextProps.vmTrace) this.buildCallStack(nextProps.vmTrace)
this.setState({'currentSelected': -1})
this.updateState(nextProps, 0) this.updateState(nextProps, 0)
}, },
buildCallStack: function (vmTrace) { buildCallStack: function (vmTrace) {
if (!vmTrace) { if (!vmTrace) return
return
}
var callStack = [] var callStack = []
var callStackFrame = {}
var depth = -1 var depth = -1
this.refs.storageResolver.init(this.props.transaction)
for (var k = 0; k < vmTrace.length; k++) { for (var k = 0; k < vmTrace.length; k++) {
var trace = vmTrace[k] var trace = vmTrace[k]
if (trace.depth === undefined || trace.depth === depth) { if (trace.depth === undefined || trace.depth === depth) continue
continue
}
if (trace.depth > depth) { if (trace.depth > depth) {
callStack.push(trace.address) // new context if (k === 0) {
callStack.push('0x' + vmTrace[k].address) // new context
} else {
// getting the address from the stack
var callTrace = vmTrace[k - 1]
var address = callTrace.stack[callTrace.stack.length - 2]
callStack.push(address) // new context
}
} else if (trace.depth < depth) { } else if (trace.depth < depth) {
callStack.pop() // returning from context callStack.pop() // returning from context
} }
depth = trace.depth depth = trace.depth
this.state.callStack[k] = callStack.slice(0) callStackFrame[k] = callStack.slice(0)
this.refs.storageResolver.trackStorageChange(k, trace)
} }
this.setState({'callStack': callStackFrame})
}, },
updateState: function (props, vmTraceIndex) { updateState: function (props, vmTraceIndex) {
if (!props.vmTrace || !props.vmTrace[vmTraceIndex]) { if (!props.vmTrace || !props.vmTrace[vmTraceIndex]) return
return
}
var previousIndex = this.state.currentSelected var previousIndex = this.state.currentSelected
var stateChanges = {} var stateChanges = {}
var stack
if (props.vmTrace[vmTraceIndex].stack) { // there's always a stack if (props.vmTrace[vmTraceIndex].stack) { // there's always a stack
var stack = props.vmTrace[vmTraceIndex].stack stack = props.vmTrace[vmTraceIndex].stack.slice(0)
stack.reverse() stack.reverse()
stateChanges['currentStack'] = stack Object.assign(stateChanges, { 'currentStack': stack })
}
var currentAddress = this.state.currentAddress
var addressIndex = this.shouldUpdateStateProperty('address', vmTraceIndex, previousIndex, props.vmTrace)
if (addressIndex > -1) {
currentAddress = props.vmTrace[addressIndex].address
this.resolveAddress(currentAddress)
Object.assign(stateChanges, { 'currentAddress': currentAddress })
} }
var newContextLoaded = false
var depthIndex = this.shouldUpdateStateProperty('depth', vmTraceIndex, previousIndex, props.vmTrace) var depthIndex = this.shouldUpdateStateProperty('depth', vmTraceIndex, previousIndex, props.vmTrace)
if (depthIndex > -1) { if (depthIndex > -1) {
Object.assign(stateChanges, { 'currentCallStack': this.state.callStack[depthIndex] }) Object.assign(stateChanges, {'currentCallStack': this.state.callStack[depthIndex]})
// updating exectution context:
var address = this.resolveAddress(depthIndex, props)
if (address !== this.state.currentAddress) {
var self = this
this.ensureExecutingCodeUpdated(address, vmTraceIndex, props, function (code) {
if (self.state.currentAddress !== address) {
console.log('updating executing code ' + self.state.currentAddress + ' -> ' + address)
self.setState(
{
'selectedInst': code.instructionsIndexByBytesOffset[props.vmTrace[vmTraceIndex].pc],
'executingCode': code.code,
'currentAddress': address
})
}
})
newContextLoaded = true
}
} }
if (!newContextLoaded) {
var storageIndex = this.shouldUpdateStateProperty('storage', vmTraceIndex, previousIndex, props.vmTrace) Object.assign(stateChanges,
if (storageIndex > -1) { {
Object.assign(stateChanges, { 'currentStorage': props.vmTrace[storageIndex].storage }) 'selectedInst': this.getExecutingCodeFromCache(this.state.currentAddress).instructionsIndexByBytesOffset[props.vmTrace[vmTraceIndex].pc]
})
} }
Object.assign(stateChanges, { 'currentSelected': vmTraceIndex })
this.refs.storageResolver.rebuildStorageAt(vmTraceIndex, props.transaction, function (storage) {
Object.assign(stateChanges, { 'currentStorage': storage })
})
var memoryIndex = this.shouldUpdateStateProperty('memory', vmTraceIndex, previousIndex, props.vmTrace) var memoryIndex = this.shouldUpdateStateProperty('memory', vmTraceIndex, previousIndex, props.vmTrace)
if (memoryIndex > -1) { if (memoryIndex > -1) {
Object.assign(stateChanges, { 'currentMemory': this.formatMemory(props.vmTrace[memoryIndex].memory, 16) }) Object.assign(stateChanges, { 'currentMemory': this.formatMemory(props.vmTrace[memoryIndex].memory, 16) })
@ -226,19 +279,50 @@ module.exports = React.createClass({
Object.assign(stateChanges, { 'currentCallData': [props.vmTrace[callDataIndex].calldata] }) Object.assign(stateChanges, { 'currentCallData': [props.vmTrace[callDataIndex].calldata] })
} }
stateChanges['selectedInst'] = this.state.instructionsIndexByBytesOffset[currentAddress][props.vmTrace[vmTraceIndex].pc]
stateChanges['currentSelected'] = vmTraceIndex
stateChanges['currentStepInfo'] = [ stateChanges['currentStepInfo'] = [
'Current Step: ' + props.vmTrace[vmTraceIndex].steps, 'Current Step: ' + props.vmTrace[vmTraceIndex].steps,
'Adding Memory: ' + (props.vmTrace[vmTraceIndex].memexpand ? props.vmTrace[vmTraceIndex].memexpand : ''), 'Adding Memory: ' + (props.vmTrace[vmTraceIndex].memexpand ? props.vmTrace[vmTraceIndex].memexpand : ''),
'Step Cost: ' + props.vmTrace[vmTraceIndex].gascost, 'Step Cost: ' + props.vmTrace[vmTraceIndex].gascost,
'Remaining Gas: ' + props.vmTrace[vmTraceIndex].gas 'Remaining Gas: ' + props.vmTrace[vmTraceIndex].gas
] ]
this.refs.slider.setValue(vmTraceIndex) this.refs.slider.setValue(vmTraceIndex)
this.setState(stateChanges) this.setState(stateChanges)
}, },
ensureExecutingCodeUpdated: function (address, vmTraceIndex, props, callBack) {
this.resolveCode(address, vmTraceIndex, props, function (address, code) {
callBack(code)
})
},
resolveAddress: function (vmTraceIndex, props) {
var address = props.vmTrace[vmTraceIndex].address
if (vmTraceIndex > 0) {
var stack = this.state.callStack[vmTraceIndex] // callcode, delegatecall, ...
address = stack[stack.length - 1]
}
return address
},
resolveCode: function (address, vmTraceIndex, props, callBack) {
var cache = this.getExecutingCodeFromCache(address)
if (cache) {
callBack(address, cache)
return
}
if (vmTraceIndex === 0 && props.transaction.to === null) { // start of the trace
callBack(address, this.cacheExecutingCode(address, props.transaction.input))
return
}
var self = this
this.loadCode(address, function (code) {
callBack(address, self.cacheExecutingCode(address, code))
})
},
shouldUpdateStateProperty: function (vmTraceName, nextIndex, previousIndex, vmTrace) { shouldUpdateStateProperty: function (vmTraceName, nextIndex, previousIndex, vmTrace) {
var propIndex = -1 var propIndex = -1
if (previousIndex + 1 === nextIndex) { if (previousIndex + 1 === nextIndex) {
@ -265,6 +349,16 @@ module.exports = React.createClass({
return index return index
}, },
jumpToNextCall: function () {
var i = this.state.currentSelected
while (++i < this.props.vmTrace.length) {
if (this.isCallInstruction(i)) {
this.selectState(i + 1)
break
}
}
},
stepIntoBack: function () { stepIntoBack: function () {
this.moveSelection(-1) this.moveSelection(-1)
}, },

@ -1,12 +1,16 @@
'use strict' 'use strict'
var React = require('react') var React = require('react')
var TxBrowser = require('./txBrowser') var TxBrowser = require('./txBrowser')
var VmTraceBrowser = require('./vmTraceBrowser') var AssemblyItemsBrowser = require('./assemblyItemsBrowser')
var style = require('./basicStyles') var style = require('./basicStyles')
module.exports = React.createClass({ module.exports = React.createClass({
getInitialState: function () { getInitialState: function () {
return {vmTrace: null, state: '', currentStep: -1} return {
vmTrace: null,
state: '',
currentStep: -1
}
}, },
childContextTypes: { childContextTypes: {
@ -25,19 +29,22 @@ module.exports = React.createClass({
<div style={style.container}> <div style={style.container}>
{this.state.state} {this.state.state}
</div> </div>
<VmTraceBrowser vmTrace={this.state.vmTrace} /> <AssemblyItemsBrowser vmTrace={this.state.vmTrace} transaction={this.state.transaction} />
</div> </div>
) )
}, },
retrieveVmTrace: function (blockNumber, txNumber) { retrieveVmTrace: function (blockNumber, txNumber, tx) {
this.setState({state: 'loading...'}) if (this.state.state !== '') return
var self = this var self = this
this.setState({state: 'loading...'})
this.props.web3.debug.trace(blockNumber, parseInt(txNumber), function (error, result) { this.props.web3.debug.trace(blockNumber, parseInt(txNumber), function (error, result) {
if (error) { if (error) {
console.log(error) console.log(error)
} else { } else {
self.setState({vmTrace: result, state: ''}) self.setState({vmTrace: result, transaction: tx, state: ''})
} }
}) })
} }

@ -0,0 +1,113 @@
'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]
}
}
}
}
})

@ -19,8 +19,10 @@ module.exports = React.createClass({
var tx = this.context.web3.eth.getTransactionFromBlock(this.state.blockNumber, this.state.txNumber) var tx = this.context.web3.eth.getTransactionFromBlock(this.state.blockNumber, this.state.txNumber)
if (tx) { if (tx) {
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)
} else {
console.log('cannot find ' + this.state.blockNumber + ' ' + this.state.txNumber)
} }
this.props.onNewTxRequested(this.state.blockNumber, parseInt(this.state.txNumber))
}, },
updateBlockN: function (ev) { updateBlockN: function (ev) {

@ -1,13 +0,0 @@
'use strict'
var React = require('react')
var AssemblyItemsBrowser = require('./assemblyItemsBrowser')
module.exports = React.createClass({
render: function () {
return (
<div>
<AssemblyItemsBrowser vmTrace={this.props.vmTrace} />
</div>
)
}
})

@ -1,8 +0,0 @@
'use strict'
module.exports = {
retrieveVmTrace: function (blockNumber, txNumber, callBack) {
this.context.web3.debug.trace(blockNumber, parseInt(txNumber), function (error, result) {
callBack(error, result)
})
}
}

@ -89,6 +89,12 @@ module.exports = {
web3._extend({ web3._extend({
property: 'debug', property: 'debug',
methods: [ methods: [
new web3._extend.Method({
name: 'storageAt',
call: 'debug_storageAt',
inputFormatter: [null, null, null],
params: 3
}),
new web3._extend.Method({ new web3._extend.Method({
name: 'trace', name: 'trace',
call: 'debug_trace', call: 'debug_trace',

Loading…
Cancel
Save