load storage async

pull/7/head
yann300 9 years ago
parent 71cd5801aa
commit cfe1903a05
  1. 168
      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,6 +6,7 @@ var ButtonNavigator = require('./vmTraceButtonNavigator')
var codeUtils = require('./codeUtils')
var style = require('./basicStyles')
var Slider = require('./slider')
var StorageResolver = require('./storageResolver.js')
module.exports = React.createClass({
contextTypes: {
@ -24,14 +25,17 @@ module.exports = React.createClass({
currentCallData: null,
currentStepInfo: null,
codes: {}, // assembly items instructions list by contract addesses
executingCode: [], // code currently loaded in the debugger
instructionsIndexByBytesOffset: {}, // mapping between bytes offset and instructions index.
callStack: {}
callStack: {},
storageStates: {}
}
},
getDefaultProps: function () {
return {
vmTrace: null
vmTrace: null,
transaction: null
}
},
@ -53,8 +57,10 @@ module.exports = React.createClass({
stepIntoBack={this.stepIntoBack}
stepIntoForward={this.stepIntoForward}
stepOverBack={this.stepOverBack}
stepOverForward={this.stepOverForward} />
stepOverForward={this.stepOverForward}
jumpToNextCall={this.jumpToNextCall} />
</div>
<StorageResolver ref='storageResolver' transaction={this.props.transaction} />
<div style={style.container}>
<table>
<tbody>
@ -138,83 +144,130 @@ module.exports = React.createClass({
return ret
},
resolveAddress: function (address) {
if (!this.state.codes[address]) {
var hexCode = this.context.web3.eth.getCode(address)
loadCode: function (address, callback) {
console.log('loading new code from web3 ' + address)
this.context.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.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 () {
if (this.props.vmTrace) {
return this.state.codes[this.state.currentAddress].map(function (item, i) {
if (this.props.vmTrace && this.state.executingCode) {
return this.state.executingCode.map(function (item, i) {
return <option key={i} value={i}>{item}</option>
})
}
},
componentWillReceiveProps: function (nextProps) {
this.setState(this.getInitialState())
if (!nextProps.vmTrace) {
return
}
this.buildCallStack(nextProps.vmTrace)
this.setState({'currentSelected': -1})
this.updateState(nextProps, 0)
},
buildCallStack: function (vmTrace) {
if (!vmTrace) {
return
}
if (!vmTrace) return
var callStack = []
var callStackFrame = {}
var depth = -1
this.refs.storageResolver.init(this.props.transaction)
for (var k = 0; k < vmTrace.length; k++) {
var trace = vmTrace[k]
if (trace.depth === undefined || trace.depth === depth) {
continue
}
if (trace.depth === undefined || trace.depth === depth) continue
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) {
callStack.pop() // returning from context
}
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) {
if (!props.vmTrace || !props.vmTrace[vmTraceIndex]) {
return
}
if (!props.vmTrace || !props.vmTrace[vmTraceIndex]) return
var previousIndex = this.state.currentSelected
var stateChanges = {}
var 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()
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 })
Object.assign(stateChanges, { 'currentStack': stack })
}
var newContextLoaded = false
var depthIndex = this.shouldUpdateStateProperty('depth', vmTraceIndex, previousIndex, props.vmTrace)
if (depthIndex > -1) {
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
}
var storageIndex = this.shouldUpdateStateProperty('storage', vmTraceIndex, previousIndex, props.vmTrace)
if (storageIndex > -1) {
Object.assign(stateChanges, { 'currentStorage': props.vmTrace[storageIndex].storage })
}
if (!newContextLoaded) {
Object.assign(stateChanges,
{
'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)
if (memoryIndex > -1) {
@ -226,19 +279,50 @@ module.exports = React.createClass({
Object.assign(stateChanges, { 'currentCallData': [props.vmTrace[callDataIndex].calldata] })
}
stateChanges['selectedInst'] = this.state.instructionsIndexByBytesOffset[currentAddress][props.vmTrace[vmTraceIndex].pc]
stateChanges['currentSelected'] = vmTraceIndex
stateChanges['currentStepInfo'] = [
'Current Step: ' + props.vmTrace[vmTraceIndex].steps,
'Adding Memory: ' + (props.vmTrace[vmTraceIndex].memexpand ? props.vmTrace[vmTraceIndex].memexpand : ''),
'Step Cost: ' + props.vmTrace[vmTraceIndex].gascost,
'Remaining Gas: ' + props.vmTrace[vmTraceIndex].gas
]
this.refs.slider.setValue(vmTraceIndex)
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) {
var propIndex = -1
if (previousIndex + 1 === nextIndex) {
@ -265,6 +349,16 @@ module.exports = React.createClass({
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 () {
this.moveSelection(-1)
},

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

@ -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({
property: 'debug',
methods: [
new web3._extend.Method({
name: 'storageAt',
call: 'debug_storageAt',
inputFormatter: [null, null, null],
params: 3
}),
new web3._extend.Method({
name: 'trace',
call: 'debug_trace',

Loading…
Cancel
Save