Merge pull request #28 from yann300/vmTrace

align trace with geth trace
pull/7/head
yann300 9 years ago committed by GitHub
commit b9cac79586
  1. 98
      src/asmCode.js
  2. 29
      src/basicPanel.js
  3. 41
      src/buttonNavigator.js
  4. 44
      src/calldataPanel.js
  5. 44
      src/callstackPanel.js
  6. 101
      src/codeManager.js
  7. 14
      src/codeResolver.js
  8. 44
      src/debugger.js
  9. 32
      src/eventManager.js
  10. 13
      src/index.js
  11. 85
      src/memoryPanel.js
  12. 4
      src/slider.js
  13. 44
      src/stackPanel.js
  14. 48
      src/stepManager.js
  15. 88
      src/sticker.js
  16. 57
      src/storagePanel.js
  17. 88
      src/traceAnalyser.js
  18. 32
      src/traceCache.js
  19. 186
      src/traceManager.js
  20. 53
      src/traceManagerUtil.js
  21. 23
      src/traceRetriever.js
  22. 43
      src/traceStepManager.js
  23. 51
      src/txBrowser.js
  24. 8
      src/util.js
  25. 48
      src/vmDebugger.js
  26. 4
      src/web3Admin.js

@ -1,94 +1,64 @@
'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
codeManager: React.PropTypes.object,
root: React.PropTypes.object,
tx: React.PropTypes.object
},
getInitialState: function () {
return {
code: [],
selected: -1,
address: '' // selected instruction in the asm
}
},
getDefaultProps: function () {
return {
currentStepIndex: -1
code: '',
address: ''
}
},
render: function () {
return (
<select
size='10'
ref='itemsList'
style={style.instructionsList}
value={this.state.selected}>
<select size='10' ref='itemsList' style={style.instructionsList}>
{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>
})
}
componentDidMount: function () {
var self = this
this.context.codeManager.register('indexChanged', this, this.indexChanged)
this.context.codeManager.register('codeChanged', this, this.codeChanged)
this.context.codeManager.register('loadingCode', this, function (address) {
})
this.context.root.register('indexChanged', this, function (index) {
self.context.codeManager.resolveStep(index, self.context.tx)
})
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.currentStepIndex < 0) return
codeResolver.setWeb3(this.context.web3)
var self = this
this.context.traceManager.getCurrentCalledAddressAt(nextProps.currentStepIndex, function (error, address) {
if (error) {
console.log(error)
} else {
self.ensureCodeLoaded(address, nextProps.currentStepIndex)
}
indexChanged: function (index) {
this.refs.itemsList.value = index
},
codeChanged: function (code, address, index) {
this.setState({
code: code,
address: address
})
this.refs.itemsList.value = index
},
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) {
if (window.ethDebuggerSelectedItem !== currentStep) {
console.log(currentStep + ' discarded. current is ' + window.ethDebuggerSelectedItem)
return
}
self.setState({
code: code,
address: address
})
self.setInstructionIndex(address, currentStep)
})
} else {
this.setInstructionIndex(this.state.address, currentStep)
shouldComponentUpdate: function (nextProps, nextState) {
if (nextState.address === this.state.address) {
return false
}
return true
},
setInstructionIndex: function (address, step) {
var self = this
this.context.traceManager.getCurrentPC(step, function (error, instIndex) {
if (error) {
console.log(error)
} else {
self.setState({
selected: codeResolver.getInstructionIndex(address, instIndex)
})
}
})
renderAssemblyItems: function () {
if (this.state && this.state.code) {
return this.state.code.map(function (item, i) {
return <option key={i} value={i}>{item}</option>
})
}
}
})

@ -6,8 +6,7 @@ module.exports = React.createClass({
getDefaultProps: function () {
return {
data: null,
name: null,
renderRow: null
name: null
}
},
@ -18,33 +17,9 @@ module.exports = React.createClass({
{this.props.name}
</div>
<div style={style.panel.tableContainer}>
<table style={style.panel.table}>
<tbody>
{this.renderItems()}
</tbody>
</table>
<pre style={Object.assign(style.panel.table, style.font)} >{this.props.data}</pre>
</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,33 +10,54 @@ module.exports = React.createClass({
stepIntoBack: React.PropTypes.func.isRequired,
stepIntoForward: 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 () {
return (
<div>
<button onClick={this.props.stepIntoBack} disabled={this.checkButtonState(-1)}>
<button ref='intoback' onClick={this.props.stepIntoBack}>
Step Into Back
</button>
<button onClick={this.props.stepOverBack} disabled={this.checkButtonState(-1)}>
<button ref='overback' onClick={this.props.stepOverBack}>
Step Over Back
</button>
<button onClick={this.props.stepOverForward} disabled={this.checkButtonState(1)}>
<button ref='overforward' onClick={this.props.stepOverForward}>
Step Over Forward
</button>
<button onClick={this.props.stepIntoForward} disabled={this.checkButtonState(1)}>
<button ref='intoforward' onClick={this.props.stepIntoForward}>
Step Into Forward
</button>
<button ref='nextcall' onClick={this.props.jumpNextCall}>
Jump Next Call
</button>
</div>
)
},
checkButtonState: function (incr) {
if (incr === -1) {
return this.props.step === 0 ? 'disabled' : ''
} else if (incr === 1) {
return this.props.step >= this.props.max - 1 ? 'disabled' : ''
shouldComponentUpdate: function () {
return false
},
stepChanged: function (step) {
this.refs.intoback.disabled = step <= 0
this.refs.overback.disabled = step <= 0
if (!this.context.traceManager) {
this.refs.intoforward.disabled = true
this.refs.overforward.disabled = true
this.refs.nextcall.disabled = true
} else {
var self = this
this.context.traceManager.getLength(function (error, length) {
if (error) {
console.log(error)
} else {
self.refs.intoforward.disabled = step >= length - 1
self.refs.overforward.disabled = step >= length - 1
self.refs.nextcall.disabled = step >= length - 1
}
})
}
}
})

@ -4,13 +4,9 @@ var BasicPanel = require('./basicPanel')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object
},
getDefaultProps: function () {
return {
currentStepIndex: -1
}
traceManager: React.PropTypes.object,
codeManager: React.PropTypes.object,
root: React.PropTypes.object
},
getInitialState: function () {
@ -25,19 +21,29 @@ module.exports = React.createClass({
)
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.currentStepIndex < 0) return
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return
componentDidMount: function () {
var self = this
this.context.traceManager.getCallDataAt(nextProps.currentStepIndex, function (error, calldata) {
if (error) {
console.log(error)
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) {
self.setState({
data: calldata
})
}
this.context.root.register('indexChanged', this, function (index) {
if (index < 0) return
if (self.context.root.ethDebuggerSelectedItem !== index) return
self.context.traceManager.getCallDataAt(index, function (error, calldata) {
if (error) {
console.log(error)
} else if (self.context.root.ethDebuggerSelectedItem === index) {
self.setState({
data: self.format(calldata)
})
}
})
})
},
format: function (calldata) {
var ret = ''
for (var key in calldata) {
ret += calldata[key] + '\n'
}
return ret
}
})

@ -4,13 +4,9 @@ var BasicPanel = require('./basicPanel')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object
},
getDefaultProps: function () {
return {
currentStepIndex: -1
}
traceManager: React.PropTypes.object,
codeManager: React.PropTypes.object,
root: React.PropTypes.object
},
getInitialState: function () {
@ -25,19 +21,29 @@ module.exports = React.createClass({
)
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.currentStepIndex < 0) return
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return
componentDidMount: function () {
var self = this
this.context.traceManager.getCallStackAt(nextProps.currentStepIndex, function (error, callstack) {
if (error) {
console.log(error)
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) {
self.setState({
data: callstack
})
}
this.context.root.register('indexChanged', this, function (index) {
if (index < 0) return
if (self.context.root.ethDebuggerSelectedItem !== index) return
self.context.traceManager.getCallStackAt(index, function (error, callstack) {
if (error) {
console.log(error)
} else if (self.context.root.ethDebuggerSelectedItem === index) {
self.setState({
data: self.format(callstack)
})
}
})
})
},
format: function (callstack) {
var ret = ''
for (var key in callstack) {
ret += callstack[key] + '\n'
}
return ret
}
})

@ -0,0 +1,101 @@
'use strict'
var traceManagerUtil = require('./traceManagerUtil')
var codeResolver = require('./codeResolver')
var util = require('./util')
var EventManager = require('./eventManager')
/*
resolve contract code referenced by vmtrace in order to be used by asm listview.
events:
- indexChanged: triggered when an item is selected
- codeChanged: triggered when an item (in a different context) is selected
- loadingCode: triggerred when loading new code
- resolvingStep: when CodeManager resolves code/selected instruction of a new step
*/
function CodeManager (_web3, _traceManager) {
util.extend(this, new EventManager())
this.web3 = _web3
this.isLoading = false
this.traceManager = _traceManager
this.currentAddress = ''
codeResolver.setWeb3(_web3)
}
CodeManager.prototype.resolveStep = function (stepIndex, tx) {
if (stepIndex < 0) return
this.trigger('resolvingStep')
var self = this
if (stepIndex === 0) {
self.ensureCodeLoaded(tx.to, stepIndex, tx)
} else {
this.traceManager.getCurrentCalledAddressAt(stepIndex, function (error, address) {
if (error) {
console.log(error)
} else {
self.ensureCodeLoaded(address, stepIndex, tx)
}
})
}
}
CodeManager.prototype.ensureCodeLoaded = function (address, currentStep, tx) {
var self = this
if (address !== this.currentAddress) {
if (traceManagerUtil.isContractCreation(address)) {
this.traceManager.getContractCreationCode(address, function (error, hexCode) {
// contract creation
if (error) {
console.log(error)
} else {
var codes = codeResolver.cacheExecutingCode(address, hexCode)
self.trigger('loadingCode', [address])
self.getInstructionIndex(address, currentStep, function (error, result) {
if (!error) {
self.trigger('codeChanged', [codes.code, address, result])
self.trigger('indexChanged', [result])
self.currentAddress = address
} else {
console.log(error)
}
})
}
})
} else {
codeResolver.resolveCode(address, currentStep, tx, function (address, code) {
// resoling code from stack
self.trigger('loadingCode', [address])
self.getInstructionIndex(address, currentStep, function (error, result) {
if (!error) {
self.trigger('codeChanged', [code, address, result])
self.trigger('indexChanged', [result])
self.currentAddress = address
} else {
console.log(error)
}
})
})
}
} else {
// only set selected item
this.getInstructionIndex(this.currentAddress, currentStep, function (error, result) {
if (!error) {
self.trigger('indexChanged', [result])
}
})
}
}
CodeManager.prototype.getInstructionIndex = function (address, step, callback) {
this.traceManager.getCurrentPC(step, function (error, instIndex) {
if (error) {
console.log(error)
callback('Cannot retrieve current PC for ' + step, null)
} else {
var itemIndex = codeResolver.getInstructionIndex(address, instIndex)
callback(null, itemIndex)
}
})
}
module.exports = CodeManager

@ -18,11 +18,6 @@ module.exports = {
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)
@ -41,9 +36,14 @@ module.exports = {
},
cacheExecutingCode: function (address, hexCode) {
var codes = this.formatCode(hexCode)
this.codes[address] = codes.code
this.instructionsIndexByBytesOffset[address] = codes.instructionsIndexByBytesOffset
return codes
},
formatCode: function (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]

@ -2,58 +2,63 @@
var React = require('react')
var TxBrowser = require('./txBrowser')
var StepManager = require('./stepManager')
var AssemblyItemsBrowser = require('./vmDebugger')
var TraceManager = require('./traceManager')
var VmDebugger = require('./vmDebugger')
var style = require('./basicStyles')
var util = require('./util')
var EventManager = require('./eventManager')
module.exports = React.createClass({
ethDebuggerSelectedItem: -1,
getInitialState: function () {
return {
currentStepIndex: -1, // index of the selected item in the vmtrace
tx: null,
traceManager: null
tx: null
}
},
childContextTypes: {
web3: React.PropTypes.object,
traceManager: React.PropTypes.object,
codeManager: React.PropTypes.object,
root: React.PropTypes.object,
tx: React.PropTypes.object
},
getChildContext: function () {
return {
web3: this.props.web3,
traceManager: this.state.traceManager,
web3: this.props.context.web3,
traceManager: this.props.context.traceManager,
codeManager: this.props.context.codeManager,
root: this,
tx: this.state.tx
}
},
componentDidMount: function () {
this.setState({
traceManager: new TraceManager(this.props.web3)
})
},
render: function () {
return (
<div style={style.font}>
<h1 style={style.container}>Eth Debugger</h1>
<TxBrowser onNewTxRequested={this.startDebugging} />
<StepManager ref='stepManager' onStepChanged={this.stepChanged} />
<AssemblyItemsBrowser ref='assemblyitemsbrowser' currentStepIndex={this.state.currentStepIndex} />
<VmDebugger ref='assemblyitemsbrowser' currentStepIndex={this.state.currentStepIndex} />
</div>
)
},
stepChanged: function (stepIndex) {
this.trigger('indexChanged', [stepIndex])
this.setState({
currentStepIndex: stepIndex
})
},
componentWillMount: function () {
util.extend(this, new EventManager())
},
startDebugging: function (blockNumber, txIndex, tx) {
if (this.state.traceManager.isLoading) {
if (this.props.context.traceManager.isLoading) {
return
}
console.log('loading trace...')
@ -61,12 +66,13 @@ module.exports = React.createClass({
tx: tx
})
var self = this
this.state.traceManager.resolveTrace(blockNumber, txIndex, function (success) {
this.props.context.traceManager.resolveTrace(tx, function (success) {
console.log('trace loaded ' + success)
self.setState({
currentStepIndex: 0
})
self.refs.stepManager.newTraceAvailable()
if (success) {
self.trigger('newTraceLoaded')
} else {
console.log('trace not loaded')
}
})
}
})

@ -0,0 +1,32 @@
'use strict'
function EventManager () {
this.registered = {}
}
EventManager.prototype.unregister = function (eventName, obj) {
for (var reg in this.registered[eventName]) {
if (this.registered[eventName][reg] && this.registered[eventName][reg].obj === obj) {
this.registered[eventName].splice(reg, 1)
return
}
}
}
EventManager.prototype.register = function (eventName, obj, func) {
if (!this.registered[eventName]) {
this.registered[eventName] = []
}
this.registered[eventName].push({
obj: obj,
func: func
})
}
EventManager.prototype.trigger = function (eventName, args) {
for (var listener in this.registered[eventName]) {
var l = this.registered[eventName][listener]
l.func.apply(l.obj, args)
}
}
module.exports = EventManager

@ -3,17 +3,24 @@ var ReactDOM = require('react-dom')
var React = require('react')
var Web3 = require('web3')
var Web3Admin = require('./web3Admin')
var TraceManager = require('./traceManager')
var CodeManager = require('./codeManager')
function loadWeb3 () {
function loadContext () {
var web3 = new Web3()
web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545'))
Web3Admin.extend(web3)
return web3
var traceManager = new TraceManager(web3)
return {
web3: web3,
traceManager: traceManager,
codeManager: new CodeManager(web3, traceManager)
}
}
var Debugger = require('./debugger')
ReactDOM.render(
<Debugger web3={loadWeb3()} />,
<Debugger context={loadContext()} />,
document.getElementById('app')
)

@ -1,18 +1,13 @@
'use strict'
var React = require('react')
var BasicPanel = require('./basicPanel')
var style = require('./basicStyles')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object,
web3: React.PropTypes.object
},
getDefaultProps: function () {
return {
currentStepIndex: -1
}
web3: React.PropTypes.object,
codeManager: React.PropTypes.object,
root: React.PropTypes.object
},
getInitialState: function () {
@ -23,56 +18,42 @@ module.exports = React.createClass({
render: function () {
return (
<BasicPanel name='Memory' data={this.state.data} renderRow={this.renderMemoryRow} />
<BasicPanel name='Memory' data={this.state.data} />
)
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.currentStepIndex < 0) return
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return
componentDidMount: function () {
var self = this
this.context.traceManager.getMemoryAt(nextProps.currentStepIndex, function (error, memory) {
if (error) {
console.log(error)
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) {
self.setState({
data: self.formatMemory(memory, 16)
})
}
this.context.root.register('indexChanged', this, function (index) {
if (index < 0) return
if (self.context.root.ethDebuggerSelectedItem !== index) return
self.context.traceManager.getMemoryAt(index, function (error, memory) {
if (error) {
console.log(error)
} else if (self.context.root.ethDebuggerSelectedItem === index) {
self.setState({
data: self.formatMemory(memory, 16)
})
}
})
})
},
renderMemoryRow: function (data) {
var ret = []
if (data) {
for (var key in data) {
var memSlot = data[key]
ret.push(
<tr key={key}>
<td>
<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>)
}
formatMemory: function (mem, width) {
var ret = ''
if (!mem) {
return ret
}
if (!mem.substr) {
mem = mem.join('') // geth returns an array, eth return raw string
}
return ret
},
formatMemory: function (mem, width) {
var ret = []
for (var k = 0; k < mem.length; k += (width * 2)) {
var memory = mem.substr(k, width * 2)
ret.push({
address: this.context.web3.toHex(k),
content: this.tryAsciiFormat(memory)
})
var content = this.tryAsciiFormat(memory)
ret += this.context.web3.toHex(k) + ' ' + content.raw + ' ' + content.ascii + '\n'
}
return ret
},
@ -81,12 +62,12 @@ module.exports = React.createClass({
var ret = { ascii: '', raw: '' }
for (var k = 0; k < memorySlot.length; k += 2) {
var raw = memorySlot.substr(k, 2)
var ascii = this.context.web3.toAscii(raw)
if (ascii === String.fromCharCode(0)) {
ret.ascii += '?'
} else {
ret.ascii += ascii
var ascii = String.fromCharCode(parseInt(raw, 16))
ascii = ascii.replace(/\W/g, '?')
if (ascii === '') {
ascii = '?'
}
ret.ascii += ascii
ret.raw += ' ' + raw
}
return ret

@ -32,6 +32,10 @@ module.exports = React.createClass({
)
},
shouldComponentUpdate: function (nextProps, nextState) {
return (nextProps.max !== this.props.max || nextProps.min !== this.props.min)
},
componentDidMount: function () {
this.setValue(0)
},

@ -4,13 +4,9 @@ var BasicPanel = require('./basicPanel')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object
},
getDefaultProps: function () {
return {
currentStepIndex: -1
}
traceManager: React.PropTypes.object,
codeManager: React.PropTypes.object,
root: React.PropTypes.object
},
getInitialState: function () {
@ -25,19 +21,29 @@ module.exports = React.createClass({
)
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.currentStepIndex < 0) return
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return
componentDidMount: function () {
var self = this
this.context.traceManager.getStackAt(nextProps.currentStepIndex, function (error, stack) {
if (error) {
console.log(error)
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) {
self.setState({
data: stack
})
}
this.context.root.register('indexChanged', this, function (index) {
if (index < 0) return
if (self.context.root.ethDebuggerSelectedItem !== index) return
self.context.traceManager.getStackAt(index, function (error, stack) {
if (error) {
console.log(error)
} else if (self.context.root.ethDebuggerSelectedItem === index) {
self.setState({
data: self.format(stack)
})
}
})
})
},
format: function (stack) {
var ret = ''
for (var key in stack) {
ret += stack[key] + '\n'
}
return ret
}
})

@ -10,7 +10,8 @@ module.exports = React.createClass({
},
contextTypes: {
traceManager: React.PropTypes.object
traceManager: React.PropTypes.object,
root: React.PropTypes.object
},
getInitialState: function () {
@ -29,26 +30,32 @@ module.exports = React.createClass({
min='0'
max={this.state.traceLength} />
<ButtonNavigator
ref='buttons'
stepIntoBack={this.stepIntoBack}
stepIntoForward={this.stepIntoForward}
stepOverBack={this.stepOverBack}
stepOverForward={this.stepOverForward}
jumpToNextCall={this.jumpToNextCall}
jumpNextCall={this.jumpToNextCall}
max={this.state.traceLength} />
</div>
)
},
componentDidMount: function () {
this.updateGlobalSelectedItem(0)
var self = this
this.context.root.register('newTraceLoaded', this, function () {
self.newTraceAvailable()
})
this.changeState(-1)
},
updateGlobalSelectedItem: function (value) {
window.ethDebuggerSelectedItem = value
this.context.root.ethDebuggerSelectedItem = value
},
init: function () {
this.refs.slider.setValue(0)
this.changeState(0)
},
newTraceAvailable: function () {
@ -64,40 +71,59 @@ module.exports = React.createClass({
},
sliderMoved: function (step) {
this.props.onStepChanged(step)
if (!this.context.traceManager.inRange(step)) {
return
}
this.changeState(step)
},
stepIntoForward: function () {
if (!this.context.traceManager.isLoaded()) {
return
}
var step = this.state.currentStepIndex + 1
this.props.onStepChanged(step)
if (!this.context.traceManager.inRange(step)) {
return
}
this.refs.slider.setValue(step)
this.changeState(step)
},
stepIntoBack: function () {
if (!this.context.traceManager.isLoaded()) {
return
}
var step = this.state.currentStepIndex - 1
this.props.onStepChanged(step)
if (!this.context.traceManager.inRange(step)) {
return
}
this.refs.slider.setValue(step)
this.changeState(step)
},
stepOverForward: function () {
if (!this.context.traceManager.isLoaded()) {
return
}
var step = this.context.traceManager.findStepOverForward(this.state.currentStepIndex)
this.props.onStepChanged(step)
this.refs.slider.setValue(step)
this.changeState(step)
},
stepOverBack: function () {
if (!this.context.traceManager.isLoaded()) {
return
}
var step = this.context.traceManager.findStepOverBack(this.state.currentStepIndex)
this.props.onStepChanged(step)
this.refs.slider.setValue(step)
this.changeState(step)
},
jumpToNextCall: function () {
if (!this.context.traceManager.isLoaded()) {
return
}
var step = this.context.traceManager.findNextCall(this.state.currentStepIndex)
this.props.onStepChanged(step)
this.refs.slider.setValue(step)
this.changeState(step)
},
@ -107,5 +133,7 @@ module.exports = React.createClass({
this.setState({
currentStepIndex: step
})
this.refs.buttons.stepChanged(step)
this.props.onStepChanged(step)
}
})

@ -3,13 +3,9 @@ var React = require('react')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object
},
getDefaultProps: function () {
return {
currentStepIndex: -1
}
traceManager: React.PropTypes.object,
codeManager: React.PropTypes.object,
root: React.PropTypes.object
},
getInitialState: function () {
@ -80,48 +76,50 @@ module.exports = React.createClass({
return null
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.currentStepIndex < 0) return
componentDidMount: function () {
var self = this
this.context.traceManager.getCurrentStep(nextProps.currentStepIndex, function (error, step) {
if (error) {
console.log(error)
} else {
self.setState({
step: step
})
}
})
this.context.root.register('indexChanged', this, function (index) {
if (index < 0) return
this.context.traceManager.getMemExpand(nextProps.currentStepIndex, function (error, addmem) {
if (error) {
console.log(error)
} else {
self.setState({
addmemory: addmem
})
}
})
self.context.traceManager.getCurrentStep(index, function (error, step) {
if (error) {
console.log(error)
} else {
self.setState({
step: step
})
}
})
this.context.traceManager.getStepCost(nextProps.currentStepIndex, function (error, gas) {
if (error) {
console.log(error)
} else {
self.setState({
gas: gas
})
}
})
self.context.traceManager.getMemExpand(index, function (error, addmem) {
if (error) {
console.log(error)
} else {
self.setState({
addmemory: addmem
})
}
})
this.context.traceManager.getRemainingGas(nextProps.currentStepIndex, function (error, remaingas) {
if (error) {
console.log(error)
} else {
self.setState({
remainingGas: remaingas
})
}
self.context.traceManager.getStepCost(index, function (error, gas) {
if (error) {
console.log(error)
} else {
self.setState({
gas: gas
})
}
})
self.context.traceManager.getRemainingGas(index, function (error, remaingas) {
if (error) {
console.log(error)
} else {
self.setState({
remainingGas: remaingas
})
}
})
})
}
})

@ -1,18 +1,13 @@
'use strict'
var React = require('react')
var BasicPanel = require('./basicPanel')
var style = require('./basicStyles')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object,
tx: React.PropTypes.object
},
getDefaultProps: function () {
return {
currentStepIndex: -1
}
tx: React.PropTypes.object,
codeManager: React.PropTypes.object,
root: React.PropTypes.object
},
getInitialState: function () {
@ -23,40 +18,32 @@ module.exports = React.createClass({
render: function () {
return (
<BasicPanel name='Storage' data={this.state.data} renderRow={this.renderStorageRow} />
<BasicPanel name='Storage' data={this.state.data} />
)
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.currentStepIndex < 0) return
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return
componentDidMount: function () {
var self = this
this.context.traceManager.getStorageAt(nextProps.currentStepIndex, this.context.tx.blockNumber.toString(), this.context.tx.transactionIndex, function (error, storage) {
if (error) {
console.log(error)
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) {
self.setState({
data: storage
})
}
this.context.root.register('indexChanged', this, function (index) {
if (index < 0) return
if (self.context.root.ethDebuggerSelectedItem !== index) return
self.context.traceManager.getStorageAt(index, self.context.tx, function (error, storage) {
if (error) {
console.log(error)
} else if (self.context.root.ethDebuggerSelectedItem === index) {
self.setState({
data: self.formatStorage(storage)
})
}
})
})
},
renderStorageRow: function (data) {
var ret = []
if (data) {
for (var key in data) {
ret.push(
<tr key={key}>
<td>
<pre style={style.font} >{key}</pre>
</td>
<td>
<pre style={style.font}>{data[key]}</pre>
</td>
</tr>)
}
formatStorage: function (storage) {
var ret = ''
for (var key in storage) {
ret += key + ' ' + storage[key] + '\n'
}
return ret
}

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

@ -5,9 +5,14 @@ function TraceCache () {
TraceCache.prototype.init = function () {
// ...Changes contains index in the vmtrace of the corresponding changes
this.depthChanges = []
this.memoryChanges = []
this.callChanges = []
this.returnChanges = []
this.calls = {}
this.contractCreation = {}
this.callDataChanges = []
this.memoryChanges = []
this.storageChanges = []
this.sstore = {} // all sstore occurence in the trace
this.callStack = {} // contains all callStack by vmtrace index (we need to rebuild it, callstack is not included in the vmtrace)
@ -21,8 +26,27 @@ TraceCache.prototype.pushMemoryChanges = function (value) {
this.memoryChanges.push(value)
}
TraceCache.prototype.pushDepthChanges = function (value) {
this.depthChanges.push(value)
TraceCache.prototype.pushCallChanges = function (step, value) {
this.callChanges.push(value)
this.calls[value] = {
op: step.op
}
}
TraceCache.prototype.pushContractCreationFromMemory = function (index, token, trace, lastMemoryChange) {
var memory = trace[lastMemoryChange].memory
var stack = trace[index].stack
var offset = 2 * parseInt(stack[stack.length - 2], 16)
var size = 2 * parseInt(stack[stack.length - 3], 16)
this.contractCreation[token] = '0x' + memory.join('').substr(offset, size)
}
TraceCache.prototype.pushContractCreation = function (token, code) {
this.contractCreation[token] = code
}
TraceCache.prototype.pushReturnChanges = function (value) {
this.returnChanges.push(value)
}
TraceCache.prototype.pushCallStack = function (index, callStack) {

@ -13,27 +13,36 @@ function TraceManager (_web3) {
this.traceAnalyser = new TraceAnalyser(this.traceCache)
this.traceRetriever = new TraceRetriever(_web3)
this.traceStepManager = new TraceStepManager(this.traceAnalyser)
this.tx
}
// init section
TraceManager.prototype.resolveTrace = function (blockNumber, txNumber, callback) {
this.isLoading = true
TraceManager.prototype.resolveTrace = function (tx, callback) {
this.tx = tx
this.init()
if (!this.web3) callback(false)
this.isLoading = true
var self = this
this.traceRetriever.getTrace(blockNumber, parseInt(txNumber), function (error, result) {
self.trace = result
this.traceRetriever.getTrace(tx.hash, function (error, result) {
if (error) {
console.log(error)
self.isLoading = false
} else {
self.traceAnalyser.analyse(result, function (error, result) {
if (error) {
console.log(error)
callback(false)
} else {
callback(true)
}
})
if (result.structLogs.length > 0) {
self.trace = result.structLogs
self.traceAnalyser.analyse(result.structLogs, tx, function (error, result) {
if (error) {
console.log(error)
callback(false)
} else {
callback(true)
}
self.isLoading = false
})
} else {
console.log(tx.hash + ' is not a contract invokation or contract creation.')
self.isLoading = false
}
}
})
}
@ -44,40 +53,71 @@ TraceManager.prototype.init = function () {
}
// API section
TraceManager.prototype.inRange = function (step) {
return this.isLoaded() && step >= 0 && step < this.trace.length
}
TraceManager.prototype.isLoaded = function () {
return !this.isLoading && this.trace !== null
}
TraceManager.prototype.getLength = function (callback) {
if (!this.trace) callback('no trace available', null)
callback(null, this.trace.length)
if (!this.trace) {
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) {
var check = this.checkRequestedStep(stepIndex)
if (check) {
return callback(check, null)
}
var stoChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.storageChanges)
var address = this.traceCache.sstore[stoChange].address
if (stoChange === undefined) return callback('no storage found', null)
var self = this
this.traceRetriever.getStorage(blockNumber, txIndex, address, function (error, result) {
if (error) {
console.log(error)
callback(error, null)
} else {
var storage = self.traceCache.rebuildStorage(address, result, stepIndex)
callback(null, storage)
}
})
if (this.traceRetriever.debugStorageAtAvailable()) {
var address = this.traceCache.sstore[stoChange].address
this.traceRetriever.getStorage(tx, address, function (error, result) {
if (error) {
console.log(error)
callback(error, null)
} 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) {
var check = this.checkRequestedStep(stepIndex)
if (check) {
return callback(check, null)
}
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])
}
TraceManager.prototype.getCallStackAt = function (stepIndex, callback) {
var callStackChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.depthChanges)
if (!callStackChange) return callback('no callstack found', null)
callback(null, this.traceCache.callStack[callStackChange].stack)
var check = this.checkRequestedStep(stepIndex)
if (check) {
return callback(check, null)
}
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) {
var check = this.checkRequestedStep(stepIndex)
if (check) {
return callback(check, null)
}
var stack
if (this.trace[stepIndex].stack) { // there's always a stack
stack = this.trace[stepIndex].stack.slice(0)
@ -88,48 +128,106 @@ TraceManager.prototype.getStackAt = function (stepIndex, callback) {
}
}
TraceManager.prototype.getLastDepthIndexChangeSince = function (stepIndex, callback) {
var depthIndex = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.depthChanges)
callback(null, depthIndex)
TraceManager.prototype.getLastCallChangeSince = function (stepIndex, callback) {
var check = this.checkRequestedStep(stepIndex)
if (check) {
return callback(check, null)
}
var callChange = traceManagerUtil.findLowerBound(stepIndex, this.traceCache.callChanges)
if (callChange === undefined) {
callback(null, 0)
} else {
callback(null, callChange)
}
}
TraceManager.prototype.getCurrentCalledAddressAt = function (stepIndex, callback) {
var check = this.checkRequestedStep(stepIndex)
if (check) {
return callback(check, null)
}
var self = this
this.getLastDepthIndexChangeSince(stepIndex, function (error, addressIndex) {
this.getLastCallChangeSince(stepIndex, function (error, addressIndex) {
if (error) {
callback(error, null)
} else {
callback(null, traceManagerUtil.resolveCalledAddress(addressIndex, self.trace))
if (addressIndex === 0) {
callback(null, self.tx.to)
} 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.getContractCreationCode = function (token, callback) {
if (this.traceCache.contractCreation[token]) {
callback(null, this.traceCache.contractCreation[token])
} else {
callback('no contract creation named ' + token, null)
}
}
TraceManager.prototype.getMemoryAt = function (stepIndex, callback) {
var check = this.checkRequestedStep(stepIndex)
if (check) {
return callback(check, null)
}
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)
}
TraceManager.prototype.getCurrentPC = function (stepIndex, callback) {
var check = this.checkRequestedStep(stepIndex)
if (check) {
return callback(check, null)
}
callback(null, this.trace[stepIndex].pc)
}
TraceManager.prototype.getCurrentStep = function (stepIndex, callback) {
var check = this.checkRequestedStep(stepIndex)
if (check) {
return callback(check, null)
}
callback(null, this.trace[stepIndex].steps)
}
TraceManager.prototype.getMemExpand = function (stepIndex, callback) {
var check = this.checkRequestedStep(stepIndex)
if (check) {
return callback(check, null)
}
callback(null, this.trace[stepIndex].memexpand ? this.trace[stepIndex].memexpand : '')
}
TraceManager.prototype.getStepCost = function (stepIndex, callback) {
callback(null, this.trace[stepIndex].gascost)
var check = this.checkRequestedStep(stepIndex)
if (check) {
return callback(check, null)
}
callback(null, this.trace[stepIndex].gasCost)
}
TraceManager.prototype.getRemainingGas = function (stepIndex, callback) {
var check = this.checkRequestedStep(stepIndex)
if (check) {
return callback(check, null)
}
callback(null, this.trace[stepIndex].gas)
}
TraceManager.prototype.isCreationStep = function (stepIndex) {
return traceManagerUtil.isCreateInstruction(stepIndex, this.trace)
}
// step section
TraceManager.prototype.findStepOverBack = function (currentStep) {
return this.traceStepManager.findStepOverBack(currentStep)
@ -147,4 +245,18 @@ TraceManager.prototype.findStepOutForward = function (currentStep) {
return this.traceStepManager.findStepOutForward(currentStep)
}
TraceManager.prototype.findNextCall = function (currentStep) {
return this.traceStepManager.findNextCall(currentStep)
}
// util
TraceManager.prototype.checkRequestedStep = function (stepIndex) {
if (!this.trace) {
return 'trace not loaded'
} else if (stepIndex >= this.trace.length) {
return 'trace smaller than requested'
}
return undefined
}
module.exports = TraceManager

@ -1,3 +1,4 @@
'use strict'
module.exports = {
// util section
findLowerBound: function (target, changes) {
@ -24,13 +25,53 @@ module.exports = {
}
},
// vmTraceIndex has to point to a CALL, CODECALL, ...
resolveCalledAddress: function (vmTraceIndex, trace) {
var address = trace[vmTraceIndex].address
if (vmTraceIndex > 0) {
var stack = trace[vmTraceIndex - 1].stack // callcode, delegatecall, ...
address = stack[stack.length - 2]
var step = trace[vmTraceIndex]
if (this.isCreateInstruction(step)) {
return this.contractCreationToken(vmTraceIndex)
} else if (this.isCallInstruction(step)) {
var stack = step.stack // callcode, delegatecall, ...
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'
},
isSSTOREInstruction: function (step) {
return step.op === 'SSTORE'
},
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
}
},
contractCreationToken: function (index) {
return '(Contract Creation - Step ' + index + ')'
},
isContractCreation: function (address) {
return address.indexOf('(Contract Creation - Step') !== -1
}
}

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

@ -1,35 +1,34 @@
'use strict'
var traceManagerUtil = require('./traceManagerUtil')
function TraceStepManager (_traceAnalyser) {
this.traceAnalyser = _traceAnalyser
}
TraceStepManager.prototype.isCallInstruction = function (index) {
var state = this.traceAnalyser.trace[index]
return state.instname === 'CALL' || state.instname === 'CALLCODE' || state.instname === 'CREATE' || state.instname === 'DELEGATECALL'
return traceManagerUtil.isCallInstruction(state)
}
TraceStepManager.prototype.isReturnInstruction = function (index) {
var state = this.traceAnalyser.trace[index]
return state.instname === 'RETURN'
return traceManagerUtil.isReturnInstruction(state)
}
TraceStepManager.prototype.findStepOverBack = function (currentStep) {
if (this.isReturnInstruction(currentStep - 1)) {
return this.findStepOutBack(currentStep)
} else {
return currentStep - 1
}
if (currentStep === 0) return 0
return this.findStepOutBack(currentStep)
}
TraceStepManager.prototype.findStepOverForward = function (currentStep) {
if (this.isCallInstruction(currentStep)) {
return this.findStepOutForward(currentStep)
} else {
return currentStep + 1
}
if (currentStep === this.traceAnalyser.trace.length - 1) return currentStep
return this.findStepOutForward(currentStep)
}
TraceStepManager.prototype.findStepOutBack = function (currentStep) {
if (!this.traceAnalyser.trace) {
return currentStep
}
var i = currentStep - 1
var depth = 0
while (--i >= 0) {
@ -47,9 +46,12 @@ TraceStepManager.prototype.findStepOutBack = function (currentStep) {
}
TraceStepManager.prototype.findStepOutForward = function (currentStep) {
if (!this.traceAnalyser.trace) {
return currentStep
}
var i = currentStep
var depth = 0
while (++i < this.traceAnalyser.length) {
while (++i < this.traceAnalyser.trace.length) {
if (this.isReturnInstruction(i)) {
if (depth === 0) {
break
@ -60,7 +62,20 @@ TraceStepManager.prototype.findStepOutForward = function (currentStep) {
depth++
}
}
return i + 1
return i
}
TraceStepManager.prototype.findNextCall = function (currentStep) {
if (!this.traceAnalyser.trace) {
return currentStep
}
var i = currentStep
while (++i < this.traceAnalyser.trace.length) {
if (this.isCallInstruction(i)) {
return i
}
}
return currentStep
}
module.exports = TraceStepManager

@ -1,6 +1,7 @@
'use strict'
var React = require('react')
var style = require('./basicStyles')
var traceManagerUtil = require('./traceManagerUtil')
module.exports = React.createClass({
contextTypes: {
@ -12,12 +13,26 @@ module.exports = React.createClass({
},
getInitialState: function () {
return {blockNumber: '1382256', txNumber: '1', from: '', to: '', hash: ''}
return {blockNumber: '1000110', txNumber: '0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51', from: '', to: '', hash: ''}
},
// creation 0xa9619e1d0a35b2c1d686f5b661b3abd87f998d2844e8e9cc905edb57fc9ce349
// invokation 0x71a6d583d16d142c5c3e8903060e8a4ee5a5016348a9448df6c3e63b68076ec4
// test:
// creation: 0x72908de76f99fca476f9e3a3b5d352f350a98cd77d09cebfc59ffe32a6ecaa0b
// invokation: 0x20ef65b8b186ca942fcccd634f37074dde49b541c27994fc7596740ef44cfd51
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.to) {
tx.to = traceManagerUtil.contractCreationToken('0')
}
this.setState({from: tx.from, to: tx.to, hash: tx.hash})
this.props.onNewTxRequested(this.state.blockNumber, parseInt(this.state.txNumber), tx)
} else {
@ -25,6 +40,10 @@ module.exports = React.createClass({
}
},
updateTxhash: function (ev) {
this.state.hash = ev.target.value
},
updateBlockN: function (ev) {
this.state.blockNumber = ev.target.value
},
@ -36,8 +55,8 @@ module.exports = React.createClass({
render: function () {
return (
<div style={style.container}>
<input onChange={this.updateBlockN} type='text' placeholder={'Block number or hash (default 1382256)' + this.state.blockNumber}></input>
<input onChange={this.updateTxN} type='text' placeholder={'Transaction Number (default 1) ' + this.state.txNumber}></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 or hash (default 2) ' + this.state.txNumber}></input>
<button onClick={this.submit}>
Get
</button>
@ -45,16 +64,28 @@ module.exports = React.createClass({
<table>
<tbody>
<tr>
<td>Hash: </td>
<td>{this.state.hash}</td>
<td>
Hash:
</td>
<td>
{this.state.hash}
</td>
</tr>
<tr>
<td>From: </td>
<td>{this.state.from}</td>
<td>
From:
</td>
<td>
{this.state.from}
</td>
</tr>
<tr>
<td>To: </td>
<td>{this.state.to}</td>
<td>
To:
</td>
<td>
{this.state.to}
</td>
</tr>
</tbody>
</table>

@ -0,0 +1,8 @@
'use strict'
module.exports = {
extend: function (destination, source) {
for (var property in source) {
destination[property] = source[property]
}
}
}

@ -10,56 +10,37 @@ var StackPanel = require('./stackPanel')
var StoragePanel = require('./storagePanel')
module.exports = React.createClass({
contextTypes: {
traceManager: React.PropTypes.object
},
getInitialState: function () {
return {
currentAddress: null
}
},
getDefaultProps: function () {
return {
currentStepIndex: -1 // index of the selected item in the vmtrace
}
},
render: function () {
return (
<div style={this.props.vmTrace === null ? style.hidden : style.display}>
<div style={style.container}>
<span style={style.address}>Current code: {this.state.currentAddress}</span>
</div>
<div style={style.container}>
<table>
<tbody>
<tr>
<td>
<ASMCode currentStepIndex={this.props.currentStepIndex} />
<ASMCode />
<div style={Object.assign(style.inline, style.sticker)}>
<Sticker currentStepIndex={this.props.currentStepIndex} />
<Sticker />
</div>
</td>
<td>
<CalldataPanel currentStepIndex={this.props.currentStepIndex} />
<StackPanel />
</td>
</tr>
<tr>
<td>
<StackPanel currentStepIndex={this.props.currentStepIndex} />
<StoragePanel />
</td>
<td>
<CallstackPanel currentStepIndex={this.props.currentStepIndex} />
<MemoryPanel />
</td>
</tr>
<tr>
<td>
<StoragePanel currentStepIndex={this.props.currentStepIndex} />
<CalldataPanel />
</td>
<td>
<MemoryPanel currentStepIndex={this.props.currentStepIndex} />
<CallstackPanel />
</td>
</tr>
</tbody>
@ -67,20 +48,5 @@ module.exports = React.createClass({
</div>
</div>
)
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.currentStepIndex < 0) return
if (window.ethDebuggerSelectedItem !== nextProps.currentStepIndex) return
var self = this
this.context.traceManager.getCurrentCalledAddressAt(nextProps.currentStepIndex, function (error, address) {
if (error) {
console.log(error)
} else if (window.ethDebuggerSelectedItem === nextProps.currentStepIndex) {
self.setState({
currentAddress: address
})
}
})
}
})

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

Loading…
Cancel
Save