├── .babelrc ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── Dockerfile ├── LICENSE ├── README.md ├── docker-compose.yml ├── egd_demo_gif.gif ├── index.html ├── package-lock.json ├── package.json ├── src ├── Logger.ts ├── Server.ts ├── api │ ├── blockchain │ │ ├── IWeb3.ts │ │ ├── Web3Configuration.ts │ │ └── Web3Instance.ts │ ├── bytecode │ │ ├── DisassembledContract.ts │ │ ├── Disassembler.ts │ │ ├── EVMDisassembler.test.ts │ │ ├── EVMDisassembler.ts │ │ ├── Opcode.ts │ │ ├── Opcodes.ts │ │ └── Operation.ts │ ├── cfg │ │ ├── CFGBlocks.ts │ │ ├── CFGCreator.ts │ │ ├── EthereumCFGCreator.test.ts │ │ ├── EthereumCFGCreator.ts │ │ ├── GraphVizService.test.ts │ │ ├── GraphVizService.ts │ │ └── OperationBlock.ts │ ├── service │ │ ├── bean │ │ │ ├── Block.ts │ │ │ ├── CFGContract.ts │ │ │ ├── ContractFile.ts │ │ │ ├── Storage.ts │ │ │ ├── Transaction.ts │ │ │ ├── TransactionBase.ts │ │ │ └── TransactionReceipt.ts │ │ ├── controller │ │ │ ├── ContractController.ts │ │ │ ├── ControlFlowGraphController.ts │ │ │ ├── DebuggerController.ts │ │ │ ├── DisassembleController.ts │ │ │ ├── FileController.ts │ │ │ ├── SolcController.ts │ │ │ ├── StorageRecoverController.ts │ │ │ └── TransactionController.ts │ │ ├── request │ │ │ ├── DeployContractRequest.ts │ │ │ ├── RunContractFunctionRequest.ts │ │ │ ├── SaveFileRequest.ts │ │ │ ├── SolcChangeVersionRequest.ts │ │ │ ├── StringBodyRequest.ts │ │ │ └── TransactionRequest.ts │ │ ├── response │ │ │ ├── CFGResponse.ts │ │ │ ├── DisassembledContractResponse.ts │ │ │ ├── OperationResponse.ts │ │ │ └── TraceResponse.ts │ │ └── service │ │ │ ├── BlockService.ts │ │ │ ├── BlockServiceImpl.ts │ │ │ ├── CFGService.ts │ │ │ ├── ContractService.ts │ │ │ ├── FileService.ts │ │ │ ├── FileServiceDefault.ts │ │ │ ├── Solc.ts │ │ │ ├── StorageRecover.ts │ │ │ ├── TransactionService.ts │ │ │ └── TransactionServiceImpl.ts │ └── symbolic │ │ └── evm │ │ ├── DebugTrace.ts │ │ ├── EVM.ts │ │ ├── EVMExecutor.test.ts │ │ ├── EVMExecutor.ts │ │ ├── EVMMemory.test.ts │ │ ├── EVMMemory.ts │ │ ├── EVMStack.test.ts │ │ ├── EVMStack.ts │ │ ├── EVMStorage.ts │ │ ├── Symbols.ts │ │ ├── UintUtils.ts │ │ ├── Word.ts │ │ └── exec │ │ ├── Add.test.ts │ │ ├── Add.ts │ │ ├── Addmod.test.ts │ │ ├── Addmod.ts │ │ ├── Address.test.ts │ │ ├── Address.ts │ │ ├── And.test.ts │ │ ├── And.ts │ │ ├── Balance.test.ts │ │ ├── Balance.ts │ │ ├── Blockhash.test.ts │ │ ├── Blockhash.ts │ │ ├── Byte.test.ts │ │ ├── Byte.ts │ │ ├── Call.test.ts │ │ ├── Call.ts │ │ ├── Callcode.test.ts │ │ ├── Callcode.ts │ │ ├── Calldatacopy.test.ts │ │ ├── Calldatacopy.ts │ │ ├── Calldataload.test.ts │ │ ├── Calldataload.ts │ │ ├── Calldatasize.test.ts │ │ ├── Calldatasize.ts │ │ ├── Caller.test.ts │ │ ├── Caller.ts │ │ ├── Callvalue.test.ts │ │ ├── Callvalue.ts │ │ ├── Codecopy.test.ts │ │ ├── Codecopy.ts │ │ ├── Codesize.test.ts │ │ ├── Codesize.ts │ │ ├── Coinbase.test.ts │ │ ├── Coinbase.ts │ │ ├── Create.test.ts │ │ ├── Create.ts │ │ ├── Delegatecall.test.ts │ │ ├── Delegatecall.ts │ │ ├── Difficulty.test.ts │ │ ├── Difficulty.ts │ │ ├── Div.test.ts │ │ ├── Div.ts │ │ ├── Dup.test.ts │ │ ├── Dup.ts │ │ ├── Eq.test.ts │ │ ├── Eq.ts │ │ ├── Executor.ts │ │ ├── Exp.test.ts │ │ ├── Exp.ts │ │ ├── Extcodecopy.test.ts │ │ ├── Extcodecopy.ts │ │ ├── Extcodesize.test.ts │ │ ├── Extcodesize.ts │ │ ├── Gas.test.ts │ │ ├── Gas.ts │ │ ├── Gaslimit.test.ts │ │ ├── Gaslimit.ts │ │ ├── Gasprice.test.ts │ │ ├── Gasprice.ts │ │ ├── Gt.test.ts │ │ ├── Gt.ts │ │ ├── IsZero.test.ts │ │ ├── IsZero.ts │ │ ├── Jump.test.ts │ │ ├── Jump.ts │ │ ├── Jumpi.test.ts │ │ ├── Jumpi.ts │ │ ├── Log.test.ts │ │ ├── Log.ts │ │ ├── Lt.test.ts │ │ ├── Lt.ts │ │ ├── MLoad.test.ts │ │ ├── MLoad.ts │ │ ├── MStore.test.ts │ │ ├── MStore.ts │ │ ├── MStore8.test.ts │ │ ├── Mod.test.ts │ │ ├── Mod.ts │ │ ├── Msize.test.ts │ │ ├── Msize.ts │ │ ├── Mstore8.ts │ │ ├── Mul.test.ts │ │ ├── Mul.ts │ │ ├── Mulmod.test.ts │ │ ├── Mulmod.ts │ │ ├── Nop.ts │ │ ├── Not.test.ts │ │ ├── Not.ts │ │ ├── Number.test.ts │ │ ├── Number.ts │ │ ├── OpcodeExecutor.ts │ │ ├── Opcodes.ts │ │ ├── Or.test.ts │ │ ├── Or.ts │ │ ├── Origin.test.ts │ │ ├── Origin.ts │ │ ├── Pc.test.ts │ │ ├── Pc.ts │ │ ├── Pop.test.ts │ │ ├── Pop.ts │ │ ├── Push.test.ts │ │ ├── Push.ts │ │ ├── Return.ts │ │ ├── Returndatacopy.test.ts │ │ ├── Returndatacopy.ts │ │ ├── Returndatasize.test.ts │ │ ├── Returndatasize.ts │ │ ├── Sar.test.ts │ │ ├── Sar.ts │ │ ├── Sdiv.test.ts │ │ ├── Sdiv.ts │ │ ├── Selfdestruct.test.ts │ │ ├── Selfdestruct.ts │ │ ├── Sgt.test.ts │ │ ├── Sgt.ts │ │ ├── Sha3.test.ts │ │ ├── Sha3.ts │ │ ├── Shl.test.ts │ │ ├── Shl.ts │ │ ├── Shr.test.ts │ │ ├── Shr.ts │ │ ├── Signextend.test.ts │ │ ├── Signextend.ts │ │ ├── Sload.test.ts │ │ ├── Sload.ts │ │ ├── Slt.test.ts │ │ ├── Slt.ts │ │ ├── Smod.test.ts │ │ ├── Smod.ts │ │ ├── Sstore.test.ts │ │ ├── Sstore.ts │ │ ├── Staticcall.test.ts │ │ ├── Staticcall.ts │ │ ├── Sub.test.ts │ │ ├── Sub.ts │ │ ├── Swap.test.ts │ │ ├── Swap.ts │ │ ├── TestUtils.ts │ │ ├── Timestamp.test.ts │ │ ├── Timestamp.ts │ │ ├── Xor.test.ts │ │ └── Xor.ts ├── client │ ├── App.js │ ├── _redux │ │ ├── Actions.js │ │ ├── Constants.js │ │ ├── Reducers │ │ │ ├── Reducers.js │ │ │ └── index.js │ │ ├── Store.js │ │ ├── sagas │ │ │ ├── contractsSaga.js │ │ │ ├── contractsSaga.test.js │ │ │ ├── sagas.js │ │ │ ├── toolsSaga.js │ │ │ ├── toolsSaga.test.js │ │ │ ├── utils.js │ │ │ ├── versionsSaga.js │ │ │ └── versionsSagas.test.js │ │ └── selectors.js │ ├── components │ │ ├── Accordion │ │ │ ├── Accordion.js │ │ │ ├── Accordion.scss │ │ │ └── AccordionSection │ │ │ │ ├── AccordionSection.js │ │ │ │ └── AccordionSection.scss │ │ ├── ControlFlowGraph │ │ │ ├── ControlFlowGraph.js │ │ │ └── ControlFlowGraph.scss │ │ ├── Disassembler │ │ │ ├── Bytecode │ │ │ │ ├── Bytecode.js │ │ │ │ └── Bytecode.scss │ │ │ ├── Disassembler.js │ │ │ ├── Disassembler.scss │ │ │ └── Operations │ │ │ │ ├── Operations.js │ │ │ │ └── Operations.scss │ │ ├── Dropdown │ │ │ ├── Dropdown.js │ │ │ └── Dropdown.scss │ │ ├── EVMState │ │ │ ├── EVMState.js │ │ │ └── EVMState.scss │ │ ├── Editor │ │ │ ├── AceEditor │ │ │ │ └── AceEditor.js │ │ │ └── Editor.js │ │ ├── Form │ │ │ ├── Form.js │ │ │ └── Form.scss │ │ ├── Graph │ │ │ ├── Graph.js │ │ │ └── Graph.scss │ │ ├── Hamburger │ │ │ ├── Hamburger.js │ │ │ └── Hamburger.scss │ │ ├── Icon │ │ │ ├── Icon.js │ │ │ ├── Icon.scss │ │ │ └── SVG │ │ │ │ ├── cancel-circle.svg │ │ │ │ ├── circle-left.svg │ │ │ │ ├── circle-right.svg │ │ │ │ ├── cogs.svg │ │ │ │ ├── cross.svg │ │ │ │ ├── menu.svg │ │ │ │ └── spinner.svg │ │ ├── Main │ │ │ ├── Main.js │ │ │ └── Main.scss │ │ ├── MessageComp │ │ │ ├── MessageComp.js │ │ │ └── MessageComp.scss │ │ ├── Modal │ │ │ ├── Modal.js │ │ │ └── Modal.scss │ │ ├── Panel │ │ │ ├── Panel.js │ │ │ └── Panel.scss │ │ ├── SettingsBar │ │ │ ├── SettingsBar.js │ │ │ └── SettingsBar.scss │ │ ├── SideBar │ │ │ ├── SideBar.js │ │ │ └── SideBar.scss │ │ ├── StorageViewer │ │ │ ├── StorageViewer.js │ │ │ └── StorageViewer.scss │ │ ├── Tab │ │ │ ├── Tab.js │ │ │ ├── Tab.scss │ │ │ ├── TabMenuItem │ │ │ │ ├── TabMenuItem.js │ │ │ │ └── TabMenuItem.scss │ │ │ └── TabPanel │ │ │ │ ├── TabPanel.js │ │ │ │ └── TabPanel.scss │ │ ├── TopNavBar │ │ │ ├── TopNavBar.js │ │ │ └── TopNavBar.scss │ │ ├── TransactionDebugger │ │ │ ├── TransactionDebugger.js │ │ │ └── TransactionDebugger.scss │ │ └── Version │ │ │ ├── Version.js │ │ │ └── Version.scss │ ├── styles │ │ ├── App.scss │ │ ├── animate.scss │ │ ├── reset.scss │ │ ├── transitions │ │ │ ├── fade.scss │ │ │ ├── scale.scss │ │ │ └── slide.scss │ │ ├── utils.js │ │ └── variables.scss │ └── utils │ │ └── baseUrl.js ├── index.js ├── inversify │ ├── ioc.ts │ └── types.ts ├── routes.ts └── run-server.ts ├── tsconfig.json ├── tsoa.json └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"], 3 | "plugins": ["@babel/plugin-transform-runtime"] 4 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | token 3 | dist/* 4 | .DS_Store 5 | coverage/* 6 | .vscode/* 7 | contracts/* 8 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.com/ 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | src/routes.ts 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | printWidth: 120 2 | semi: false 3 | singleQuote: true 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8-alpine 2 | 3 | RUN apk update && apk upgrade && \ 4 | apk add --no-cache bash git python g++ gcc libgcc libstdc++ linux-headers make 5 | 6 | RUN npm install --quiet node-gyp -g 7 | 8 | WORKDIR /opt/app 9 | 10 | COPY package.json ./ 11 | 12 | RUN npm i -g npm@^6.1.0 && npm install 13 | 14 | COPY . . 15 | 16 | EXPOSE 9090 17 | 18 | CMD [ "npm", "run", "start" ] 19 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | services: 4 | debugger: 5 | image: ethereum-graph-debugger 6 | build: 7 | context: ./ 8 | environment: 9 | - BLOCKCHAIN_HOST=ganache:8545 10 | ports: 11 | - 9090:9090 12 | volumes: 13 | - ./src:/opt/app/src:delegated 14 | - ./contracts:/opt/app/contracts:delegated 15 | 16 | ganache: 17 | image: trufflesuite/ganache-cli:v6.1.6 18 | container_name: ganache 19 | restart: always 20 | command: -l 804247552 -h 0.0.0.0 -d 21 | ports: 22 | - 8545:8545 23 | -------------------------------------------------------------------------------- /egd_demo_gif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fergarrui/ethereum-graph-debugger/d7310f960c726ee04c54163b2803c3a5dda97b36/egd_demo_gif.gif -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Ethereum Debugger 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Logger.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from 'winston' 2 | 3 | const logger = new Logger() 4 | 5 | export { logger } 6 | -------------------------------------------------------------------------------- /src/Server.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata' 2 | import { Express } from 'express' 3 | import express = require('express') 4 | import methodOverride = require('method-override') 5 | import * as bodyParser from 'body-parser' 6 | import { logger } from './Logger' 7 | import { transports } from 'winston' 8 | import * as expressWinston from 'express-winston' 9 | 10 | export class Server { 11 | express: Express 12 | constructor() { 13 | this.express = express() 14 | this.express.use(bodyParser.urlencoded({ extended: true })) 15 | this.express.use(bodyParser.json()) 16 | this.express.use(methodOverride()) 17 | } 18 | 19 | setLogConfig(logLevel: string, productionMode: boolean) { 20 | const consoleLogConfig: object = productionMode ? { json: true } : { colorize: true } 21 | 22 | if (logLevel === 'info') { 23 | logger.add(transports.Console, { 24 | type: 'debug', 25 | colorize: true, 26 | prettyPrint: true, 27 | handleExceptions: true, 28 | humanReadableUnhandledException: true 29 | }) 30 | } 31 | 32 | if (logLevel === 'debug') { 33 | this.express.use( 34 | expressWinston.errorLogger({ 35 | transports: [new transports.Console(consoleLogConfig)] 36 | }) 37 | ) 38 | } 39 | 40 | if (logLevel === 'info') { 41 | this.express.use( 42 | expressWinston.logger({ 43 | transports: [new transports.Console(consoleLogConfig)] 44 | }) 45 | ) 46 | } 47 | return this 48 | } 49 | 50 | // withErrorHandlers() { 51 | // this.express.use(clientError) 52 | // this.express.use(systemError) 53 | // return this 54 | // } 55 | 56 | async startOn(port: number): Promise { 57 | return new Promise((resolve, reject) => { 58 | if (!port) { 59 | reject('Invalid port') 60 | return 61 | } 62 | const instance = this.express.listen(port, () => { 63 | logger.info(`Server Started!`) 64 | logger.info(`listening on port ${port}`) 65 | resolve(instance) 66 | }) 67 | }) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/api/blockchain/IWeb3.ts: -------------------------------------------------------------------------------- 1 | export interface IWeb3 { 2 | getInstance(): any 3 | } 4 | -------------------------------------------------------------------------------- /src/api/blockchain/Web3Configuration.ts: -------------------------------------------------------------------------------- 1 | export class Web3Configuration { 2 | blockchainHost: string // url + port 3 | blockchainProtocol: string // http or https 4 | blockchainBasicAuthUsername: string 5 | blockchainBasicAuthPassword: string 6 | } 7 | -------------------------------------------------------------------------------- /src/api/blockchain/Web3Instance.ts: -------------------------------------------------------------------------------- 1 | import { IWeb3 } from './IWeb3' 2 | import { Web3Configuration } from './Web3Configuration'; 3 | const Web3 = require('web3') 4 | 5 | export class Web3Instance implements IWeb3 { 6 | web3Instance: any 7 | 8 | constructor(config: Web3Configuration) { 9 | const isEmpty = Object.values(config).every(x => (!x) || x === '') 10 | if (isEmpty) { 11 | this.web3Instance = new Web3(process.env.BLOCKCHAIN_HOST? `http://${process.env.BLOCKCHAIN_HOST }`:'http://127.0.0.1:8545') 12 | } else { 13 | const protocol = config.blockchainProtocol || 'http' 14 | const url = config.blockchainHost || process.env.BLOCKCHAIN_HOST || '127.0.0.1:8545' 15 | let blockchainUrl = `${protocol}://` 16 | if (config.blockchainBasicAuthUsername && config.blockchainBasicAuthPassword) { 17 | blockchainUrl += `${config.blockchainBasicAuthUsername}:${config.blockchainBasicAuthPassword}@` 18 | } 19 | blockchainUrl += `${url}` 20 | this.web3Instance = new Web3(blockchainUrl) 21 | } 22 | } 23 | 24 | getInstance() { 25 | return this.web3Instance 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/api/bytecode/DisassembledContract.ts: -------------------------------------------------------------------------------- 1 | import { Operation } from './Operation' 2 | 3 | export interface DisassembledContract { 4 | hasConstructor: boolean 5 | constructor: Operation[] 6 | runtime: Operation[] 7 | bytecode: string 8 | runtimeBytecode: string 9 | } 10 | -------------------------------------------------------------------------------- /src/api/bytecode/Disassembler.ts: -------------------------------------------------------------------------------- 1 | import { Operation } from './Operation' 2 | import { DisassembledContract } from './DisassembledContract' 3 | 4 | export interface Disassembler { 5 | disassembleSourceCode(contractName: string, source: string, path: string): DisassembledContract 6 | disassembleContract(bytecode: string): DisassembledContract 7 | disassembleBytecode(bytecode: string): Operation[] 8 | } 9 | -------------------------------------------------------------------------------- /src/api/bytecode/Opcode.ts: -------------------------------------------------------------------------------- 1 | export interface Opcode { 2 | name: string 3 | opcode: number 4 | parameters: number 5 | } 6 | -------------------------------------------------------------------------------- /src/api/bytecode/Operation.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from './Opcode' 2 | 3 | export interface Operation { 4 | offset: number 5 | opcode: Opcode 6 | argument: any 7 | begin?: number 8 | end?: number 9 | repeated?: number 10 | } 11 | -------------------------------------------------------------------------------- /src/api/cfg/CFGBlocks.ts: -------------------------------------------------------------------------------- 1 | import { OperationBlock } from './OperationBlock' 2 | 3 | export class CFGBlocks { 4 | blocks = {} 5 | 6 | push(block: OperationBlock, offset: number) { 7 | this.blocks[offset] = block 8 | } 9 | 10 | get(offset: number): OperationBlock { 11 | const block: OperationBlock = this.blocks[offset] 12 | if(!block) { 13 | for (const key of Object.keys(this.blocks)) { 14 | const b: OperationBlock = this.blocks[key] 15 | const found = b.operations.find(op => op.offset === offset) 16 | if (found) { 17 | return b 18 | } 19 | } 20 | } 21 | return block 22 | } 23 | 24 | keys(): number[] { 25 | return Object.keys(this.blocks).map(e => parseFloat(e)) 26 | } 27 | 28 | values(): OperationBlock[][] { 29 | return Object.values(this.blocks) 30 | } 31 | 32 | length(): number { 33 | return this.keys().length 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/api/cfg/CFGCreator.ts: -------------------------------------------------------------------------------- 1 | import { Operation } from '../bytecode/Operation' 2 | import { CFGBlocks } from './CFGBlocks' 3 | 4 | export interface CFGCreator { 5 | divideBlocks(ops: Operation[]): CFGBlocks 6 | } 7 | -------------------------------------------------------------------------------- /src/api/cfg/EthereumCFGCreator.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata' 2 | import { CFGCreator } from './CFGCreator' 3 | import { injectable } from 'inversify' 4 | import { Operation } from '../bytecode/Operation' 5 | import { OperationBlock } from './OperationBlock' 6 | import { CFGBlocks } from './CFGBlocks' 7 | 8 | @injectable() 9 | export class EthereumCFGCreator implements CFGCreator { 10 | private readonly blockEnd = ['JUMPI', 'JUMP', 'STOP', 'REVERT', 'RETURN', 'INVALID'] 11 | 12 | divideBlocks(ops: Operation[]): CFGBlocks { 13 | const blocks: CFGBlocks = new CFGBlocks() 14 | let startIndex = 0 15 | for (let i = 0; i < ops.length; i++) { 16 | const op: Operation = ops[i] 17 | if (this.blockEnd.includes(op.opcode.name) || i === ops.length - 1) { 18 | this.addNewBlock(ops, startIndex, i, blocks) 19 | startIndex = i + 1 20 | } 21 | } 22 | return blocks 23 | } 24 | 25 | private addNewBlock(ops: Operation[], startIndex: number, i: number, blocks: CFGBlocks) { 26 | const newBlockOps = ops.slice(startIndex, i + 1) 27 | const firstBlockOp = newBlockOps[0] 28 | const newBlock: OperationBlock = { 29 | offset: firstBlockOp.offset, 30 | operations: newBlockOps 31 | } 32 | blocks.push(newBlock, firstBlockOp.offset) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/api/cfg/GraphVizService.test.ts: -------------------------------------------------------------------------------- 1 | import { EthereumCFGCreator } from './EthereumCFGCreator' 2 | import { Disassembler } from '../bytecode/Disassembler' 3 | import { OpcodeExecutor } from '../symbolic/evm/exec/OpcodeExecutor' 4 | import { EVMExecutor } from '../symbolic/evm/EVMExecutor' 5 | import { createExecutor, createEVMDisassembler } from '../symbolic/evm/exec/TestUtils' 6 | import { GraphVizService } from './GraphVizService' 7 | 8 | describe('GraphVizService', () => { 9 | let cfgCreator: EthereumCFGCreator 10 | let disassembler: Disassembler 11 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 12 | let graph: GraphVizService 13 | 14 | beforeEach(() => { 15 | cfgCreator = new EthereumCFGCreator() 16 | disassembler = createEVMDisassembler() 17 | graph = new GraphVizService() 18 | }) 19 | 20 | it('Create simple DOT string', () => { 21 | const bytecode = 22 | '608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e2179b8e146044575b600080fd5b348015604f57600080fd5b5060566058565b005b5600a165627a7a723058202b2566218088bf32ca5a2f029f04a425067ebe1770872a7b06934044a63a81630029' 23 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 24 | executor.run(0) 25 | const blocks = executor.blocks 26 | const dotString = graph.createDotFromBlocks(blocks, undefined) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /src/api/cfg/OperationBlock.ts: -------------------------------------------------------------------------------- 1 | import { Operation } from '../bytecode/Operation' 2 | 3 | export interface OperationBlock { 4 | offset: number 5 | operations: Operation[] 6 | childA?: number 7 | childB?: number 8 | } 9 | -------------------------------------------------------------------------------- /src/api/service/bean/Block.ts: -------------------------------------------------------------------------------- 1 | export interface Block { 2 | difficulty: number; 3 | extraData?: string; 4 | gasLimit: number; 5 | gasUsed: number; 6 | hash: string; 7 | logsBloom?: string; 8 | miner?: string; 9 | mixHash?: string; 10 | nonce: string; 11 | number: number; 12 | parentHash: string; 13 | receiptsRoot?: string; 14 | sha3Uncles?: string; 15 | size?: number; 16 | stateRoot?: string; 17 | timestamp: number; 18 | totalDifficulty?: number; 19 | transactions: string[]; 20 | transactionsRoot?: string; 21 | uncles: any[]; 22 | } 23 | -------------------------------------------------------------------------------- /src/api/service/bean/CFGContract.ts: -------------------------------------------------------------------------------- 1 | import { CFGBlocks } from '../../cfg/CFGBlocks' 2 | import { Operation } from '../../bytecode/Operation' 3 | 4 | export interface CFGContract { 5 | contractConstructor?: { 6 | bytecode: Operation[] 7 | blocks: CFGBlocks, 8 | rawBytecode 9 | } 10 | contractRuntime: { 11 | bytecode: Operation[] 12 | blocks: CFGBlocks 13 | rawBytecode: string 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/api/service/bean/ContractFile.ts: -------------------------------------------------------------------------------- 1 | export interface ContractFile { 2 | name: string 3 | code: string 4 | path: string 5 | } 6 | -------------------------------------------------------------------------------- /src/api/service/bean/Storage.ts: -------------------------------------------------------------------------------- 1 | export class Storage { 2 | storage: any = {} 3 | 4 | setStorage(key: string, value: string, block: number, transactionHash: string, transactionIndex: number) { 5 | this.storage[key] = { 6 | value, 7 | transactionHash, 8 | block, 9 | transactionIndex 10 | } as StorageEntry 11 | } 12 | } 13 | 14 | export interface StorageEntry { 15 | value: string 16 | transactionHash: string 17 | block: number 18 | transactionIndex: number 19 | } 20 | -------------------------------------------------------------------------------- /src/api/service/bean/Transaction.ts: -------------------------------------------------------------------------------- 1 | import { TransactionBase } from "./TransactionBase"; 2 | 3 | export interface Transaction extends TransactionBase { 4 | blockHash: string; 5 | blockNumber: number; 6 | hash: string; 7 | to: string, 8 | input: string; 9 | nonce: number; 10 | transactionIndex: number; 11 | } 12 | -------------------------------------------------------------------------------- /src/api/service/bean/TransactionBase.ts: -------------------------------------------------------------------------------- 1 | export interface TransactionBase { 2 | from?: string; 3 | gas?: number; 4 | gasPrice?: number; 5 | value?: number; 6 | } 7 | -------------------------------------------------------------------------------- /src/api/service/bean/TransactionReceipt.ts: -------------------------------------------------------------------------------- 1 | export interface TransactionReceipt { 2 | transactionHash: string 3 | data: string 4 | to: string 5 | from: string 6 | blockNumber: number 7 | transactionIndex: number 8 | contractAddress: string 9 | } 10 | -------------------------------------------------------------------------------- /src/api/service/controller/DisassembleController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Route, Query, Post, Body } from 'tsoa' 2 | import { provideSingleton, inject } from '../../../inversify/ioc' 3 | import { TYPES } from '../../../inversify/types' 4 | import { Disassembler } from '../../bytecode/Disassembler' 5 | import { DisassembledContract } from '../../bytecode/DisassembledContract' 6 | import { DisassembledContractResponse } from '../response/DisassembledContractResponse' 7 | import { logger } from '../../../Logger' 8 | import { StringBodyRequest } from '../request/StringBodyRequest'; 9 | 10 | @Route('disassemble') 11 | @provideSingleton(DisassembleController) 12 | export class DisassembleController extends Controller { 13 | constructor(@inject(TYPES.Disassembler) private disassembler: Disassembler) { 14 | super() 15 | } 16 | 17 | @Post() 18 | async disassembleSourceCode( 19 | @Body() source: StringBodyRequest, 20 | @Query('name') name: string, 21 | @Query('path') path: string 22 | ): Promise { 23 | try { 24 | const disassembled: DisassembledContract = this.disassembler.disassembleSourceCode(name, source.request, path) 25 | return this.contractToResponse(disassembled) 26 | } catch (err) { 27 | logger.error(err) 28 | throw new Error(err.message) 29 | } 30 | } 31 | 32 | private contractToResponse(disassembled: DisassembledContract): DisassembledContractResponse { 33 | return { 34 | bytecode: disassembled.bytecode, 35 | hasConstructor: disassembled.hasConstructor, 36 | constructorOperations: disassembled.constructor.map(op => { 37 | return { 38 | offset: op.offset, 39 | opcode: op.opcode, 40 | argument: op.argument.toString(16) 41 | } 42 | }), 43 | runtimeOperations: disassembled.runtime.map(op => { 44 | return { 45 | offset: op.offset, 46 | opcode: op.opcode, 47 | argument: op.argument.toString(16) 48 | } 49 | }) 50 | } as DisassembledContractResponse 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/api/service/controller/FileController.ts: -------------------------------------------------------------------------------- 1 | import { Route, Controller, Get, Path, Query, Post, Body } from 'tsoa' 2 | import { provideSingleton, inject } from '../../../inversify/ioc' 3 | import { ContractFile } from '../bean/ContractFile' 4 | import { TYPES } from '../../../inversify/types' 5 | import { FileService } from '../service/FileService' 6 | import { logger } from '../../../Logger' 7 | import { SaveFileRequest } from '../request/SaveFileRequest'; 8 | 9 | @Route('files') 10 | @provideSingleton(FileController) 11 | export class FileController extends Controller { 12 | constructor(@inject(TYPES.FileService) private fileService: FileService) { 13 | super() 14 | } 15 | 16 | @Get('{dir}') 17 | async findContractsInDir(@Path() dir: string, @Query('extension') extension: string): Promise { 18 | try { 19 | let directory = dir 20 | if (!dir || dir === '' || dir === ' ') { 21 | directory = './contracts' 22 | } 23 | const contracts = await this.fileService.findContractssWithExtension(directory, extension) 24 | if (contracts.length === 0) { 25 | throw new Error(`No contracts found at ${dir} with extension ${extension}`) 26 | } 27 | return contracts 28 | } catch (err) { 29 | logger.error(err) 30 | throw new Error(err.message) 31 | } 32 | } 33 | 34 | @Post() 35 | async saveFile(@Body() saveFileRequest: SaveFileRequest) { 36 | try { 37 | await this.fileService.saveFile(saveFileRequest.path, saveFileRequest.name, saveFileRequest.content) 38 | } catch (err) { 39 | logger.error(err) 40 | throw new Error(err.message) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/api/service/controller/SolcController.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Route, Get, Post, Body } from "tsoa"; 2 | import { provideSingleton, inject } from "../../../inversify/ioc"; 3 | import { TYPES } from "../../../inversify/types"; 4 | import { Solc } from "../service/Solc"; 5 | import { SolcChangeVersionRequest } from "../request/SolcChangeVersionRequest"; 6 | 7 | @Route('solc') 8 | @provideSingleton(SolcController) 9 | export class SolcController extends Controller { 10 | 11 | constructor( 12 | @inject(TYPES.Solc) private solc: Solc 13 | ){ 14 | super() 15 | } 16 | 17 | @Get() 18 | async getVersion(): Promise { 19 | return this.solc.checkVersion() 20 | } 21 | 22 | @Get('list') 23 | async getAvailableVersions(): Promise { 24 | return this.solc.listVersions() 25 | } 26 | 27 | @Post() 28 | async changeVersion( 29 | @Body() solcChangeVersionRequest: SolcChangeVersionRequest 30 | ) { 31 | return this.solc.changeVersion(solcChangeVersionRequest.version) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/api/service/controller/StorageRecoverController.ts: -------------------------------------------------------------------------------- 1 | import { Route, Path, Controller, Get, Query } from 'tsoa' 2 | import { provideSingleton } from '../../../inversify/ioc'; 3 | import { inject } from 'inversify'; 4 | import { TYPES } from '../../../inversify/types'; 5 | import { StorageRecover } from '../service/StorageRecover'; 6 | import { Web3Configuration } from '../../blockchain/Web3Configuration'; 7 | import { logger } from '../../../Logger' 8 | 9 | @Route('storage') 10 | @provideSingleton(StorageRecoverController) 11 | export class StorageRecoverController extends Controller { 12 | 13 | constructor(@inject(TYPES.StorageRecover) private storageRecover: StorageRecover) { 14 | super() 15 | } 16 | 17 | @Get('{contractAddress}') 18 | async getStorage( 19 | @Path() contractAddress: string, 20 | @Query('startBlock') startBlock: number, 21 | @Query('endBlock') endBlock: number, 22 | @Query('blockchainHost') blockchainHost?: string, 23 | @Query('blockchainProtocol') blockchainProtocol?: string, 24 | @Query('blockchainBasicAuthUsername') blockchainBasicAuthUsername?: string, 25 | @Query('blockchainBasicAuthPassword') blockchainBasicAuthPassword?: string, 26 | @Query('existingStorage') existingStorage?: string 27 | ) { 28 | try { 29 | const config = { 30 | blockchainHost, 31 | blockchainProtocol, 32 | blockchainBasicAuthUsername, 33 | blockchainBasicAuthPassword 34 | } as Web3Configuration 35 | return this.storageRecover.recoverStorage(contractAddress, startBlock, endBlock, config, existingStorage) 36 | } catch (err) { 37 | logger.error(err) 38 | throw new Error(err.message) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/api/service/controller/TransactionController.ts: -------------------------------------------------------------------------------- 1 | import { Route, Path, Controller, Get, Query } from 'tsoa' 2 | import { provideSingleton, inject } from '../../../inversify/ioc' 3 | import { TYPES } from '../../../inversify/types' 4 | import { TransactionService } from '../service/TransactionService' 5 | import { TransactionReceipt } from '../bean/TransactionReceipt' 6 | import { logger } from '../../../Logger' 7 | import { Web3Configuration } from 'src/api/blockchain/Web3Configuration'; 8 | 9 | @Route('tx') 10 | @provideSingleton(TransactionController) 11 | export class TransactionController extends Controller { 12 | constructor(@inject(TYPES.TransactionService) private transactionService: TransactionService) { 13 | super() 14 | } 15 | 16 | @Get('{tx}/receipt') 17 | async getReceipt(@Path() tx: string, 18 | @Query('blockchainHost') blockchainHost?: string, 19 | @Query('blockchainProtocol') blockchainProtocol?: string, 20 | @Query('blockchainBasicAuthUsername') blockchainBasicAuthUsername?: string, 21 | @Query('blockchainBasicAuthPassword') blockchainBasicAuthPassword?: string 22 | ): Promise { 23 | try { 24 | const config = { 25 | blockchainHost, 26 | blockchainProtocol, 27 | blockchainBasicAuthUsername, 28 | blockchainBasicAuthPassword 29 | } as Web3Configuration 30 | return this.transactionService.findTransactionReceipt(tx, config) 31 | } catch (err) { 32 | logger.error(err) 33 | throw new Error(err.message) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/api/service/request/DeployContractRequest.ts: -------------------------------------------------------------------------------- 1 | import { TransactionRequest } from "./TransactionRequest"; 2 | 3 | export interface DeployContractRequest extends TransactionRequest { 4 | name: string, 5 | source: string, 6 | path: string 7 | } 8 | -------------------------------------------------------------------------------- /src/api/service/request/RunContractFunctionRequest.ts: -------------------------------------------------------------------------------- 1 | import { TransactionRequest } from "./TransactionRequest"; 2 | 3 | export interface RunContractFunctionRequest extends TransactionRequest { 4 | abi: any 5 | params: string[] 6 | } 7 | -------------------------------------------------------------------------------- /src/api/service/request/SaveFileRequest.ts: -------------------------------------------------------------------------------- 1 | export interface SaveFileRequest { 2 | path: string 3 | name: string 4 | content: string 5 | } 6 | -------------------------------------------------------------------------------- /src/api/service/request/SolcChangeVersionRequest.ts: -------------------------------------------------------------------------------- 1 | export interface SolcChangeVersionRequest { 2 | version: string 3 | } 4 | -------------------------------------------------------------------------------- /src/api/service/request/StringBodyRequest.ts: -------------------------------------------------------------------------------- 1 | export interface StringBodyRequest { 2 | request: string 3 | } 4 | -------------------------------------------------------------------------------- /src/api/service/request/TransactionRequest.ts: -------------------------------------------------------------------------------- 1 | import { TransactionBase } from "../bean/TransactionBase"; 2 | 3 | export interface TransactionRequest extends TransactionBase { 4 | blockchainHost?: string, 5 | blockchainProtocol?: string, 6 | blockchainBasicAuthUsername?: string, 7 | blockchainBasicAuthPassword?: string 8 | } 9 | -------------------------------------------------------------------------------- /src/api/service/response/CFGResponse.ts: -------------------------------------------------------------------------------- 1 | import { OperationResponse } from './OperationResponse' 2 | 3 | export interface GFCResponse { 4 | cfg: string 5 | operations: OperationResponse[] 6 | } 7 | -------------------------------------------------------------------------------- /src/api/service/response/DisassembledContractResponse.ts: -------------------------------------------------------------------------------- 1 | import { OperationResponse } from './OperationResponse' 2 | 3 | export interface DisassembledContractResponse { 4 | hasConstructor: boolean 5 | constructorOperations: OperationResponse[] 6 | runtimeOperations: OperationResponse[] 7 | bytecode: string 8 | } 9 | -------------------------------------------------------------------------------- /src/api/service/response/OperationResponse.ts: -------------------------------------------------------------------------------- 1 | import { Opcode } from '../../bytecode/Opcode' 2 | 3 | export interface OperationResponse { 4 | offset: number 5 | opcode: Opcode 6 | argument: string 7 | begin?: number 8 | end?: number 9 | } 10 | -------------------------------------------------------------------------------- /src/api/service/response/TraceResponse.ts: -------------------------------------------------------------------------------- 1 | import { OperationResponse } from './OperationResponse' 2 | 3 | export interface TraceResponse { 4 | cfg: string 5 | operations: OperationResponse[] 6 | trace: { 7 | key: string 8 | log: { 9 | depth: number 10 | error: string 11 | gas: number 12 | gasCost: number 13 | memory: string[] 14 | op: string 15 | pc: number 16 | stack: string[] 17 | storage: any 18 | }[] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/api/service/service/BlockService.ts: -------------------------------------------------------------------------------- 1 | import { Block } from "../bean/Block"; 2 | import { Web3Configuration } from "../../blockchain/Web3Configuration"; 3 | 4 | export interface BlockService { 5 | getBlock(blockNumber: number, config: Web3Configuration): Promise 6 | } 7 | -------------------------------------------------------------------------------- /src/api/service/service/BlockServiceImpl.ts: -------------------------------------------------------------------------------- 1 | import { BlockService } from "./BlockService"; 2 | import { Block } from "../bean/Block"; 3 | import { Web3Configuration } from "../../blockchain/Web3Configuration"; 4 | import { IWeb3 } from "../../blockchain/IWeb3"; 5 | import { Web3Instance } from "../../blockchain/Web3Instance"; 6 | import { injectable } from 'inversify' 7 | import { logger } from "../../../Logger"; 8 | 9 | @injectable() 10 | export class BlockServiceImpl implements BlockService { 11 | 12 | async getBlock(blockNumber: number, config: Web3Configuration): Promise { 13 | const iWeb3: IWeb3 = new Web3Instance(config) 14 | const web3 = iWeb3.getInstance() 15 | const block: Block = await web3.eth.getBlock(blockNumber, false) 16 | if (!block) { 17 | logger.info(`Block ${blockNumber} not found in node`) 18 | } 19 | return block 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/api/service/service/FileService.ts: -------------------------------------------------------------------------------- 1 | import { ContractFile } from '../bean/ContractFile' 2 | 3 | export interface FileService { 4 | findContractssWithExtension(dir: string, extension: string): Promise 5 | saveFile(dir: string, name: string, content: string) 6 | } 7 | -------------------------------------------------------------------------------- /src/api/service/service/FileServiceDefault.ts: -------------------------------------------------------------------------------- 1 | import { FileService } from './FileService' 2 | import { ContractFile } from '../bean/ContractFile' 3 | import { injectable } from 'inversify' 4 | var recursive = require('recursive-readdir') 5 | var path = require('path') 6 | var fs = require('fs') 7 | 8 | @injectable() 9 | export class FileServiceDefault implements FileService { 10 | async findContractssWithExtension(dir: string, extension: string): Promise { 11 | const files = await recursive(dir, [`!*.{${extension},evm}`]) 12 | 13 | return await files 14 | .map(file => { 15 | const dirName = path.dirname(file) 16 | const fileContent = fs.readFileSync(file, 'utf8') 17 | return { 18 | name: path.basename(file), 19 | code: fileContent, 20 | path: dirName 21 | } as ContractFile 22 | }) 23 | .sort((a, b) => (a.name > b.name ? 1 : -1)) 24 | } 25 | 26 | async saveFile(dir: string, name: string, content: string) { 27 | const filePath = path.join(dir, name) 28 | return fs.writeFileSync(filePath, content) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/api/service/service/Solc.ts: -------------------------------------------------------------------------------- 1 | import { injectable } from "inversify"; 2 | let solc = require('solc') 3 | const request = require("request") 4 | 5 | @injectable() 6 | export class Solc { 7 | 8 | private readonly SOLC_VERSIONS_URL = "https://ethereum.github.io/solc-bin/bin/list.json" 9 | 10 | getInstance() { 11 | return solc 12 | } 13 | 14 | async changeVersion(version: string): Promise { 15 | return new Promise((resolve, reject) => { 16 | solc.loadRemoteVersion(version, function(err, s) { 17 | if(err) { 18 | return reject(err) 19 | } 20 | solc = s 21 | return resolve(s.version()) 22 | }) 23 | }) 24 | } 25 | 26 | async listVersions(): Promise { 27 | return new Promise((resolve, reject) => { 28 | 29 | const prepareResponse = (body: any) => { 30 | const releases = body.releases 31 | 32 | return Object.keys(releases).map(release => { 33 | return { 34 | version: release, 35 | commit: releases[release].replace('soljson-', '').replace('.js', '') 36 | } 37 | }) 38 | } 39 | 40 | request({ 41 | url: this.SOLC_VERSIONS_URL, 42 | json: true 43 | }, function (error, response, body) { 44 | if (error) { 45 | reject(error) 46 | } 47 | if(response.statusCode === 200) { 48 | resolve(prepareResponse(body)) 49 | } 50 | }) 51 | }) 52 | } 53 | 54 | checkVersion(): string { 55 | return solc.version() 56 | } 57 | 58 | checkSimpleVersion(): string { 59 | let version: string = solc.version() 60 | if (version.startsWith('v')) { 61 | version = version.substr(1, version.length) 62 | } 63 | const indexOfPlus = version.indexOf('+') 64 | return version.substr(0, indexOfPlus) 65 | } 66 | 67 | isVersion5OrAbove(): boolean { 68 | const version = this.checkSimpleVersion() 69 | const versionSplitted = version.split('.') 70 | const minor = versionSplitted[1] 71 | return parseInt(minor) >= 5 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/api/service/service/TransactionService.ts: -------------------------------------------------------------------------------- 1 | import { TransactionReceipt } from '../bean/TransactionReceipt' 2 | import { DebugTrace } from '../../symbolic/evm/DebugTrace' 3 | import { Web3Configuration } from 'src/api/blockchain/Web3Configuration'; 4 | import { Transaction } from '../bean/Transaction'; 5 | 6 | export interface TransactionService { 7 | findTransactionReceipt(transactionHash: string, config: Web3Configuration): Promise 8 | findTransaction(transactionHash: string, config: Web3Configuration): Promise 9 | findTransactionTrace(transactionHash: string, bytecode: string, config: Web3Configuration): Promise 10 | getTrace(transactionHash: string, config: Web3Configuration): Promise 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/DebugTrace.ts: -------------------------------------------------------------------------------- 1 | export interface DebugTrace { 2 | id: number 3 | jsonrpc: number 4 | result: { 5 | gas: number 6 | returnValue: string 7 | structLogs: { 8 | depth: number 9 | error: string 10 | gas: number 11 | gasCost: number 12 | memory: string[] 13 | op: string 14 | pc: number 15 | stack: string[] 16 | storage: any 17 | }[] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/EVM.ts: -------------------------------------------------------------------------------- 1 | import { EVMStack } from './EVMStack' 2 | import { EVMStorage } from './EVMStorage' 3 | import { EVMMemory } from './EVMMemory' 4 | import { Word } from './Word' 5 | 6 | export class EVM { 7 | stack: EVMStack 8 | storage: EVMStorage 9 | memory: EVMMemory 10 | nextJumpLocation: Word 11 | 12 | constructor() { 13 | this.stack = new EVMStack() 14 | this.storage = new EVMStorage() 15 | this.memory = new EVMMemory(64) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/EVMStack.test.ts: -------------------------------------------------------------------------------- 1 | import { EVMStack } from './EVMStack' 2 | import { Word } from './Word' 3 | 4 | describe('EVMStack', () => { 5 | let stack: EVMStack 6 | 7 | beforeEach(() => { 8 | stack = new EVMStack() 9 | }) 10 | 11 | it('test push', () => { 12 | stack.push(Word.createLiteral('80')) 13 | stack.push(Word.createLiteral('81')) 14 | expect(stack.length()).toEqual(2) 15 | }) 16 | 17 | it('Test pop', () => { 18 | stack.push(Word.createLiteral('80')) 19 | stack.push(Word.createLiteral('81')) 20 | const pop = stack.pop() 21 | expect(pop).toEqual(Word.createLiteral('81')) 22 | expect(stack.length()).toEqual(1) 23 | }) 24 | 25 | it('Test peek', () => { 26 | stack.push(Word.createLiteral('80')) 27 | stack.push(Word.createLiteral('81')) 28 | const pop = stack.peek() 29 | expect(pop).toEqual(Word.createLiteral('81')) 30 | expect(stack.length()).toEqual(2) 31 | }) 32 | 33 | it('Test get', () => { 34 | stack.push(Word.createLiteral('80')) 35 | stack.push(Word.createLiteral('81')) 36 | stack.push(Word.createLiteral('82')) 37 | const get0 = stack.get(0) 38 | const get1 = stack.get(1) 39 | const get2 = stack.get(2) 40 | expect(get0).toEqual(Word.createLiteral('82')) 41 | expect(get1).toEqual(Word.createLiteral('81')) 42 | expect(get2).toEqual(Word.createLiteral('80')) 43 | expect(stack.length()).toEqual(3) 44 | }) 45 | 46 | it('Test put', () => { 47 | stack.push(Word.createLiteral('01')) 48 | stack.push(Word.createLiteral('02')) 49 | stack.push(Word.createLiteral('03')) 50 | stack.put(2, Word.createLiteral('99')) 51 | stack.put(0, Word.createLiteral('ff')) 52 | const top0 = stack.pop() 53 | const top1 = stack.pop() 54 | const top2 = stack.pop() 55 | expect(top0).toEqual(Word.createLiteral('ff')) 56 | expect(top1).toEqual(Word.createLiteral('02')) 57 | expect(top2).toEqual(Word.createLiteral('99')) 58 | }) 59 | }) 60 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/EVMStack.ts: -------------------------------------------------------------------------------- 1 | import { Word } from './Word' 2 | 3 | export class EVMStack { 4 | stack: Word[] = [] 5 | 6 | push(word: Word) { 7 | this.stack.push(word) 8 | } 9 | 10 | pop(): Word { 11 | return this.stack.pop() 12 | } 13 | 14 | peek(): Word { 15 | return this.stack[this.stack.length - 1] 16 | } 17 | 18 | get(index: number): Word { 19 | return this.stack[this.stack.length - 1 - index] 20 | } 21 | 22 | put(index: number, word: Word) { 23 | this.stack[this.stack.length - 1 - index] = word 24 | } 25 | 26 | length(): number { 27 | return this.stack.length 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/EVMStorage.ts: -------------------------------------------------------------------------------- 1 | import { Word } from './Word' 2 | 3 | export class EVMStorage { 4 | storage = {} 5 | length 6 | 7 | store(slot: Word, value: Word) { 8 | if (!slot || !value) { 9 | return 10 | } 11 | if (slot.isSymbolic) { 12 | this.storage[slot.symbol] = value 13 | } else { 14 | this.storage[slot.value] = value 15 | } 16 | } 17 | 18 | load(slot: Word): Word { 19 | if (!slot) { 20 | return 21 | } 22 | if (slot.isSymbolic) { 23 | return this.storage[slot.symbol] 24 | } else { 25 | return this.storage[slot.value] 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/Symbols.ts: -------------------------------------------------------------------------------- 1 | export enum Symbols { 2 | // temporal placeholder until full symexec is implemented 3 | UNKNOWN = 'UNKNOWN', 4 | EMPTY = 'EMPTY', 5 | 6 | CALL = 'CALL', 7 | STATICCALL = 'STATICCALL', 8 | CALLCODE = 'CALLCODE', 9 | DELEGATECALL = 'DELEGATECALL', 10 | RETURNDATASIZE = 'RETURNDATASIZE', 11 | RETURNDATACOPY = 'RETURNDATACOPY', 12 | 13 | ADDRESS = 'ADDRESS', 14 | ORIGIN = 'ORIGIN', 15 | CALLER = 'CALLER', 16 | BALANCE = 'BALANCE', 17 | CALLVALUE = 'CALLVALUE', 18 | CALLDATASIZE = 'CALLDATASIZE', 19 | CALLDATALOAD = 'CALLDATALOAD', 20 | CODESIZE = 'CODESIZE', 21 | GAS = 'GAS', 22 | BLOCKHASH = 'BLOCKHASH', 23 | COINBASE = 'COINBASE', 24 | TIMESTAMP = 'TIMESTAMP', 25 | NUMBER = 'NUMBER', 26 | DIFFICULTY = 'DIFFICULTY', 27 | GASLIMIT = 'GASLIMIT', 28 | GASPRICE = 'GASPRICE', 29 | EXTCODESIZE = 'EXTCODESIZE', 30 | CREATE = 'CREATE' 31 | } 32 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/UintUtils.ts: -------------------------------------------------------------------------------- 1 | let BN = require('bn.js') 2 | 3 | export class UintUtils { 4 | static TWO_POW_256 = new BN('10000000000000000000000000000000000000000000000000000000000000000', 16) 5 | static ZERO = new BN('00', 16) 6 | static ONE = new BN('01', 16) 7 | static MAX_INTEGER = new BN('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16) 8 | } 9 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/Word.ts: -------------------------------------------------------------------------------- 1 | import { Symbols } from './Symbols' 2 | let BN = require('bn.js') 3 | 4 | export class Word { 5 | static WORD_LENGTH_IN_BYTES = 32 6 | 7 | isSymbolic: boolean 8 | value?: any 9 | symbol?: Symbols 10 | 11 | static createLiteral(valueHex: string): Word { 12 | return { isSymbolic: false, value: new BN(valueHex, 16) } as Word 13 | } 14 | 15 | static createSymbolic(symbol: Symbols): Word { 16 | return { isSymbolic: true, symbol: symbol } as Word 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Add.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | import { UintUtils } from '../UintUtils' 7 | 8 | export class Add implements Executor { 9 | execute(op: Operation, evm: EVM) { 10 | const operand1 = evm.stack.pop() 11 | const operand2 = evm.stack.pop() 12 | if (!operand1 || !operand2) { 13 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 14 | return 15 | } 16 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 17 | const op1Value = operand1.value 18 | const op2Value = operand2.value 19 | let result = op1Value.add(op2Value).mod(UintUtils.TWO_POW_256) 20 | evm.stack.push(Word.createLiteral(result.toString(16))) 21 | } else { 22 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Addmod.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | import { UintUtils } from '../UintUtils' 7 | 8 | export class Addmod implements Executor { 9 | execute(op: Operation, evm: EVM) { 10 | const operand1 = evm.stack.pop() 11 | const operand2 = evm.stack.pop() 12 | const mod = evm.stack.pop() 13 | if (!operand1 || !operand2 || !mod) { 14 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 15 | return 16 | } 17 | if (!operand1.isSymbolic && !operand2.isSymbolic && !mod.isSymbolic) { 18 | if (mod.value.eq(UintUtils.ZERO)) { 19 | evm.stack.push(Word.createLiteral('00')) 20 | } else { 21 | const result = operand1.value.add(operand2.value).mod(mod.value) 22 | evm.stack.push(Word.createLiteral(result.toString(16))) 23 | } 24 | } else { 25 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Address.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Address', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test address', () => { 21 | const bytecode = '604030' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.ADDRESS)) 25 | expect(executor.evm.stack.length()).toEqual(2) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Address.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Address implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.push(Word.createSymbolic(Symbols.ADDRESS)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/And.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class And implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | const operand1 = evm.stack.pop() 10 | const operand2 = evm.stack.pop() 11 | if (!operand1 || !operand2) { 12 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 13 | return 14 | } 15 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 16 | const op1Value = operand1.value 17 | const op2Value = operand2.value 18 | let result = op1Value.and(op2Value) 19 | evm.stack.push(Word.createLiteral(result.toString(16))) 20 | } else { 21 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Balance.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Balance', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test balance', () => { 21 | const bytecode = '604031' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.BALANCE)) 25 | expect(executor.evm.stack.length()).toEqual(1) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Balance.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Balance implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.pop() 10 | evm.stack.push(Word.createSymbolic(Symbols.BALANCE)) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Blockhash.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Blockhash', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Blockhash', () => { 21 | const bytecode = '604040' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.BLOCKHASH)) 25 | expect(executor.evm.stack.length()).toEqual(1) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Blockhash.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Blockhash implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.pop() 10 | evm.stack.push(Word.createSymbolic(Symbols.BLOCKHASH)) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Byte.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | let BN = require('bn.js') 7 | 8 | export class Byte implements Executor { 9 | execute(op: Operation, evm: EVM) { 10 | const operand1 = evm.stack.pop() 11 | const operand2 = evm.stack.pop() 12 | if (!operand1 || !operand2) { 13 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 14 | return 15 | } 16 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 17 | const pos = operand1.value 18 | const word = operand2.value 19 | let result = new BN(0) 20 | if (!pos.gten(32)) { 21 | result = new BN(word.shrn((31 - pos.toNumber()) * 8).andln(0xff)) 22 | } 23 | evm.stack.push(Word.createLiteral(result.toString(16))) 24 | } else { 25 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Call.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Call', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Call', () => { 21 | const bytecode = '6010602060306040605060606070f1' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.CALL)) 25 | expect(executor.evm.stack.length()).toEqual(1) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Call.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Call implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.pop() 10 | evm.stack.pop() 11 | evm.stack.pop() 12 | evm.stack.pop() 13 | evm.stack.pop() 14 | evm.stack.pop() 15 | evm.stack.pop() 16 | evm.stack.push(Word.createSymbolic(Symbols.CALL)) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Callcode.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Callcode', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Callcode', () => { 21 | const bytecode = '6010602060306040605060606070f2' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.CALLCODE)) 25 | expect(executor.evm.stack.length()).toEqual(1) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Callcode.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Callcode implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.pop() 10 | evm.stack.pop() 11 | evm.stack.pop() 12 | evm.stack.pop() 13 | evm.stack.pop() 14 | evm.stack.pop() 15 | evm.stack.pop() 16 | evm.stack.push(Word.createSymbolic(Symbols.CALLCODE)) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Calldatacopy.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | 8 | describe('Calldatacopy', () => { 9 | let cfgCreator: EthereumCFGCreator 10 | let disassembler: Disassembler 11 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 12 | 13 | beforeEach(() => { 14 | cfgCreator = new EthereumCFGCreator() 15 | disassembler = createEVMDisassembler() 16 | }) 17 | 18 | it('Test calldatacopy', () => { 19 | const bytecode = '60406041604237' 20 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 21 | executor.run(0) 22 | expect(executor.evm.stack.length()).toEqual(0) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Calldatacopy.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | 5 | export class Calldatacopy implements Executor { 6 | execute(op: Operation, evm: EVM) { 7 | evm.stack.pop() 8 | evm.stack.pop() 9 | evm.stack.pop() 10 | // TODO write symbol in memory (memory doesn't support symbolic values yet) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Calldataload.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Calldataload', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test calldataload', () => { 21 | const bytecode = '604035' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.CALLDATALOAD)) 25 | expect(executor.evm.stack.length()).toEqual(1) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Calldataload.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Calldataload implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.pop() 10 | evm.stack.push(Word.createSymbolic(Symbols.CALLDATALOAD)) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Calldatasize.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Calldatasize', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test calldatasize', () => { 21 | const bytecode = '604036' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.CALLDATASIZE)) 25 | expect(executor.evm.stack.get(1)).toEqual(Word.createLiteral('40')) 26 | expect(executor.evm.stack.length()).toEqual(2) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Calldatasize.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Calldatasize implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.push(Word.createSymbolic(Symbols.CALLDATASIZE)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Caller.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Caller', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test caller', () => { 21 | const bytecode = '604033' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.CALLER)) 25 | expect(executor.evm.stack.length()).toEqual(2) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Caller.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Caller implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.push(Word.createSymbolic(Symbols.CALLER)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Callvalue.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Callvalue', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test callvalue', () => { 21 | const bytecode = '604034' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.CALLVALUE)) 25 | expect(executor.evm.stack.get(1)).toEqual(Word.createLiteral('40')) 26 | expect(executor.evm.stack.length()).toEqual(2) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Callvalue.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Callvalue implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.push(Word.createSymbolic(Symbols.CALLVALUE)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Codecopy.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Codecopy', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test codecopy', () => { 21 | const bytecode = '60406041604239' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.length()).toEqual(0) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Codecopy.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | 5 | export class Codecopy implements Executor { 6 | execute(op: Operation, evm: EVM) { 7 | evm.stack.pop() 8 | evm.stack.pop() 9 | evm.stack.pop() 10 | // TODO Push symbol when EVMMemory supports symbols 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Codesize.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Codesize', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test codesize', () => { 21 | const bytecode = '604038' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.CODESIZE)) 25 | expect(executor.evm.stack.length()).toEqual(2) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Codesize.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Codesize implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.push(Word.createSymbolic(Symbols.CODESIZE)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Coinbase.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Coinbase', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Coinbase', () => { 21 | const bytecode = '604041' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.COINBASE)) 25 | expect(executor.evm.stack.length()).toEqual(2) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Coinbase.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Coinbase implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.push(Word.createSymbolic(Symbols.COINBASE)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Create.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Create', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Create', () => { 21 | const bytecode = '604060416042f0' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.CREATE)) 25 | expect(executor.evm.stack.length()).toEqual(1) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Create.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Create implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.pop() 10 | evm.stack.pop() 11 | evm.stack.pop() 12 | evm.stack.push(Word.createSymbolic(Symbols.CREATE)) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Delegatecall.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Delegatecall', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Delegatecall', () => { 21 | const bytecode = '601060206030604060506060f4' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.DELEGATECALL)) 25 | expect(executor.evm.stack.length()).toEqual(1) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Delegatecall.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Delegatecall implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.pop() 10 | evm.stack.pop() 11 | evm.stack.pop() 12 | evm.stack.pop() 13 | evm.stack.pop() 14 | evm.stack.pop() 15 | evm.stack.push(Word.createSymbolic(Symbols.DELEGATECALL)) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Difficulty.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Difficulty', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Difficulty', () => { 21 | const bytecode = '604044' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.DIFFICULTY)) 25 | expect(executor.evm.stack.length()).toEqual(2) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Difficulty.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Difficulty implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.push(Word.createSymbolic(Symbols.DIFFICULTY)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Div.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | import { UintUtils } from '../UintUtils' 7 | 8 | export class Div implements Executor { 9 | execute(op: Operation, evm: EVM) { 10 | const operand1 = evm.stack.pop() 11 | const operand2 = evm.stack.pop() 12 | if (!operand1 || !operand2) { 13 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 14 | return 15 | } 16 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 17 | const op1Value = operand1.value 18 | const op2Value = operand2.value 19 | let result = UintUtils.ZERO 20 | if (!op2Value.eq(UintUtils.ZERO)) { 21 | result = op1Value.div(op2Value) 22 | } 23 | evm.stack.push(Word.createLiteral(result.toString(16))) 24 | } else { 25 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Dup.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | 5 | export class Dup implements Executor { 6 | execute(op: Operation, evm: EVM) { 7 | const index = parseInt(op.opcode.name.slice(3)) 8 | const wordToDuplicate = evm.stack.get(index - 1) 9 | evm.stack.push(wordToDuplicate) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Eq.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('EQ', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test EQ false', () => { 21 | const bytecode = '6002600114' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createLiteral('00')) 25 | expect(executor.evm.stack.length()).toEqual(1) 26 | }) 27 | 28 | it('Test EQ true', () => { 29 | const bytecode = '6001600114' 30 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 31 | executor.run(0) 32 | expect(executor.evm.stack.get(0)).toEqual(Word.createLiteral('01')) 33 | expect(executor.evm.stack.length()).toEqual(1) 34 | }) 35 | 36 | it('Test EQ Symbolic', () => { 37 | const bytecode = '60203414' 38 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 39 | executor.run(0) 40 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.UNKNOWN)) 41 | expect(executor.evm.stack.length()).toEqual(1) 42 | }) 43 | }) 44 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Eq.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Eq implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | const operand1 = evm.stack.pop() 10 | const operand2 = evm.stack.pop() 11 | if (!operand1 || !operand2) { 12 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 13 | return 14 | } 15 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 16 | if (operand1.value.eq(operand2.value)) { 17 | evm.stack.push(Word.createLiteral('01')) 18 | } else { 19 | evm.stack.push(Word.createLiteral('00')) 20 | } 21 | } else { 22 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Executor.ts: -------------------------------------------------------------------------------- 1 | import { EVM } from '../EVM' 2 | import { Operation } from '../../../bytecode/Operation' 3 | 4 | export interface Executor { 5 | execute(op: Operation, evm: EVM) 6 | } 7 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Exp.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | import { UintUtils } from '../UintUtils' 7 | let BN = require('bn.js') 8 | 9 | export class Exp implements Executor { 10 | execute(op: Operation, evm: EVM) { 11 | const base = evm.stack.pop() 12 | const exp = evm.stack.pop() 13 | if (!base || !exp) { 14 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 15 | return 16 | } 17 | if (!base.isSymbolic && !exp.isSymbolic) { 18 | const baseValue = base.value 19 | const expValue = exp.value 20 | 21 | let result = UintUtils.ONE 22 | if (!expValue.eq(UintUtils.ZERO)) { 23 | const mod = BN.red(UintUtils.TWO_POW_256) 24 | const b = baseValue.toRed(mod) 25 | result = b.redPow(expValue) 26 | } 27 | evm.stack.push(Word.createLiteral(result.toString(16))) 28 | } else { 29 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Extcodecopy.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Extcodecopy', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test extcodecopy', () => { 21 | const bytecode = '60406041604260433c' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.length()).toEqual(0) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Extcodecopy.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | 5 | export class Extcodecopy implements Executor { 6 | execute(op: Operation, evm: EVM) { 7 | evm.stack.pop() 8 | evm.stack.pop() 9 | evm.stack.pop() 10 | evm.stack.pop() 11 | // TODO push symbol to memory when it is supported 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Extcodesize.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Extcodesize', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test extcodesize', () => { 21 | const bytecode = '60403b' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.EXTCODESIZE)) 25 | expect(executor.evm.stack.length()).toEqual(1) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Extcodesize.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Extcodesize implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.pop() 10 | evm.stack.push(Word.createSymbolic(Symbols.EXTCODESIZE)) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Gas.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Gas', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Gas', () => { 21 | const bytecode = '60405a' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.GAS)) 25 | expect(executor.evm.stack.length()).toEqual(2) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Gas.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Gas implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.push(Word.createSymbolic(Symbols.GAS)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Gaslimit.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Gaslimit', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Gaslimit', () => { 21 | const bytecode = '604045' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.GASLIMIT)) 25 | expect(executor.evm.stack.length()).toEqual(2) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Gaslimit.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Gaslimit implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.push(Word.createSymbolic(Symbols.GASLIMIT)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Gasprice.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Gasprice', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test gasprice', () => { 21 | const bytecode = '60403a' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.GASPRICE)) 25 | expect(executor.evm.stack.get(1)).toEqual(Word.createLiteral('40')) 26 | expect(executor.evm.stack.length()).toEqual(2) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Gasprice.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Gasprice implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.push(Word.createSymbolic(Symbols.GASPRICE)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Gt.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Gt implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | const operand1 = evm.stack.pop() 10 | const operand2 = evm.stack.pop() 11 | if (!operand1 || !operand2) { 12 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 13 | return 14 | } 15 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 16 | if (operand1.value.gt(operand2.value)) { 17 | evm.stack.push(Word.createLiteral('01')) 18 | } else { 19 | evm.stack.push(Word.createLiteral('00')) 20 | } 21 | } else { 22 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/IsZero.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | 9 | describe('IsZero', () => { 10 | let cfgCreator: EthereumCFGCreator 11 | let disassembler: Disassembler 12 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 13 | 14 | beforeEach(() => { 15 | cfgCreator = new EthereumCFGCreator() 16 | disassembler = createEVMDisassembler() 17 | }) 18 | 19 | it('Test IsZero false', () => { 20 | const bytecode = '604015' 21 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 22 | executor.run(0) 23 | expect(executor.evm.stack.get(0)).toEqual(Word.createLiteral('00')) 24 | expect(executor.evm.stack.length()).toEqual(1) 25 | }) 26 | 27 | it('Test IsZero true', () => { 28 | const bytecode = '600015' 29 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 30 | executor.run(0) 31 | expect(executor.evm.stack.get(0)).toEqual(Word.createLiteral('01')) 32 | expect(executor.evm.stack.length()).toEqual(1) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/IsZero.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class IsZero implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | const value = evm.stack.pop() 10 | if (!value) { 11 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 12 | return 13 | } 14 | if (!value.isSymbolic) { 15 | if (value.value.isZero()) { 16 | evm.stack.push(Word.createLiteral('01')) 17 | } else { 18 | evm.stack.push(Word.createLiteral('00')) 19 | } 20 | } else { 21 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Jump.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | 9 | describe('Jump', () => { 10 | let cfgCreator: EthereumCFGCreator 11 | let disassembler: Disassembler 12 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 13 | 14 | beforeEach(() => { 15 | cfgCreator = new EthereumCFGCreator() 16 | disassembler = createEVMDisassembler() 17 | }) 18 | 19 | it('Test Jump', () => { 20 | const bytecode = '6001604056' 21 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 22 | executor.run(0) 23 | expect(executor.evm.stack.length()).toEqual(1) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Jump.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | 5 | export class Jump implements Executor { 6 | execute(op: Operation, evm: EVM) { 7 | const jumpLocation = evm.stack.pop() 8 | evm.nextJumpLocation = jumpLocation 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Jumpi.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | 8 | describe('Jumpi', () => { 9 | let cfgCreator: EthereumCFGCreator 10 | let disassembler: Disassembler 11 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 12 | 13 | beforeEach(() => { 14 | cfgCreator = new EthereumCFGCreator() 15 | disassembler = createEVMDisassembler() 16 | }) 17 | 18 | it('Test Jumpi', () => { 19 | const bytecode = '6001604057' 20 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 21 | executor.run(0) 22 | expect(executor.evm.stack.length()).toEqual(0) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Jumpi.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | 5 | export class Jumpi implements Executor { 6 | execute(op: Operation, evm: EVM) { 7 | const jumpLocation = evm.stack.pop() 8 | evm.stack.pop() 9 | evm.nextJumpLocation = jumpLocation 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Log.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | 5 | export class Log implements Executor { 6 | execute(op: Operation, evm: EVM) { 7 | const index = parseInt(op.opcode.name.slice(3)) 8 | const stackToRemove = index + 2 9 | let i = 1 10 | while (i <= stackToRemove) { 11 | evm.stack.pop() 12 | i++ 13 | } 14 | // Don't need to store logs anywhere 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Lt.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Lt implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | const operand1 = evm.stack.pop() 10 | const operand2 = evm.stack.pop() 11 | if (!operand1 || !operand2) { 12 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 13 | return 14 | } 15 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 16 | if (operand1.value.lt(operand2.value)) { 17 | evm.stack.push(Word.createLiteral('01')) 18 | } else { 19 | evm.stack.push(Word.createLiteral('00')) 20 | } 21 | } else { 22 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/MLoad.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | 9 | describe('MLoad', () => { 10 | let cfgCreator: EthereumCFGCreator 11 | let disassembler: Disassembler 12 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 13 | 14 | beforeEach(() => { 15 | cfgCreator = new EthereumCFGCreator() 16 | disassembler = createEVMDisassembler() 17 | }) 18 | 19 | it('Test MLoad from 0', () => { 20 | const bytecode = '6080600052600051' 21 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 22 | executor.run(0) 23 | expect(executor.evm.stack.length()).toEqual(1) 24 | expect(executor.evm.stack.get(0)).toEqual( 25 | Word.createLiteral('0000000000000000000000000000000000000000000000000000000000000080') 26 | ) 27 | }) 28 | 29 | it('Test MLoad from arbitrary offset', () => { 30 | const bytecode = '6080602052602051' 31 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 32 | executor.run(0) 33 | expect(executor.evm.stack.length()).toEqual(1) 34 | expect(executor.evm.stack.get(0)).toEqual( 35 | Word.createLiteral('0000000000000000000000000000000000000000000000000000000000000080') 36 | ) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/MLoad.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Symbols } from '../Symbols' 5 | import { Word } from '../Word' 6 | 7 | export class MLoad implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | const location = evm.stack.pop() 10 | // TODO support symbolic memory 11 | if (!location) { 12 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 13 | return 14 | } 15 | if (!location.isSymbolic) { 16 | if (location.value.bitLength() > 53) { 17 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 18 | return 19 | } 20 | const memoryValue = evm.memory.loadWord(location.value.toNumber()) 21 | evm.stack.push(memoryValue) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/MStore.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | 8 | describe('MStore', () => { 9 | let cfgCreator: EthereumCFGCreator 10 | let disassembler: Disassembler 11 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 12 | 13 | beforeEach(() => { 14 | cfgCreator = new EthereumCFGCreator() 15 | disassembler = createEVMDisassembler() 16 | }) 17 | 18 | it('Test MStore', () => { 19 | const bytecode = '6080604052' 20 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 21 | executor.run(0) 22 | expect(executor.evm.stack.length()).toEqual(0) 23 | expect(executor.evm.memory.memory.toString('hex')).toEqual( 24 | '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000' 25 | ) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/MStore.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | 5 | export class MStore implements Executor { 6 | execute(op: Operation, evm: EVM) { 7 | const location = evm.stack.pop() 8 | const value = evm.stack.pop() 9 | if (!location || !value) { 10 | return 11 | } 12 | if (!location.isSymbolic) { 13 | if (location.value.bitLength() > 53) { 14 | return 15 | } 16 | evm.memory.writeWord(location.value.toNumber(), value) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/MStore8.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | 8 | describe('Mstore8', () => { 9 | let cfgCreator: EthereumCFGCreator 10 | let disassembler: Disassembler 11 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 12 | 13 | beforeEach(() => { 14 | cfgCreator = new EthereumCFGCreator() 15 | disassembler = createEVMDisassembler() 16 | }) 17 | 18 | it('Test Mstore', () => { 19 | const bytecode = '6099604053' 20 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 21 | executor.run(0) 22 | expect(executor.evm.stack.length()).toEqual(0) 23 | expect(executor.evm.memory.memory.toString('hex')).toEqual( 24 | '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' 25 | ) 26 | }) 27 | 28 | it('Test Mstore masking whole word', () => { 29 | const bytecode = '619876604053' 30 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 31 | executor.run(0) 32 | expect(executor.evm.stack.length()).toEqual(0) 33 | expect(executor.evm.memory.memory.toString('hex')).toEqual( 34 | '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' 35 | ) 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Mod.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | import { UintUtils } from '../UintUtils' 7 | 8 | export class Mod implements Executor { 9 | execute(op: Operation, evm: EVM) { 10 | const operand1 = evm.stack.pop() 11 | const operand2 = evm.stack.pop() 12 | if (!operand1 || !operand2) { 13 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 14 | return 15 | } 16 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 17 | if (operand2.value.eq(UintUtils.ZERO)) { 18 | evm.stack.push(Word.createLiteral('00')) 19 | } else { 20 | const result = operand1.value.mod(operand2.value) 21 | evm.stack.push(Word.createLiteral(result.toString(16))) 22 | } 23 | } else { 24 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Msize.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | 9 | describe('Msize', () => { 10 | let cfgCreator: EthereumCFGCreator 11 | let disassembler: Disassembler 12 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 13 | 14 | beforeEach(() => { 15 | cfgCreator = new EthereumCFGCreator() 16 | disassembler = createEVMDisassembler() 17 | }) 18 | 19 | it('Test Msize, empty', () => { 20 | const bytecode = '59' 21 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 22 | executor.run(0) 23 | expect(executor.evm.stack.get(0)).toEqual(Word.createLiteral('00')) 24 | expect(executor.evm.stack.length()).toEqual(1) 25 | }) 26 | 27 | it('Test Msize', () => { 28 | const bytecode = '608060405259' 29 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 30 | executor.run(0) 31 | expect(executor.evm.stack.get(0)).toEqual(Word.createLiteral('60')) 32 | expect(executor.evm.stack.length()).toEqual(1) 33 | }) 34 | 35 | it('Test Msize 2', () => { 36 | const bytecode = '608060405260ff60605359' 37 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 38 | executor.run(0) 39 | expect(executor.evm.stack.get(0)).toEqual(Word.createLiteral('80')) 40 | expect(executor.evm.stack.length()).toEqual(1) 41 | }) 42 | 43 | it('Test Msize 3', () => { 44 | const bytecode = '608060405260ff60605360ee60ff5259' 45 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 46 | executor.run(0) 47 | expect(executor.evm.stack.get(0)).toEqual(Word.createLiteral('120')) 48 | expect(executor.evm.stack.length()).toEqual(1) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Msize.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | 6 | export class Msize implements Executor { 7 | execute(op: Operation, evm: EVM) { 8 | evm.stack.push(Word.createLiteral(evm.memory.wordCount().toString(16))) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Mstore8.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | 5 | export class MStore8 implements Executor { 6 | execute(op: Operation, evm: EVM) { 7 | const location = evm.stack.pop() 8 | const value = evm.stack.pop() 9 | if (!location || !value) { 10 | return 11 | } 12 | if (!location.isSymbolic && !value.isSymbolic) { 13 | evm.memory.writeByte(location.value.toNumber(), value) 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Mul.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | import { UintUtils } from '../UintUtils' 7 | 8 | export class Mul implements Executor { 9 | execute(op: Operation, evm: EVM) { 10 | const operand1 = evm.stack.pop() 11 | const operand2 = evm.stack.pop() 12 | if (!operand1 || !operand2) { 13 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 14 | return 15 | } 16 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 17 | const op1Value = operand1.value 18 | const op2Value = operand2.value 19 | let result = op1Value.mul(op2Value).mod(UintUtils.TWO_POW_256) 20 | evm.stack.push(Word.createLiteral(result.toString(16))) 21 | } else { 22 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Mulmod.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Mulmod', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Mulmod', () => { 21 | const bytecode = '60026003600309' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createLiteral('01')) 25 | expect(executor.evm.stack.length()).toEqual(1) 26 | }) 27 | 28 | it('Test Mulmod 2', () => { 29 | const bytecode = '60006003600309' 30 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 31 | executor.run(0) 32 | expect(executor.evm.stack.get(0)).toEqual(Word.createLiteral('00')) 33 | expect(executor.evm.stack.length()).toEqual(1) 34 | }) 35 | 36 | it('Test Mulmod Symbolic', () => { 37 | const bytecode = '60203409' 38 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 39 | executor.run(0) 40 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.UNKNOWN)) 41 | expect(executor.evm.stack.length()).toEqual(1) 42 | }) 43 | }) 44 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Mulmod.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | import { UintUtils } from '../UintUtils' 7 | 8 | export class Mulmod implements Executor { 9 | execute(op: Operation, evm: EVM) { 10 | const operand1 = evm.stack.pop() 11 | const operand2 = evm.stack.pop() 12 | const mod = evm.stack.pop() 13 | if (!operand1 || !operand2 || !mod) { 14 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 15 | return 16 | } 17 | if (!operand1.isSymbolic && !operand2.isSymbolic && !mod.isSymbolic) { 18 | const op1Value = operand1.value 19 | const op2Value = operand2.value 20 | const modValue = mod.value 21 | if (modValue.eq(UintUtils.ZERO)) { 22 | evm.stack.push(Word.createLiteral('00')) 23 | } else { 24 | let result = op1Value.mul(op2Value).mod(modValue) 25 | evm.stack.push(Word.createLiteral(result.toString(16))) 26 | } 27 | } else { 28 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Nop.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | 5 | export class Nop implements Executor { 6 | execute(op: Operation, evm: EVM) {} 7 | } 8 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Not.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Not implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | const operand1 = evm.stack.pop() 10 | if (!operand1) { 11 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 12 | return 13 | } 14 | if (!operand1.isSymbolic) { 15 | const op1Value = operand1.value 16 | let result = op1Value.notn(256) 17 | evm.stack.push(Word.createLiteral(result.toString(16))) 18 | } else { 19 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Number.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Number', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Number', () => { 21 | const bytecode = '604043' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.NUMBER)) 25 | expect(executor.evm.stack.length()).toEqual(2) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Number.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Number implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.push(Word.createSymbolic(Symbols.NUMBER)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Or.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Or implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | const operand1 = evm.stack.pop() 10 | const operand2 = evm.stack.pop() 11 | if (!operand1 || !operand2) { 12 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 13 | return 14 | } 15 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 16 | const op1Value = operand1.value 17 | const op2Value = operand2.value 18 | let result = op1Value.or(op2Value) 19 | evm.stack.push(Word.createLiteral(result.toString(16))) 20 | } else { 21 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Origin.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Origin', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test origin', () => { 21 | const bytecode = '604032' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.ORIGIN)) 25 | expect(executor.evm.stack.length()).toEqual(2) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Origin.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Origin implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.push(Word.createSymbolic(Symbols.ORIGIN)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Pc.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Pc', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Pc', () => { 21 | const bytecode = '58' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createLiteral('00')) 25 | expect(executor.evm.stack.length()).toEqual(1) 26 | }) 27 | 28 | it('Test Pc higher offset', () => { 29 | const bytecode = '604061112258' 30 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 31 | executor.run(0) 32 | expect(executor.evm.stack.get(0)).toEqual(Word.createLiteral('05')) 33 | expect(executor.evm.stack.length()).toEqual(3) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Pc.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | 6 | export class Pc implements Executor { 7 | execute(op: Operation, evm: EVM) { 8 | evm.stack.push(Word.createLiteral(op.offset.toString(16))) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Pop.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | 9 | describe('Pop', () => { 10 | let cfgCreator: EthereumCFGCreator 11 | let disassembler: Disassembler 12 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 13 | 14 | beforeEach(() => { 15 | cfgCreator = new EthereumCFGCreator() 16 | disassembler = createEVMDisassembler() 17 | }) 18 | 19 | it('Test POP', () => { 20 | const bytecode = '604050' 21 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 22 | executor.run(0) 23 | expect(executor.evm.stack.length()).toEqual(0) 24 | }) 25 | 26 | it('Test PUSH & POP', () => { 27 | const bytecode = '6040608050' 28 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 29 | executor.run(0) 30 | const expectedWord1: Word = Word.createLiteral('40') 31 | expect(executor.evm.stack.stack).toContainEqual(expectedWord1) 32 | expect(executor.evm.stack.length()).toEqual(1) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Pop.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | 5 | export class Pop implements Executor { 6 | execute(op: Operation, evm: EVM) { 7 | evm.stack.pop() 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Push.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata' 2 | 3 | import { Executor } from './Executor' 4 | import { EVM } from '../EVM' 5 | import { Word } from '../Word' 6 | import { Operation } from '../../../bytecode/Operation' 7 | 8 | export class Push implements Executor { 9 | execute(op: Operation, evm: EVM) { 10 | const word: Word = { 11 | isSymbolic: false, 12 | value: op.argument 13 | } 14 | evm.stack.push(word) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Return.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | 5 | export class Return implements Executor { 6 | execute(op: Operation, evm: EVM) { 7 | evm.stack.pop() 8 | evm.stack.pop() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Returndatacopy.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Returndatacopy', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Returndatacopy', () => { 21 | const bytecode = '6010602060303e' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.RETURNDATACOPY)) 25 | expect(executor.evm.stack.length()).toEqual(1) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Returndatacopy.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Returndatacopy implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.pop() 10 | evm.stack.pop() 11 | evm.stack.pop() 12 | evm.stack.push(Word.createSymbolic(Symbols.RETURNDATACOPY)) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Returndatasize.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Returndatasize', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Returndatasize', () => { 21 | const bytecode = '60403d' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.RETURNDATASIZE)) 25 | expect(executor.evm.stack.length()).toEqual(2) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Returndatasize.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Returndatasize implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.push(Word.createSymbolic(Symbols.RETURNDATASIZE)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Sar.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | import { UintUtils } from '../UintUtils'; 7 | 8 | export class Sar implements Executor { 9 | execute(op: Operation, evm: EVM) { 10 | const operand1 = evm.stack.pop() 11 | const operand2 = evm.stack.pop() 12 | if (!operand1 || !operand2) { 13 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 14 | return 15 | } 16 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 17 | const op1Value = operand1.value 18 | const op2Value = operand2.value 19 | 20 | let result 21 | const isSigned = op2Value.testn(255) 22 | if(op1Value.gten(256)) { 23 | if (isSigned) { 24 | result = UintUtils.MAX_INTEGER 25 | } else { 26 | result = UintUtils.ZERO 27 | } 28 | evm.stack.push(Word.createLiteral(result.toString(16))) 29 | return 30 | } 31 | 32 | const temp = op2Value.shrn(op1Value.toNumber()) 33 | if (isSigned) { 34 | const shifted = 255 - op1Value.toNumber() 35 | const mask = UintUtils.MAX_INTEGER.shrn(shifted).shln(shifted) 36 | result = temp.ior(mask) 37 | } else { 38 | result = temp 39 | } 40 | 41 | evm.stack.push(Word.createLiteral(result.toString(16))) 42 | } else { 43 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Sdiv.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | import { UintUtils } from '../UintUtils' 7 | 8 | export class Sdiv implements Executor { 9 | execute(op: Operation, evm: EVM) { 10 | const operand1 = evm.stack.pop() 11 | const operand2 = evm.stack.pop() 12 | if (!operand1 || !operand2) { 13 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 14 | return 15 | } 16 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 17 | if (operand2.value.eq(UintUtils.ZERO)) { 18 | evm.stack.push(Word.createLiteral('00')) 19 | } else { 20 | const a = operand1.value.fromTwos(256) 21 | const b = operand2.value.fromTwos(256) 22 | const result = a.div(b).toTwos(256) 23 | evm.stack.push(Word.createLiteral(result.toString(16))) 24 | } 25 | } else { 26 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Selfdestruct.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Selfdestruct', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Selfdestruct', () => { 21 | const bytecode = '60106020ff' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createLiteral('10')) 25 | expect(executor.evm.stack.length()).toEqual(1) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Selfdestruct.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | 5 | export class Selfdestruct implements Executor { 6 | execute(op: Operation, evm: EVM) { 7 | evm.stack.pop() 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Sgt.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Sgt implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | const operand1 = evm.stack.pop() 10 | const operand2 = evm.stack.pop() 11 | if (!operand1 || !operand2) { 12 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 13 | return 14 | } 15 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 16 | if (operand1.value.fromTwos(256).gt(operand2.value.fromTwos(256))) { 17 | evm.stack.push(Word.createLiteral('01')) 18 | } else { 19 | evm.stack.push(Word.createLiteral('00')) 20 | } 21 | } else { 22 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Sha3.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | let BN = require('bn.js') 7 | const utils = require('ethereumjs-util') 8 | 9 | export class Sha3 implements Executor { 10 | execute(op: Operation, evm: EVM) { 11 | const operand1 = evm.stack.pop() 12 | const operand2 = evm.stack.pop() 13 | if (!operand1 || !operand2) { 14 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 15 | return 16 | } 17 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 18 | const offset = operand1.value 19 | const length = operand2.value 20 | if (offset.bitLength() > 53 || length.bitLength() > 53) { 21 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 22 | return 23 | } 24 | const memoryContent = evm.memory.load(offset.toNumber(), length.toNumber()) 25 | const result = new BN(utils.keccak256(memoryContent)) 26 | evm.stack.push(Word.createLiteral(result.toString(16))) 27 | } else { 28 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Shl.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | import { UintUtils } from '../UintUtils'; 7 | 8 | export class Shl implements Executor { 9 | execute(op: Operation, evm: EVM) { 10 | const operand1 = evm.stack.pop() 11 | const operand2 = evm.stack.pop() 12 | if (!operand1 || !operand2) { 13 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 14 | return 15 | } 16 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 17 | const op1Value = operand1.value 18 | const op2Value = operand2.value 19 | if(op1Value.gten(256)) { 20 | evm.stack.push(Word.createLiteral('00')) 21 | return 22 | } 23 | 24 | let result = op2Value.shln(op1Value.toNumber()).iand(UintUtils.MAX_INTEGER) 25 | evm.stack.push(Word.createLiteral(result.toString(16))) 26 | } else { 27 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Shr.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Shr implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | const operand1 = evm.stack.pop() 10 | const operand2 = evm.stack.pop() 11 | if (!operand1 || !operand2) { 12 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 13 | return 14 | } 15 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 16 | const op1Value = operand1.value 17 | const op2Value = operand2.value 18 | if(op1Value.gten(256)) { 19 | evm.stack.push(Word.createLiteral('00')) 20 | return 21 | } 22 | 23 | let result = op2Value.shrn(op1Value.toNumber()) 24 | evm.stack.push(Word.createLiteral(result.toString(16))) 25 | } else { 26 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Signextend.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | let BN = require('bn.js') 7 | 8 | export class Signextend implements Executor { 9 | execute(op: Operation, evm: EVM) { 10 | const k = evm.stack.pop() 11 | const val = evm.stack.pop() 12 | if (!k || !val) { 13 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 14 | return 15 | } 16 | if (!k.isSymbolic && !val.isSymbolic) { 17 | const valArray = val.value.toArrayLike(Buffer, 'be', 32) 18 | let kValue = k.value 19 | let extend = false 20 | 21 | if (kValue.lten(31)) { 22 | kValue = kValue.toNumber() 23 | if (valArray[31 - kValue] & 0x80) { 24 | extend = true 25 | } 26 | 27 | for (let i = 30 - kValue; i >= 0; i--) { 28 | valArray[i] = extend ? 0xff : 0 29 | } 30 | } 31 | evm.stack.push(Word.createLiteral(new BN(valArray).toString(16))) 32 | } else { 33 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Sload.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Sload', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Sload', () => { 21 | const bytecode = '6020600155600154' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.length()).toEqual(1) 25 | expect(executor.evm.stack.get(0)).toEqual(Word.createLiteral('20')) 26 | }) 27 | 28 | it('Test Sload symbolic', () => { 29 | const bytecode = '34600155600154' 30 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 31 | executor.run(0) 32 | expect(executor.evm.stack.length()).toEqual(1) 33 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.CALLVALUE)) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Sload.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Sload implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | const slot = evm.stack.pop() 10 | if (!slot) { 11 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 12 | return 13 | } 14 | let value = evm.storage.load(slot) 15 | if (!value) { 16 | // This only has sense during CFG creation 17 | value = Word.createSymbolic(Symbols.UNKNOWN) 18 | } 19 | evm.stack.push(value) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Slt.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Slt implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | const operand1 = evm.stack.pop() 10 | const operand2 = evm.stack.pop() 11 | if (!operand1 || !operand2) { 12 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 13 | return 14 | } 15 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 16 | if (operand1.value.fromTwos(256).lt(operand2.value.fromTwos(256))) { 17 | evm.stack.push(Word.createLiteral('01')) 18 | } else { 19 | evm.stack.push(Word.createLiteral('00')) 20 | } 21 | } else { 22 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Smod.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | import { UintUtils } from '../UintUtils' 7 | 8 | export class Smod implements Executor { 9 | execute(op: Operation, evm: EVM) { 10 | const operand1 = evm.stack.pop() 11 | const operand2 = evm.stack.pop() 12 | if (!operand1 || !operand2) { 13 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 14 | return 15 | } 16 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 17 | if (operand2.value.eq(UintUtils.ZERO)) { 18 | evm.stack.push(Word.createLiteral('00')) 19 | } else { 20 | const a = operand1.value.fromTwos(256) 21 | const b = operand2.value.fromTwos(256) 22 | let result = a.abs().mod(b.abs()) 23 | if (a.isNeg()) { 24 | result = result.ineg() 25 | } 26 | evm.stack.push(Word.createLiteral(result.toTwos(256).toString(16))) 27 | } 28 | } else { 29 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Sstore.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | 5 | export class Sstore implements Executor { 6 | execute(op: Operation, evm: EVM) { 7 | const slot = evm.stack.pop() 8 | const value = evm.stack.pop() 9 | evm.storage.store(slot, value) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Staticcall.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | 10 | describe('Staticcall', () => { 11 | let cfgCreator: EthereumCFGCreator 12 | let disassembler: Disassembler 13 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 14 | 15 | beforeEach(() => { 16 | cfgCreator = new EthereumCFGCreator() 17 | disassembler = createEVMDisassembler() 18 | }) 19 | 20 | it('Test Staticcall', () => { 21 | const bytecode = '601060206030604060506060fa' 22 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 23 | executor.run(0) 24 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.STATICCALL)) 25 | expect(executor.evm.stack.length()).toEqual(1) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Staticcall.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Staticcall implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.pop() 10 | evm.stack.pop() 11 | evm.stack.pop() 12 | evm.stack.pop() 13 | evm.stack.pop() 14 | evm.stack.pop() 15 | evm.stack.push(Word.createSymbolic(Symbols.STATICCALL)) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Sub.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Sub implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | const operand1 = evm.stack.pop() 10 | const operand2 = evm.stack.pop() 11 | if (!operand1 || !operand2) { 12 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 13 | return 14 | } 15 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 16 | const op1Value = operand1.value 17 | const op2Value = operand2.value 18 | let result = op1Value.sub(op2Value).toTwos(256) 19 | evm.stack.push(Word.createLiteral(result.toString(16))) 20 | } else { 21 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Swap.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | 5 | export class Swap implements Executor { 6 | execute(op: Operation, evm: EVM) { 7 | const index = parseInt(op.opcode.name.slice(4)) 8 | const stack0 = evm.stack.get(0) 9 | const stackIndex = evm.stack.get(index) 10 | evm.stack.put(0, stackIndex) 11 | evm.stack.put(index, stack0) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/TestUtils.ts: -------------------------------------------------------------------------------- 1 | import { Disassembler } from '../../../bytecode/Disassembler' 2 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 3 | import { OpcodeExecutor } from './OpcodeExecutor' 4 | import { Operation } from '../../../bytecode/Operation' 5 | import { CFGBlocks } from '../../../cfg/CFGBlocks' 6 | import { EVMExecutor } from '../EVMExecutor' 7 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler'; 8 | import { ContractService } from '../../../service/service/ContractService'; 9 | import { Solc } from '../../../service/service/Solc'; 10 | 11 | export function createExecutor( 12 | disassembler: Disassembler, 13 | bytecode: string, 14 | cfgCreator: EthereumCFGCreator, 15 | opcodeExecutor: OpcodeExecutor 16 | ) { 17 | const ops: Operation[] = disassembler.disassembleBytecode(bytecode) 18 | const blocks: CFGBlocks = cfgCreator.divideBlocks(ops) 19 | return new EVMExecutor(blocks, opcodeExecutor) 20 | } 21 | 22 | export function createEVMDisassembler(): EVMDisassembler { 23 | return new EVMDisassembler(new ContractService(new Solc()), new Solc()) 24 | } 25 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Timestamp.test.ts: -------------------------------------------------------------------------------- 1 | import { createExecutor, createEVMDisassembler } from './TestUtils' 2 | import { EVMExecutor } from '../EVMExecutor' 3 | import { EthereumCFGCreator } from '../../../cfg/EthereumCFGCreator' 4 | import { Disassembler } from '../../../bytecode/Disassembler' 5 | import { OpcodeExecutor } from './OpcodeExecutor' 6 | import { EVMDisassembler } from '../../../bytecode/EVMDisassembler' 7 | import { Word } from '../Word' 8 | import { Symbols } from '../Symbols' 9 | import { ContractService } from '../../../service/service/ContractService'; 10 | 11 | describe('Timestamp', () => { 12 | let cfgCreator: EthereumCFGCreator 13 | let disassembler: Disassembler 14 | let opcodeExecutor: OpcodeExecutor = new OpcodeExecutor() 15 | 16 | beforeEach(() => { 17 | cfgCreator = new EthereumCFGCreator() 18 | disassembler = createEVMDisassembler() 19 | }) 20 | 21 | it('Test Timestamp', () => { 22 | const bytecode = '604042' 23 | const executor: EVMExecutor = createExecutor(disassembler, bytecode, cfgCreator, opcodeExecutor) 24 | executor.run(0) 25 | expect(executor.evm.stack.get(0)).toEqual(Word.createSymbolic(Symbols.TIMESTAMP)) 26 | expect(executor.evm.stack.length()).toEqual(2) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Timestamp.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Timestamp implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | evm.stack.push(Word.createSymbolic(Symbols.TIMESTAMP)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/api/symbolic/evm/exec/Xor.ts: -------------------------------------------------------------------------------- 1 | import { Executor } from './Executor' 2 | import { EVM } from '../EVM' 3 | import { Operation } from '../../../bytecode/Operation' 4 | import { Word } from '../Word' 5 | import { Symbols } from '../Symbols' 6 | 7 | export class Xor implements Executor { 8 | execute(op: Operation, evm: EVM) { 9 | const operand1 = evm.stack.pop() 10 | const operand2 = evm.stack.pop() 11 | if (!operand1 || !operand2) { 12 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 13 | return 14 | } 15 | if (!operand1.isSymbolic && !operand2.isSymbolic) { 16 | const op1Value = operand1.value 17 | const op2Value = operand2.value 18 | let result = op1Value.xor(op2Value) 19 | evm.stack.push(Word.createLiteral(result.toString(16))) 20 | } else { 21 | evm.stack.push(Word.createSymbolic(Symbols.UNKNOWN)) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/client/_redux/Constants.js: -------------------------------------------------------------------------------- 1 | export const SELECT_EDITOR_LINES = 'SELECT_EDITOR_LINES'; 2 | export const SHOW_EVM_STATE = 'SHOW_EVM_STATE'; 3 | export const HIDE_EVM_STATE = 'HIDE_EVM_STATE'; 4 | export const SHOW_LOADING_MESSAGE = 'SHOW_LOADING_MESSAGE'; 5 | export const HIDE_LOADING_MESSAGE = 'HIDE_LOADING_MESSAGE'; 6 | export const SHOW_ERROR_MESSAGE = 'SHOW_ERROR_MESSAGE'; 7 | export const HIDE_ERROR_MESSAGE = 'HIDE_ERROR_MESSAGE'; 8 | export const GET_ERROR_MESSAGE = 'GET_ERROR_MESSAGE'; 9 | export const GET_HOST = 'GET_HOST'; 10 | export const GET_PROTOCOL = 'GET_PROTOCOL'; 11 | export const GET_USERNAME = 'GET_USERNAME'; 12 | export const GET_PASSWORD = 'GET_PASSWORD'; 13 | export const GET_VERSION_NUM = 'GET_VERSION_NUM'; 14 | export const ADD_VERSION = 'ADD_VERSION'; 15 | export const VERSIONS_FETCHED = 'VERSIONS_FETCHED'; 16 | export const TOGGLE_ERROR_MESSAGE = 'TOGGLE_ERROR_MESSAGE'; 17 | export const TOGGLE_LOADING_MESSAGE = 'TOGGLE_LOADING_MESSAGE'; 18 | export const TOGGLE_EVM_STATE = 'TOGGLE_EVM_STATE'; 19 | export const GET_PARAMETER = 'GET_PARAMETER'; 20 | export const FETCH_VERSIONS_SUCCESS = 'FETCH_VERSIONS_SUCCESS'; 21 | export const FETCH_CONTRACTS_SUCCESS = 'FETCH_CONTRACTS_SUCCESS'; 22 | export const FETCH_CONTRACTS = 'FETCH_CONTRACTS'; 23 | export const FETCH_SOLC_VERSIONS = 'FETCH_SOLC_VERSIONS'; 24 | export const POST_VERSION = 'POST_VERSION'; 25 | export const FETCH_TRANSACTION_DEBUGGER = 'FETCH_TRANSACTION_DEBUGGER'; 26 | export const FETCH_STORAGE = 'FETCH_STORAGE'; 27 | export const FETCH_GRAPH = 'FETCH_GRAPH'; 28 | export const FETCH_DISASSEMBLER = 'FETCH_DISASSEMBLER'; 29 | export const DEBUGGER_FETCH_SUCCESS = 'DEBUGGER_FETCH_SUCCESS'; 30 | export const STORAGE_FETCH_SUCCESS = 'STORAGE_FETCH_SUCCESS'; 31 | export const GRAPH_FETCH_SUCCESS = 'GRAPH_FETCH_SUCCESS'; 32 | export const DISASSEMBLER_FETCH_SUCCESS = 'DISASSEMBLER_FETCH_SUCCESS'; 33 | -------------------------------------------------------------------------------- /src/client/_redux/Reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | import { selectLines, selectEVMState, loadingMessage, errorMessage, displayVersionNumber, versions, contracts, tools } from './reducers'; 4 | 5 | export default combineReducers({ 6 | selectLines, 7 | selectEVMState, 8 | loadingMessage, 9 | errorMessage, 10 | displayVersionNumber, 11 | versions, 12 | contracts, 13 | tools 14 | }); -------------------------------------------------------------------------------- /src/client/_redux/Store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux'; 2 | import createSagaMiddleware from 'redux-saga'; 3 | import { composeWithDevTools } from 'redux-devtools-extension'; 4 | import thunk from 'redux-thunk'; 5 | import logger from 'redux-logger'; 6 | 7 | import rootReducer from './reducers'; 8 | import rootSaga from './sagas/sagas'; 9 | 10 | const sagaMiddleware = createSagaMiddleware(); 11 | 12 | const store = createStore( 13 | rootReducer, 14 | composeWithDevTools(applyMiddleware(sagaMiddleware, thunk))); 15 | 16 | sagaMiddleware.run(rootSaga); 17 | 18 | export default store; 19 | 20 | -------------------------------------------------------------------------------- /src/client/_redux/sagas/contractsSaga.js: -------------------------------------------------------------------------------- 1 | import * as selectors from '../selectors'; 2 | 3 | import { select, put, takeEvery, call } from 'redux-saga/effects'; 4 | import { baseUrl, fetchData } from './utils'; 5 | 6 | export function* contractsWatcher() { 7 | yield takeEvery('FETCH_CONTRACTS', fetchContracts) 8 | } 9 | 10 | export function* fetchContracts() { 11 | const parameter = yield select(selectors.getParameter); 12 | const endpoint = `${baseUrl}files/${encodeURIComponent(parameter) || ' '}?extension=sol`; 13 | try { 14 | yield put({ type: 'TOGGLE_LOADING_MESSAGE', payload: { isLoadingMessageOn: true, message: 'Loading...' } }) 15 | const contracts = yield call(fetchData, endpoint); 16 | yield put({ type: 'FETCH_CONTRACTS_SUCCESS', payload: { contracts: contracts } }); 17 | yield put({ type: 'TOGGLE_LOADING_MESSAGE', payload: { isLoadingMessageOn: false } }) 18 | } 19 | catch(error) { 20 | yield put({ type: 'TOGGLE_ERROR_MESSAGE', payload: { isErrorMessageOn: true, message: error.message } }); 21 | yield put({ type: 'TOGGLE_LOADING_MESSAGE', payload: { isLoadingMessageOn: false } }); 22 | } 23 | } -------------------------------------------------------------------------------- /src/client/_redux/sagas/sagas.js: -------------------------------------------------------------------------------- 1 | import { all } from 'redux-saga/effects'; 2 | 3 | import { contractsWatcher } from './contractsSaga'; 4 | import { versionsWatcher } from './versionsSaga'; 5 | import { toolsWatcher } from './toolsSaga'; 6 | 7 | export default function* rootSaga() { 8 | yield all([ 9 | versionsWatcher(), 10 | contractsWatcher(), 11 | toolsWatcher() 12 | ]) 13 | } -------------------------------------------------------------------------------- /src/client/_redux/sagas/toolsSaga.test.js: -------------------------------------------------------------------------------- 1 | import { call, put } from 'redux-saga/effects'; 2 | import { baseUrl, fetchData, postData } from './utils'; 3 | 4 | import { fetchTransactionDebugger, fetchStorage, fetchGraph, fetchDisassembler } from './toolsSaga'; 5 | 6 | describe.skip('should fetch data with the right parameters', () => { 7 | const debuggerGen = fetchTransactionDebugger(); 8 | const storageGen = fetchStorage(); 9 | const graphGen = fetchGraph(); 10 | const disassemblerGen = fetchDisassembler(); 11 | 12 | const headers = { 13 | method: 'POST', 14 | body: JSON.stringify({ request: body }), 15 | headers:{ 16 | 'Content-Type': 'application/json' 17 | } 18 | } 19 | 20 | it('should fetch transaction debugger', () => { 21 | const response = call(postData, url, ) 22 | }); 23 | 24 | it('should fetch disassembler', () => { 25 | 26 | }); 27 | 28 | it('should fetch control flowg raph', () => { 29 | 30 | }); 31 | 32 | it('should fetch storagr', () => { 33 | 34 | }); 35 | }) -------------------------------------------------------------------------------- /src/client/_redux/sagas/utils.js: -------------------------------------------------------------------------------- 1 | export const baseUrl = 'http://localhost:9090/'; 2 | 3 | export const fetchData = (endpoint) => fetch(endpoint).then(res => res.json()) 4 | 5 | export const postData = (endpoint, headers) => fetch(endpoint, headers).then(response => { 6 | if (response.ok) { 7 | return response; 8 | } else { 9 | var error = new Error('Error ' + response.status + ': ' + response.statusText); 10 | 11 | error.response = response; 12 | throw error; 13 | } 14 | }, 15 | error => { 16 | var errmess = new Error(error.message); 17 | throw errmess; 18 | }) 19 | .then(response => response.json()) -------------------------------------------------------------------------------- /src/client/_redux/sagas/versionsSaga.js: -------------------------------------------------------------------------------- 1 | import { put, takeEvery, takeLatest, call } from 'redux-saga/effects'; 2 | import { baseUrl, fetchData, postData } from './utils'; 3 | 4 | export function* versionsWatcher() { 5 | yield takeEvery('FETCH_SOLC_VERSIONS', fetchSolcVersions); 6 | yield takeLatest('POST_VERSION', postVersion); 7 | } 8 | 9 | export function* fetchSolcVersions() { 10 | const endpoint = `${baseUrl}solc/list`; 11 | try { 12 | yield put({ type: 'TOGGLE_LOADING_MESSAGE', payload: { isLoadingMessageOn: true, message: 'Loading...' } }) 13 | const versions = yield call(fetchData, endpoint); 14 | yield put({ type: 'FETCH_VERSIONS_SUCCESS', payload: { versions: versions } }); 15 | yield put({ type: 'TOGGLE_LOADING_MESSAGE', payload: { isLoadingMessageOn: false } }) 16 | } 17 | catch(error) { 18 | yield put({ type: 'TOGGLE_ERROR_MESSAGE', payload: { isErrorMessageOn: true, message: error.message } }); 19 | yield put({ type: 'TOGGLE_LOADING_MESSAGE', payload: { isLoadingMessageOn: false } }); 20 | } 21 | } 22 | 23 | export function* postVersion(action) { 24 | const endpoint = `${baseUrl}solc`; 25 | 26 | try { 27 | yield put({ type: 'TOGGLE_LOADING_MESSAGE', payload: { isLoadingMessageOn: true, message: 'Loading... This might take a while' } }) 28 | const headers = { 29 | method: 'POST', 30 | body: JSON.stringify(action.payload.version), 31 | headers: { 32 | 'Content-Type': 'application/json' 33 | }, 34 | credentials: 'same-origin' 35 | } 36 | 37 | const version = yield call(postData, endpoint, headers); 38 | yield put({ type: 'ADD_VERSION', payload: { version: version } }); 39 | yield put({ type: 'TOGGLE_LOADING_MESSAGE', payload: { isLoadingMessageOn: false } }) 40 | } catch(error) { 41 | yield put({ type: 'TOGGLE_ERROR_MESSAGE', payload: { isErrorMessageOn: true, message: error.message } }); 42 | yield put({ type: 'TOGGLE_LOADING_MESSAGE', payload: { isLoadingMessageOn: false } }); 43 | } 44 | } -------------------------------------------------------------------------------- /src/client/_redux/selectors.js: -------------------------------------------------------------------------------- 1 | export const getParameter = state => { 2 | return state.contracts.parameter; 3 | } 4 | 5 | export const getVersions = state => { 6 | return state.versions.versionsList; 7 | } 8 | 9 | export const getPostedVersions = state => { 10 | return state.versions.postedVersions 11 | } 12 | 13 | export const getContracts = state => { 14 | return state.contracts.contracts 15 | } 16 | 17 | export const getTransactionDebugger = state => { 18 | return state.tools.transactionDebugger; 19 | } 20 | 21 | export const getDisassembler = state => { 22 | return state.tools.disassembler; 23 | } 24 | 25 | export const getGraph = state => { 26 | return state.tools.graph; 27 | } 28 | 29 | export const getStorage = state => { 30 | return state.tools.storage; 31 | } 32 | 33 | export const getTabs = state => { 34 | return state.tools.tabs 35 | } 36 | 37 | export const getHasToolFetched = state => { 38 | return state.tools.hasFetched 39 | } 40 | 41 | export const getToolIsLoading = state => { 42 | return state.tools.isLoading 43 | } -------------------------------------------------------------------------------- /src/client/components/Accordion/Accordion.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './Accordion.scss'; 4 | 5 | const Accordion = ({ children }) => ( 6 |
7 | {children} 8 |
9 | ) 10 | 11 | Accordion.displayName = 'Accordion'; 12 | 13 | export default Accordion; -------------------------------------------------------------------------------- /src/client/components/Accordion/Accordion.scss: -------------------------------------------------------------------------------- 1 | .accordion { 2 | min-height: 100%; 3 | padding: 30px; 4 | text-align: center; 5 | } -------------------------------------------------------------------------------- /src/client/components/Accordion/AccordionSection/AccordionSection.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import classnames from 'classnames/bind'; 4 | 5 | import styles from './AccordionSection.scss'; 6 | 7 | const cx = classnames.bind(styles); 8 | 9 | class AccordionSection extends React.Component { 10 | constructor(props) { 11 | super(props); 12 | 13 | this.state = { 14 | expanded: false, 15 | } 16 | } 17 | 18 | handleClick() { 19 | this.setState(prevState => ({ 20 | expanded: !prevState.expanded, 21 | })) 22 | } 23 | 24 | render() { 25 | 26 | const { expanded } = this.state; 27 | const { title, content, children } = this.props; 28 | 29 | const titleClasses = cx({ 30 | 'accordion-section__title': true, 31 | 'accordion-section__title--expanded': !!expanded, 32 | }); 33 | 34 | const contentClasses = cx({ 35 | 'accordion-section__content': true, 36 | 'accordion-section__content--expanded': !!expanded, 37 | }) 38 | 39 | return ( 40 |
41 |
this.handleClick()}> 42 | {title} 43 |
44 |
45 |
46 | {children} 47 |
48 |
49 |
50 | ); 51 | } 52 | } 53 | 54 | AccordionSection.displayName = 'AccordionSection'; 55 | 56 | export default AccordionSection; -------------------------------------------------------------------------------- /src/client/components/Accordion/AccordionSection/AccordionSection.scss: -------------------------------------------------------------------------------- 1 | $parent: '.accordion-section__content'; 2 | 3 | .accordion-section { 4 | margin: 0 auto; 5 | width: 100%; 6 | 7 | &__title { 8 | padding: 30px 30px; 9 | cursor: pointer; 10 | transform: translate3d(0, 0, 0); 11 | color: $color-green; 12 | position: relative; 13 | font-size: 20px; 14 | background: $color-grey; 15 | margin-bottom: -1px; 16 | border-bottom: 1px solid $color-green; 17 | text-align: left; 18 | text-transform: uppercase; 19 | transition: color 0.3s, background 0.3s; 20 | 21 | &::after { 22 | content: "+"; 23 | font-size: 18px; 24 | color: $color-green; 25 | transition: transform .5s ease-in-out, color 0.5s; 26 | position: absolute; 27 | right: 30px; 28 | font-family: monospace; 29 | } 30 | 31 | &--expanded { 32 | transition: background .5s; 33 | background: $color-green; 34 | color: $color-grey; 35 | 36 | &::after { 37 | content: "-"; 38 | transform: rotate(-360deg); 39 | color: $color-grey; 40 | } 41 | } 42 | } 43 | 44 | &__content { 45 | overflow: hidden; 46 | max-height: 0; 47 | transition: max-height .5s; 48 | margin: 0; 49 | padding: 0 30px; 50 | border: solid 1px $color-grey; 51 | border-top: 0; 52 | background: $color-grey; 53 | 54 | &__item { 55 | padding: 30px 0; 56 | margin: 0; 57 | opacity: 0; 58 | transition: opacity .5s; 59 | } 60 | 61 | &--expanded { 62 | max-height: 500px; 63 | overflow: auto; 64 | 65 | #{$parent}__item { 66 | opacity: 1; 67 | } 68 | } 69 | } 70 | 71 | &:after { 72 | width: 100%; 73 | height: 10px; 74 | display: block; 75 | background: $color-green; 76 | content: ''; 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /src/client/components/ControlFlowGraph/ControlFlowGraph.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Graph from '../Graph/Graph'; 4 | 5 | import styles from './ControlFlowGraph.scss'; 6 | 7 | const ControlFlowGraphRuntime = ({ contractName, contractPath, graphResponse, type }) => { 8 | return ( 9 |
10 | 17 |
18 | ); 19 | } 20 | 21 | ControlFlowGraphRuntime.displayName = 'ControlFlowGraphRuntime'; 22 | 23 | export default ControlFlowGraphRuntime; 24 | -------------------------------------------------------------------------------- /src/client/components/ControlFlowGraph/ControlFlowGraph.scss: -------------------------------------------------------------------------------- 1 | .control-flow-graph { 2 | position: relative; 3 | 4 | &__body { 5 | width: 100%; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/client/components/Disassembler/Bytecode/Bytecode.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './Bytecode.scss'; 4 | 5 | const Bytecode = ({ bytecode }) => ( 6 |
7 |

{bytecode}

8 |
9 | ) 10 | 11 | Bytecode.displayName = 'ByteCode'; 12 | 13 | export default Bytecode; -------------------------------------------------------------------------------- /src/client/components/Disassembler/Bytecode/Bytecode.scss: -------------------------------------------------------------------------------- 1 | .bytecode { 2 | 3 | p { 4 | word-break: break-all; 5 | } 6 | } -------------------------------------------------------------------------------- /src/client/components/Disassembler/Disassembler.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Accordion from '../Accordion/Accordion'; 4 | import AccordionSection from '../Accordion/AccordionSection/AccordionSection'; 5 | import Operations from './Operations/Operations'; 6 | import Bytecode from './Bytecode/Bytecode'; 7 | 8 | import styles from './Disassembler.scss'; 9 | 10 | const Disassembler = ({ disassemblerResponse }) => { 11 | 12 | return ( 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | ); 27 | 28 | } 29 | 30 | Disassembler.displayName = 'Disassembler'; 31 | 32 | export default Disassembler; 33 | -------------------------------------------------------------------------------- /src/client/components/Disassembler/Disassembler.scss: -------------------------------------------------------------------------------- 1 | .disassembler { 2 | position: relative; 3 | color: #fff; 4 | 5 | &__body { 6 | width: 100%; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/client/components/Disassembler/Operations/Operations.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './Operations.scss' 4 | 5 | const Operations = ({ items }) => { 6 | return ( 7 |
8 |
9 |
10 | [Offset] 11 |
12 |
13 | [Opcode hex] 14 |
15 |
16 | [Opcode] 17 |
18 |
19 | [Argument] 20 |
21 |
22 | {items.map((item, i) => { 23 | return ( 24 |
25 |
26 | {`0x`}{item.offset.toString(16)} 27 |
28 |
29 | {`0x`}{item.opcode.opcode.toString(16)} 30 |
31 |
32 | {item.opcode.name} 33 |
34 | { item.opcode.parameters > 0 && 35 |
36 | {`0x`}{item.argument} 37 |
38 | } 39 |
40 | ) 41 | })} 42 |
43 | ) 44 | } 45 | 46 | Operations.displayName = 'Operations'; 47 | 48 | export default Operations; -------------------------------------------------------------------------------- /src/client/components/Disassembler/Operations/Operations.scss: -------------------------------------------------------------------------------- 1 | .operations { 2 | display: flex; 3 | justify-content: flex-start; 4 | 5 | &__item { 6 | flex: 0 0 170px; 7 | text-align: left; 8 | 9 | span { 10 | word-break: break-all; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/client/components/Dropdown/Dropdown.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classnames from 'classnames/bind'; 3 | 4 | import styles from './Dropdown.scss'; 5 | 6 | const cx = classnames.bind(styles); 7 | 8 | const Dropdown = ({ active, children, versions, settings, onClick }) => { 9 | 10 | const dropdownClasses = cx({ 11 | 'dropdown': true, 12 | 'dropdown--active': !!active, 13 | 'dropdown--versions': !!versions, 14 | 'dropdown--settings': !!settings 15 | }); 16 | 17 | return ( 18 |
19 | { children } 20 |
21 | ) 22 | } 23 | 24 | export default Dropdown; -------------------------------------------------------------------------------- /src/client/components/Dropdown/Dropdown.scss: -------------------------------------------------------------------------------- 1 | .dropdown { 2 | width: 100%; 3 | position: absolute; 4 | right: 0; 5 | top: -262px; 6 | background: $color-dark-grey; 7 | z-index: -3; 8 | opacity: 0; 9 | padding: 10px 10px 20px 10px; 10 | transition: top 0.3s, opacity 0.4s; 11 | 12 | &--settings { 13 | width: 330px; 14 | } 15 | 16 | &--active { 17 | opacity: 1; 18 | top: 100%; 19 | } 20 | } -------------------------------------------------------------------------------- /src/client/components/EVMState/EVMState.scss: -------------------------------------------------------------------------------- 1 | $parent: '.evm-state__item'; 2 | 3 | .evm-state { 4 | padding: 10px; 5 | 6 | &__item { 7 | padding: 10px 0; 8 | 9 | &:first-of-type { 10 | display: flex; 11 | 12 | #{$parent}__content { 13 | margin: 0 0 0 5px; 14 | } 15 | } 16 | 17 | &__title { 18 | 19 | h4 { 20 | color: $color-light-grey; 21 | } 22 | } 23 | 24 | &__content { 25 | margin: 5px 0 0 0; 26 | 27 | span { 28 | color: $color-green; 29 | } 30 | 31 | &__row { 32 | padding: 2px; 33 | margin: 3px 0; 34 | border-bottom: 0.5px solid $color-light-grey; 35 | 36 | span { 37 | font-size: 11px; 38 | word-break: break-all; 39 | } 40 | 41 | &__key { 42 | position: relative; 43 | padding: 0 0 2px 0; 44 | border-bottom: 0.5px dotted $color-green; 45 | } 46 | 47 | &__value { 48 | padding: 5px 0 0 0; 49 | } 50 | 51 | &__key, &__value { 52 | 53 | .label { 54 | position: relative; 55 | width: 17px; 56 | height: 17px; 57 | border-radius: 50%; 58 | color: $color-light-grey; 59 | border: 1px solid $color-light-grey; 60 | margin: 0 5px 0 0; 61 | 62 | span { 63 | position: absolute; 64 | top: 50%; 65 | left: 50%; 66 | transform: translate(-50%, -50%); 67 | } 68 | } 69 | } 70 | } 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/client/components/Editor/Editor.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import CodeEditor from './AceEditor/AceEditor'; 4 | 5 | 6 | const Editor = ({ code, path, name, index, changeCode }) => { 7 | return ( 8 | 20 | 21 | ); 22 | } 23 | 24 | let timeout = null 25 | 26 | const onChange = (code, path, name, event) => { 27 | 28 | clearTimeout(timeout) 29 | 30 | timeout = setTimeout(() => { 31 | fetch(`http://localhost:9090/files`,{ 32 | body: JSON.stringify({ 33 | path: path, 34 | name: name, 35 | content: code 36 | }), 37 | method: 'POST', 38 | headers: { 39 | 'Content-Type': 'application/json' 40 | } 41 | }) 42 | }, 1000) 43 | } 44 | 45 | export default Editor; 46 | -------------------------------------------------------------------------------- /src/client/components/Form/Form.scss: -------------------------------------------------------------------------------- 1 | .form { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | 6 | &__inputs { 7 | flex: 1 1 auto; 8 | 9 | &__item { 10 | padding: 5px 10px; 11 | 12 | input { 13 | width: 100%; 14 | padding: 10px 0; 15 | font-size: 14px; 16 | background: transparent; 17 | color: $color-light-grey; 18 | border-bottom: 1px dotted $color-light-grey; 19 | transition: border-color 0.2s; 20 | 21 | &:-webkit-autofill, 22 | &:-webkit-autofill:hover, 23 | &:-webkit-autofill:focus { 24 | border-bottom: 1px dotted $color-green; 25 | -webkit-text-fill-color: $color-light-grey; 26 | box-shadow: 0 0 0px 1000px $color-dark-grey inset; 27 | transition: background-color 5000s ease-in-out 0s; 28 | } 29 | 30 | &:focus { 31 | border-color: $color-green; 32 | outline: 0 none; 33 | } 34 | } 35 | } 36 | } 37 | 38 | button { 39 | padding: 10px; 40 | border-radius: 5px; 41 | background: transparent; 42 | border: 1px solid $color-light-grey; 43 | cursor: pointer; 44 | transition: border-color 0.2s; 45 | 46 | &:last-of-type { 47 | margin-left: 10px; 48 | } 49 | 50 | &:focus { 51 | border-color: $color-green; 52 | outline: 0 none; 53 | 54 | span { 55 | color: $color-green; 56 | } 57 | } 58 | 59 | &:hover { 60 | border-color: $color-green; 61 | 62 | span { 63 | color: $color-green; 64 | } 65 | } 66 | 67 | span { 68 | color: $color-light-grey; 69 | font-size: 14px; 70 | transition: color 0.2s; 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/client/components/Graph/Graph.scss: -------------------------------------------------------------------------------- 1 | .graph-container { 2 | max-height: 1000px; 3 | overflow: hidden; 4 | } 5 | 6 | .graph-container a:hover { 7 | cursor: pointer; 8 | } 9 | -------------------------------------------------------------------------------- /src/client/components/Hamburger/Hamburger.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './Hamburger.scss'; 4 | 5 | import classnames from 'classnames/bind'; 6 | 7 | const cx = classnames.bind(styles); 8 | 9 | const Hamburger = ({ clicked, onIconClick }) => { 10 | 11 | const topLayerClasses = cx({ 12 | 'hamburger__top-layer': true, 13 | 'hamburger__top-layer--active': !!clicked, 14 | }); 15 | 16 | const middleLayerClasses = cx({ 17 | 'hamburger__middle-layer': true, 18 | 'hamburger__middle-layer--active': !!clicked, 19 | }); 20 | 21 | const bottomLayerClasses = cx({ 22 | 'hamburger__bottom-layer': true, 23 | 'hamburger__bottom-layer--active': !!clicked, 24 | }); 25 | 26 | return ( 27 |
28 |
29 |
30 |
31 |
32 | ) 33 | } 34 | 35 | Hamburger.displayName = 'Hamburger'; 36 | 37 | export default Hamburger; -------------------------------------------------------------------------------- /src/client/components/Hamburger/Hamburger.scss: -------------------------------------------------------------------------------- 1 | .hamburger { 2 | cursor: pointer; 3 | 4 | 5 | &__top-layer, &__middle-layer, &__bottom-layer { 6 | margin: 4px 0; 7 | width: 27px; 8 | height: 3px; 9 | background: $color-green; 10 | border-radius: 2px; 11 | opacity: 1; 12 | transform: translate(0, 0); 13 | transition: transform 0.3s; 14 | } 15 | 16 | &:hover & { 17 | 18 | &__top-layer { 19 | transform: translate(0, -3px); 20 | } 21 | 22 | &__bottom-layer { 23 | transform: translate(0, 3px); 24 | } 25 | } 26 | } 27 | 28 | //State 29 | .hamburger { 30 | 31 | &__top-layer--active { 32 | transform: translate(0, -3px) rotate(45deg); 33 | transform-origin: bottom left; 34 | } 35 | 36 | &__middle-layer--active { 37 | opacity: 0; 38 | } 39 | 40 | &__bottom-layer--active { 41 | transform: translate(0, 3px) rotate(-45deg); 42 | transform-origin: top left; 43 | } 44 | 45 | &:hover & { 46 | 47 | &__top-layer--active { 48 | transform: translate(0, -3px) rotate(45deg); 49 | transform-origin: bottom left; 50 | } 51 | 52 | &__middle-layer--active { 53 | opacity: 0; 54 | } 55 | 56 | &__bottom-layer--active { 57 | transform: translate(0, 3px) rotate(-45deg); 58 | transform-origin: top left; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/client/components/Icon/Icon.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import SVGInline from 'react-svg-inline'; 4 | 5 | import CircleLeft from './SVG/circle-left.svg'; 6 | import CircleRight from './SVG/circle-right.svg'; 7 | import Cross from './SVG/cross.svg'; 8 | import Menu from './SVG/menu.svg'; 9 | import Spinner from './SVG/spinner.svg'; 10 | import Cogs from './SVG/cogs.svg'; 11 | 12 | import styles from './Icon.scss'; 13 | 14 | const Icon = ({ iconName, onClick }) => { 15 | 16 | const icons = { CircleLeft, CircleRight, Menu, Cross, Spinner, Cogs }; 17 | 18 | return ( 19 |
20 | 21 |
22 | ); 23 | } 24 | 25 | Icon.displayName = 'Icon'; 26 | 27 | export default Icon; -------------------------------------------------------------------------------- /src/client/components/Icon/Icon.scss: -------------------------------------------------------------------------------- 1 | .icon { 2 | 3 | svg { 4 | display: block; 5 | width: 1.5em; 6 | height: 1.5em; 7 | stroke-width: 0; 8 | stroke: $color-green; 9 | fill: $color-green; 10 | pointer-events: none; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/client/components/Icon/SVG/cancel-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | cancel-circle 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/client/components/Icon/SVG/circle-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | circle-left 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/client/components/Icon/SVG/circle-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | circle-right 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/client/components/Icon/SVG/cogs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | cogs 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/client/components/Icon/SVG/cross.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | cross 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/client/components/Icon/SVG/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | menu 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/client/components/Icon/SVG/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | spinner9 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/client/components/Main/Main.scss: -------------------------------------------------------------------------------- 1 | .main-comp { 2 | display: flex; 3 | width: 100%; 4 | padding: 34px 0 20px 0; 5 | background: $color-black; 6 | 7 | &__left { 8 | width: 40%; 9 | position: relative; 10 | padding-top: 30px; 11 | padding-left: 10px; 12 | 13 | &__control { 14 | position: absolute; 15 | top: -14px; 16 | left: 10px; 17 | z-index: 4; 18 | 19 | button { 20 | background: transparent; 21 | outline: transparent; 22 | cursor: pointer; 23 | } 24 | } 25 | 26 | &__side-bar { 27 | position: absolute; 28 | top: 30px; 29 | left: -380px; 30 | transition: left 0.2s; 31 | pointer-events: none; 32 | } 33 | } 34 | 35 | &__right { 36 | flex: 1 1 auto; 37 | width: 100%; 38 | height: 100%; 39 | padding: 0 20px; 40 | overflow-x: auto; 41 | } 42 | } 43 | 44 | //State 45 | .main-comp { 46 | &__left { 47 | 48 | &__side-bar { 49 | 50 | &--open { 51 | left: 0; 52 | z-index: 6; 53 | pointer-events: auto; 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /src/client/components/MessageComp/MessageComp.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | import * as actions from '../../_redux/actions.js'; 5 | 6 | import Icon from '../Icon/Icon'; 7 | 8 | import styles from './MessageComp.scss'; 9 | 10 | const mapDispatchToProps = { 11 | toggleErrorMessage: actions.toggleErrorMessage 12 | } 13 | 14 | const mapStateToProps = state => ({ 15 | errorMessage: state.errorMessage.message 16 | }); 17 | 18 | class MessageComp extends React.Component { 19 | constructor(props) { 20 | super(props); 21 | 22 | this.handleKeyUp = this.handleKeyUp.bind(this); 23 | } 24 | 25 | componentDidMount() { 26 | document.addEventListener('keydown', this.handleKeyUp); 27 | } 28 | 29 | UNSAFE_componentWillMount() { 30 | document.removeEventListener('keydown', this.handleKeyUp); 31 | } 32 | 33 | handleKeyUp(event) { 34 | if(event.keyCode !== 27) { 35 | return; 36 | } 37 | this.props.toggleErrorMessage(false); 38 | } 39 | 40 | render() { 41 | const { message, toggleErrorMessage, errorMessage } = this.props; 42 | 43 | return ( 44 |
45 |
46 |
47 |

{`${message}`}

48 |
49 |
50 | { 51 | !errorMessage 52 | ? 53 | :
54 | 55 | {`/ ESC`} 56 |
57 | } 58 |
59 |
60 |
61 | ) 62 | } 63 | } 64 | 65 | MessageComp.displayName = 'MessageComp'; 66 | 67 | export default connect(mapStateToProps, mapDispatchToProps)(MessageComp); -------------------------------------------------------------------------------- /src/client/components/MessageComp/MessageComp.scss: -------------------------------------------------------------------------------- 1 | .message-comp { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width:100%; 6 | height: 100%; 7 | background: rgba(0, 0, 0, 0.8); 8 | z-index: 9999; 9 | 10 | &__main { 11 | position: absolute; 12 | top: 50%; 13 | left: 50%; 14 | transform: translate(-50%, -50%); 15 | 16 | &__text { 17 | text-align: center; 18 | color: $color-green; 19 | } 20 | 21 | &__button { 22 | display: table; 23 | margin: 30px auto; 24 | 25 | svg { 26 | width: 40px; 27 | height: 40px; 28 | fill: $color-green; 29 | stroke: $color-green; 30 | animation: rotate 5s infinite; 31 | } 32 | 33 | &__item { 34 | 35 | button { 36 | padding: 10px; 37 | font-size: 14px; 38 | border: 1px solid $color-green; 39 | border-radius: 5px; 40 | background: transparent; 41 | outline: transparent; 42 | cursor: pointer; 43 | 44 | &:hover { 45 | opacity: 0.6; 46 | } 47 | } 48 | 49 | span { 50 | color: $color-green; 51 | font-size: 16px; 52 | } 53 | 54 | .escape { 55 | margin-left: 7px; 56 | font-size: 22px; 57 | } 58 | } 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/client/components/Modal/Modal.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Icon from '../Icon/Icon'; 4 | 5 | import styles from './Modal.scss'; 6 | 7 | const Modal = ({ onIconClick, children }) => { 8 | 9 | return ( 10 |
11 |
12 |
13 | 16 |
17 |
18 | {children} 19 |
20 |
21 |
22 | ) 23 | } 24 | 25 | Modal.displayName = 'Modal'; 26 | 27 | export default Modal; 28 | -------------------------------------------------------------------------------- /src/client/components/Modal/Modal.scss: -------------------------------------------------------------------------------- 1 | .modal { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width:100%; 6 | height: 100%; 7 | background: rgba(0, 0, 0, 0.8); 8 | z-index: 9999; 9 | 10 | 11 | &__main { 12 | position: fixed; 13 | width: 80%; 14 | height: auto; 15 | top:50%; 16 | left:50%; 17 | padding: 50px; 18 | background: $color-dark-grey; 19 | box-shadow: 0.3px 0.4px 12px 0px $color-light-green; 20 | border-radius: 12px; 21 | transform: translate(-50%,-50%); 22 | 23 | &__button { 24 | position: absolute; 25 | top: 5px; 26 | right: 10px; 27 | 28 | button { 29 | background: transparent; 30 | outline: transparent; 31 | cursor: pointer; 32 | } 33 | 34 | svg { 35 | width: 10px; 36 | height: 10px; 37 | stroke: $color-light-grey; 38 | fill: $color-light-grey; 39 | } 40 | } 41 | 42 | &__body { 43 | width: 100%; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/client/components/Panel/Panel.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import TransactionDebugger from '../TransactionDebugger/TransactionDebugger'; 4 | import Disassembler from '../Disassembler/Disassembler'; 5 | import ControlFlowGraph from '../ControlFlowGraph/ControlFlowGraph'; 6 | import StorageViewer from '../StorageViewer/StorageViewer'; 7 | 8 | import styles from './Panel.scss'; 9 | 10 | const Panel = ({ type, contractName, contractCode, contractPath, debuggerResponse, graphResponse, disassemblerResponse, storageResponse }) => { 11 | console.log(disassemblerResponse) 12 | return ( 13 |
14 | {type === 'Transaction Debugger' && 15 | 20 | } 21 | {type === 'Disassembler' && 22 | 25 | } 26 | {type === 'Control Flow Graph Runtime' && 27 | 34 | } 35 | {type === 'Control Flow Graph Constructor' && 36 | 43 | } 44 | {type === 'Storage Viewer' && 45 | 46 | } 47 |
48 | ); 49 | } 50 | 51 | Panel.displayName = 'Panel'; 52 | 53 | export default Panel; 54 | -------------------------------------------------------------------------------- /src/client/components/Panel/Panel.scss: -------------------------------------------------------------------------------- 1 | .panel { 2 | background: $color-dark-grey; 3 | color: $color-green; 4 | } -------------------------------------------------------------------------------- /src/client/components/SettingsBar/SettingsBar.scss: -------------------------------------------------------------------------------- 1 | .settings-bar { 2 | width: 100%; 3 | background: $color-dark-grey; 4 | padding: 0 5px 10px 5px; 5 | 6 | &__buttons { 7 | padding: 5px 10px; 8 | display: flex; 9 | justify-content: space-between; 10 | 11 | button { 12 | padding: 10px; 13 | border-radius: 5px; 14 | background: transparent; 15 | border: 1px solid $color-light-grey; 16 | cursor: pointer; 17 | transition: border-color 0.2s; 18 | 19 | &:last-of-type { 20 | margin-left: 10px; 21 | } 22 | 23 | &:focus { 24 | border-color: $color-green; 25 | outline: 0 none; 26 | 27 | span { 28 | color: $color-green; 29 | } 30 | } 31 | 32 | &:hover { 33 | border-color: $color-green; 34 | 35 | span { 36 | color: $color-green; 37 | } 38 | } 39 | 40 | span { 41 | color: $color-light-grey; 42 | font-size: 14px; 43 | transition: color 0.2s; 44 | } 45 | } 46 | } 47 | 48 | &__message { 49 | display: table; 50 | margin: 20px auto 0 auto; 51 | 52 | p { 53 | color: $color-green; 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/client/components/SideBar/SideBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './SideBar.scss'; 4 | 5 | class SideBar extends React.Component { 6 | 7 | handleClick(type) { 8 | this.props.onClick(type); 9 | } 10 | 11 | render() { 12 | 13 | const { onTransactionDebuggerClick, onControlFlowGraphRuntimeClick, onControlFlowGraphConstructorClick, onDisassemblerClick, onViewStorageClick } = this.props; 14 | 15 | return ( 16 |
17 |
18 | Debug Transaction 19 |
20 |
21 | Disassembler 22 |
23 |
24 | Control Flow Graph Constructor 25 |
26 |
27 | Control Flow Graph Runtime 28 |
29 |
30 | View Storage 31 |
32 |
33 | ) 34 | } 35 | } 36 | 37 | SideBar.displayName = 'SideBar'; 38 | 39 | export default SideBar; 40 | -------------------------------------------------------------------------------- /src/client/components/SideBar/SideBar.scss: -------------------------------------------------------------------------------- 1 | .side-bar { 2 | width: 100%; 3 | padding: 10px 0; 4 | background: $color-grey; 5 | 6 | &__item { 7 | width: 100%; 8 | padding: 20px; 9 | //background: $color-dark-grey; 10 | color: $color-green; 11 | text-transform: uppercase; 12 | border-bottom: 1px solid $color-green; 13 | border-top: 1px solid $color-green; 14 | transition: background 0.2s, color 0.2s; 15 | cursor: pointer; 16 | 17 | &:hover { 18 | background: $color-green; 19 | color: $color-dark-grey; 20 | } 21 | } 22 | } 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/client/components/StorageViewer/StorageViewer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './StorageViewer.scss'; 4 | 5 | const StorageViewer = ({ storageResponse }) => { 6 | 7 | return ( 8 |
9 |
10 |
11 | 12 | Slot 13 | 14 |
15 |
16 | 17 | Value 18 | 19 |
20 |
21 | 22 | Set in block 23 | 24 |
25 |
26 | 27 | Set in transaction 28 | 29 |
30 |
31 |
32 | { 33 | Object.entries(storageResponse.storage).map(([key, val]) => { 34 | return ( 35 |
36 |
37 | {`0x${key}`} 38 |
39 |
40 | {`0x${val.value}`} 41 |
42 |
43 | {`${val.block}`} 44 |
45 |
46 | {`${val.transactionHash}`} 47 |
48 |
49 | ) 50 | }) 51 | } 52 |
53 | 54 |
55 | ) 56 | } 57 | 58 | export default StorageViewer; -------------------------------------------------------------------------------- /src/client/components/StorageViewer/StorageViewer.scss: -------------------------------------------------------------------------------- 1 | .storage { 2 | padding: 15px; 3 | 4 | &__header { 5 | display: flex; 6 | justify-content: space-around; 7 | border-bottom: 1px dotted $color-green; 8 | 9 | &__item { 10 | flex: 0 1 50%; 11 | padding: 20px; 12 | text-align: center; 13 | 14 | &:first-of-type { 15 | border-right: 1px dotted $color-green; 16 | } 17 | 18 | span { 19 | text-transform: uppercase; 20 | color: $color-white; 21 | } 22 | } 23 | } 24 | 25 | &__body { 26 | 27 | &__item { 28 | display: flex; 29 | padding: 0 10px; 30 | border-bottom: 1px dotted $color-green; 31 | 32 | &__col { 33 | flex: 0 1 50%; 34 | padding: 20px 10px; 35 | word-break: break-all; 36 | 37 | span { 38 | color: $color-white; 39 | } 40 | 41 | &:first-of-type { 42 | border-right: 1px dotted $color-green; 43 | } 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/client/components/Tab/Tab.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import TabMenuItem from './TabMenuItem/TabMenuItem'; 4 | 5 | import styles from './Tab.scss'; 6 | 7 | class Tab extends React.Component { 8 | constructor(props) { 9 | super(props); 10 | 11 | this.state = { 12 | currentTabIndex: 0, 13 | } 14 | } 15 | 16 | setActiveTab(index) { 17 | this.setState({ 18 | currentTabIndex: index, 19 | }); 20 | } 21 | 22 | handleIconClick(event, index) { 23 | event.stopPropagation(); 24 | 25 | const { children } = this.props; 26 | 27 | this.setState({ 28 | currentTabIndex: 29 | index === children.length - 1 && index === this.state.currentTabIndex ? 0 30 | : index === this.state.currentTabIndex ? index 31 | : this.state.currentTabIndex, 32 | }); 33 | 34 | this.props.onMenuItemIconClick(index); 35 | } 36 | 37 | render() { 38 | const { currentTabIndex } = this.state; 39 | 40 | const children = React.Children.map(this.props.children, (child, index) => { 41 | if(!!child) { 42 | return React.cloneElement(child, { 43 | index, 44 | active: index === currentTabIndex, 45 | }); 46 | } 47 | }); 48 | 49 | return ( 50 |
51 |
52 | {React.Children.map(children, (child, i) => { 53 | return ( 54 | this.setActiveTab(i)} 59 | onIconClick={(e) => this.handleIconClick(e, i)} 60 | /> 61 | ) 62 | })} 63 |
64 |
65 | { children } 66 |
67 |
68 | ); 69 | } 70 | } 71 | 72 | export default Tab; 73 | 74 | Tab.displayName = 'Tab'; 75 | -------------------------------------------------------------------------------- /src/client/components/Tab/Tab.scss: -------------------------------------------------------------------------------- 1 | .tab { 2 | width: 100%; 3 | overflow: hidden; 4 | 5 | &__navigation { 6 | width: 100%; 7 | display: flex; 8 | text-transform: uppercase; 9 | overflow-x: auto; 10 | overflow-y: hidden; 11 | } 12 | 13 | &__panels { 14 | position: relative; 15 | } 16 | } 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/client/components/Tab/TabMenuItem/TabMenuItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Icon from '../../Icon/Icon'; 4 | 5 | import classnames from 'classnames/bind'; 6 | 7 | import styles from './TabMenuItem.scss'; 8 | 9 | const cx = classnames.bind(styles); 10 | 11 | const TabMenuItem = ({ name, onMenuItemClick, active, onIconClick, evm }) => { 12 | 13 | const classes = cx({ 14 | 'tab-menu-item': true, 15 | 'tab-menu-item--active': !!active, 16 | }); 17 | 18 | return ( 19 |
20 | { 21 | !evm && 22 |
23 | 24 |
25 | } 26 |
27 | {name} 28 |
29 |
30 | ); 31 | } 32 | 33 | 34 | TabMenuItem.displayName = 'TabMenuItem'; 35 | 36 | export default TabMenuItem; 37 | -------------------------------------------------------------------------------- /src/client/components/Tab/TabMenuItem/TabMenuItem.scss: -------------------------------------------------------------------------------- 1 | .tab-menu-item { 2 | position: relative; 3 | height: 60px; 4 | flex: 1 1 auto; 5 | padding: 15px 20px 20px 20px; 6 | text-align: center; 7 | background: $color-grey; 8 | color: $color-green; 9 | z-index: 0; 10 | box-shadow: inset 2px 1px 5px (#000 + 30); 11 | overflow: hidden; 12 | text-overflow: ellipsis; 13 | transform: translate(0, 20px); 14 | transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, color 0.2s ease-in-out, background 0.2s ease-in-out; 15 | cursor: pointer; 16 | 17 | &:first-of-type { 18 | border-top-left-radius: 8px; 19 | } 20 | 21 | &:last-of-type { 22 | border-top-right-radius: 8px; 23 | } 24 | 25 | &:hover { 26 | transform: translate(0, 15px); 27 | } 28 | 29 | &__icon { 30 | position: absolute; 31 | top: 5px; 32 | right: 5px; 33 | 34 | svg { 35 | width: 10px; 36 | height: 10px; 37 | fill: $color-green; 38 | stroke: $color-green; 39 | pointer-events: none; 40 | transition: fill 0.2s ease-in-out, stroke 0.2s ease-in-out; 41 | } 42 | } 43 | } 44 | 45 | //State 46 | .tab-menu-item { 47 | 48 | &--active { 49 | transform: translate(0, 10px); 50 | box-shadow: 2px 5px 6px 2px ($color-grey - 30); 51 | color: $color-grey; 52 | background: $color-green; 53 | 54 | &:hover { 55 | transform: translate(0, 10px); 56 | } 57 | } 58 | 59 | &--active & { 60 | 61 | &__icon { 62 | 63 | svg { 64 | fill: $color-grey; 65 | stroke: $color-grey; 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/client/components/Tab/TabPanel/TabPanel.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import styles from './TabPanel.scss'; 4 | 5 | import classnames from 'classnames/bind'; 6 | 7 | const cx = classnames.bind(styles); 8 | 9 | const TabPanel = ({ children, active }) => { 10 | 11 | const tabPanelClasses = cx({ 12 | 'tab-panel': true, 13 | 'tab-panel--active': !!active, 14 | }); 15 | return ( 16 |
17 | {children} 18 |
19 | ) 20 | } 21 | 22 | TabPanel.displayName = 'TabPanel'; 23 | 24 | export default TabPanel; 25 | -------------------------------------------------------------------------------- /src/client/components/Tab/TabPanel/TabPanel.scss: -------------------------------------------------------------------------------- 1 | .tab-panel { 2 | display: none; 3 | // width: 100%; 4 | // padding: 34px 0 20px 0; 5 | // background: $color-black; 6 | border-bottom-left-radius: 8px; 7 | border-bottom-right-radius: 8px; 8 | } 9 | // &__left { 10 | // width: 40%; 11 | // position: relative; 12 | // padding-top: 30px; 13 | // padding-left: 10px; 14 | 15 | // &__control { 16 | // position: absolute; 17 | // top: -14px; 18 | // left: 10px; 19 | // z-index: 4; 20 | 21 | // button { 22 | // background: transparent; 23 | // outline: transparent; 24 | // cursor: pointer; 25 | // } 26 | // } 27 | 28 | // &__side-bar { 29 | // position: absolute; 30 | // top: 30px; 31 | // left: -262px; 32 | // transition: left 0.2s; 33 | // pointer-events: none; 34 | // } 35 | // } 36 | 37 | // &__right { 38 | // flex: 1 1 auto; 39 | // width: 100%; 40 | // height: 100%; 41 | // padding: 0 20px; 42 | // overflow-x: auto; 43 | // } 44 | // } 45 | 46 | //State 47 | .tab-panel { 48 | 49 | &--active { 50 | display: flex; 51 | } 52 | 53 | // &__left { 54 | 55 | // &__side-bar { 56 | 57 | // &--open { 58 | // left: 0; 59 | // z-index: 6; 60 | // pointer-events: auto; 61 | // } 62 | // } 63 | // } 64 | } 65 | -------------------------------------------------------------------------------- /src/client/components/TopNavBar/TopNavBar.scss: -------------------------------------------------------------------------------- 1 | .top-navbar { 2 | display: flex; 3 | align-items: center; 4 | width: 100%; 5 | position: fixed; 6 | top: 0; 7 | left: 0; 8 | right: 0; 9 | background: $color-dark-grey; 10 | z-index: 5; 11 | 12 | &__form { 13 | padding: 20px; 14 | background: $color-dark-grey; 15 | flex: 1 1 auto; 16 | } 17 | 18 | &__settings-dropdown { 19 | display: flex; 20 | justify-content: flex-end; 21 | flex: 0 1 auto; 22 | position: relative; 23 | padding: 30px 20px; 24 | height: 88px; 25 | cursor: pointer; 26 | background: $color-dark-grey; 27 | } 28 | 29 | &__versions-dropdown { 30 | flex: 0 0 250px; 31 | position: relative; 32 | padding-left: 10px; 33 | padding: 24.5px 20px; 34 | height: 88px; 35 | background: $color-dark-grey; 36 | 37 | &__text { 38 | position: absolute; 39 | top: 50%; 40 | left: 20px; 41 | transform: translate(0, -50%); 42 | 43 | span { 44 | color: $color-green; 45 | } 46 | } 47 | 48 | &__toggler { 49 | position: absolute; 50 | top: 50%; 51 | right: 0; 52 | transform: translate(0, -50%); 53 | padding: 10px; 54 | border-radius: 5px; 55 | background: transparent; 56 | border: 1px solid $color-light-grey; 57 | cursor: pointer; 58 | z-index: 3; 59 | transition: opacity 0.7, border-color 0.2s; 60 | 61 | span { 62 | color: $color-light-grey; 63 | transition: color 0.1s ease-in; 64 | font-size: 14px; 65 | } 66 | 67 | &:focus { 68 | border-color: $color-green; 69 | outline: 0 none; 70 | 71 | span { 72 | color: $color-green; 73 | } 74 | } 75 | 76 | &:hover { 77 | border-color: $color-green; 78 | 79 | span { 80 | color: $color-green; 81 | } 82 | } 83 | } 84 | } 85 | } 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/client/components/TransactionDebugger/TransactionDebugger.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Graph from '../Graph/Graph'; 4 | 5 | import styles from './TransactionDebugger.scss'; 6 | 7 | const TransactionDebugger = ({ contractName, contractPath, debuggerResponse }) => { 8 | 9 | return ( 10 |
11 | 19 |
20 | ); 21 | } 22 | 23 | TransactionDebugger.displayName = 'TransactionDebugger'; 24 | 25 | export default TransactionDebugger; 26 | -------------------------------------------------------------------------------- /src/client/components/TransactionDebugger/TransactionDebugger.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fergarrui/ethereum-graph-debugger/d7310f960c726ee04c54163b2803c3a5dda97b36/src/client/components/TransactionDebugger/TransactionDebugger.scss -------------------------------------------------------------------------------- /src/client/components/Version/Version.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | 4 | import * as actions from '../../_redux/actions'; 5 | 6 | import styles from './Version.scss'; 7 | 8 | const mapDispatchToProps = { 9 | getVersionNumber: actions.getVersionNumber, 10 | postVersion: actions.postVersion 11 | }; 12 | 13 | const Version = ({ data, getVersionNumber, onVersionItemClick, postVersion }) => { 14 | 15 | const handleSelect = (item) => { 16 | postVersion({ version: item.commit }); 17 | onVersionItemClick(); 18 | getVersionNumber(item.version); 19 | } 20 | 21 | return ( 22 |
23 | { 24 | data.map(item => { 25 | return ( 26 |
handleSelect(item)} 30 | > 31 | {item.version} 32 |
33 | )}) 34 | } 35 |
36 | ) 37 | } 38 | 39 | export default connect(null, mapDispatchToProps)(Version); -------------------------------------------------------------------------------- /src/client/components/Version/Version.scss: -------------------------------------------------------------------------------- 1 | .version { 2 | max-height: 300px; 3 | overflow: auto; 4 | padding: 20px 10px; 5 | 6 | &__item { 7 | width: 100%; 8 | padding: 10px 20px; 9 | border-bottom: 1px dotted $color-light-grey; 10 | transition: border-bottom-color 0.3s; 11 | cursor: pointer; 12 | 13 | &:hover { 14 | border-bottom-color: $color-green; 15 | 16 | span { 17 | color: $color-green; 18 | } 19 | } 20 | 21 | span { 22 | color: $color-light-grey; 23 | transition: color 0.2s; 24 | pointer-events: none; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/client/styles/App.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Montserrat'); 2 | 3 | .app { 4 | width: 100%; 5 | position: relative; 6 | font-family: 'Montserrat', sans-serif; 7 | 8 | &__tabs { 9 | margin-top: 50px; 10 | padding: 50px 10px; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/client/styles/animate.scss: -------------------------------------------------------------------------------- 1 | @keyframes rotate { 2 | 0% { 3 | transform: rotate(0); 4 | } 5 | 6 | 100% { 7 | transform: rotate(360deg); 8 | } 9 | } -------------------------------------------------------------------------------- /src/client/styles/reset.scss: -------------------------------------------------------------------------------- 1 | * { 2 | border: 0; 3 | padding: 0; 4 | margin: 0; 5 | box-sizing: border-box; 6 | } 7 | 8 | body { 9 | background: $color-light-grey; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/client/styles/transitions/fade.scss: -------------------------------------------------------------------------------- 1 | //fade 2 | .enter { 3 | opacity: 0.01; 4 | 5 | &.enterActive { 6 | transition: opacity 0.3s ease-in; 7 | opacity: 1; 8 | } 9 | } 10 | 11 | .leave { 12 | opacity: 1; 13 | 14 | &.leaveActive { 15 | transition: opacity 0.3s ease-in; 16 | opacity: 0.01; 17 | } 18 | } 19 | 20 | .appear { 21 | opacity: 0.01; 22 | 23 | &.appearActive { 24 | transition: opacity 0.3s ease-in; 25 | opacity: 1; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/client/styles/transitions/scale.scss: -------------------------------------------------------------------------------- 1 | //scale 2 | .enter { 3 | opacity: 0.01; 4 | transform: scale(0); 5 | 6 | &.enterActive { 7 | transition: opacity 0.4s ease-in, transform 0.3s ease-in-out; 8 | opacity: 1; 9 | transform: scale(1); 10 | } 11 | } 12 | 13 | .leave { 14 | opacity: 1; 15 | transform: scale(1); 16 | 17 | &.leaveActive { 18 | transition: opacity 0.4s ease-in, transform 0.3s ease-in-out; 19 | opacity: 0.01; 20 | transform: scale(0); 21 | } 22 | } 23 | 24 | .appear { 25 | opacity: 0.01; 26 | transform: scale(0); 27 | 28 | 29 | &.appearActive { 30 | transition: opacity 0.4s ease-in, transform 0.3s ease-in-out; 31 | opacity: 1; 32 | transform: scale(1); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/client/styles/transitions/slide.scss: -------------------------------------------------------------------------------- 1 | //slide 2 | .enter { 3 | opacity: 0.01; 4 | top: -100%; 5 | 6 | &.enterActive { 7 | transition: opacity 0.3s ease-in, top 0.3s ease-in; 8 | opacity: 1; 9 | top: 0; 10 | } 11 | } 12 | 13 | .leave { 14 | opacity: 1; 15 | top: 0; 16 | 17 | &.leaveActive { 18 | transition: opacity 0.3s ease-in, top 0.3s ease-in; 19 | opacity: 0.01; 20 | top: -100%; 21 | } 22 | } 23 | 24 | .appear { 25 | opacity: 0.01; 26 | top: -100%; 27 | 28 | &.appearActive { 29 | transition: opacity 0.3s ease-in, top 0.3s ease-in; 30 | opacity: 1; 31 | top: 0; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/client/styles/utils.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | const resources = [ 4 | "variables.scss", 5 | "animate.scss", 6 | ]; 7 | 8 | module.exports = resources.map(file => path.resolve(__dirname, file)); -------------------------------------------------------------------------------- /src/client/styles/variables.scss: -------------------------------------------------------------------------------- 1 | //Colors 2 | $color-green: #88F87B; 3 | $color-light-green: #B4FAAC; 4 | $color-black: #1D1D1B; 5 | $color-light-grey: #B7B7B7; 6 | $color-grey: #393E41; 7 | $color-dark-grey: #232323; 8 | $color-white: #fff; -------------------------------------------------------------------------------- /src/client/utils/baseUrl.js: -------------------------------------------------------------------------------- 1 | export const baseUrl = `http://localhost:9090/`; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import "regenerator-runtime/runtime"; 5 | 6 | import store from './client/_redux/store.js'; 7 | 8 | import App from './client/App'; 9 | 10 | import styles from './client/styles/reset.scss'; 11 | 12 | ReactDOM.render(, document.getElementById('app')); 13 | -------------------------------------------------------------------------------- /src/inversify/types.ts: -------------------------------------------------------------------------------- 1 | const TYPES = { 2 | Web3Instance: Symbol.for('Web3Instance'), 3 | FileService: Symbol.for('FileService'), 4 | TransactionService: Symbol.for('TransactionService'), 5 | Disassembler: Symbol.for('EVMDisassembler'), 6 | CFGCreator: Symbol.for('CFGCreator'), 7 | GraphVizService: Symbol.for('GraphVizService'), 8 | BlockService: Symbol.for('BlockService'), 9 | CFGService: Symbol.for('CFGService'), 10 | StorageRecover: Symbol.for('StorageRecover'), 11 | OpcodeExecutor: Symbol.for('OpcodeExecutor'), 12 | ContractService: Symbol.for('ContractService'), 13 | Solc: Symbol.for('Solc') 14 | } 15 | 16 | export { TYPES } 17 | -------------------------------------------------------------------------------- /src/run-server.ts: -------------------------------------------------------------------------------- 1 | import { Server } from './Server' 2 | import { RegisterRoutes } from './routes' 3 | 4 | import './api/service/controller/DebuggerController' 5 | import './api/service/controller/TransactionController' 6 | import './api/service/controller/FileController' 7 | import './api/service/controller/DisassembleController' 8 | import './api/service/controller/ControlFlowGraphController' 9 | import './api/service/controller/StorageRecoverController' 10 | import './api/service/controller/ContractController' 11 | import './api/service/controller/SolcController' 12 | 13 | const server = new Server() 14 | // make it configurable 15 | const port = 9090 16 | RegisterRoutes(server.express) 17 | 18 | server.express.use((err: any, _req, res, next) => { 19 | const status = err.status || 500 20 | const body: any = { 21 | message: err.message || 'Sorry, there has been an error', 22 | name: err.name, 23 | status, 24 | error: true 25 | } 26 | res.status(status).json(body) 27 | next() 28 | }) 29 | 30 | const runServer = () => { 31 | server.setLogConfig('info' as any, false).startOn(port) 32 | } 33 | 34 | server.express.get('/', function(request, response) { 35 | response.sendFile(__dirname + '/index.html') 36 | }) 37 | 38 | server.express.get('/bundle.js', function(request, response) { 39 | response.sendFile(__dirname + '/bundle.js') 40 | }) 41 | 42 | runServer() 43 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "outDir": "./dist", 5 | "rootDir": "./src", 6 | "sourceMap": true, 7 | "module": "commonjs", 8 | "target": "es6", 9 | "experimentalDecorators": true, 10 | "lib": ["es6", "es2015", "es2017", "dom"], 11 | "types": ["reflect-metadata", "jest", "node"], 12 | "moduleResolution": "node", 13 | "emitDecoratorMetadata": true, 14 | "allowSyntheticDefaultImports": true 15 | }, 16 | "include": ["src/**/*"], 17 | "exclude": ["node_modules"], 18 | "compileOnSave": false 19 | } 20 | -------------------------------------------------------------------------------- /tsoa.json: -------------------------------------------------------------------------------- 1 | { 2 | "routes": { 3 | "entryFile": "./src/run-server.ts", 4 | "routesDir": "./src", 5 | "iocModule" : "./src/inversify/ioc" 6 | } 7 | } 8 | --------------------------------------------------------------------------------