Unverified Commit 5c1c75f0 authored by yann300's avatar yann300 Committed by GitHub

Merge pull request #995 from ethereum/move_debugger

move debugger logic from remix-ide to remix-debug
parents 8846f8d4 c4bb550d
'use strict' 'use strict'
var EthDebugger = require('./src/Ethdebugger') var EthDebugger = require('./src/Ethdebugger')
var TransactionDebugger = require('./src/debugger/debugger')
var StorageViewer = require('./src/storage/storageViewer') var StorageViewer = require('./src/storage/storageViewer')
var StorageResolver = require('./src/storage/storageResolver') var StorageResolver = require('./src/storage/storageResolver')
...@@ -19,6 +20,7 @@ var BreakpointManager = remixLib.code.BreakpointManager ...@@ -19,6 +20,7 @@ var BreakpointManager = remixLib.code.BreakpointManager
*/ */
module.exports = { module.exports = {
EthDebugger: EthDebugger, EthDebugger: EthDebugger,
TransactionDebugger: TransactionDebugger,
/** /**
* constructor * constructor
* *
......
...@@ -13,11 +13,7 @@ var remixLib = require('remix-lib') ...@@ -13,11 +13,7 @@ var remixLib = require('remix-lib')
var TraceManager = remixLib.trace.TraceManager var TraceManager = remixLib.trace.TraceManager
var CodeManager = remixLib.code.CodeManager var CodeManager = remixLib.code.CodeManager
var traceHelper = remixLib.helpers.trace var traceHelper = remixLib.helpers.trace
var init = remixLib.init
var executionContext = remixLib.execution.executionContext
var EventManager = remixLib.EventManager var EventManager = remixLib.EventManager
var Web3Providers = remixLib.vm.Web3Providers
var DummyProvider = remixLib.vm.DummyProvider
/** /**
* Ethdebugger is a wrapper around a few classes that helps debugging a transaction * Ethdebugger is a wrapper around a few classes that helps debugging a transaction
...@@ -36,17 +32,12 @@ function Ethdebugger (opts) { ...@@ -36,17 +32,12 @@ function Ethdebugger (opts) {
this.opts = opts || {} this.opts = opts || {}
if (!this.opts.compilationResult) this.opts.compilationResult = () => { return null } if (!this.opts.compilationResult) this.opts.compilationResult = () => { return null }
this.executionContext = opts.executionContext || executionContext this.web3 = opts.web3
this.web3 = opts.web3 || this.executionContext.web3
this.event = new EventManager() this.event = new EventManager()
this.tx this.tx
this.web3Providers = new Web3Providers()
this.addProvider('DUMMYWEB3', new DummyProvider())
this.switchProvider('DUMMYWEB3')
this.traceManager = new TraceManager({web3: this.web3}) this.traceManager = new TraceManager({web3: this.web3})
this.codeManager = new CodeManager(this.traceManager) this.codeManager = new CodeManager(this.traceManager)
this.solidityProxy = new SolidityProxy(this.traceManager, this.codeManager) this.solidityProxy = new SolidityProxy(this.traceManager, this.codeManager)
...@@ -163,39 +154,10 @@ Ethdebugger.prototype.storageViewAt = function (step, address) { ...@@ -163,39 +154,10 @@ Ethdebugger.prototype.storageViewAt = function (step, address) {
address: address address: address
}, this.storageResolver, this.traceManager) }, this.storageResolver, this.traceManager)
} }
/* set env */
Ethdebugger.prototype.web3 = function () {
return this.web3
}
Ethdebugger.prototype.addProvider = function (type, obj) { Ethdebugger.prototype.updateWeb3 = function (web3) {
this.web3Providers.addProvider(type, obj) this.web3 = web3
this.event.trigger('providerAdded', [type]) this.setManagers()
}
Ethdebugger.prototype.switchProvider = function (type) {
var self = this
this.web3Providers.get(type, function (error, obj) {
if (error) {
console.log('provider ' + type + ' not defined')
} else {
self.web3 = obj
self.setManagers()
// self.traceManager.web3 = self.web3
self.executionContext.detectNetwork((error, network) => {
if (error || !network) {
self.web3Debug = obj
self.web3 = obj
} else {
var webDebugNode = init.web3DebugNode(network.name)
self.web3Debug = !webDebugNode ? obj : webDebugNode
self.web3 = !webDebugNode ? obj : webDebugNode
}
self.setManagers()
})
self.event.trigger('providerChanged', [type])
}
})
} }
Ethdebugger.prototype.debug = function (tx) { Ethdebugger.prototype.debug = function (tx) {
......
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var localDecoder = require('../solidity-decoder/localDecoder')
var StorageViewer = require('../storage/storageViewer')
class DebuggerSolidityLocals {
constructor (tx, _stepManager, _traceManager, _internalTreeCall) {
this.event = new EventManager()
this.stepManager = _stepManager
this.internalTreeCall = _internalTreeCall
this.storageResolver = null
this.traceManager = _traceManager
this.tx = tx
}
init (sourceLocation) {
const self = this
var decodeTimeout = null
if (!this.storageResolver) {
return self.event.trigger('solidityLocalsMessage', ['storage not ready'])
}
if (decodeTimeout) {
window.clearTimeout(decodeTimeout)
}
self.event.trigger('solidityLocalsUpdating')
decodeTimeout = setTimeout(function () {
self.decode(sourceLocation)
}, 500)
}
decode (sourceLocation) {
const self = this
self.event.trigger('solidityLocalsMessage', [''])
self.traceManager.waterfall([
self.traceManager.getStackAt,
self.traceManager.getMemoryAt,
self.traceManager.getCurrentCalledAddressAt],
self.stepManager.currentStepIndex,
(error, result) => {
if (error) {
return console.log(error)
}
var stack = result[0].value
var memory = result[1].value
try {
var storageViewer = new StorageViewer({ stepIndex: self.stepManager.currentStepIndex, tx: self.tx, address: result[2].value }, self.storageResolver, self.traceManager)
localDecoder.solidityLocals(self.stepManager.currentStepIndex, self.internalTreeCall, stack, memory, storageViewer, sourceLocation).then((locals) => {
if (!locals.error) {
self.event.trigger('solidityLocals', [locals])
}
if (!Object.keys(locals).length) {
self.event.trigger('solidityLocalsMessage', ['no locals'])
}
})
} catch (e) {
self.event.trigger('solidityLocalsMessage', [e.message])
}
})
}
}
module.exports = DebuggerSolidityLocals
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var ui = remixLib.helpers.ui
var StorageResolver = require('../storage/storageResolver')
var StorageViewer = require('../storage/storageViewer')
var DebuggerSolidityState = require('./solidityState')
var DebuggerSolidityLocals = require('./solidityLocals')
class VmDebuggerLogic {
constructor (_debugger, tx, _stepManager, _traceManager, _codeManager, _solidityProxy, _callTree) {
this.event = new EventManager()
this.debugger = _debugger
this.stepManager = _stepManager
this._traceManager = _traceManager
this._codeManager = _codeManager
this._solidityProxy = _solidityProxy
this._callTree = _callTree
this.storageResolver = null
this.tx = tx
this.debuggerSolidityState = new DebuggerSolidityState(tx, _stepManager, _traceManager, _codeManager, _solidityProxy)
this.debuggerSolidityLocals = new DebuggerSolidityLocals(tx, _stepManager, _traceManager, _callTree)
}
start () {
this.listenToEvents()
this.listenToCodeManagerEvents()
this.listenToTraceManagerEvents()
this.listenToFullStorageChanges()
this.listenToNewChanges()
this.listenToSolidityStateEvents()
this.listenToSolidityLocalsEvents()
}
listenToEvents () {
const self = this
this.debugger.event.register('traceUnloaded', function () {
self.event.trigger('traceUnloaded')
})
this.debugger.event.register('newTraceLoaded', function () {
self.event.trigger('newTraceLoaded')
})
}
listenToCodeManagerEvents () {
const self = this
this._codeManager.event.register('changed', function (code, address, index) {
self.event.trigger('codeManagerChanged', [code, address, index])
})
}
listenToTraceManagerEvents () {
const self = this
this.event.register('indexChanged', this, function (index) {
if (index < 0) return
if (self.stepManager.currentStepIndex !== index) return
self.event.trigger('indexUpdate', [index])
self._traceManager.getCallDataAt(index, function (error, calldata) {
if (error) {
console.log(error)
self.event.trigger('traceManagerCallDataUpdate', [{}])
} else if (self.stepManager.currentStepIndex === index) {
self.event.trigger('traceManagerCallDataUpdate', [calldata])
}
})
self._traceManager.getMemoryAt(index, function (error, memory) {
if (error) {
console.log(error)
self.event.trigger('traceManagerMemoryUpdate', [{}])
} else if (self.stepManager.currentStepIndex === index) {
self.event.trigger('traceManagerMemoryUpdate', [ui.formatMemory(memory, 16)])
}
})
self._traceManager.getCallStackAt(index, function (error, callstack) {
if (error) {
console.log(error)
self.event.trigger('traceManagerCallStackUpdate', [{}])
} else if (self.stepManager.currentStepIndex === index) {
self.event.trigger('traceManagerCallStackUpdate', [callstack])
}
})
self._traceManager.getStackAt(index, function (error, callstack) {
if (error) {
console.log(error)
self.event.trigger('traceManagerStackUpdate', [{}])
} else if (self.stepManager.currentStepIndex === index) {
self.event.trigger('traceManagerStackUpdate', [callstack])
}
})
self._traceManager.getCurrentCalledAddressAt(index, (error, address) => {
if (error) return
if (!self.storageResolver) return
var storageViewer = new StorageViewer({ stepIndex: self.stepManager.currentStepIndex, tx: self.tx, address: address }, self.storageResolver, self._traceManager)
storageViewer.storageRange((error, storage) => {
if (error) {
console.log(error)
self.event.trigger('traceManagerStorageUpdate', [{}])
} else if (self.stepManager.currentStepIndex === index) {
var header = storageViewer.isComplete(address) ? 'completely loaded' : 'partially loaded...'
self.event.trigger('traceManagerStorageUpdate', [storage, header])
}
})
})
self._traceManager.getCurrentStep(index, function (error, step) {
self.event.trigger('traceCurrentStepUpdate', [error, step])
})
self._traceManager.getMemExpand(index, function (error, addmem) {
self.event.trigger('traceMemExpandUpdate', [error, addmem])
})
self._traceManager.getStepCost(index, function (error, gas) {
self.event.trigger('traceStepCostUpdate', [error, gas])
})
self._traceManager.getCurrentCalledAddressAt(index, function (error, address) {
self.event.trigger('traceCurrentCalledAddressAtUpdate', [error, address])
})
self._traceManager.getRemainingGas(index, function (error, remaining) {
self.event.trigger('traceRemainingGasUpdate', [error, remaining])
})
self._traceManager.getReturnValue(index, function (error, returnValue) {
if (error) {
self.event.trigger('traceReturnValueUpdate', [[error]])
} else if (self.stepManager.currentStepIndex === index) {
self.event.trigger('traceReturnValueUpdate', [[returnValue]])
}
})
})
}
listenToFullStorageChanges () {
const self = this
this.address = []
this.traceLength = 0
self.debugger.event.register('newTraceLoaded', function (length) {
self._traceManager.getAddresses(function (error, addresses) {
if (error) return
self.event.trigger('traceAddressesUpdate', [addresses])
self.addresses = addresses
})
self._traceManager.getLength(function (error, length) {
if (error) return
self.event.trigger('traceLengthUpdate', [length])
self.traceLength = length
})
})
self.debugger.event.register('indexChanged', this, function (index) {
if (index < 0) return
if (self.stepManager.currentStepIndex !== index) return
if (!self.storageResolver) return
if (index !== self.traceLength - 1) {
return self.event.trigger('traceLengthUpdate', [{}])
}
var storageJSON = {}
for (var k in self.addresses) {
var address = self.addresses[k]
var storageViewer = new StorageViewer({ stepIndex: self.stepManager.currentStepIndex, tx: self.tx, address: address }, self.storageResolver, self._traceManager)
storageViewer.storageRange(function (error, result) {
if (!error) {
storageJSON[address] = result
self.event.trigger('traceLengthUpdate', [storageJSON])
}
})
}
})
}
listenToNewChanges () {
const self = this
self.debugger.event.register('newTraceLoaded', this, function () {
self.storageResolver = new StorageResolver({web3: self.debugger.web3})
self.debuggerSolidityState.storageResolver = self.storageResolver
self.debuggerSolidityLocals.storageResolver = self.storageResolver
self.event.trigger('newTrace', [])
})
self.debugger.event.register('callTreeReady', function () {
if (self.debugger.callTree.reducedTrace.length) {
return self.event.trigger('newCallTree', [])
}
})
}
listenToSolidityStateEvents () {
const self = this
this.event.register('indexChanged', this.debuggerSolidityState.init.bind(this.debuggerSolidityState))
this.debuggerSolidityState.event.register('solidityState', function (state) {
self.event.trigger('solidityState', [state])
})
this.debuggerSolidityState.event.register('solidityStateMessage', function (message) {
self.event.trigger('solidityStateMessage', [message])
})
this.debuggerSolidityState.event.register('solidityStateUpdating', function () {
self.event.trigger('solidityStateUpdating', [])
})
this.event.register('traceUnloaded', this.debuggerSolidityState.reset.bind(this.debuggerSolidityState))
this.event.register('newTraceLoaded', this.debuggerSolidityState.reset.bind(this.debuggerSolidityState))
}
listenToSolidityLocalsEvents () {
const self = this
this.event.register('sourceLocationChanged', this.debuggerSolidityLocals.init.bind(this.debuggerSolidityLocals))
this.debuggerSolidityLocals.event.register('solidityLocals', function (state) {
self.event.trigger('solidityLocals', [state])
})
this.debuggerSolidityLocals.event.register('solidityLocalsMessage', function (message) {
self.event.trigger('solidityLocalsMessage', [message])
})
this.debuggerSolidityLocals.event.register('solidityLocalsUpdating', function () {
self.event.trigger('solidityLocalsUpdating', [])
})
this.debuggerSolidityLocals.event.register('traceReturnValueUpdate', function (data, header) {
self.event.trigger('traceReturnValueUpdate', [data, header])
})
}
}
module.exports = VmDebuggerLogic
'use strict'
var Ethdebugger = require('../Ethdebugger')
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var traceHelper = remixLib.helpers.trace
var StepManager = require('./stepManager')
var VmDebuggerLogic = require('./VmDebugger')
function Debugger (options) {
var self = this
this.event = new EventManager()
this.offsetToLineColumnConverter = options.offsetToLineColumnConverter
this.compiler = options.compiler
this.debugger = new Ethdebugger({
web3: options.web3,
compilationResult: () => {
var compilationResult = this.compiler.lastCompilationResult
if (compilationResult) {
return compilationResult.data
}
return null
}
})
this.breakPointManager = new remixLib.code.BreakpointManager(this.debugger, (sourceLocation) => {
return self.offsetToLineColumnConverter.offsetToLineColumn(sourceLocation, sourceLocation.file, this.compiler.lastCompilationResult.source.sources, this.compiler.lastCompilationResult.data.sources)
}, (step) => {
self.event.trigger('breakpointStep', [step])
})
this.debugger.setBreakpointManager(this.breakPointManager)
this.debugger.event.register('newTraceLoaded', this, function () {
self.event.trigger('debuggerStatus', [true])
})
this.debugger.event.register('traceUnloaded', this, function () {
self.event.trigger('debuggerStatus', [false])
})
this.event.register('breakpointStep', function (step) {
self.step_manager.jumpTo(step)
})
}
Debugger.prototype.registerAndHighlightCodeItem = function (index) {
const self = this
// register selected code item, highlight the corresponding source location
if (!self.compiler.lastCompilationResult) return
self.debugger.traceManager.getCurrentCalledAddressAt(index, (error, address) => {
if (error) return console.log(error)
self.debugger.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, index, self.compiler.lastCompilationResult.data.contracts, function (error, rawLocation) {
if (!error && self.compiler.lastCompilationResult && self.compiler.lastCompilationResult.data) {
var lineColumnPos = self.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, self.compiler.lastCompilationResult.source.sources, self.compiler.lastCompilationResult.data.sources)
self.event.trigger('newSourceLocation', [lineColumnPos, rawLocation])
} else {
self.event.trigger('newSourceLocation', [null])
}
})
})
}
Debugger.prototype.updateWeb3 = function (web3) {
this.debugger.web3 = web3
}
Debugger.prototype.debug = function (blockNumber, txNumber, tx, loadingCb) {
const self = this
let web3 = this.debugger.web3
if (this.debugger.traceManager.isLoading) {
return
}
if (tx) {
if (!tx.to) {
tx.to = traceHelper.contractCreationToken('0')
}
return self.debugTx(tx, loadingCb)
}
try {
if (txNumber.indexOf('0x') !== -1) {
return web3.eth.getTransaction(txNumber, function (_error, result) {
let tx = result
self.debugTx(tx, loadingCb)
})
}
web3.eth.getTransactionFromBlock(blockNumber, txNumber, function (_error, result) {
let tx = result
self.debugTx(tx, loadingCb)
})
} catch (e) {
console.error(e.message)
}
}
Debugger.prototype.debugTx = function (tx, loadingCb) {
const self = this
this.step_manager = new StepManager(this.debugger, this.debugger.traceManager)
this.debugger.codeManager.event.register('changed', this, (code, address, instIndex) => {
self.debugger.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, this.step_manager.currentStepIndex, this.debugger.solidityProxy.contracts, (error, sourceLocation) => {
if (!error) {
self.vmDebuggerLogic.event.trigger('sourceLocationChanged', [sourceLocation])
}
})
})
this.vmDebuggerLogic = new VmDebuggerLogic(this.debugger, tx, this.step_manager, this.debugger.traceManager, this.debugger.codeManager, this.debugger.solidityProxy, this.debugger.callTree)
this.step_manager.event.register('stepChanged', this, function (stepIndex) {
self.debugger.codeManager.resolveStep(stepIndex, tx)
self.step_manager.event.trigger('indexChanged', [stepIndex])
self.vmDebuggerLogic.event.trigger('indexChanged', [stepIndex])
self.registerAndHighlightCodeItem(stepIndex)
})
loadingCb()
this.debugger.debug(tx)
}
Debugger.prototype.unload = function () {
this.debugger.unLoad()
this.event.trigger('debuggerUnloaded')
}
module.exports = Debugger
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var stateDecoder = require('../solidity-decoder/stateDecoder')
var StorageViewer = require('../storage/storageViewer')
class DebuggerSolidityState {
constructor (tx, _stepManager, _traceManager, _codeManager, _solidityProxy) {
this.event = new EventManager()
this.storageResolver = null
this.stepManager = _stepManager
this.traceManager = _traceManager
this.codeManager = _codeManager
this.solidityProxy = _solidityProxy
this.stateVariablesByAddresses = {}
this.tx = tx
}
init (index) {
var self = this
var decodeTimeout = null
if (index < 0) {
return self.event.trigger('solidityStateMessage', ['invalid step index'])
}
if (self.stepManager.currentStepIndex !== index) return
if (!self.solidityProxy.loaded()) {
return self.event.trigger('solidityStateMessage', ['invalid step index'])
}
if (!self.storageResolver) {
return
}
if (decodeTimeout) {
window.clearTimeout(decodeTimeout)
}
self.event.trigger('solidityStateUpdating')
decodeTimeout = setTimeout(function () {
self.decode(index)
}, 500)
}
reset () {
this.stateVariablesByAddresses = {}
}
decode (index) {
const self = this
self.traceManager.getCurrentCalledAddressAt(self.stepManager.currentStepIndex, function (error, address) {
if (error) {
return self.event.trigger('solidityState', [{}])
}
if (self.stateVariablesByAddresses[address]) {
return self.extractStateVariables(self.stateVariablesByAddresses[address], address)
}
self.solidityProxy.extractStateVariablesAt(index, function (error, stateVars) {
if (error) {
return self.event.trigger('solidityState', [{}])
}
self.stateVariablesByAddresses[address] = stateVars
self.extractStateVariables(stateVars, address)
})
})
}
extractStateVariables (stateVars, address) {
const self = this
var storageViewer = new StorageViewer({ stepIndex: self.stepManager.currentStepIndex, tx: self.tx, address: address }, self.storageResolver, self.traceManager)
stateDecoder.decodeState(stateVars, storageViewer).then((result) => {
self.event.trigger('solidityStateMessage', [''])
if (result.error) {
return self.event.trigger('solidityStateMessage', [result.error])
}
self.event.trigger('solidityState', [result])
})
}
}
module.exports = DebuggerSolidityState
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
class DebuggerStepManager {
constructor (_debugger, traceManager) {
this.event = new EventManager()
this.debugger = _debugger
this.traceManager = traceManager
this.currentStepIndex = 0
this.traceLength = 0
this.revertionPoint = null
this.listenToEvents()
}
listenToEvents () {
const self = this
this.debugger.event.register('newTraceLoaded', this, function () {
self.traceManager.getLength(function (error, newLength) {
if (error) {
return console.log(error)
}
if (self.traceLength !== newLength) {
self.event.trigger('traceLengthChanged', [newLength])
self.traceLength = newLength
}
self.jumpTo(0)
})
})
this.debugger.callTree.event.register('callTreeReady', () => {
if (self.debugger.callTree.functionCallStack.length) {
self.jumpTo(self.debugger.callTree.functionCallStack[0])
}
})
this.event.register('indexChanged', this, (index) => {
if (index < 0) return
if (self.currentStepIndex !== index) return
self.traceManager.buildCallPath(index, (error, callsPath) => {
if (error) {
console.log(error)
return self.event.trigger('revertWarning', [''])
}
self.currentCall = callsPath[callsPath.length - 1]
if (self.currentCall.reverted) {
let revertedReason = self.currentCall.outofgas ? 'outofgas' : ''
self.revertionPoint = self.currentCall.return
return self.event.trigger('revertWarning', [revertedReason])
}
for (var k = callsPath.length - 2; k >= 0; k--) {
var parent = callsPath[k]
if (!parent.reverted) continue
self.revertionPoint = parent.return
self.event.trigger('revertWarning', ['parenthasthrown'])
}
self.event.trigger('revertWarning', [''])
})
})
}
triggerStepChanged (step) {
const self = this
this.traceManager.getLength(function (error, length) {
let stepState = 'valid'
if (error) {
stepState = 'invalid'
} else if (step <= 0) {
stepState = 'initial'
} else if (step >= length - 1) {
stepState = 'end'
}
let jumpOutDisabled = (step === self.traceManager.findStepOut(step))
self.event.trigger('stepChanged', [step, stepState, jumpOutDisabled])
})
}
stepIntoBack () {
if (!this.traceManager.isLoaded()) return
var step = this.currentStepIndex - 1
this.currentStepIndex = step
if (!this.traceManager.inRange(step)) {
return
}
this.event.trigger('stepChanged', [step])
}
stepIntoForward () {
if (!this.traceManager.isLoaded()) return
var step = this.currentStepIndex + 1
this.currentStepIndex = step
if (!this.traceManager.inRange(step)) {
return
}
this.event.trigger('stepChanged', [step])
}
stepOverBack () {
if (!this.traceManager.isLoaded()) return
var step = this.traceManager.findStepOverBack(this.currentStepIndex)
this.currentStepIndex = step
this.event.trigger('stepChanged', [step])
}
stepOverForward () {
if (!this.traceManager.isLoaded()) return
var step = this.traceManager.findStepOverForward(this.currentStepIndex)
this.currentStepIndex = step
this.event.trigger('stepChanged', [step])
}
jumpOut () {
if (!this.traceManager.isLoaded()) return
var step = this.traceManager.findStepOut(this.currentStepIndex)
this.currentStepIndex = step
this.event.trigger('stepChanged', [step])
}
jumpTo (step) {
if (!this.traceManager.inRange(step)) return
this.currentStepIndex = step
this.event.trigger('stepChanged', [step])
}
jumpToException () {
this.jumpTo(this.revertionPoint)
}
jumpNextBreakpoint () {
this.debugger.breakpointManager.jumpNextBreakpoint(this.currentStepIndex, true)
}
jumpPreviousBreakpoint () {
this.debugger.breakpointManager.jumpPreviousBreakpoint(this.currentStepIndex, true)
}
}
module.exports = DebuggerStepManager
...@@ -67,7 +67,6 @@ async function extractHexValue (location, storageResolver, byteLength) { ...@@ -67,7 +67,6 @@ async function extractHexValue (location, storageResolver, byteLength) {
try { try {
slotvalue = await readFromStorage(location.slot, storageResolver) slotvalue = await readFromStorage(location.slot, storageResolver)
} catch (e) { } catch (e) {
console.log(e)
return '0x' return '0x'
} }
return extractHexByteSlice(slotvalue, byteLength, location.offset) return extractHexByteSlice(slotvalue, byteLength, location.offset)
......
'use strict' // 'use strict'
var tape = require('tape') // var tape = require('tape')
var remixLib = require('remix-lib') // var remixLib = require('remix-lib')
var compilerInput = remixLib.helpers.compiler.compilerInput // var compilerInput = remixLib.helpers.compiler.compilerInput
var vmCall = require('./vmCall') // var vmCall = require('./vmCall')
var Debugger = require('../src/Ethdebugger') // var Debugger = require('../src/Ethdebugger')
var compiler = require('solc') // var compiler = require('solc')
//
require('./decoder/decodeInfo.js') // require('./decoder/decodeInfo.js')
require('./decoder/storageLocation.js') // require('./decoder/storageLocation.js')
require('./decoder/storageDecoder.js') // require('./decoder/storageDecoder.js')
require('./decoder/localDecoder.js') // require('./decoder/localDecoder.js')
//
var BreakpointManager = remixLib.code.BreakpointManager // var BreakpointManager = remixLib.code.BreakpointManager
//
tape('debug contract', function (t) { // tape('debug contract', function (t) {
t.plan(12) // t.plan(12)
var privateKey = Buffer.from('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a', 'hex') // var privateKey = Buffer.from('dae9801649ba2d95a21e688b56f77905e5667c44ce868ec83f82e838712a2c7a', 'hex')
var vm = vmCall.initVM(t, privateKey) // var vm = vmCall.initVM(t, privateKey)
var output = compiler.compile(compilerInput(ballot)) // var output = compiler.compile(compilerInput(ballot))
output = JSON.parse(output) // output = JSON.parse(output)
var web3VM = new remixLib.vm.Web3VMProvider() // var web3VM = new remixLib.vm.Web3VMProvider()
web3VM.setVM(vm) // web3VM.setVM(vm)
vmCall.sendTx(vm, {nonce: 0, privateKey: privateKey}, null, 0, output.contracts['test.sol']['Ballot'].evm.bytecode.object, (error, txHash) => { // vmCall.sendTx(vm, {nonce: 0, privateKey: privateKey}, null, 0, output.contracts['test.sol']['Ballot'].evm.bytecode.object, (error, txHash) => {
if (error) { // if (error) {
t.end(error) // t.end(error)
} else { // } else {
web3VM.eth.getTransaction(txHash, (error, tx) => { // web3VM.eth.getTransaction(txHash, (error, tx) => {
if (error) { // if (error) {
t.end(error) // t.end(error)
} else { // } else {
var debugManager = new Debugger({ // var debugManager = new Debugger({
compilationResult: function () { // compilationResult: function () {
return output // return output
} // }
}) // })
//
debugManager.addProvider('web3vmprovider', web3VM) // debugManager.addProvider('web3vmprovider', web3VM)
debugManager.switchProvider('web3vmprovider') // debugManager.switchProvider('web3vmprovider')
//
debugManager.callTree.event.register('callTreeReady', () => { // debugManager.callTree.event.register('callTreeReady', () => {
testDebugging(t, debugManager) // testDebugging(t, debugManager)
}) // })
//
debugManager.debug(tx) // debugManager.debug(tx)
} // }
}) // })
} // }
}) // })
}) // })
//
//
function testDebugging (t, debugManager) { // function testDebugging (t, debugManager) {
// stack // // stack
debugManager.traceManager.getStackAt(4, (error, callstack) => { // debugManager.traceManager.getStackAt(4, (error, callstack) => {
if (error) return t.end(error) // if (error) return t.end(error)
t.equal(JSON.stringify(callstack), JSON.stringify([ '0x0000000000000000000000000000000000000000000000000000000000000000' ])) // t.equal(JSON.stringify(callstack), JSON.stringify([ '0x0000000000000000000000000000000000000000000000000000000000000000' ]))
}) // })
//
debugManager.traceManager.getStackAt(41, (error, callstack) => { // debugManager.traceManager.getStackAt(41, (error, callstack) => {
if (error) return t.end(error) // if (error) return t.end(error)
//
/* // /*
t.equal(JSON.stringify(callstack), JSON.stringify(['0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000004b0897b0513fdc7c541b6d9d7e929c4e5364d2db', '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000001', '0x000000000000000000000000000000000000000000000000000000000000002d'])) // t.equal(JSON.stringify(callstack), JSON.stringify(['0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000004b0897b0513fdc7c541b6d9d7e929c4e5364d2db', '0x0000000000000000000000000000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000000000000000000000000001', '0x000000000000000000000000000000000000000000000000000000000000002d']))
*/ // */
}) // })
//
// storage // // storage
debugManager.traceManager.getCurrentCalledAddressAt(38, (error, address) => { // debugManager.traceManager.getCurrentCalledAddressAt(38, (error, address) => {
if (error) return t.end(error) // if (error) return t.end(error)
var storageView = debugManager.storageViewAt(38, address) // var storageView = debugManager.storageViewAt(38, address)
storageView.storageRange((error, storage) => { // storageView.storageRange((error, storage) => {
if (error) return t.end(error) // if (error) return t.end(error)
t.equal(JSON.stringify(storage), JSON.stringify({ '0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563': { key: '0x0000000000000000000000000000000000000000000000000000000000000000', value: '0x0000000000000000000000004b0897b0513fdc7c541b6d9d7e929c4e5364d2db' } })) // t.equal(JSON.stringify(storage), JSON.stringify({ '0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563': { key: '0x0000000000000000000000000000000000000000000000000000000000000000', value: '0x0000000000000000000000004b0897b0513fdc7c541b6d9d7e929c4e5364d2db' } }))
}) // })
}) // })
//
debugManager.extractStateAt(116, (error, state) => { // debugManager.extractStateAt(116, (error, state) => {
if (error) return t.end(error) // if (error) return t.end(error)
debugManager.decodeStateAt(116, state, (error, decodedState) => { // debugManager.decodeStateAt(116, state, (error, decodedState) => {
if (error) return t.end(error) // if (error) return t.end(error)
t.equal(decodedState['chairperson'].value, '0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB') // t.equal(decodedState['chairperson'].value, '0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB')
t.equal(decodedState['chairperson'].type, 'address') // t.equal(decodedState['chairperson'].type, 'address')
t.equal(decodedState['proposals'].value[0].value.voteCount.value, '0') // t.equal(decodedState['proposals'].value[0].value.voteCount.value, '0')
t.equal(decodedState['proposals'].value[0].value.voteCount.type, 'uint256') // t.equal(decodedState['proposals'].value[0].value.voteCount.type, 'uint256')
t.equal(decodedState['proposals'].value[0].type, 'struct Ballot.Proposal') // t.equal(decodedState['proposals'].value[0].type, 'struct Ballot.Proposal')
t.equal(decodedState['proposals'].length, '0x1') // t.equal(decodedState['proposals'].length, '0x1')
t.equal(decodedState['proposals'].type, 'struct Ballot.Proposal[]') // t.equal(decodedState['proposals'].type, 'struct Ballot.Proposal[]')
}) // })
}) // })
//
debugManager.traceManager.getCurrentCalledAddressAt(104, (error, address) => { // debugManager.traceManager.getCurrentCalledAddressAt(104, (error, address) => {
if (error) return t.end(error) // if (error) return t.end(error)
debugManager.sourceLocationFromVMTraceIndex(address, 104, (error, location) => { // debugManager.sourceLocationFromVMTraceIndex(address, 104, (error, location) => {
if (error) return t.end(error) // if (error) return t.end(error)
debugManager.decodeLocalsAt(104, location, (error, decodedlocals) => { // debugManager.decodeLocalsAt(104, location, (error, decodedlocals) => {
if (error) return t.end(error) // if (error) return t.end(error)
t.equal(JSON.stringify(decodedlocals), JSON.stringify({'p': {'value': '45', 'type': 'uint256'}, 'addressLocal': {'value': '0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB', 'type': 'address'}, 'proposalsLocals': {'value': [{'value': {'voteCount': {'value': '0', 'type': 'uint256'}}, 'type': 'struct Ballot.Proposal'}], 'length': '0x1', 'type': 'struct Ballot.Proposal[]'}})) // t.equal(JSON.stringify(decodedlocals), JSON.stringify({'p': {'value': '45', 'type': 'uint256'}, 'addressLocal': {'value': '0x4B0897B0513FDC7C541B6D9D7E929C4E5364D2DB', 'type': 'address'}, 'proposalsLocals': {'value': [{'value': {'voteCount': {'value': '0', 'type': 'uint256'}}, 'type': 'struct Ballot.Proposal'}], 'length': '0x1', 'type': 'struct Ballot.Proposal[]'}}))
}) // })
}) // })
}) // })
//
var sourceMappingDecoder = new remixLib.SourceMappingDecoder() // var sourceMappingDecoder = new remixLib.SourceMappingDecoder()
var breakPointManager = new BreakpointManager(debugManager, (rawLocation) => { // var breakPointManager = new BreakpointManager(debugManager, (rawLocation) => {
return sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, sourceMappingDecoder.getLinebreakPositions(ballot)) // return sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, sourceMappingDecoder.getLinebreakPositions(ballot))
}) // })
//
breakPointManager.add({fileName: 'test.sol', row: 23}) // breakPointManager.add({fileName: 'test.sol', row: 23})
//
breakPointManager.event.register('breakpointHit', function (sourceLocation, step) { // breakPointManager.event.register('breakpointHit', function (sourceLocation, step) {
console.log('breakpointHit') // console.log('breakpointHit')
t.equal(JSON.stringify(sourceLocation), JSON.stringify({ start: 587, length: 1, file: 0, jump: '-' })) // t.equal(JSON.stringify(sourceLocation), JSON.stringify({ start: 587, length: 1, file: 0, jump: '-' }))
t.equal(step, 74) // t.equal(step, 74)
}) // })
//
breakPointManager.event.register('noBreakpointHit', function () { // breakPointManager.event.register('noBreakpointHit', function () {
t.end('noBreakpointHit') // t.end('noBreakpointHit')
console.log('noBreakpointHit') // console.log('noBreakpointHit')
}) // })
breakPointManager.jumpNextBreakpoint(0, true) // breakPointManager.jumpNextBreakpoint(0, true)
} // }
//
var ballot = `pragma solidity ^0.5.0; // var ballot = `pragma solidity ^0.5.0;
contract Ballot { // contract Ballot {
//
struct Voter { // struct Voter {
uint weight; // uint weight;
bool voted; // bool voted;
uint8 vote; // uint8 vote;
address delegate; // address delegate;
} // }
struct Proposal { // struct Proposal {
uint voteCount; // uint voteCount;
} // }
//
address chairperson; // address chairperson;
mapping(address => Voter) voters; // mapping(address => Voter) voters;
Proposal[] proposals; // Proposal[] proposals;
//
/// Create a new ballot with $(_numProposals) different proposals. // /// Create a new ballot with $(_numProposals) different proposals.
constructor() public { // constructor() public {
uint p = 45; // uint p = 45;
chairperson = msg.sender; // chairperson = msg.sender;
address addressLocal = msg.sender; // copy of state variable // address addressLocal = msg.sender; // copy of state variable
voters[chairperson].weight = 1; // voters[chairperson].weight = 1;
proposals.length = 1; // proposals.length = 1;
Proposal[] storage proposalsLocals = proposals; // copy of state variable // Proposal[] storage proposalsLocals = proposals; // copy of state variable
} // }
//
/// Give $(toVoter) the right to vote on this ballot. // /// Give $(toVoter) the right to vote on this ballot.
/// May only be called by $(chairperson). // /// May only be called by $(chairperson).
function giveRightToVote(address toVoter) public { // function giveRightToVote(address toVoter) public {
if (msg.sender != chairperson || voters[toVoter].voted) return; // if (msg.sender != chairperson || voters[toVoter].voted) return;
voters[toVoter].weight = 1; // voters[toVoter].weight = 1;
} // }
//
/// Delegate your vote to the voter $(to). // /// Delegate your vote to the voter $(to).
function delegate(address to) public { // function delegate(address to) public {
Voter storage sender = voters[msg.sender]; // assigns reference // Voter storage sender = voters[msg.sender]; // assigns reference
if (sender.voted) return; // if (sender.voted) return;
while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender) // while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender)
to = voters[to].delegate; // to = voters[to].delegate;
if (to == msg.sender) return; // if (to == msg.sender) return;
sender.voted = true; // sender.voted = true;
sender.delegate = to; // sender.delegate = to;
Voter storage delegateTo = voters[to]; // Voter storage delegateTo = voters[to];
if (delegateTo.voted) // if (delegateTo.voted)
proposals[delegateTo.vote].voteCount += sender.weight; // proposals[delegateTo.vote].voteCount += sender.weight;
else // else
delegateTo.weight += sender.weight; // delegateTo.weight += sender.weight;
} // }
//
/// Give a single vote to proposal $(toProposal). // /// Give a single vote to proposal $(toProposal).
function vote(uint8 toProposal) public { // function vote(uint8 toProposal) public {
Voter storage sender = voters[msg.sender]; // Voter storage sender = voters[msg.sender];
if (sender.voted || toProposal >= proposals.length) return; // if (sender.voted || toProposal >= proposals.length) return;
sender.voted = true; // sender.voted = true;
sender.vote = toProposal; // sender.vote = toProposal;
proposals[toProposal].voteCount += sender.weight; // proposals[toProposal].voteCount += sender.weight;
} // }
//
function winningProposal() public view returns (uint8 _winningProposal) { // function winningProposal() public view returns (uint8 _winningProposal) {
uint256 winningVoteCount = 0; // uint256 winningVoteCount = 0;
for (uint8 prop = 0; prop < proposals.length; prop++) // for (uint8 prop = 0; prop < proposals.length; prop++)
if (proposals[prop].voteCount > winningVoteCount) { // if (proposals[prop].voteCount > winningVoteCount) {
winningVoteCount = proposals[prop].voteCount; // winningVoteCount = proposals[prop].voteCount;
_winningProposal = prop; // _winningProposal = prop;
} // }
} // }
}` // }`
...@@ -67,7 +67,7 @@ var vm = new EthJSVM({ ...@@ -67,7 +67,7 @@ var vm = new EthJSVM({
vm.stateManager = stateManager vm.stateManager = stateManager
vm.blockchain = stateManager.blockchain vm.blockchain = stateManager.blockchain
vm.trie = stateManager.trie vm.trie = stateManager.trie
vm.stateManager.checkpoint() vm.stateManager.checkpoint(() => {})
var web3VM = new Web3VMProvider() var web3VM = new Web3VMProvider()
web3VM.setVM(vm) web3VM.setVM(vm)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment