From 1b379f6c507556130ffc1658e2735aa59ea511a3 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 21 Mar 2018 13:48:04 +0100 Subject: [PATCH 1/8] alert remix-debugger deprecated --- remix-debugger/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/remix-debugger/README.md b/remix-debugger/README.md index 99745fc323..2da32212e5 100644 --- a/remix-debugger/README.md +++ b/remix-debugger/README.md @@ -1,5 +1,6 @@ # `remix-debugger` - +# (Remix debugger has been deprecated and is not maintained anymore - the `remix-debug` module can be used to build your own debugger) + The Remix Debugger is a webapp to debug the Ethereum VM and transactions. + [Installation](#installation) From bb0ecccb519c95e8e8a311d563ccfbb22bf494d0 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 21 Mar 2018 14:32:56 +0100 Subject: [PATCH 2/8] add remix-debug --- remix-debug/index.js | 22 +++++ remix-debug/package.json | 96 ++++++++++++++++++++++ remix-debug/src/Ethdebugger.js | 146 +++++++++++++++++++++++++++++++++ 3 files changed, 264 insertions(+) create mode 100644 remix-debug/index.js create mode 100644 remix-debug/package.json create mode 100644 remix-debug/src/Ethdebugger.js diff --git a/remix-debug/index.js b/remix-debug/index.js new file mode 100644 index 0000000000..b1286590a5 --- /dev/null +++ b/remix-debug/index.js @@ -0,0 +1,22 @@ +'use strict' +var remixCore = require('remix-core') +var EthDebugger = require('./src/Ethdebugger') + +/* + Use of breakPointManager : + + var breakPointManager = new BreakpointManager(this.debugger, (sourceLocation) => { + return line/column from offset (sourceLocation) + }) + this.debugger.setBreakpointManager(breakPointManager) +*/ +module.exports = { + EthDebugger: EthDebugger, + /** + * constructor + * + * @param {Object} _debugger - type of EthDebugger + * @return {Function} _locationToRowConverter - function implemented by editor which return a column/line position for a char source location + */ + BreakpointManager: remixCore.code.BreakpointManager +} diff --git a/remix-debug/package.json b/remix-debug/package.json new file mode 100644 index 0000000000..cc93324d07 --- /dev/null +++ b/remix-debug/package.json @@ -0,0 +1,96 @@ +{ + "name": "remix-debug", + "version": "0.0.1", + "description": "Ethereum IDE and tools for the web", + "contributors": [ + { + "name": "Yann Levreau", + "email": "yann@ethdev.com" + }, + { + "name": "Liana Husikyan", + "email": "liana@ethdev.com" + } + ], + "main": "./index.js", + "devDependencies": { + "babel-eslint": "^7.1.1", + "babel-plugin-transform-object-assign": "^6.22.0", + "babel-plugin-yo-yoify": "^0.3.3", + "babel-polyfill": "^6.22.0", + "babel-preset-env": "^1.6.1", + "babel-preset-es2015": "^6.24.0", + "babel-preset-stage-0": "^6.24.1", + "babelify": "^7.3.0", + "notify-error": "^1.2.0", + "npm-run-all": "^4.1.2", + "remix-core": "latest", + "remix-lib": "latest", + "remix-solidity": "latest", + "standard": "^7.0.1", + "standard-reporter": "^1.0.5" + }, + "scripts": { + "build": "mkdirp build; browserify index.js > build/app.js", + "lint": "standard | notify-error" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/ethereum/remix.git" + }, + "author": "cpp-ethereum team", + "license": "MIT", + "bugs": { + "url": "https://github.com/ethereum/remix/issues" + }, + "homepage": "https://github.com/ethereum/remix#readme", + "standard": { + "ignore": [ + "node_modules/*", + "build/*", + "test/resources/*" + ] + }, + "babel": { + "plugins": [ + "transform-es2015-template-literals", + "transform-es2015-literals", + "transform-es2015-function-name", + "transform-es2015-arrow-functions", + "transform-es2015-block-scoped-functions", + "transform-es2015-classes", + "transform-es2015-object-super", + "transform-es2015-shorthand-properties", + "transform-es2015-duplicate-keys", + "transform-es2015-computed-properties", + "transform-es2015-for-of", + "transform-es2015-sticky-regex", + "transform-es2015-unicode-regex", + "check-es2015-constants", + "transform-es2015-spread", + "transform-es2015-parameters", + "transform-es2015-destructuring", + "transform-es2015-block-scoping", + "transform-object-assign" + ] + }, + "browserify": { + "transform": [ + [ + "babelify", + { + "sourceMapsAbsolute": false, + "sourceMaps": true, + "plugins": [ + [ + "transform-object-assign" + ] + ], + "presets": [ + "es2015" + ] + } + ] + ] + } +} diff --git a/remix-debug/src/Ethdebugger.js b/remix-debug/src/Ethdebugger.js new file mode 100644 index 0000000000..eb7dd6b5af --- /dev/null +++ b/remix-debug/src/Ethdebugger.js @@ -0,0 +1,146 @@ +'use strict' +var remixCore = require('remix-core') +var TraceManager = remixCore.trace.TraceManager +var remixLib = require('remix-lib') +var global = remixLib.global +var init = remixLib.init +var executionContext = remixLib.execution.executionContext +var EventManager = remixLib.EventManager +var Web3Providers = remixLib.vm.Web3Providers +var DummyProvider = remixLib.vm.DummyProvider +var CodeManager = remixCore.code.CodeManager +var remixSolidity = require('remix-solidity') +var SolidityProxy = remixSolidity.SolidityProxy +var InternalCallTree = remixSolidity.InternalCallTree + +/** + * Ethdebugger is a wrapper around a few classes that helps debugging a transaction + * + * - Web3Providers - define which environment (web3) the transaction will be retrieved from + * - TraceManager - Load / Analyze the trace and retrieve details of specific test + * - CodeManager - Retrieve loaded byte code and help to resolve AST item from vmtrace index + * - SolidityProxy - Basically used to extract state variable from AST + * - Breakpoint Manager - Used to add / remove / jumpto breakpoint + * - InternalCallTree - Used to retrieved local variables + * + * @param {Map} opts - { function compilationResult } // + */ +function Ethdebugger (opts) { + this.opts = opts || {} + if (!this.opts.compilationResult) this.opts.compilationResult = () => { return null } + + var self = this + this.event = new EventManager() + + this.tx + + this.web3Providers = new Web3Providers() + this.addProvider('DUMMYWEB3', new DummyProvider()) + this.switchProvider('DUMMYWEB3') + + this.traceManager = new TraceManager() + this.codeManager = new CodeManager(this.traceManager) + this.solidityProxy = new SolidityProxy(this.traceManager, this.codeManager) + + this.callTree = new InternalCallTree(this.event, this.traceManager, this.solidityProxy, this.codeManager, { includeLocalVariables: true }) + + this.event.register('indexChanged', this, function (index) { + self.codeManager.resolveStep(index, self.tx) + }) +} + +Ethdebugger.prototype.sourceLocationFromVMTraceIndex = function (address, stepIndex, callback) { + this.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, stepIndex, this.solidityProxy.contracts, (error, rawLocation) => { + callback(error, rawLocation) + }) +} + +Ethdebugger.prototype.sourceLocationFromInstructionIndex = function (address, instIndex, callback) { + this.debugger.callTree.sourceLocationTracker.getSourceLocationFromInstructionIndex(address, instIndex, this.solidityProxy.contracts, function (error, rawLocation) { + callback(error, rawLocation) + }) +} + +Ethdebugger.prototype.setBreakpointManager = function (breakpointManager) { + this.breakpointManager = breakpointManager +} + +Ethdebugger.prototype.resolveStep = function (index) { + this.codeManager.resolveStep(index, this.tx) +} + +Ethdebugger.prototype.web3 = function () { + return global.web3 +} + +Ethdebugger.prototype.addProvider = function (type, obj) { + this.web3Providers.addProvider(type, obj) + this.event.trigger('providerAdded', [type]) +} + +Ethdebugger.prototype.switchProvider = function (type) { + var self = this + this.web3Providers.get(type, function (error, obj) { + if (error) { + console.log('provider ' + type + ' not defined') + } else { + global.web3 = obj + executionContext.detectNetwork((error, network) => { + if (error || !network) { + global.web3Debug = obj + } else { + var webDebugNode = init.web3DebugNode(network.name) + global.web3Debug = !webDebugNode ? obj : webDebugNode + } + }) + self.event.trigger('providerChanged', [type]) + } + }) +} + +Ethdebugger.prototype.setCompilationResult = function (compilationResult) { + if (compilationResult && compilationResult.sources && compilationResult.contracts) { + this.solidityProxy.reset(compilationResult) + } else { + this.solidityProxy.reset({}) + } +} + +Ethdebugger.prototype.debug = function (tx) { + this.setCompilationResult(this.opts.compilationResult()) + if (tx instanceof Object) { + this.txBrowser.load(tx.hash) + } else if (tx instanceof String) { + this.txBrowser.load(tx) + } +} + +Ethdebugger.prototype.unLoad = function () { + this.traceManager.init() + this.codeManager.clear() + this.stepManager.reset() + this.event.trigger('traceUnloaded') +} + +Ethdebugger.prototype.debug = function (blockNumber, txIndex, tx) { + if (this.traceManager.isLoading) { + return + } + this.setCompilationResult(this.opts.compilationResult()) + console.log('loading trace...') + this.tx = tx + var self = this + this.traceManager.resolveTrace(tx, function (error, result) { + console.log('trace loaded ' + result) + if (result) { + self.event.trigger('newTraceLoaded', [self.traceManager.trace]) + if (self.breakpointManager && self.breakpointManager.hasBreakpoint()) { + self.breakpointManager.jumpNextBreakpoint(false) + } + } else { + self.statusMessage = error ? error.message : 'Trace not loaded' + } + }) +} + +module.exports = Ethdebugger From 593192296266f4ee3efe3ac1e505ef9b1b79f373 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 21 Mar 2018 16:15:59 +0100 Subject: [PATCH 3/8] refactor EthDebugger --- remix-debug/src/Ethdebugger.js | 108 ++++++++++++++++++++++++++++----- 1 file changed, 93 insertions(+), 15 deletions(-) diff --git a/remix-debug/src/Ethdebugger.js b/remix-debug/src/Ethdebugger.js index eb7dd6b5af..12b4c2839d 100644 --- a/remix-debug/src/Ethdebugger.js +++ b/remix-debug/src/Ethdebugger.js @@ -1,7 +1,9 @@ 'use strict' var remixCore = require('remix-core') var TraceManager = remixCore.trace.TraceManager +var StorageViewer = remixCore.storage.StorageViewer var remixLib = require('remix-lib') +var traceHelper = remixLib.helpers.trace var global = remixLib.global var init = remixLib.init var executionContext = remixLib.execution.executionContext @@ -11,7 +13,10 @@ var DummyProvider = remixLib.vm.DummyProvider var CodeManager = remixCore.code.CodeManager var remixSolidity = require('remix-solidity') var SolidityProxy = remixSolidity.SolidityProxy +var stateDecoder = remixSolidity.stateDecoder +var localDecoder = remixSolidity.localDecoder var InternalCallTree = remixSolidity.InternalCallTree +var StorageResolver = remixCore.storage.StorageResolver /** * Ethdebugger is a wrapper around a few classes that helps debugging a transaction @@ -22,6 +27,7 @@ var InternalCallTree = remixSolidity.InternalCallTree * - SolidityProxy - Basically used to extract state variable from AST * - Breakpoint Manager - Used to add / remove / jumpto breakpoint * - InternalCallTree - Used to retrieved local variables + * - StorageResolver - Help resolving the storage accross different steps * * @param {Map} opts - { function compilationResult } // */ @@ -29,7 +35,6 @@ function Ethdebugger (opts) { this.opts = opts || {} if (!this.opts.compilationResult) this.opts.compilationResult = () => { return null } - var self = this this.event = new EventManager() this.tx @@ -41,14 +46,24 @@ function Ethdebugger (opts) { this.traceManager = new TraceManager() this.codeManager = new CodeManager(this.traceManager) this.solidityProxy = new SolidityProxy(this.traceManager, this.codeManager) + this.storageResolver = null this.callTree = new InternalCallTree(this.event, this.traceManager, this.solidityProxy, this.codeManager, { includeLocalVariables: true }) +} - this.event.register('indexChanged', this, function (index) { - self.codeManager.resolveStep(index, self.tx) - }) +Ethdebugger.prototype.resolveStep = function (index) { + this.codeManager.resolveStep(index, this.tx) } +Ethdebugger.prototype.setCompilationResult = function (compilationResult) { + if (compilationResult && compilationResult.sources && compilationResult.contracts) { + this.solidityProxy.reset(compilationResult) + } else { + this.solidityProxy.reset({}) + } +} + +/* resolve source location */ Ethdebugger.prototype.sourceLocationFromVMTraceIndex = function (address, stepIndex, callback) { this.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, stepIndex, this.solidityProxy.contracts, (error, rawLocation) => { callback(error, rawLocation) @@ -61,14 +76,81 @@ Ethdebugger.prototype.sourceLocationFromInstructionIndex = function (address, in }) } +/* breakpoint */ Ethdebugger.prototype.setBreakpointManager = function (breakpointManager) { this.breakpointManager = breakpointManager } -Ethdebugger.prototype.resolveStep = function (index) { - this.codeManager.resolveStep(index, this.tx) +/* decode locals */ +Ethdebugger.prototype.extractLocalsAt = function (step, callback) { + callback(null, this.callTree.findScope(step)) +} + +Ethdebugger.prototype.decodeLocalsAt = function (step, sourceLocation, callback) { + this.traceManager.waterfall([ + this.traceManager.getStackAt, + this.traceManager.getMemoryAt, + this.traceManager.getCurrentCalledAddressAt], + step, + (error, result) => { + if (!error) { + var stack = result[0].value + var memory = result[1].value + try { + var storageViewer = new StorageViewer({ + stepIndex: step, + tx: this.tx, + address: result[2].value + }, this.storageResolver, this.traceManager) + localDecoder.solidityLocals(step, this.callTree, stack, memory, storageViewer, sourceLocation).then((locals) => { + if (!locals.error) { + callback(null, locals) + } else { + callback(locals.error) + } + }) + } catch (e) { + callback(e.message) + } + } else { + callback(error) + } + }) } +/* decode state */ +Ethdebugger.prototype.extractStateAt = function (step, callback) { + this.solidityProxy.extractStateVariablesAt(step, function (error, stateVars) { + callback(error, stateVars) + }) +} + +Ethdebugger.prototype.decodeStateAt = function (step, stateVars, callback) { + this.traceManager.getCurrentCalledAddressAt(step, (error, address) => { + if (error) return callback(error) + var storageViewer = new StorageViewer({ + stepIndex: step, + tx: this.tx, + address: address + }, this.storageResolver, this.traceManager) + stateDecoder.decodeState(stateVars, storageViewer).then((result) => { + if (!result.error) { + callback(null, result) + } else { + callback(result.error) + } + }) + }) +} + +Ethdebugger.prototype.storageViewAt = function (step, address) { + return new StorageViewer({ + stepIndex: step, + tx: this.tx, + address: address + }, this.storageResolver, this.traceManager) +} +/* set env */ Ethdebugger.prototype.web3 = function () { return global.web3 } @@ -98,14 +180,6 @@ Ethdebugger.prototype.switchProvider = function (type) { }) } -Ethdebugger.prototype.setCompilationResult = function (compilationResult) { - if (compilationResult && compilationResult.sources && compilationResult.contracts) { - this.solidityProxy.reset(compilationResult) - } else { - this.solidityProxy.reset({}) - } -} - Ethdebugger.prototype.debug = function (tx) { this.setCompilationResult(this.opts.compilationResult()) if (tx instanceof Object) { @@ -122,10 +196,13 @@ Ethdebugger.prototype.unLoad = function () { this.event.trigger('traceUnloaded') } -Ethdebugger.prototype.debug = function (blockNumber, txIndex, tx) { +Ethdebugger.prototype.debug = function (tx) { if (this.traceManager.isLoading) { return } + if (!tx.to) { + tx.to = traceHelper.contractCreationToken('0') + } this.setCompilationResult(this.opts.compilationResult()) console.log('loading trace...') this.tx = tx @@ -137,6 +214,7 @@ Ethdebugger.prototype.debug = function (blockNumber, txIndex, tx) { if (self.breakpointManager && self.breakpointManager.hasBreakpoint()) { self.breakpointManager.jumpNextBreakpoint(false) } + self.storageResolver = new StorageResolver() } else { self.statusMessage = error ? error.message : 'Trace not loaded' } From 8084f7fd05ef1012bd4a2d5814899a30a14054fc Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 21 Mar 2018 16:16:08 +0100 Subject: [PATCH 4/8] readme --- remix-debug/README.md | 66 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 remix-debug/README.md diff --git a/remix-debug/README.md b/remix-debug/README.md new file mode 100644 index 0000000000..1e08330d7b --- /dev/null +++ b/remix-debug/README.md @@ -0,0 +1,66 @@ +# `remix-debug` + +remix-debug is a libraries which wrap other remix-* libraries to make debugging thereum transactions easier. + ++ [Installation](#installation) ++ [Development](#development) ++ [First steps](#firststeps) ++ [Tests](#tests) + +## Installation + +Make sure Node is [installed on your setup](https://docs.npmjs.com/getting-started/installing-node), and that a [local `geth`/`eth` node is running](../README.md#how-to-use). + +```bash +npm install remix-debug +``` + +## Development + +```bash +var Debugger = require('remix-debug').EthDebugger +var BreakpointManager = require('remix-debug').EthDebugger + +var debugger = new Debugger({ + compilationResult: () => { + return compilationResult // that helps resolving source location + } +}) + +debugger.addProvider(web3, 'web3') +debugger.switchProvider('web3') + +var breakPointManager = new remixCore.code.BreakpointManager(this.debugger, (sourceLocation) => { + // return offsetToLineColumn +}) +debugger.setBreakpointManager(breakPointManager) +breakPointManager.add({fileName, row}) +breakPointManager.add({fileName, row}) + +debugger.debug() + +// this.traceManager.getCurrentCalledAddressAt + +debugger.event.register('newTraceLoaded', () => { + // start doing basic stuff like retrieving step details + debugger.traceManager.getCallStackAt(34, (error, callstack) => {}) +}) + +debugger.callTree.register('callTreeReady', () => { + // start doing more complex stuff like resolvng local variables + breakPointManager.jumpNextBreakpoint(true) + + var storageView = debugger.storageViewAt(38, , + storageView.storageSlot(0, (error, storage) => {}) + storageView.storageRange(error, storage) => {}) // retrieve 0 => 1000 slots + + debugger.extractStateAt(23, (error, state) => { + debugger.decodeStateAt(23, state, (error, decodedState) => {}) + }) + + debugger.sourceLocationFromVMTraceIndex(, 23, (error, location) => { + debugger.decodeLocalsAt(23, location, (error, decodedlocals) => {}) + }) + + debugger.extractLocalsAt(23, (null, locals) => {} +``` From da65fddfb373b822accc721407ff2b2fa0e0efe7 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 21 Mar 2018 16:16:23 +0100 Subject: [PATCH 5/8] version bump --- remix-debug/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remix-debug/package.json b/remix-debug/package.json index cc93324d07..bd13d5fe13 100644 --- a/remix-debug/package.json +++ b/remix-debug/package.json @@ -1,6 +1,6 @@ { "name": "remix-debug", - "version": "0.0.1", + "version": "0.0.2", "description": "Ethereum IDE and tools for the web", "contributors": [ { From 5c2e00daa3a227a1bdebbadab91cbeeffd8fa7b3 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 21 Mar 2018 16:17:21 +0100 Subject: [PATCH 6/8] fix readme --- remix-debug/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/remix-debug/README.md b/remix-debug/README.md index 1e08330d7b..0ceee57f60 100644 --- a/remix-debug/README.md +++ b/remix-debug/README.md @@ -4,8 +4,6 @@ remix-debug is a libraries which wrap other remix-* libraries to make debugging + [Installation](#installation) + [Development](#development) -+ [First steps](#firststeps) -+ [Tests](#tests) ## Installation From 9f3c0fc9a0a81ca072840e640df31ce40476e106 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 21 Mar 2018 16:49:25 +0100 Subject: [PATCH 7/8] update read me --- remix-debug/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/remix-debug/README.md b/remix-debug/README.md index 0ceee57f60..e09d659d26 100644 --- a/remix-debug/README.md +++ b/remix-debug/README.md @@ -7,7 +7,6 @@ remix-debug is a libraries which wrap other remix-* libraries to make debugging ## Installation -Make sure Node is [installed on your setup](https://docs.npmjs.com/getting-started/installing-node), and that a [local `geth`/`eth` node is running](../README.md#how-to-use). ```bash npm install remix-debug @@ -61,4 +60,6 @@ debugger.callTree.register('callTreeReady', () => { }) debugger.extractLocalsAt(23, (null, locals) => {} + +}) ``` From 77e437fe65f857e5b0bda67f932e73fd3b4bcad1 Mon Sep 17 00:00:00 2001 From: yann300 Date: Wed, 21 Mar 2018 16:53:05 +0100 Subject: [PATCH 8/8] update readme --- remix-debug/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remix-debug/README.md b/remix-debug/README.md index e09d659d26..b9571350b2 100644 --- a/remix-debug/README.md +++ b/remix-debug/README.md @@ -1,6 +1,6 @@ # `remix-debug` -remix-debug is a libraries which wrap other remix-* libraries to make debugging thereum transactions easier. +remix-debug wrap other remix-* libraries and can be used to debug Ethereum transactions. + [Installation](#installation) + [Development](#development)