From c1891a78c64f624df0f3706882fe55c8f649e2ca Mon Sep 17 00:00:00 2001 From: fergarrui Date: Mon, 6 May 2019 18:09:29 +0100 Subject: [PATCH] Improve general CFG, improve .evm support --- README.md | 2 +- package-lock.json | 150 +++++++++++++----- package.json | 4 +- src/api/bytecode/EVMDisassembler.ts | 11 +- src/api/bytecode/Opcodes.ts | 4 + src/api/cfg/CFGBlocks.ts | 4 - .../service/controller/DebuggerController.ts | 3 +- src/api/service/service/CFGService.ts | 50 ++++++ .../service/service/TransactionServiceImpl.ts | 12 +- src/api/symbolic/evm/EVMExecutor.ts | 39 ++++- 10 files changed, 225 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 1145e41..2de29ad 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ There are already tools that allow you to debug Ethereum transactions (Solidity) Use one of these releases: - * solc 0.4.24 compatible with ganache use: [v2.2.0](https://github.com/fergarrui/ethereum-graph-debugger/releases/tag/v2.2.0) + * solc 0.4.24 compatible with ganache use: [v2.3.0](https://github.com/fergarrui/ethereum-graph-debugger/releases/tag/v2.3.0) * solc 0.5.8 (not compatible with ganache) use: [v3.1.0](https://github.com/fergarrui/ethereum-graph-debugger/releases/tag/v3.1.0) If you want to use master (it can be more unstable), clone and start the application diff --git a/package-lock.json b/package-lock.json index c2a0d87..788aacd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ethereum-graph-debugger", - "version": "3.1.0", + "version": "2.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3049,11 +3049,6 @@ "delayed-stream": "~1.0.0" } }, - "command-exists": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.8.tgz", - "integrity": "sha512-PM54PkseWbiiD/mMsbvW351/u+dafwTJ0ye2qB60G1aGQP9j3xK2gmMDc+R34L3nDtx4qMCitXT75mkbkGJDLw==" - }, "commander": { "version": "2.20.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", @@ -7397,8 +7392,7 @@ "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" }, "is-windows": { "version": "1.0.2", @@ -8950,7 +8944,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, "requires": { "graceful-fs": "^4.1.2", "parse-json": "^2.2.0", @@ -9007,6 +9000,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.11.2.tgz", "integrity": "sha1-1rQzixEKWOIdrlzrz9u/0rxM2zs=" }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -10482,7 +10480,8 @@ "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true }, "osenv": { "version": "0.1.5", @@ -10761,7 +10760,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, "requires": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", @@ -11408,7 +11406,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, "requires": { "load-json-file": "^1.0.0", "normalize-package-data": "^2.3.2", @@ -11419,7 +11416,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, "requires": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" @@ -11429,7 +11425,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, "requires": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" @@ -11439,7 +11434,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, "requires": { "pinkie-promise": "^2.0.0" } @@ -11800,9 +11794,9 @@ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=" }, "require-main-filename": { "version": "1.0.1", @@ -12641,18 +12635,106 @@ } }, "solc": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/solc/-/solc-0.5.8.tgz", - "integrity": "sha512-RQ2SlwPBOBSV7ktNQJkvbiQks3t+3V9dsqD014EdstxnJzSxBuOvbt3P5QXpNPYW1DsEmF7dhOaT3JL7yEae/A==", + "version": "0.4.25", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.4.25.tgz", + "integrity": "sha512-jU1YygRVy6zatgXrLY2rRm7HW1d7a8CkkEgNJwvH2VLpWhMFsMdWcJn6kUqZwcSz/Vm+w89dy7Z/aB5p6AFTrg==", "requires": { - "command-exists": "^1.2.8", "fs-extra": "^0.30.0", - "keccak": "^1.0.2", "memorystream": "^0.3.1", - "require-from-string": "^2.0.0", - "semver": "^5.5.0", - "tmp": "0.0.33", - "yargs": "^11.0.0" + "require-from-string": "^1.1.0", + "semver": "^5.3.0", + "yargs": "^4.7.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "yargs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", + "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", + "requires": { + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "lodash.assign": "^4.0.3", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.1", + "which-module": "^1.0.0", + "window-size": "^0.2.0", + "y18n": "^3.2.1", + "yargs-parser": "^2.4.1" + } + }, + "yargs-parser": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", + "requires": { + "camelcase": "^3.0.0", + "lodash.assign": "^4.0.6" + } + } } }, "source-list-map": { @@ -13111,7 +13193,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, "requires": { "is-utf8": "^0.2.0" } @@ -13568,14 +13649,6 @@ "setimmediate": "^1.0.4" } }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "requires": { - "os-tmpdir": "~1.0.2" - } - }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -15839,6 +15912,11 @@ "string-width": "^2.1.1" } }, + "window-size": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", + "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=" + }, "winston": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.4.tgz", diff --git a/package.json b/package.json index 96d7e65..1a82ee6 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "ethereum-graph-debugger", "author": "Fernando Garcia", "license": "GPL", - "version": "3.1.0", + "version": "2.3.0", "description": "Ethereum graph debugger", "main": "dist/run-server.js", "scripts": { @@ -83,7 +83,7 @@ "recursive-readdir": "^2.2.2", "redux-thunk": "^2.3.0", "reflect-metadata": "^0.1.12", - "solc": "^0.5.8", + "solc": "^0.4.24", "terser": "3.14.1", "tsoa": "2.1.8", "web3": "1.0.0-beta.37", diff --git a/src/api/bytecode/EVMDisassembler.ts b/src/api/bytecode/EVMDisassembler.ts index ef64c1a..80ea409 100644 --- a/src/api/bytecode/EVMDisassembler.ts +++ b/src/api/bytecode/EVMDisassembler.ts @@ -46,12 +46,14 @@ export class EVMDisassembler implements Disassembler { if (bytecode.startsWith('0x')) { code = bytecode.slice(2) } + if (code.includes(EVMDisassembler.metadataPrefix)) { code = code.split(EVMDisassembler.metadataPrefix)[0] } - + + code = code.length % 2 !== 0 ? code.substr(0, code.length-1): code if (code.length % 2 !== 0) { - throw new Error(`Bad input, bytecode length not even: ${code}, length: ${code.length}`) + throw new Error(`disassembleContract - Bad input, bytecode length not even: ${code}, length: ${code.length}`) } const operations: Operation[] = this.disassembleBytecode(bytecode) @@ -60,7 +62,7 @@ export class EVMDisassembler implements Disassembler { let runtime = operations if (hasConstructor) { // pre- 0.5.* the opcode we are searching is 'STOP', post 0.5.* is INVALID - const firstStopIndex = operations.findIndex(op => op.opcode.name === 'INVALID') + const firstStopIndex = operations.findIndex(op => op.opcode.name === 'STOP') constructor = operations.slice(0, firstStopIndex + 1) runtime = this.adjustRuntimeOffset(operations.slice(firstStopIndex + 1, operations.length)) } @@ -82,8 +84,9 @@ export class EVMDisassembler implements Disassembler { if (code.includes(EVMDisassembler.metadataPrefix)) { code = code.split(EVMDisassembler.metadataPrefix)[0] } + code = code.length % 2 !== 0 ? code.substr(0, code.length-1): code if (code.length % 2 !== 0) { - throw new Error(`Bad input, bytecode length not even: ${code}, length: ${code.length}`) + throw new Error(`disassembleBytecode - Bad input, bytecode length not even: ${code}, length: ${code.length}`) } let offset = 0 const operations = code.match(/.{1,2}/g) diff --git a/src/api/bytecode/Opcodes.ts b/src/api/bytecode/Opcodes.ts index 47dd392..d8eec65 100644 --- a/src/api/bytecode/Opcodes.ts +++ b/src/api/bytecode/Opcodes.ts @@ -155,6 +155,10 @@ export class Opcodes { static isJump(op: Opcode) { return op.name.startsWith('JUMP') } + + static isJumpOp(op: string) { + return op.startsWith('JUMP') && op !== 'JUMPDEST' + } } Opcodes.populate() diff --git a/src/api/cfg/CFGBlocks.ts b/src/api/cfg/CFGBlocks.ts index 0d67096..567c980 100644 --- a/src/api/cfg/CFGBlocks.ts +++ b/src/api/cfg/CFGBlocks.ts @@ -7,10 +7,6 @@ export class CFGBlocks { this.blocks[offset] = block } - // getBlock(offset: number): OperationBlock { - // return this.blocks[offset] - // } - get(offset: number): OperationBlock { const block: OperationBlock = this.blocks[offset] if(!block) { diff --git a/src/api/service/controller/DebuggerController.ts b/src/api/service/controller/DebuggerController.ts index 8c7e615..29f675d 100644 --- a/src/api/service/controller/DebuggerController.ts +++ b/src/api/service/controller/DebuggerController.ts @@ -42,7 +42,7 @@ export class DebuggerController extends Controller { blockchainBasicAuthPassword } as Web3Configuration const contractBlocks: CFGContract = await this.cfgService.buildCFGFromSource(name, source, path) - const runtimeRawBytecode = `0x${contractBlocks.contractRuntime.rawBytecode}` + const runtimeRawBytecode = contractBlocks.contractRuntime.rawBytecode.startsWith('0x')? contractBlocks.contractRuntime.rawBytecode: `0x${contractBlocks.contractRuntime.rawBytecode}` const trace: DebugTrace = await this.transactionService.findTransactionTrace(tx, runtimeRawBytecode, config) const cfg = this.createCFG(contractBlocks, false, trace) return this.buildResponse(contractBlocks, false, cfg, trace) @@ -57,6 +57,7 @@ export class DebuggerController extends Controller { if (constructor) { blocks = contractBlocks.contractConstructor.blocks } + this.cfgService.completeCFGWithTrace(blocks, trace) return this.graphVizService.createDotFromBlocks(blocks, trace) } diff --git a/src/api/service/service/CFGService.ts b/src/api/service/service/CFGService.ts index 9b1edfd..d9f9836 100644 --- a/src/api/service/service/CFGService.ts +++ b/src/api/service/service/CFGService.ts @@ -8,6 +8,11 @@ import { Operation } from '../../bytecode/Operation' import { EVMExecutor } from '../../symbolic/evm/EVMExecutor' import { OpcodeExecutor } from '../../symbolic/evm/exec/OpcodeExecutor' import { CFGBlocks } from '../../cfg/CFGBlocks' +import { logger } from '../../../Logger'; +import { DebugTrace } from '../../symbolic/evm/DebugTrace'; +import { Opcodes } from '../../bytecode/Opcodes'; +import { OperationBlock } from '../../cfg/OperationBlock'; +let BN = require('bn.js') @injectable() export class CFGService { @@ -34,6 +39,47 @@ export class CFGService { return this.buildCfgContract(contract) } + completeCFGWithTrace(blocks: CFGBlocks, trace: DebugTrace) { + for (const log of trace.result.structLogs) { + if (!this.jumpHasBothChildren(log.op, log.pc, blocks)) { + const block = blocks.get(log.pc) + this.populateMissingBranch(block, blocks, log.stack, log.op) + } + } + } + + private populateMissingBranch(block: OperationBlock, blocks: CFGBlocks, stack: string[], op: string) { + if (op === 'JUMP') { + const dest = stack[stack.length-1] + const destOffset = new BN(dest, 16).toNumber() + block.childA = destOffset + } + } + + private jumpHasBothChildren(opcode: string, offset: number, blocks: CFGBlocks): boolean { + if (!Opcodes.isJumpOp(opcode)) { + return true + } + const block = blocks.get(offset) + if (!block) { + return true + } + if (opcode === 'JUMPI') { + if(!block.childA || !block.childB) { + return false + } else { + return true + } + } + if (opcode === 'JUMP') { + if (block.childA || block.childB) { + return true + } else { + return false + } + } + } + private buildCfgContract(contract: DisassembledContract): CFGContract { const runtimeBlocks = this.calculateCfgBlocks(contract.runtime) const cfgContract: CFGContract = { @@ -54,9 +100,13 @@ export class CFGService { } private calculateCfgBlocks(ops: Operation[]): CFGBlocks { + logger.info('Calculating CFG blocks') const blocks = this.cfgCreator.divideBlocks(ops) const executor = new EVMExecutor(blocks, this.opExecutor) executor.run(0) + logger.info('Calculated dynamic CFG, checking if there are orphan nodes not inspected...') + executor.runOrphans() + logger.info(`Orphan blocks analyzed, tried to complete CFG inspecting bytecode`) return executor.blocks } } diff --git a/src/api/service/service/TransactionServiceImpl.ts b/src/api/service/service/TransactionServiceImpl.ts index 9db680c..d0c9dda 100644 --- a/src/api/service/service/TransactionServiceImpl.ts +++ b/src/api/service/service/TransactionServiceImpl.ts @@ -80,8 +80,16 @@ export class TransactionServiceImpl implements TransactionService { trace: DebugTrace, web3: any ): Promise { - const cleanBytecode = EVMDisassembler.removeMetadata(bytecode) - const cleanDeployedBytecode = EVMDisassembler.removeMetadata(deployedBytecode) + let cleanBytecode = EVMDisassembler.removeMetadata(bytecode) + let cleanDeployedBytecode = EVMDisassembler.removeMetadata(deployedBytecode) + + + if (cleanBytecode.length % 2 !== 0) { + cleanBytecode = cleanBytecode.substr(0, cleanBytecode.length-1) + } + if (cleanDeployedBytecode.length % 2 !== 0) { + cleanDeployedBytecode = cleanDeployedBytecode.substr(0, cleanDeployedBytecode.length-1) + } if (cleanBytecode === cleanDeployedBytecode) { return this.buildTrace(trace, trace.result.structLogs.filter(log => log.depth === 0)) } diff --git a/src/api/symbolic/evm/EVMExecutor.ts b/src/api/symbolic/evm/EVMExecutor.ts index 51e005c..c7bf7ec 100644 --- a/src/api/symbolic/evm/EVMExecutor.ts +++ b/src/api/symbolic/evm/EVMExecutor.ts @@ -5,6 +5,8 @@ import { Opcodes } from '../../bytecode/Opcodes' import { Operation } from '../../bytecode/Operation' import { OpcodeExecutor } from './exec/OpcodeExecutor' import { Executor } from './exec/Executor' +import { Word } from './Word'; +import { logger } from '../../../Logger'; export class EVMExecutor { readonly NO_NEXT_BLOCK = ['JUMP', 'STOP', 'REVERT', 'RETURN', 'INVALID'] @@ -35,12 +37,33 @@ export class EVMExecutor { block.childB = nextBlock.offset } } - if (nextBlock.offset !== 0) { + if (nextBlock.offset !== 0 && !this.alreadyRunOffsets.includes(nextBlock.offset)) { this.run(nextBlock.offset) } } } + runOrphans() { + while (this.blocks.keys().length !== this.alreadyRunOffsets.length) { + logger.info(`There are ${this.blocks.keys().length - this.alreadyRunOffsets.length} orphan block(s) that need to be analyzed`) + const pickOrphan = this.blocks.keys().find(key => !this.alreadyRunOffsets.includes(key)) + this.alreadyRunOffsets.push(pickOrphan) + const orphanBlock = this.blocks.get(pickOrphan) + if (orphanBlock) { + const nextBlocks: OperationBlock[] = this.findNextBlocks(orphanBlock) + for (const nextBlock of nextBlocks) { + if (orphanBlock.childA !== nextBlock.offset && orphanBlock.childB !== nextBlock.offset) { + if (!orphanBlock.childA) { + orphanBlock.childA = nextBlock.offset + } else if (!orphanBlock.childB) { + orphanBlock.childB = nextBlock.offset + } + } + } + } + } + } + private runBlock(block: OperationBlock) { for (const op of block.operations) { const executor: Executor = this.executor.ops[op.opcode.name] @@ -56,12 +79,20 @@ export class EVMExecutor { const ops = block.operations const lastOp: Operation = ops[ops.length - 1] if (Opcodes.isJump(lastOp.opcode)) { - const jumpLocation = this.evm.nextJumpLocation + let jumpLocation = this.evm.nextJumpLocation this.evm.nextJumpLocation = undefined + if (!jumpLocation) { + // try to get it heuristically + const prevLastOpcode = ops[ops.length - 2] + if(prevLastOpcode.opcode.name.startsWith('PUSH')) { + jumpLocation = Word.createLiteral(prevLastOpcode.argument) + } + } + if (jumpLocation && !jumpLocation.isSymbolic) { const nextOffset = jumpLocation.value.toNumber() const locationBlock: OperationBlock = this.blocks.get(nextOffset) - if (locationBlock && !this.alreadyRunOffsets.includes(nextOffset)) { + if (locationBlock) { nextBlocks.push(locationBlock) } } @@ -69,7 +100,7 @@ export class EVMExecutor { if (!this.NO_NEXT_BLOCK.includes(lastOp.opcode.name)) { const nextOffset = lastOp.offset + 1 const nextBlock = this.blocks.get(nextOffset) - if (nextBlock && !this.alreadyRunOffsets.includes(nextOffset)) { + if (nextBlock) { nextBlocks.push(nextBlock) } }