From 92697eba4b58f7cdfd67fa58fe2f7bdc4bb1b2a2 Mon Sep 17 00:00:00 2001 From: Piotr Szlachciak Date: Sun, 2 Feb 2020 12:10:36 +0100 Subject: [PATCH] Refactor ExecutionResult (#48) * Reorganize files * Simplify ExecutionResult * Make ExecutionResult a discriminated union * Reduce Memory to a single class * Make parseBytecode easier to read --- packages/evm/package.json | 3 +- packages/evm/src/{evm => }/Address.ts | 0 packages/evm/src/{evm => }/Byte.ts | 0 packages/evm/src/{evm => }/Bytes32.ts | 0 .../evm/src/{evm => }/ExecutionContext.ts | 17 +--- packages/evm/src/ExecutionResult.ts | 27 ++++++ packages/evm/src/{evm => }/Memory.ts | 76 ++++++---------- packages/evm/src/Message.ts | 16 ++++ packages/evm/src/{evm => }/Stack.ts | 0 packages/evm/src/{evm => }/State.ts | 0 packages/evm/src/cloneBlock.ts | 31 ------- packages/evm/src/createBlock.ts | 16 ---- packages/evm/src/createGenesisBlock.ts | 28 ------ packages/evm/src/{evm => }/errors.ts | 0 packages/evm/src/evm/Message.ts | 18 ---- packages/evm/src/evm/executeCode.ts | 53 ----------- packages/evm/src/executeCode.ts | 44 +++++++++ packages/evm/src/executeTransaction.ts | 27 ------ packages/evm/src/includeTransactions.ts | 51 ----------- packages/evm/src/model/Account.ts | 8 -- packages/evm/src/model/Block.ts | 20 ---- packages/evm/src/model/ExecutedTransaction.ts | 14 --- packages/evm/src/model/ExecutionError.ts | 3 - packages/evm/src/model/Log.ts | 7 -- packages/evm/src/model/PendingTransaction.ts | 8 -- packages/evm/src/model/index.ts | 8 -- packages/evm/src/model/utils.ts | 4 - packages/evm/src/{evm => }/opcodes/Opcode.ts | 0 packages/evm/src/{evm => }/opcodes/bytes32.ts | 0 packages/evm/src/{evm => }/opcodes/control.ts | 0 .../evm/src/{evm => }/opcodes/gasCosts.ts | 1 + packages/evm/src/{evm => }/opcodes/helpers.ts | 0 packages/evm/src/{evm => }/opcodes/index.ts | 0 packages/evm/src/{evm => }/opcodes/invalid.ts | 0 packages/evm/src/{evm => }/opcodes/memory.ts | 0 packages/evm/src/{evm => }/opcodes/stack.ts | 0 packages/evm/src/{evm => }/opcodes/storage.ts | 0 packages/evm/src/{evm => }/parseBytecode.ts | 8 +- packages/evm/test/{evm => }/Bytes32.test.ts | 4 +- .../test/{evm => }/ExecutionContext.test.ts | 13 +-- packages/evm/test/Memory.test.ts | 91 +++++++++++++++++++ packages/evm/test/{evm => }/Stack.test.ts | 6 +- packages/evm/test/{evm => }/State.test.ts | 6 +- packages/evm/test/cloneBlock.test.ts | 36 -------- packages/evm/test/createGenesisBlock.test.ts | 61 ------------- packages/evm/test/evm/Memory.test.ts | 91 ------------------- packages/evm/test/evm/exception.test.ts | 37 -------- packages/evm/test/{evm => }/gasLimit.test.ts | 2 +- packages/evm/test/{evm => }/helpers/Int256.ts | 2 +- .../test/{evm => }/helpers/executeAssembly.ts | 21 +++-- .../test/{evm => }/helpers/expectations.ts | 40 ++++---- packages/evm/test/{evm => }/helpers/index.ts | 0 packages/evm/test/{evm => }/helpers/memory.ts | 0 .../opcodes/bytes32/cases/deth/add.ts | 0 .../opcodes/bytes32/cases/geth/README.md | 0 .../opcodes/bytes32/cases/geth/add.json | 0 .../opcodes/bytes32/cases/geth/and.json | 0 .../opcodes/bytes32/cases/geth/byte.json | 0 .../opcodes/bytes32/cases/geth/div.json | 0 .../opcodes/bytes32/cases/geth/eq.json | 0 .../opcodes/bytes32/cases/geth/exp.json | 0 .../opcodes/bytes32/cases/geth/gt.json | 0 .../opcodes/bytes32/cases/geth/lt.json | 0 .../opcodes/bytes32/cases/geth/mod.json | 0 .../opcodes/bytes32/cases/geth/mul.json | 0 .../opcodes/bytes32/cases/geth/or.json | 0 .../opcodes/bytes32/cases/geth/sar.json | 0 .../opcodes/bytes32/cases/geth/sdiv.json | 0 .../opcodes/bytes32/cases/geth/sgt.json | 0 .../opcodes/bytes32/cases/geth/shl.json | 0 .../opcodes/bytes32/cases/geth/shr.json | 0 .../bytes32/cases/geth/signextend.json | 0 .../opcodes/bytes32/cases/geth/slt.json | 0 .../opcodes/bytes32/cases/geth/smod.json | 0 .../opcodes/bytes32/cases/geth/sub.json | 0 .../opcodes/bytes32/cases/geth/xor.json | 0 .../opcodes/bytes32/cases/helpers.ts | 0 .../{evm => }/opcodes/bytes32/cases/index.ts | 0 .../opcodes/bytes32/cases/parity/README.md | 0 .../opcodes/bytes32/cases/parity/add.ts | 0 .../opcodes/bytes32/cases/parity/addmod.ts | 0 .../opcodes/bytes32/cases/parity/and.ts | 0 .../opcodes/bytes32/cases/parity/byte.ts | 0 .../opcodes/bytes32/cases/parity/div.ts | 0 .../opcodes/bytes32/cases/parity/eq.ts | 0 .../opcodes/bytes32/cases/parity/exp.ts | 0 .../opcodes/bytes32/cases/parity/gt.ts | 0 .../opcodes/bytes32/cases/parity/iszero.ts | 0 .../opcodes/bytes32/cases/parity/lt.ts | 0 .../opcodes/bytes32/cases/parity/mod.ts | 0 .../opcodes/bytes32/cases/parity/mul.ts | 0 .../opcodes/bytes32/cases/parity/mulmod.ts | 0 .../opcodes/bytes32/cases/parity/not.ts | 0 .../opcodes/bytes32/cases/parity/or.ts | 0 .../opcodes/bytes32/cases/parity/sar.ts | 0 .../opcodes/bytes32/cases/parity/sdiv.ts | 0 .../opcodes/bytes32/cases/parity/sgt.ts | 0 .../opcodes/bytes32/cases/parity/shl.ts | 0 .../opcodes/bytes32/cases/parity/shr.ts | 0 .../bytes32/cases/parity/signextend.ts | 0 .../opcodes/bytes32/cases/parity/slt.ts | 0 .../opcodes/bytes32/cases/parity/smod.ts | 0 .../opcodes/bytes32/cases/parity/sub.ts | 0 .../opcodes/bytes32/cases/parity/xor.ts | 0 .../opcodes/bytes32/machineWord.test.ts | 8 +- .../evm/test/{evm => }/opcodes/dup.test.ts | 9 +- .../test/{evm => }/opcodes/invalid.test.ts | 2 +- .../evm/test/{evm => }/opcodes/jump.test.ts | 22 +++-- .../evm/test/{evm => }/opcodes/memory.test.ts | 20 ++-- .../evm/test/{evm => }/opcodes/pop.test.ts | 6 +- .../evm/test/{evm => }/opcodes/push.test.ts | 8 +- .../evm/test/{evm => }/opcodes/return.test.ts | 10 +- .../evm/test/{evm => }/opcodes/revert.test.ts | 16 +--- .../evm/test/{evm => }/opcodes/stop.test.ts | 6 +- .../test/{evm => }/opcodes/storage.test.ts | 8 +- .../evm/test/{evm => }/opcodes/swap.test.ts | 10 +- 116 files changed, 329 insertions(+), 694 deletions(-) rename packages/evm/src/{evm => }/Address.ts (100%) rename packages/evm/src/{evm => }/Byte.ts (100%) rename packages/evm/src/{evm => }/Bytes32.ts (100%) rename packages/evm/src/{evm => }/ExecutionContext.ts (71%) create mode 100644 packages/evm/src/ExecutionResult.ts rename packages/evm/src/{evm => }/Memory.ts (72%) create mode 100644 packages/evm/src/Message.ts rename packages/evm/src/{evm => }/Stack.ts (100%) rename packages/evm/src/{evm => }/State.ts (100%) delete mode 100644 packages/evm/src/cloneBlock.ts delete mode 100644 packages/evm/src/createBlock.ts delete mode 100644 packages/evm/src/createGenesisBlock.ts rename packages/evm/src/{evm => }/errors.ts (100%) delete mode 100644 packages/evm/src/evm/Message.ts delete mode 100644 packages/evm/src/evm/executeCode.ts create mode 100644 packages/evm/src/executeCode.ts delete mode 100644 packages/evm/src/executeTransaction.ts delete mode 100644 packages/evm/src/includeTransactions.ts delete mode 100644 packages/evm/src/model/Account.ts delete mode 100644 packages/evm/src/model/Block.ts delete mode 100644 packages/evm/src/model/ExecutedTransaction.ts delete mode 100644 packages/evm/src/model/ExecutionError.ts delete mode 100644 packages/evm/src/model/Log.ts delete mode 100644 packages/evm/src/model/PendingTransaction.ts delete mode 100644 packages/evm/src/model/index.ts delete mode 100644 packages/evm/src/model/utils.ts rename packages/evm/src/{evm => }/opcodes/Opcode.ts (100%) rename packages/evm/src/{evm => }/opcodes/bytes32.ts (100%) rename packages/evm/src/{evm => }/opcodes/control.ts (100%) rename packages/evm/src/{evm => }/opcodes/gasCosts.ts (91%) rename packages/evm/src/{evm => }/opcodes/helpers.ts (100%) rename packages/evm/src/{evm => }/opcodes/index.ts (100%) rename packages/evm/src/{evm => }/opcodes/invalid.ts (100%) rename packages/evm/src/{evm => }/opcodes/memory.ts (100%) rename packages/evm/src/{evm => }/opcodes/stack.ts (100%) rename packages/evm/src/{evm => }/opcodes/storage.ts (100%) rename packages/evm/src/{evm => }/parseBytecode.ts (90%) rename packages/evm/test/{evm => }/Bytes32.test.ts (98%) rename packages/evm/test/{evm => }/ExecutionContext.test.ts (75%) create mode 100644 packages/evm/test/Memory.test.ts rename packages/evm/test/{evm => }/Stack.test.ts (93%) rename packages/evm/test/{evm => }/State.test.ts (95%) delete mode 100644 packages/evm/test/cloneBlock.test.ts delete mode 100644 packages/evm/test/createGenesisBlock.test.ts delete mode 100644 packages/evm/test/evm/Memory.test.ts delete mode 100644 packages/evm/test/evm/exception.test.ts rename packages/evm/test/{evm => }/gasLimit.test.ts (84%) rename packages/evm/test/{evm => }/helpers/Int256.ts (93%) rename packages/evm/test/{evm => }/helpers/executeAssembly.ts (86%) rename packages/evm/test/{evm => }/helpers/expectations.ts (52%) rename packages/evm/test/{evm => }/helpers/index.ts (100%) rename packages/evm/test/{evm => }/helpers/memory.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/deth/add.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/README.md (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/add.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/and.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/byte.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/div.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/eq.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/exp.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/gt.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/lt.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/mod.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/mul.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/or.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/sar.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/sdiv.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/sgt.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/shl.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/shr.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/signextend.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/slt.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/smod.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/sub.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/geth/xor.json (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/helpers.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/index.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/README.md (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/add.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/addmod.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/and.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/byte.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/div.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/eq.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/exp.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/gt.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/iszero.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/lt.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/mod.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/mul.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/mulmod.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/not.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/or.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/sar.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/sdiv.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/sgt.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/shl.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/shr.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/signextend.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/slt.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/smod.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/sub.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/cases/parity/xor.ts (100%) rename packages/evm/test/{evm => }/opcodes/bytes32/machineWord.test.ts (93%) rename packages/evm/test/{evm => }/opcodes/dup.test.ts (66%) rename packages/evm/test/{evm => }/opcodes/invalid.test.ts (92%) rename packages/evm/test/{evm => }/opcodes/jump.test.ts (82%) rename packages/evm/test/{evm => }/opcodes/memory.test.ts (86%) rename packages/evm/test/{evm => }/opcodes/pop.test.ts (58%) rename packages/evm/test/{evm => }/opcodes/push.test.ts (68%) rename packages/evm/test/{evm => }/opcodes/return.test.ts (73%) rename packages/evm/test/{evm => }/opcodes/revert.test.ts (61%) rename packages/evm/test/{evm => }/opcodes/stop.test.ts (54%) rename packages/evm/test/{evm => }/opcodes/storage.test.ts (90%) rename packages/evm/test/{evm => }/opcodes/swap.test.ts (57%) diff --git a/packages/evm/package.json b/packages/evm/package.json index 2882df1..f6ec21b 100644 --- a/packages/evm/package.json +++ b/packages/evm/package.json @@ -22,7 +22,8 @@ }, "dependencies": { "@types/bn.js": "^4.11.5", - "bn.js": "^5.1.1" + "bn.js": "^5.1.1", + "js-sha3": "^0.8.0" }, "devDependencies": { "@types/chai": "^4.2.7", diff --git a/packages/evm/src/evm/Address.ts b/packages/evm/src/Address.ts similarity index 100% rename from packages/evm/src/evm/Address.ts rename to packages/evm/src/Address.ts diff --git a/packages/evm/src/evm/Byte.ts b/packages/evm/src/Byte.ts similarity index 100% rename from packages/evm/src/evm/Byte.ts rename to packages/evm/src/Byte.ts diff --git a/packages/evm/src/evm/Bytes32.ts b/packages/evm/src/Bytes32.ts similarity index 100% rename from packages/evm/src/evm/Bytes32.ts rename to packages/evm/src/Bytes32.ts diff --git a/packages/evm/src/evm/ExecutionContext.ts b/packages/evm/src/ExecutionContext.ts similarity index 71% rename from packages/evm/src/evm/ExecutionContext.ts rename to packages/evm/src/ExecutionContext.ts index 2d47bdf..c57eac2 100644 --- a/packages/evm/src/evm/ExecutionContext.ts +++ b/packages/evm/src/ExecutionContext.ts @@ -1,6 +1,6 @@ import { Stack } from './Stack' import { OutOfGas } from './errors' -import { Memory, GasAwareMemory } from './Memory' +import { Memory } from './Memory' import { State } from './State' import { Byte } from './Byte' import { Message } from './Message' @@ -10,8 +10,7 @@ import { parseBytecode } from './parseBytecode' export class ExecutionContext { code: Opcode[] stack = new Stack() - memory: GasAwareMemory - state: State + memory: Memory returnValue?: Byte[] reverted = false programCounter = 0 @@ -19,13 +18,9 @@ export class ExecutionContext { private _gasUsed = 0 private _gasRefund = 0 - constructor (public message: Message) { - this.state = message.state.clone() + constructor (public message: Message, public state: State) { this.code = parseBytecode(message.code) - this.memory = new GasAwareMemory( - new Memory(), - this.useGas.bind(this), - ) + this.memory = new Memory(this.useGas.bind(this)) } get gasUsed () { @@ -40,10 +35,6 @@ export class ExecutionContext { } } - useRemainingGas () { - this._gasUsed = this.message.gasLimit - } - get gasRefund () { return this._gasRefund } diff --git a/packages/evm/src/ExecutionResult.ts b/packages/evm/src/ExecutionResult.ts new file mode 100644 index 0000000..9033ede --- /dev/null +++ b/packages/evm/src/ExecutionResult.ts @@ -0,0 +1,27 @@ +import { State } from './State' +import { Byte } from './Byte' +import { VMError } from './errors' + +export type ExecutionResult = + | ExecutionSuccess + | ExecutionRevert + | ExecutionError + +export interface ExecutionSuccess { + type: 'ExecutionSuccess', + state: State, + gasUsed: number, + gasRefund: number, + returnValue: Byte[], +} + +export interface ExecutionRevert { + type: 'ExecutionRevert', + gasUsed: number, + returnValue: Byte[], +} + +export interface ExecutionError { + type: 'ExecutionError', + error: VMError, +} diff --git a/packages/evm/src/evm/Memory.ts b/packages/evm/src/Memory.ts similarity index 72% rename from packages/evm/src/evm/Memory.ts rename to packages/evm/src/Memory.ts index cbab27f..f02d545 100644 --- a/packages/evm/src/evm/Memory.ts +++ b/packages/evm/src/Memory.ts @@ -1,20 +1,38 @@ import { GasCost } from './opcodes' import { Byte } from './Byte' -export interface IMemory { - getSize (): number, - getBytes (offset: number, length: number): Byte[], - setBytes (offset: number, bytes: Byte[]): void, -} +export class Memory { + private items: Byte[] = [] + private memoryUsed = 0 + private gasUsed = 0 -export class GasAwareMemory implements IMemory { constructor ( - public memory: Memory, private useGas: (gas: number) => void, ) {} - private memoryUsed = 0 - private gasUsed = 0 + getSize () { + return this.items.length + } + + getBytes (offset: number, length: number) { + this.onMemoryAccess(offset, length) + if (length === 0) { + return [] + } + this.expand(offset + length) + return this.items.slice(offset, offset + length) + } + + setBytes (offset: number, bytes: Byte[]) { + this.onMemoryAccess(offset, bytes.length) + if (bytes.length === 0) { + return + } + this.expand(offset + bytes.length) + for (let i = 0; i < bytes.length; i++) { + this.items[offset + i] = bytes[i] + } + } private onMemoryAccess (offset: number, length: number) { if (length === 0) { @@ -31,50 +49,10 @@ export class GasAwareMemory implements IMemory { } } - getSize () { - return this.memory.getSize() - } - - getBytes (offset: number, length: number) { - this.onMemoryAccess(offset, length) - return this.memory.getBytes(offset, length) - } - - setBytes (offset: number, bytes: Byte[]) { - this.onMemoryAccess(offset, bytes.length) - this.memory.setBytes(offset, bytes) - } -} - -export class Memory implements IMemory { - private items: Byte[] = [] - private expand (targetSize: number) { const targetLength = 32 * Math.ceil(targetSize / 32) for (let i = this.items.length; i < targetLength; i++) { this.items[i] = 0 as Byte } } - - getSize () { - return this.items.length - } - - getBytes (offset: number, length: number) { - if (length === 0) { - return [] - } - this.expand(offset + length) - return this.items.slice(offset, offset + length) - } - - setBytes (offset: number, bytes: Byte[]) { - if (bytes.length === 0) { - return - } - this.expand(offset + bytes.length) - for (let i = 0; i < bytes.length; i++) { - this.items[offset + i] = bytes[i] - } - } } diff --git a/packages/evm/src/Message.ts b/packages/evm/src/Message.ts new file mode 100644 index 0000000..6d63087 --- /dev/null +++ b/packages/evm/src/Message.ts @@ -0,0 +1,16 @@ +import { Address } from './Address' +import { Bytes32 } from './Bytes32' +import { Byte } from './Byte' + +export interface Message { + readonly account: Address, + readonly code: readonly Byte[], + readonly data: readonly Byte[], + readonly origin: Address, + readonly sender: Address, + readonly gasLimit: number, + readonly gasPrice: Bytes32, + readonly value: Bytes32, + readonly enableStateModifications: boolean, + readonly callDepth: number, +} diff --git a/packages/evm/src/evm/Stack.ts b/packages/evm/src/Stack.ts similarity index 100% rename from packages/evm/src/evm/Stack.ts rename to packages/evm/src/Stack.ts diff --git a/packages/evm/src/evm/State.ts b/packages/evm/src/State.ts similarity index 100% rename from packages/evm/src/evm/State.ts rename to packages/evm/src/State.ts diff --git a/packages/evm/src/cloneBlock.ts b/packages/evm/src/cloneBlock.ts deleted file mode 100644 index 6419a20..0000000 --- a/packages/evm/src/cloneBlock.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Bytes20, Bytes32, Account, Block } from './model' - -export function cloneBlock (block: Block): Block { - return { - parent: block.parent, - accounts: cloneAccounts(block.accounts), - transactions: [...block.transactions], - parameters: { ...block.parameters }, - } -} - -function cloneAccounts (accounts: Map) { - const clone = new Map() - for (const [address, account] of accounts) { - clone.set(address, cloneAccount(account)) - } - return clone -} - -function cloneAccount (account: Account): Account { - return { - balance: account.balance, - code: account.code, - nonce: account.nonce, - storage: cloneStorage(account.storage), - } -} - -function cloneStorage (storage: Map) { - return new Map(storage) -} diff --git a/packages/evm/src/createBlock.ts b/packages/evm/src/createBlock.ts deleted file mode 100644 index 0b8d5a7..0000000 --- a/packages/evm/src/createBlock.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Block, PendingTransaction, BlockParameters } from './model' -import { includeTransactions } from './includeTransactions' - -export function createBlock ( - parent: Block, - transactions: PendingTransaction[], - parameters: BlockParameters, -): Block { - const block = { - parent, - accounts: parent.accounts, - parameters: { ...parameters }, - transactions: [], - } - return includeTransactions(block, transactions) -} diff --git a/packages/evm/src/createGenesisBlock.ts b/packages/evm/src/createGenesisBlock.ts deleted file mode 100644 index d4cfd5e..0000000 --- a/packages/evm/src/createGenesisBlock.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Block, Bytes20, Bytes32, Account, BlockParameters } from './model' - -export interface GenesisBlockParams { - balances: Map, - parameters: BlockParameters, -} - -export function createGenesisBlock (params: GenesisBlockParams): Block { - return { - parent: undefined, - accounts: accountsFromBalances(params.balances), - transactions: [], - parameters: { ...params.parameters }, - } -} - -function accountsFromBalances (balances: Map) { - const accounts = new Map() - for (const [address, balance] of balances) { - accounts.set(address, { - balance, - storage: new Map(), - nonce: 0, - code: '0x', - }) - } - return accounts -} diff --git a/packages/evm/src/evm/errors.ts b/packages/evm/src/errors.ts similarity index 100% rename from packages/evm/src/evm/errors.ts rename to packages/evm/src/errors.ts diff --git a/packages/evm/src/evm/Message.ts b/packages/evm/src/evm/Message.ts deleted file mode 100644 index 0db728a..0000000 --- a/packages/evm/src/evm/Message.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Address } from './Address' -import { Bytes32 } from './Bytes32' -import { Byte } from './Byte' -import { State } from './State' - -export interface Message { - account: Address, - code: Byte[], - data: Byte[], - origin: Address, - sender: Address, - gasLimit: number, - gasPrice: Bytes32, - value: Bytes32, - enableStateModifications: boolean, - callDepth: number, - state: State, -} diff --git a/packages/evm/src/evm/executeCode.ts b/packages/evm/src/evm/executeCode.ts deleted file mode 100644 index 4f775a3..0000000 --- a/packages/evm/src/evm/executeCode.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Byte } from './Byte' -import { ExecutionContext } from './ExecutionContext' -import { Memory } from './Memory' -import { Message } from './Message' -import { opSTOP } from './opcodes/control' -import { Stack } from './Stack' -import { State } from './State' -import { VMError } from './errors' - -export interface ExecutionResult { - stack: Stack, - memory: Memory, - state: State, - gasUsed: number, - gasRefund: number, - programCounter: number, - reverted: boolean, - returnValue?: Byte[], - error?: VMError, -} - -export function executeCode (message: Message): ExecutionResult { - const ctx = new ExecutionContext(message) - while (ctx.returnValue === undefined) { - const opcode = ctx.code[ctx.programCounter] || opSTOP - ctx.programCounter++ - try { - opcode(ctx) - } catch (e) { - if (e instanceof VMError) { - ctx.useRemainingGas() - return toResult(ctx, e) - } else { - throw e // this should never happen - } - } - } - return toResult(ctx) -} - -function toResult (ctx: ExecutionContext, error?: VMError): ExecutionResult { - return { - stack: ctx.stack, - memory: ctx.memory.memory, - state: (ctx.reverted || error) ? ctx.message.state : ctx.state, - gasUsed: ctx.gasUsed, - gasRefund: ctx.gasRefund, - reverted: ctx.reverted, - programCounter: ctx.programCounter, - returnValue: ctx.returnValue, - error, - } -} diff --git a/packages/evm/src/executeCode.ts b/packages/evm/src/executeCode.ts new file mode 100644 index 0000000..5019df8 --- /dev/null +++ b/packages/evm/src/executeCode.ts @@ -0,0 +1,44 @@ +import { ExecutionContext } from './ExecutionContext' +import { Message } from './Message' +import { opSTOP } from './opcodes/control' +import { VMError } from './errors' +import { ExecutionResult } from './ExecutionResult' +import { State } from './State' + +export function executeCode (message: Message, state: State): ExecutionResult { + const ctx = new ExecutionContext(message, state) + + while (ctx.returnValue === undefined) { + const opcode = ctx.code[ctx.programCounter] || opSTOP + ctx.programCounter++ + try { + opcode(ctx) + } catch (error) { + if (error instanceof VMError) { + return { + type: 'ExecutionError', + error, + } + } else { + // programmer error + throw error + } + } + } + + if (ctx.reverted) { + return { + type: 'ExecutionRevert', + gasUsed: ctx.gasUsed, + returnValue: ctx.returnValue, + } + } else { + return { + type: 'ExecutionSuccess', + gasUsed: ctx.gasUsed, + gasRefund: ctx.gasRefund, + returnValue: ctx.returnValue, + state: ctx.state, + } + } +} diff --git a/packages/evm/src/executeTransaction.ts b/packages/evm/src/executeTransaction.ts deleted file mode 100644 index 57bec59..0000000 --- a/packages/evm/src/executeTransaction.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Bytes20, Log, Block, PendingTransaction } from './model' -import { ExecutionError } from './model/ExecutionError' - -export interface TransactionExecutionResult { - gasUsed: number, - contractAddress: Bytes20 | null, - logs: Log[], - error: ExecutionError | null, -} - -/** - * Alter block state and return transaction execution results - * @param block a block to execute the transaction on - * @param transaction a transaction to execute - */ -export function executeTransaction ( - block: Block, - transaction: PendingTransaction, -): TransactionExecutionResult { - // TODO: real implementation - return { - gasUsed: 21_000, - contractAddress: null, - logs: [], - error: null, - } -} diff --git a/packages/evm/src/includeTransactions.ts b/packages/evm/src/includeTransactions.ts deleted file mode 100644 index 2a41d01..0000000 --- a/packages/evm/src/includeTransactions.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Block, PendingTransaction } from './model' -import { cloneBlock } from './cloneBlock' -import { executeTransaction } from './executeTransaction' - -export function includeTransactions ( - block: Block, - transactions: PendingTransaction[], -): Block { - return transactions.reduce(includeTransaction, block) -} - -function includeTransaction ( - block: Block, - transaction: PendingTransaction, -): Block { - const cloned = cloneBlock(block) - - const result = executeTransaction(cloned, transaction) - - if (!result.error) { - cloned.parameters.gasUsed += result.gasUsed - cloned.transactions.push({ - from: transaction.from, - to: transaction.to, - hash: transaction.hash, - contractAddress: result.contractAddress, - logs: result.logs, - gasUsed: result.gasUsed, - cumulativeGasUsed: cloned.parameters.gasUsed, - error: null, - }) - - return cloned - } else { - const reverted = cloneBlock(block) - - reverted.parameters.gasUsed += result.gasUsed - reverted.transactions.push({ - from: transaction.from, - to: transaction.to, - hash: transaction.hash, - contractAddress: null, - logs: [], - gasUsed: result.gasUsed, - cumulativeGasUsed: reverted.parameters.gasUsed, - error: result.error, - }) - - return reverted - } -} diff --git a/packages/evm/src/model/Account.ts b/packages/evm/src/model/Account.ts deleted file mode 100644 index 7d7ac5a..0000000 --- a/packages/evm/src/model/Account.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Bytes32, Bytes } from './utils' - -export interface Account { - balance: Bytes32, - nonce: number, - storage: Map, - code: Bytes, -} diff --git a/packages/evm/src/model/Block.ts b/packages/evm/src/model/Block.ts deleted file mode 100644 index a75a8f1..0000000 --- a/packages/evm/src/model/Block.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ExecutedTransaction } from './ExecutedTransaction' -import { Account } from './Account' -import { Bytes20, Bytes32, Bytes } from './utils' - -export interface Block { - parent?: Block, - accounts: Map, - transactions: ExecutedTransaction[], - parameters: BlockParameters, -} - -export interface BlockParameters { - hash: Bytes32, - number: number, - timestamp: number, - gasLimit: number, - gasUsed: number, - extraData: Bytes, - miner: Bytes20, -} diff --git a/packages/evm/src/model/ExecutedTransaction.ts b/packages/evm/src/model/ExecutedTransaction.ts deleted file mode 100644 index 59ac8e7..0000000 --- a/packages/evm/src/model/ExecutedTransaction.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Bytes32, Bytes20 } from './utils' -import { Log } from './Log' -import { ExecutionError } from './ExecutionError' - -export interface ExecutedTransaction { - hash: Bytes32, - from: Bytes20, - to: Bytes20, - gasUsed: number, - cumulativeGasUsed: number, - contractAddress: Bytes20 | null, - logs: Log[], - error: ExecutionError | null, -} diff --git a/packages/evm/src/model/ExecutionError.ts b/packages/evm/src/model/ExecutionError.ts deleted file mode 100644 index d9fe0da..0000000 --- a/packages/evm/src/model/ExecutionError.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface ExecutionError { - message: string, -} diff --git a/packages/evm/src/model/Log.ts b/packages/evm/src/model/Log.ts deleted file mode 100644 index 2c110c9..0000000 --- a/packages/evm/src/model/Log.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Bytes, Bytes32, Bytes20 } from './utils' - -export interface Log { - address: Bytes20, - data: Bytes, - topics: Bytes32[], -} diff --git a/packages/evm/src/model/PendingTransaction.ts b/packages/evm/src/model/PendingTransaction.ts deleted file mode 100644 index 3d5c02c..0000000 --- a/packages/evm/src/model/PendingTransaction.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Bytes32, Bytes20 } from './utils' - -export interface PendingTransaction { - hash: Bytes32, - from: Bytes20, - to: Bytes20, - value: Bytes32, -} diff --git a/packages/evm/src/model/index.ts b/packages/evm/src/model/index.ts deleted file mode 100644 index 85488f4..0000000 --- a/packages/evm/src/model/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export * from './Account' -export * from './Block' -export * from './ExecutedTransaction' -export * from './ExecutionError' -export * from './Log' -export * from './PendingTransaction' -export * from './PendingTransaction' -export * from './utils' diff --git a/packages/evm/src/model/utils.ts b/packages/evm/src/model/utils.ts deleted file mode 100644 index 25b61d5..0000000 --- a/packages/evm/src/model/utils.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type Bytes20 = string -export type Bytes32 = string -export type Bytes256 = string -export type Bytes = string diff --git a/packages/evm/src/evm/opcodes/Opcode.ts b/packages/evm/src/opcodes/Opcode.ts similarity index 100% rename from packages/evm/src/evm/opcodes/Opcode.ts rename to packages/evm/src/opcodes/Opcode.ts diff --git a/packages/evm/src/evm/opcodes/bytes32.ts b/packages/evm/src/opcodes/bytes32.ts similarity index 100% rename from packages/evm/src/evm/opcodes/bytes32.ts rename to packages/evm/src/opcodes/bytes32.ts diff --git a/packages/evm/src/evm/opcodes/control.ts b/packages/evm/src/opcodes/control.ts similarity index 100% rename from packages/evm/src/evm/opcodes/control.ts rename to packages/evm/src/opcodes/control.ts diff --git a/packages/evm/src/evm/opcodes/gasCosts.ts b/packages/evm/src/opcodes/gasCosts.ts similarity index 91% rename from packages/evm/src/evm/opcodes/gasCosts.ts rename to packages/evm/src/opcodes/gasCosts.ts index ba47026..6c7d06b 100644 --- a/packages/evm/src/evm/opcodes/gasCosts.ts +++ b/packages/evm/src/opcodes/gasCosts.ts @@ -10,6 +10,7 @@ export const GasCost = { SLOAD: 200, SSET: 20_000, SRESET: 5_000, + CODEDEPOSIT: 200, } export const GasRefund = { diff --git a/packages/evm/src/evm/opcodes/helpers.ts b/packages/evm/src/opcodes/helpers.ts similarity index 100% rename from packages/evm/src/evm/opcodes/helpers.ts rename to packages/evm/src/opcodes/helpers.ts diff --git a/packages/evm/src/evm/opcodes/index.ts b/packages/evm/src/opcodes/index.ts similarity index 100% rename from packages/evm/src/evm/opcodes/index.ts rename to packages/evm/src/opcodes/index.ts diff --git a/packages/evm/src/evm/opcodes/invalid.ts b/packages/evm/src/opcodes/invalid.ts similarity index 100% rename from packages/evm/src/evm/opcodes/invalid.ts rename to packages/evm/src/opcodes/invalid.ts diff --git a/packages/evm/src/evm/opcodes/memory.ts b/packages/evm/src/opcodes/memory.ts similarity index 100% rename from packages/evm/src/evm/opcodes/memory.ts rename to packages/evm/src/opcodes/memory.ts diff --git a/packages/evm/src/evm/opcodes/stack.ts b/packages/evm/src/opcodes/stack.ts similarity index 100% rename from packages/evm/src/evm/opcodes/stack.ts rename to packages/evm/src/opcodes/stack.ts diff --git a/packages/evm/src/evm/opcodes/storage.ts b/packages/evm/src/opcodes/storage.ts similarity index 100% rename from packages/evm/src/evm/opcodes/storage.ts rename to packages/evm/src/opcodes/storage.ts diff --git a/packages/evm/src/evm/parseBytecode.ts b/packages/evm/src/parseBytecode.ts similarity index 90% rename from packages/evm/src/evm/parseBytecode.ts rename to packages/evm/src/parseBytecode.ts index d827deb..cc0277d 100644 --- a/packages/evm/src/evm/parseBytecode.ts +++ b/packages/evm/src/parseBytecode.ts @@ -2,11 +2,13 @@ import { InvalidBytecode } from './errors' import { Opcode, getOpcode, makeOpPUSH, opUnreachable } from './opcodes' import { Byte } from './Byte' -export function parseBytecode (bytes: Byte[]) { +export function parseBytecode (bytes: readonly Byte[]) { const result: Opcode[] = [] for (let i = 0; i < bytes.length; i++) { const pushSize = getPushSize(bytes[i]) - if (pushSize !== 0) { + if (pushSize === 0) { + result.push(getOpcode(bytes[i])) + } else { if (i + pushSize >= bytes.length) { throw new InvalidBytecode() } @@ -16,8 +18,6 @@ export function parseBytecode (bytes: Byte[]) { result.push(opUnreachable) } i += pushSize - } else { - result.push(getOpcode(bytes[i])) } } return result diff --git a/packages/evm/test/evm/Bytes32.test.ts b/packages/evm/test/Bytes32.test.ts similarity index 98% rename from packages/evm/test/evm/Bytes32.test.ts rename to packages/evm/test/Bytes32.test.ts index 6e93fb7..a0ae3d3 100644 --- a/packages/evm/test/evm/Bytes32.test.ts +++ b/packages/evm/test/Bytes32.test.ts @@ -1,8 +1,8 @@ import { expect } from 'chai' -import { Bytes32 } from '../../src/evm/Bytes32' +import { Bytes32 } from '../src/Bytes32' import { TestCases } from './opcodes/bytes32/cases' import { TestCase } from './opcodes/bytes32/cases/helpers' -import { Byte } from '../../src/evm/Byte' +import { Byte } from '../src/Byte' describe('Bytes32', () => { runTestCases('add', TestCases.ADD) diff --git a/packages/evm/test/evm/ExecutionContext.test.ts b/packages/evm/test/ExecutionContext.test.ts similarity index 75% rename from packages/evm/test/evm/ExecutionContext.test.ts rename to packages/evm/test/ExecutionContext.test.ts index c4d6cb2..70572bc 100644 --- a/packages/evm/test/evm/ExecutionContext.test.ts +++ b/packages/evm/test/ExecutionContext.test.ts @@ -1,14 +1,15 @@ import { expect } from 'chai' -import { ExecutionContext } from '../../src/evm/ExecutionContext' -import { OutOfGas } from '../../src/evm/errors' +import { ExecutionContext } from '../src/ExecutionContext' +import { OutOfGas } from '../src/errors' import { DEFAULT_MESSAGE } from './helpers' +import { State } from '../src/State' describe('ExecutionContext', () => { function makeContext (gasLimit: number) { return new ExecutionContext({ ...DEFAULT_MESSAGE, gasLimit, - }) + }, new State()) } it('can track gas usage', () => { @@ -25,12 +26,6 @@ describe('ExecutionContext', () => { expect(() => ctx.useGas(1)).to.throw(OutOfGas) }) - it('can use all gas', () => { - const ctx = makeContext(300) - ctx.useRemainingGas() - expect(ctx.gasUsed).to.equal(300) - }) - it('can track refund', () => { const ctx = makeContext(Infinity) ctx.useGas(100) diff --git a/packages/evm/test/Memory.test.ts b/packages/evm/test/Memory.test.ts new file mode 100644 index 0000000..60966ba --- /dev/null +++ b/packages/evm/test/Memory.test.ts @@ -0,0 +1,91 @@ +import { expect } from 'chai' +import { Memory } from '../src/Memory' +import { memoryGas } from './helpers' +import { Byte } from '../src/Byte' + +describe('Memory', () => { + let gasUsed = 0 + const useGas = (gas: number) => { + gasUsed += gas + } + + beforeEach(() => { + gasUsed = 0 + }) + + it('starts with zero size', () => { + const memory = new Memory(useGas) + expect(memory.getSize()).to.equal(0) + }) + + it('expands size and pads it to 32 bytes', () => { + const memory = new Memory(useGas) + memory.setBytes(0, [0xAB, 0xCD] as Byte[]) + expect(memory.getSize()).to.equal(32) + memory.setBytes(31, [0xAB, 0xCD] as Byte[]) + expect(memory.getSize()).to.equal(64) + }) + + it('expands filling space with zeroes', () => { + const memory = new Memory(useGas) + memory.setBytes(0, [0xAB, 0xCD] as Byte[]) + memory.setBytes(3, [0xEF] as Byte[]) + expect(memory.getBytes(0, 5)).to.deep.equal([0xAB, 0xCD, 0x00, 0xEF, 0x00]) + }) + + it('getBytes results in memory expansion', () => { + const memory = new Memory(useGas) + const bytes = memory.getBytes(10, 1) + expect(bytes).to.deep.equal([0x00]) + expect(memory.getSize()).to.equal(32) + }) + + it('zero length getBytes does not expand the memory', () => { + const memory = new Memory(useGas) + const bytes = memory.getBytes(10_000, 0) + expect(bytes).to.deep.equal([]) + expect(memory.getSize()).to.equal(0) + }) + + it('zero length setBytes does not expand the memory', () => { + const memory = new Memory(useGas) + memory.setBytes(10_000, []) + expect(memory.getSize()).to.equal(0) + }) + + describe('gas usage', () => { + it('can track a single memory usage', () => { + const memory = new Memory(useGas) + memory.getBytes(300, 1) + expect(gasUsed).to.equal(memoryGas(301)) + }) + + it('does not use any gas on zero length access', () => { + const memory = new Memory(useGas) + memory.getBytes(31415, 0) + expect(gasUsed).to.equal(0) + }) + + it('can track multiple memory uses', () => { + const memory = new Memory(useGas) + memory.getBytes(0, 100) + memory.getBytes(9_000, 2_000) + memory.getBytes(15_000_000, 3) + memory.getBytes(11, 12) + expect(gasUsed).to.equal(memoryGas(15_000_003)) + }) + + it('tracks memory use for setBytes', () => { + const memory = new Memory(useGas) + memory.setBytes(300, [0xBA] as Byte[]) + expect(gasUsed).to.equal(memoryGas(301)) + }) + + it('forwards getSize', () => { + const memory = new Memory(useGas) + memory.setBytes(300, [0xBA] as Byte[]) + const size = memory.getSize() + expect(size).to.equal(Math.ceil(301 / 32) * 32) + }) + }) +}) diff --git a/packages/evm/test/evm/Stack.test.ts b/packages/evm/test/Stack.test.ts similarity index 93% rename from packages/evm/test/evm/Stack.test.ts rename to packages/evm/test/Stack.test.ts index 2d5ae43..1ecb8cf 100644 --- a/packages/evm/test/evm/Stack.test.ts +++ b/packages/evm/test/Stack.test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai' -import { Bytes32 } from '../../src/evm/Bytes32' -import { Stack } from '../../src/evm/Stack' -import { StackOverflow, StackUnderflow } from '../../src/evm/errors' +import { Bytes32 } from '../src/Bytes32' +import { Stack } from '../src/Stack' +import { StackOverflow, StackUnderflow } from '../src/errors' const A = Bytes32.ONE const B = A.add(A) diff --git a/packages/evm/test/evm/State.test.ts b/packages/evm/test/State.test.ts similarity index 95% rename from packages/evm/test/evm/State.test.ts rename to packages/evm/test/State.test.ts index 1e94ee3..0f2f1fa 100644 --- a/packages/evm/test/evm/State.test.ts +++ b/packages/evm/test/State.test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai' -import { State } from '../../src/evm/State' -import { Bytes32 } from '../../src/evm/Bytes32' -import { Byte } from '../../src/evm/Byte' +import { State } from '../src/State' +import { Bytes32 } from '../src/Bytes32' +import { Byte } from '../src/Byte' import { ADDRESS_ZERO } from './helpers' describe('State', () => { diff --git a/packages/evm/test/cloneBlock.test.ts b/packages/evm/test/cloneBlock.test.ts deleted file mode 100644 index 61bce3e..0000000 --- a/packages/evm/test/cloneBlock.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { expect } from 'chai' -import { cloneBlock } from '../src/cloneBlock' -import { Block, Account } from '../src/model' - -describe('cloneBlock', () => { - it('correctly clones a block', () => { - const account: Account = { - balance: '10', - code: '0x123', - nonce: 42, - storage: new Map([ - ['1', '2'], - ['3', '4'], - ]), - } - const block: Block = { - accounts: new Map([ - ['123', account], - ]), - parameters: { - gasLimit: 1000, - } as any, - transactions: [], - } - block.parent = block - - const clone = cloneBlock(block) - - expect(clone).to.deep.equal(block) - expect(clone.parent).to.equal(block) - expect(clone.accounts).not.to.equal(block.accounts) - expect(clone.accounts.get('123')).not.to.equal(block.accounts.get('123')) - expect(clone.parameters).not.to.equal(block.parameters) - expect(clone.transactions).not.to.equal(block.transactions) - }) -}) diff --git a/packages/evm/test/createGenesisBlock.test.ts b/packages/evm/test/createGenesisBlock.test.ts deleted file mode 100644 index cfcc61a..0000000 --- a/packages/evm/test/createGenesisBlock.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { expect } from 'chai' -import { createGenesisBlock } from '../src/createGenesisBlock' -import { BlockParameters } from '../src/model' - -describe('createGenesisBlock', () => { - const parameters: BlockParameters = { - extraData: '0x', - gasLimit: 1234, - gasUsed: 123, - hash: '0x12ab', - miner: '0x34bc', - number: 1, - timestamp: 100000, - } - - it('creates an empty block', () => { - const block = createGenesisBlock({ - balances: new Map(), - parameters, - }) - - expect(block).to.deep.equal({ - parent: undefined, - accounts: new Map(), - transactions: [], - parameters, - }) - expect(block.parameters).not.to.equal(parameters) - }) - - it('creates a block with some accounts', () => { - const block = createGenesisBlock({ - balances: new Map([ - ['0x1234', '0x4000'], - ['0x56EF', '0x2137'], - ]), - parameters, - }) - - expect(block).to.deep.equal({ - parent: undefined, - accounts: new Map([ - ['0x1234', { - balance: '0x4000', - storage: new Map(), - nonce: 0, - code: '0x', - }], - ['0x56EF', { - balance: '0x2137', - storage: new Map(), - nonce: 0, - code: '0x', - }], - ]), - transactions: [], - parameters, - }) - expect(block.parameters).not.to.equal(parameters) - }) -}) diff --git a/packages/evm/test/evm/Memory.test.ts b/packages/evm/test/evm/Memory.test.ts deleted file mode 100644 index 88fc73a..0000000 --- a/packages/evm/test/evm/Memory.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { expect } from 'chai' -import { Memory, GasAwareMemory } from '../../src/evm/Memory' -import { memoryGas } from './helpers' -import { Byte } from '../../src/evm/Byte' - -describe('Memory', () => { - it('starts with zero size', () => { - const memory = new Memory() - expect(memory.getSize()).to.equal(0) - }) - - it('expands size and pads it to 32 bytes', () => { - const memory = new Memory() - memory.setBytes(0, [0xAB, 0xCD] as Byte[]) - expect(memory.getSize()).to.equal(32) - memory.setBytes(31, [0xAB, 0xCD] as Byte[]) - expect(memory.getSize()).to.equal(64) - }) - - it('expands filling space with zeroes', () => { - const memory = new Memory() - memory.setBytes(0, [0xAB, 0xCD] as Byte[]) - memory.setBytes(3, [0xEF] as Byte[]) - expect(memory.getBytes(0, 5)).to.deep.equal([0xAB, 0xCD, 0x00, 0xEF, 0x00]) - }) - - it('getBytes results in memory expansion', () => { - const memory = new Memory() - const bytes = memory.getBytes(10, 1) - expect(bytes).to.deep.equal([0x00]) - expect(memory.getSize()).to.equal(32) - }) - - it('zero length getBytes does not expand the memory', () => { - const memory = new Memory() - const bytes = memory.getBytes(10_000, 0) - expect(bytes).to.deep.equal([]) - expect(memory.getSize()).to.equal(0) - }) - - it('zero length setBytes does not expand the memory', () => { - const memory = new Memory() - memory.setBytes(10_000, []) - expect(memory.getSize()).to.equal(0) - }) -}) - -describe('GasAwareMemory', () => { - let gasUsed = 0 - const useGas = (gas: number) => { - gasUsed += gas - } - - beforeEach(() => { - gasUsed = 0 - }) - - it('can track a single memory usage', () => { - const memory = new GasAwareMemory(new Memory(), useGas) - memory.getBytes(300, 1) - expect(gasUsed).to.equal(memoryGas(301)) - }) - - it('does not use any gas on zero length access', () => { - const memory = new GasAwareMemory(new Memory(), useGas) - memory.getBytes(31415, 0) - expect(gasUsed).to.equal(0) - }) - - it('can track multiple memory uses', () => { - const memory = new GasAwareMemory(new Memory(), useGas) - memory.getBytes(0, 100) - memory.getBytes(9_000, 2_000) - memory.getBytes(15_000_000, 3) - memory.getBytes(11, 12) - expect(gasUsed).to.equal(memoryGas(15_000_003)) - }) - - it('tracks memory use for setBytes', () => { - const memory = new GasAwareMemory(new Memory(), useGas) - memory.setBytes(300, [0xBA] as Byte[]) - expect(gasUsed).to.equal(memoryGas(301)) - }) - - it('forwards getSize', () => { - const memory = new GasAwareMemory(new Memory(), useGas) - memory.setBytes(300, [0xBA] as Byte[]) - const size = memory.getSize() - expect(size).to.equal(Math.ceil(301 / 32) * 32) - }) -}) diff --git a/packages/evm/test/evm/exception.test.ts b/packages/evm/test/evm/exception.test.ts deleted file mode 100644 index 744e82c..0000000 --- a/packages/evm/test/evm/exception.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { expect } from 'chai' -import { executeAssembly, ADDRESS_ZERO } from './helpers' -import { InvalidOpcode } from '../../src/evm/errors' -import { Bytes32 } from '../../src/evm/Bytes32' -import { State } from '../../src/evm/State' - -describe('When an exception occurs', () => { - it('all gas is used', () => { - const result = executeAssembly('INVALID', { gasLimit: 1_000_000 }) - - expect(result.error).to.be.instanceOf(InvalidOpcode) - expect(result.gasUsed).to.equal(1_000_000) - }) - - it('state changes are reverted', () => { - const account = ADDRESS_ZERO - const state = new State() - state.setStorage(account, Bytes32.ZERO, Bytes32.ONE) - - const assembly = ` - PUSH1 02 - PUSH1 00 - SSTORE - PUSH1 03 - PUSH1 01 - SSTORE - INVALID - ` - const result = executeAssembly(assembly, { account, state }) - - expect(result.error).to.be.instanceOf(InvalidOpcode) - const storageAt0 = result.state.getStorage(account, Bytes32.ZERO) - const storageAt1 = result.state.getStorage(account, Bytes32.ONE) - expect(storageAt0.equals(Bytes32.ONE)).to.equal(true) - expect(storageAt1.equals(Bytes32.ZERO)).to.equal(true) - }) -}) diff --git a/packages/evm/test/evm/gasLimit.test.ts b/packages/evm/test/gasLimit.test.ts similarity index 84% rename from packages/evm/test/evm/gasLimit.test.ts rename to packages/evm/test/gasLimit.test.ts index af51059..4946860 100644 --- a/packages/evm/test/evm/gasLimit.test.ts +++ b/packages/evm/test/gasLimit.test.ts @@ -1,5 +1,5 @@ import { expectError } from './helpers' -import { OutOfGas } from '../../src/evm/errors' +import { OutOfGas } from '../src/errors' describe('gasLimit', () => { it('fails with OutOfGas when reached', () => { diff --git a/packages/evm/test/evm/helpers/Int256.ts b/packages/evm/test/helpers/Int256.ts similarity index 93% rename from packages/evm/test/evm/helpers/Int256.ts rename to packages/evm/test/helpers/Int256.ts index 95ec028..cced4b0 100644 --- a/packages/evm/test/evm/helpers/Int256.ts +++ b/packages/evm/test/helpers/Int256.ts @@ -1,4 +1,4 @@ -import { Bytes32 } from '../../../src/evm/Bytes32' +import { Bytes32 } from '../../src/Bytes32' const HEX_REGEX = /^0x[\da-f]*$/ diff --git a/packages/evm/test/evm/helpers/executeAssembly.ts b/packages/evm/test/helpers/executeAssembly.ts similarity index 86% rename from packages/evm/test/evm/helpers/executeAssembly.ts rename to packages/evm/test/helpers/executeAssembly.ts index 16c3af9..2b3108a 100644 --- a/packages/evm/test/evm/helpers/executeAssembly.ts +++ b/packages/evm/test/helpers/executeAssembly.ts @@ -1,9 +1,9 @@ -import { executeCode } from '../../../src/evm/executeCode' -import { State } from '../../../src/evm/State' -import { Address } from '../../../src/evm/Address' -import { Byte } from '../../../src/evm/Byte' -import { Message } from '../../../src/evm/Message' -import { Bytes32 } from '../../../src/evm/Bytes32' +import { executeCode } from '../../src/executeCode' +import { State } from '../../src/State' +import { Address } from '../../src/Address' +import { Byte } from '../../src/Byte' +import { Message } from '../../src/Message' +import { Bytes32 } from '../../src/Bytes32' export const ADDRESS_ZERO = '0'.repeat(40) as Address @@ -13,7 +13,6 @@ export const DEFAULT_MESSAGE: Message = { origin: ADDRESS_ZERO, gasLimit: 1_000_000_000, gasPrice: Bytes32.ZERO, - state: new State(), callDepth: 0, data: [], enableStateModifications: true, @@ -21,9 +20,13 @@ export const DEFAULT_MESSAGE: Message = { code: [], } -export function executeAssembly (assembly: string, params?: Partial) { +export function executeAssembly ( + assembly: string, + params?: Partial, + state = new State(), +) { const code = assemblyToBytecode(assembly) - return executeCode({ ...DEFAULT_MESSAGE, ...params, code }) + return executeCode({ ...DEFAULT_MESSAGE, ...params, code }, state) } function assemblyToBytecode (code: string): Byte[] { diff --git a/packages/evm/test/evm/helpers/expectations.ts b/packages/evm/test/helpers/expectations.ts similarity index 52% rename from packages/evm/test/evm/helpers/expectations.ts rename to packages/evm/test/helpers/expectations.ts index 638afb8..4492592 100644 --- a/packages/evm/test/evm/helpers/expectations.ts +++ b/packages/evm/test/helpers/expectations.ts @@ -1,9 +1,10 @@ import { expect } from 'chai' -import { StackUnderflow } from '../../../src/evm/errors' +import { StackUnderflow } from '../../src/errors' import { executeAssembly, ADDRESS_ZERO } from './executeAssembly' import { Int256 } from './Int256' -import { Bytes32 } from '../../../src/evm/Bytes32' -import { Byte } from '../../../src/evm/Byte' +import { Bytes32 } from '../../src/Bytes32' +import { Byte } from '../../src/Byte' +import { ExecutionSuccess } from '../../src/ExecutionResult' export function expectUnderflow (opcode: string, minimumDepth: number) { for (let i = 0; i < minimumDepth; i++) { @@ -17,46 +18,53 @@ export function makeStack (depth: number) { .map((value, index) => Int256.of(depth - index)) } -export function expectStack (assembly: string, stack: string[]) { - const result = executeAssembly(assembly) - const items = result.stack['items'].map(x => x.toHex()) - expect(items).to.deep.equal(stack) +export function expectStackTop (assembly: string, value: string) { + const account = ADDRESS_ZERO + const result = executeAssembly(assembly + ' PUSH1 00 SSTORE', { account }) + if (result.type !== 'ExecutionSuccess') { + throw new Error(result.type) + } + const item = result.state.getStorage(account, Bytes32.ZERO) + expect(item.toHex()).to.equal(value) } export function expectGas (assembly: string, gasUsed: number) { const result = executeAssembly(assembly) - expect(result.gasUsed).to.equal(gasUsed) + expect(result).to.include({ gasUsed }) } -export function expectRefund (assembly: string, refund: number) { +export function expectRefund (assembly: string, gasRefund: number) { const result = executeAssembly(assembly) - expect(result.gasRefund).to.equal(refund) + expect(result).to.include({ gasRefund }) } export function expectError (assembly: string, error: unknown) { const result = executeAssembly(assembly) - expect(result.error).to.be.instanceOf(error) + expect(result.type).to.equal('ExecutionError') + expect((result as any).error).to.be.instanceOf(error) } export function expectReturn (assembly: string, value: Byte[]) { const result = executeAssembly(assembly) - expect(result.reverted).to.equal(false) - expect(result.returnValue).to.deep.equal(value) + expect(result.type).to.equal('ExecutionSuccess') + expect((result as any).returnValue).to.deep.equal(value) } export function expectRevert (assembly: string, value: Byte[]) { const result = executeAssembly(assembly) - expect(result.reverted).to.equal(true) - expect(result.returnValue).to.deep.equal(value) + expect(result.type).to.equal('ExecutionRevert') + expect((result as any).returnValue).to.deep.equal(value) } export function expectStorage (assembly: string, values: Record) { const account = ADDRESS_ZERO const result = executeAssembly(assembly, { account }) const resultingStorage: Record = {} + expect(result.type).to.equal('ExecutionSuccess') + const state = (result as ExecutionSuccess).state for (const key in values) { const location = Bytes32.fromHex(key) - resultingStorage[key] = result.state.getStorage(account, location).toHex() + resultingStorage[key] = state.getStorage(account, location).toHex() } expect(resultingStorage).to.deep.equal(values) } diff --git a/packages/evm/test/evm/helpers/index.ts b/packages/evm/test/helpers/index.ts similarity index 100% rename from packages/evm/test/evm/helpers/index.ts rename to packages/evm/test/helpers/index.ts diff --git a/packages/evm/test/evm/helpers/memory.ts b/packages/evm/test/helpers/memory.ts similarity index 100% rename from packages/evm/test/evm/helpers/memory.ts rename to packages/evm/test/helpers/memory.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/deth/add.ts b/packages/evm/test/opcodes/bytes32/cases/deth/add.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/deth/add.ts rename to packages/evm/test/opcodes/bytes32/cases/deth/add.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/README.md b/packages/evm/test/opcodes/bytes32/cases/geth/README.md similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/README.md rename to packages/evm/test/opcodes/bytes32/cases/geth/README.md diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/add.json b/packages/evm/test/opcodes/bytes32/cases/geth/add.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/add.json rename to packages/evm/test/opcodes/bytes32/cases/geth/add.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/and.json b/packages/evm/test/opcodes/bytes32/cases/geth/and.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/and.json rename to packages/evm/test/opcodes/bytes32/cases/geth/and.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/byte.json b/packages/evm/test/opcodes/bytes32/cases/geth/byte.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/byte.json rename to packages/evm/test/opcodes/bytes32/cases/geth/byte.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/div.json b/packages/evm/test/opcodes/bytes32/cases/geth/div.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/div.json rename to packages/evm/test/opcodes/bytes32/cases/geth/div.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/eq.json b/packages/evm/test/opcodes/bytes32/cases/geth/eq.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/eq.json rename to packages/evm/test/opcodes/bytes32/cases/geth/eq.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/exp.json b/packages/evm/test/opcodes/bytes32/cases/geth/exp.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/exp.json rename to packages/evm/test/opcodes/bytes32/cases/geth/exp.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/gt.json b/packages/evm/test/opcodes/bytes32/cases/geth/gt.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/gt.json rename to packages/evm/test/opcodes/bytes32/cases/geth/gt.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/lt.json b/packages/evm/test/opcodes/bytes32/cases/geth/lt.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/lt.json rename to packages/evm/test/opcodes/bytes32/cases/geth/lt.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/mod.json b/packages/evm/test/opcodes/bytes32/cases/geth/mod.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/mod.json rename to packages/evm/test/opcodes/bytes32/cases/geth/mod.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/mul.json b/packages/evm/test/opcodes/bytes32/cases/geth/mul.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/mul.json rename to packages/evm/test/opcodes/bytes32/cases/geth/mul.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/or.json b/packages/evm/test/opcodes/bytes32/cases/geth/or.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/or.json rename to packages/evm/test/opcodes/bytes32/cases/geth/or.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/sar.json b/packages/evm/test/opcodes/bytes32/cases/geth/sar.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/sar.json rename to packages/evm/test/opcodes/bytes32/cases/geth/sar.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/sdiv.json b/packages/evm/test/opcodes/bytes32/cases/geth/sdiv.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/sdiv.json rename to packages/evm/test/opcodes/bytes32/cases/geth/sdiv.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/sgt.json b/packages/evm/test/opcodes/bytes32/cases/geth/sgt.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/sgt.json rename to packages/evm/test/opcodes/bytes32/cases/geth/sgt.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/shl.json b/packages/evm/test/opcodes/bytes32/cases/geth/shl.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/shl.json rename to packages/evm/test/opcodes/bytes32/cases/geth/shl.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/shr.json b/packages/evm/test/opcodes/bytes32/cases/geth/shr.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/shr.json rename to packages/evm/test/opcodes/bytes32/cases/geth/shr.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/signextend.json b/packages/evm/test/opcodes/bytes32/cases/geth/signextend.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/signextend.json rename to packages/evm/test/opcodes/bytes32/cases/geth/signextend.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/slt.json b/packages/evm/test/opcodes/bytes32/cases/geth/slt.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/slt.json rename to packages/evm/test/opcodes/bytes32/cases/geth/slt.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/smod.json b/packages/evm/test/opcodes/bytes32/cases/geth/smod.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/smod.json rename to packages/evm/test/opcodes/bytes32/cases/geth/smod.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/sub.json b/packages/evm/test/opcodes/bytes32/cases/geth/sub.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/sub.json rename to packages/evm/test/opcodes/bytes32/cases/geth/sub.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/geth/xor.json b/packages/evm/test/opcodes/bytes32/cases/geth/xor.json similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/geth/xor.json rename to packages/evm/test/opcodes/bytes32/cases/geth/xor.json diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/helpers.ts b/packages/evm/test/opcodes/bytes32/cases/helpers.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/helpers.ts rename to packages/evm/test/opcodes/bytes32/cases/helpers.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/index.ts b/packages/evm/test/opcodes/bytes32/cases/index.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/index.ts rename to packages/evm/test/opcodes/bytes32/cases/index.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/README.md b/packages/evm/test/opcodes/bytes32/cases/parity/README.md similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/README.md rename to packages/evm/test/opcodes/bytes32/cases/parity/README.md diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/add.ts b/packages/evm/test/opcodes/bytes32/cases/parity/add.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/add.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/add.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/addmod.ts b/packages/evm/test/opcodes/bytes32/cases/parity/addmod.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/addmod.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/addmod.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/and.ts b/packages/evm/test/opcodes/bytes32/cases/parity/and.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/and.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/and.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/byte.ts b/packages/evm/test/opcodes/bytes32/cases/parity/byte.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/byte.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/byte.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/div.ts b/packages/evm/test/opcodes/bytes32/cases/parity/div.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/div.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/div.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/eq.ts b/packages/evm/test/opcodes/bytes32/cases/parity/eq.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/eq.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/eq.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/exp.ts b/packages/evm/test/opcodes/bytes32/cases/parity/exp.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/exp.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/exp.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/gt.ts b/packages/evm/test/opcodes/bytes32/cases/parity/gt.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/gt.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/gt.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/iszero.ts b/packages/evm/test/opcodes/bytes32/cases/parity/iszero.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/iszero.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/iszero.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/lt.ts b/packages/evm/test/opcodes/bytes32/cases/parity/lt.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/lt.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/lt.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/mod.ts b/packages/evm/test/opcodes/bytes32/cases/parity/mod.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/mod.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/mod.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/mul.ts b/packages/evm/test/opcodes/bytes32/cases/parity/mul.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/mul.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/mul.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/mulmod.ts b/packages/evm/test/opcodes/bytes32/cases/parity/mulmod.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/mulmod.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/mulmod.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/not.ts b/packages/evm/test/opcodes/bytes32/cases/parity/not.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/not.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/not.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/or.ts b/packages/evm/test/opcodes/bytes32/cases/parity/or.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/or.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/or.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/sar.ts b/packages/evm/test/opcodes/bytes32/cases/parity/sar.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/sar.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/sar.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/sdiv.ts b/packages/evm/test/opcodes/bytes32/cases/parity/sdiv.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/sdiv.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/sdiv.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/sgt.ts b/packages/evm/test/opcodes/bytes32/cases/parity/sgt.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/sgt.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/sgt.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/shl.ts b/packages/evm/test/opcodes/bytes32/cases/parity/shl.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/shl.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/shl.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/shr.ts b/packages/evm/test/opcodes/bytes32/cases/parity/shr.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/shr.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/shr.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/signextend.ts b/packages/evm/test/opcodes/bytes32/cases/parity/signextend.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/signextend.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/signextend.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/slt.ts b/packages/evm/test/opcodes/bytes32/cases/parity/slt.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/slt.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/slt.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/smod.ts b/packages/evm/test/opcodes/bytes32/cases/parity/smod.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/smod.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/smod.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/sub.ts b/packages/evm/test/opcodes/bytes32/cases/parity/sub.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/sub.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/sub.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/cases/parity/xor.ts b/packages/evm/test/opcodes/bytes32/cases/parity/xor.ts similarity index 100% rename from packages/evm/test/evm/opcodes/bytes32/cases/parity/xor.ts rename to packages/evm/test/opcodes/bytes32/cases/parity/xor.ts diff --git a/packages/evm/test/evm/opcodes/bytes32/machineWord.test.ts b/packages/evm/test/opcodes/bytes32/machineWord.test.ts similarity index 93% rename from packages/evm/test/evm/opcodes/bytes32/machineWord.test.ts rename to packages/evm/test/opcodes/bytes32/machineWord.test.ts index 836ebee..bdb0e9f 100644 --- a/packages/evm/test/evm/opcodes/bytes32/machineWord.test.ts +++ b/packages/evm/test/opcodes/bytes32/machineWord.test.ts @@ -1,7 +1,7 @@ import { TestCases } from './cases' import { TestCase } from './cases/helpers' -import { expectStack, expectGas } from '../../helpers' -import { GasCost } from '../../../../src/evm/opcodes' +import { expectGas, expectStackTop } from '../../helpers' +import { GasCost } from '../../../src/opcodes' describe('bytes32 opcodes', () => { runTestCases('ADD', TestCases.ADD) @@ -65,9 +65,9 @@ function runTestCases (opcode: string, testCases: TestCase[]) { it(testCase.title, () => { const assembly = testCase.stack .map(x => `PUSH32 ${x}`) - .concat(opcode) + .concat([opcode]) .join(' ') - expectStack(assembly, [testCase.expected]) + expectStackTop(assembly, testCase.expected) }) } }) diff --git a/packages/evm/test/evm/opcodes/dup.test.ts b/packages/evm/test/opcodes/dup.test.ts similarity index 66% rename from packages/evm/test/evm/opcodes/dup.test.ts rename to packages/evm/test/opcodes/dup.test.ts index ecfc887..a32e18c 100644 --- a/packages/evm/test/evm/opcodes/dup.test.ts +++ b/packages/evm/test/opcodes/dup.test.ts @@ -1,5 +1,5 @@ -import { GasCost } from '../../../src/evm/opcodes/gasCosts' -import { makeStack, expectUnderflow, expectStack, expectGas } from '../helpers' +import { GasCost } from '../../src/opcodes/gasCosts' +import { makeStack, expectUnderflow, expectGas, expectStackTop } from '../helpers' describe('DUP* opcodes', () => { const stack = makeStack(16) @@ -10,10 +10,7 @@ describe('DUP* opcodes', () => { for (let n = 1; n <= 16; n++) { describe(`DUP${n}`, () => { it('duplicates the value on the stack', () => { - expectStack(`${assembly} DUP${n}`, [ - ...stack, - stack[stack.length - n], - ]) + expectStackTop(`${assembly} DUP${n}`, stack[stack.length - n]) }) it('can cause stack underflow', () => { diff --git a/packages/evm/test/evm/opcodes/invalid.test.ts b/packages/evm/test/opcodes/invalid.test.ts similarity index 92% rename from packages/evm/test/evm/opcodes/invalid.test.ts rename to packages/evm/test/opcodes/invalid.test.ts index 2c46793..4aa4685 100644 --- a/packages/evm/test/evm/opcodes/invalid.test.ts +++ b/packages/evm/test/opcodes/invalid.test.ts @@ -1,4 +1,4 @@ -import { InvalidOpcode } from '../../../src/evm/errors' +import { InvalidOpcode } from '../../src/errors' import { expectError } from '../helpers' describe('invalid opcodes', () => { diff --git a/packages/evm/test/evm/opcodes/jump.test.ts b/packages/evm/test/opcodes/jump.test.ts similarity index 82% rename from packages/evm/test/evm/opcodes/jump.test.ts rename to packages/evm/test/opcodes/jump.test.ts index 28aedfe..46b6cc9 100644 --- a/packages/evm/test/evm/opcodes/jump.test.ts +++ b/packages/evm/test/opcodes/jump.test.ts @@ -1,6 +1,13 @@ -import { GasCost } from '../../../src/evm/opcodes/gasCosts' -import { InvalidJumpDestination } from '../../../src/evm/errors' -import { Int256, expectUnderflow, expectGas, expectStack, expectError } from '../helpers' +import { GasCost } from '../../src/opcodes/gasCosts' +import { InvalidJumpDestination } from '../../src/errors' +import { + Int256, + expectUnderflow, + expectGas, + expectStackTop, + expectError, + expectStorage, +} from '../helpers' describe('JUMP* opcodes', () => { describe('JUMPDEST', () => { @@ -18,7 +25,7 @@ describe('JUMP* opcodes', () => { it('jumps to a specified location in code', () => { const assembly = 'PUSH1 04 JUMP STOP JUMPDEST PUSH1 01' - expectStack(assembly, [Int256.of(1)]) + expectStackTop(assembly, Int256.of(1)) }) it('fails to jump to non JUMPDEST location', () => { @@ -50,7 +57,7 @@ describe('JUMP* opcodes', () => { JUMPDEST PUSH1 FF ` - expectStack(assembly, [Int256.of(0xff)]) + expectStackTop(assembly, Int256.of(0xff)) }) it('does not jump if condition is zero', () => { @@ -58,12 +65,13 @@ describe('JUMP* opcodes', () => { PUSH1 00 PUSH1 08 JUMPI - PUSH1 EE STOP JUMPDEST PUSH1 FF + PUSH1 00 + SSTORE ` - expectStack(assembly, [Int256.of(0xee)]) + expectStorage(assembly, { '00': Int256.of(0) }) }) it('fails to jump to non JUMPDEST location', () => { diff --git a/packages/evm/test/evm/opcodes/memory.test.ts b/packages/evm/test/opcodes/memory.test.ts similarity index 86% rename from packages/evm/test/evm/opcodes/memory.test.ts rename to packages/evm/test/opcodes/memory.test.ts index fbc67d6..84e8cf4 100644 --- a/packages/evm/test/evm/opcodes/memory.test.ts +++ b/packages/evm/test/opcodes/memory.test.ts @@ -1,5 +1,11 @@ -import { GasCost } from '../../../src/evm/opcodes' -import { expectGas, memoryGas, expectStack, Int256, expectUnderflow } from '../helpers' +import { GasCost } from '../../src/opcodes' +import { + expectGas, + memoryGas, + expectStackTop, + Int256, + expectUnderflow, +} from '../helpers' describe('Memory opcodes', () => { describe('MSIZE', () => { @@ -8,13 +14,13 @@ describe('Memory opcodes', () => { }) it('returns 0 initially', () => { - expectStack('MSIZE', [Int256.of(0)]) + expectStackTop('MSIZE', Int256.of(0)) }) it('returns the size of memory used', () => { const assembly = 'PUSH1 00 PUSH3 100001 MSTORE MSIZE' const expected = Math.ceil((0x100001 + 32) / 32) * 32 - expectStack(assembly, [Int256.of(expected)]) + expectStackTop(assembly, Int256.of(expected)) }) }) @@ -44,7 +50,7 @@ describe('Memory opcodes', () => { PUSH1 00 MLOAD ` - expectStack(assembly, [word]) + expectStackTop(assembly, word) }) it('can cause stack underflow', () => { @@ -78,7 +84,7 @@ describe('Memory opcodes', () => { PUSH1 00 MLOAD ` - expectStack(assembly, ['ef'.padEnd(64, '0')]) + expectStackTop(assembly, 'ef'.padEnd(64, '0')) }) it('can cause stack underflow', () => { @@ -111,7 +117,7 @@ describe('Memory opcodes', () => { PUSH1 10 MLOAD ` - expectStack(assembly, ['e'.repeat(32) + 'b'.repeat(32)]) + expectStackTop(assembly, 'e'.repeat(32) + 'b'.repeat(32)) }) it('can cause stack underflow', () => { diff --git a/packages/evm/test/evm/opcodes/pop.test.ts b/packages/evm/test/opcodes/pop.test.ts similarity index 58% rename from packages/evm/test/evm/opcodes/pop.test.ts rename to packages/evm/test/opcodes/pop.test.ts index 3395aee..31aa5b5 100644 --- a/packages/evm/test/evm/opcodes/pop.test.ts +++ b/packages/evm/test/opcodes/pop.test.ts @@ -1,5 +1,5 @@ -import { GasCost } from '../../../src/evm/opcodes/gasCosts' -import { Int256, expectUnderflow, expectGas, expectStack } from '../helpers' +import { GasCost } from '../../src/opcodes/gasCosts' +import { Int256, expectUnderflow, expectGas, expectStackTop } from '../helpers' describe('POP opcode', () => { it(`uses ${GasCost.BASE} gas`, () => { @@ -7,7 +7,7 @@ describe('POP opcode', () => { }) it('pops an item from the stack', () => { - expectStack('PUSH1 01 PUSH1 02 POP', [Int256.of(1)]) + expectStackTop('PUSH1 01 PUSH1 02 POP', Int256.of(1)) }) it('can cause a stack underflow', () => { diff --git a/packages/evm/test/evm/opcodes/push.test.ts b/packages/evm/test/opcodes/push.test.ts similarity index 68% rename from packages/evm/test/evm/opcodes/push.test.ts rename to packages/evm/test/opcodes/push.test.ts index 0cb46db..1f0b8b6 100644 --- a/packages/evm/test/evm/opcodes/push.test.ts +++ b/packages/evm/test/opcodes/push.test.ts @@ -1,6 +1,6 @@ -import { expectStack, expectGas, expectError } from '../helpers' -import { GasCost } from '../../../src/evm/opcodes/gasCosts' -import { StackOverflow } from '../../../src/evm/errors' +import { expectStackTop, expectGas, expectError } from '../helpers' +import { GasCost } from '../../src/opcodes/gasCosts' +import { StackOverflow } from '../../src/errors' describe('PUSH* opcodes', () => { for (let n = 1; n <= 32; n++) { @@ -10,7 +10,7 @@ describe('PUSH* opcodes', () => { .join('') it('pushes a value onto the stack', () => { - expectStack(`PUSH${n} ${bytes}`, [bytes.padStart(64, '0')]) + expectStackTop(`PUSH${n} ${bytes}`, bytes.padStart(64, '0')) }) it(`uses ${GasCost.VERYLOW} gas`, () => { diff --git a/packages/evm/test/evm/opcodes/return.test.ts b/packages/evm/test/opcodes/return.test.ts similarity index 73% rename from packages/evm/test/evm/opcodes/return.test.ts rename to packages/evm/test/opcodes/return.test.ts index b215f8f..796bf2f 100644 --- a/packages/evm/test/evm/opcodes/return.test.ts +++ b/packages/evm/test/opcodes/return.test.ts @@ -1,10 +1,12 @@ -import { expectStack, expectReturn, expectGas, memoryGas } from '../helpers' -import { GasCost } from '../../../src/evm/opcodes' -import { Byte } from '../../../src/evm/Byte' +import { expectReturn, expectGas, memoryGas, Int256, expectStorage } from '../helpers' +import { GasCost } from '../../src/opcodes' +import { Byte } from '../../src/Byte' describe('RETURN opcode', () => { it('halts execution', () => { - expectStack('PUSH1 00 PUSH1 00 RETURN PUSH1 01', []) + expectStorage('PUSH1 00 PUSH1 00 RETURN PUSH1 01 PUSH1 00 SSTORE', { + '00': Int256.of(0), + }) }) it(`uses ${GasCost.ZERO} gas`, () => { diff --git a/packages/evm/test/evm/opcodes/revert.test.ts b/packages/evm/test/opcodes/revert.test.ts similarity index 61% rename from packages/evm/test/evm/opcodes/revert.test.ts rename to packages/evm/test/opcodes/revert.test.ts index 48e6a10..f9dba1c 100644 --- a/packages/evm/test/evm/opcodes/revert.test.ts +++ b/packages/evm/test/opcodes/revert.test.ts @@ -1,12 +1,8 @@ -import { expectStack, expectGas, memoryGas, expectRevert, expectStorage, Int256 } from '../helpers' -import { GasCost } from '../../../src/evm/opcodes' -import { Byte } from '../../../src/evm/Byte' +import { expectGas, memoryGas, expectRevert } from '../helpers' +import { GasCost } from '../../src/opcodes' +import { Byte } from '../../src/Byte' describe('REVERT opcode', () => { - it('halts execution', () => { - expectStack('PUSH1 00 PUSH1 00 REVERT PUSH1 01', []) - }) - it(`uses ${GasCost.ZERO} gas`, () => { expectGas('PUSH1 00 PUSH1 00 REVERT', GasCost.VERYLOW * 2 + GasCost.ZERO) }) @@ -36,10 +32,4 @@ describe('REVERT opcode', () => { const gas = GasCost.VERYLOW * 2 + GasCost.ZERO + memoryGas(0x100001 + 2) expectGas(assembly, gas) }) - - it('reverts state changes', () => { - expectStorage('PUSH1 01 PUSH1 00 SSTORE PUSH1 00 DUP1 REVERT', { - [Int256.of(0)]: Int256.of(0), - }) - }) }) diff --git a/packages/evm/test/evm/opcodes/stop.test.ts b/packages/evm/test/opcodes/stop.test.ts similarity index 54% rename from packages/evm/test/evm/opcodes/stop.test.ts rename to packages/evm/test/opcodes/stop.test.ts index df85e7b..bb056af 100644 --- a/packages/evm/test/evm/opcodes/stop.test.ts +++ b/packages/evm/test/opcodes/stop.test.ts @@ -1,5 +1,5 @@ -import { GasCost } from '../../../src/evm/opcodes/gasCosts' -import { Int256, expectGas, expectStack, expectReturn } from '../helpers' +import { GasCost } from '../../src/opcodes/gasCosts' +import { Int256, expectGas, expectStackTop, expectReturn } from '../helpers' describe('STOP opcode', () => { it(`uses ${GasCost.ZERO} gas`, () => { @@ -7,7 +7,7 @@ describe('STOP opcode', () => { }) it('halts execution', () => { - expectStack('PUSH1 00 STOP NEG', [Int256.of(0)]) + expectStackTop('PUSH1 00 STOP NEG', Int256.of(0)) }) it('returns empty', () => { diff --git a/packages/evm/test/evm/opcodes/storage.test.ts b/packages/evm/test/opcodes/storage.test.ts similarity index 90% rename from packages/evm/test/evm/opcodes/storage.test.ts rename to packages/evm/test/opcodes/storage.test.ts index 079a07b..6b16a3b 100644 --- a/packages/evm/test/evm/opcodes/storage.test.ts +++ b/packages/evm/test/opcodes/storage.test.ts @@ -1,11 +1,11 @@ -import { GasCost, GasRefund } from '../../../src/evm/opcodes' +import { GasCost, GasRefund } from '../../src/opcodes' import { expectGas, expectUnderflow, expectStorage, Int256, - expectStack, expectRefund, + expectStackTop, } from '../helpers' describe('Storage opcodes', () => { @@ -72,11 +72,11 @@ describe('Storage opcodes', () => { }) it('can get a value from storage where it defaults to 0', () => { - expectStack('PUSH1 00 SLOAD', [Int256.of(0)]) + expectStackTop('PUSH1 00 SLOAD', Int256.of(0)) }) it('can get a previously set value from storage', () => { - expectStack('PUSH1 01 PUSH1 00 SSTORE PUSH1 00 SLOAD', [Int256.of(1)]) + expectStackTop('PUSH1 01 PUSH1 00 SSTORE PUSH1 00 SLOAD', Int256.of(1)) }) }) }) diff --git a/packages/evm/test/evm/opcodes/swap.test.ts b/packages/evm/test/opcodes/swap.test.ts similarity index 57% rename from packages/evm/test/evm/opcodes/swap.test.ts rename to packages/evm/test/opcodes/swap.test.ts index 2666ec3..0d406ba 100644 --- a/packages/evm/test/evm/opcodes/swap.test.ts +++ b/packages/evm/test/opcodes/swap.test.ts @@ -1,5 +1,5 @@ -import { GasCost } from '../../../src/evm/opcodes/gasCosts' -import { expectUnderflow, makeStack, expectStack, expectGas } from '../helpers' +import { GasCost } from '../../src/opcodes/gasCosts' +import { expectUnderflow, makeStack, expectStackTop, expectGas } from '../helpers' describe('SWAP* opcodes', () => { const stack = makeStack(17) @@ -11,10 +11,8 @@ describe('SWAP* opcodes', () => { for (let n = 1; n <= 16; n++) { describe(`SWAP${n}`, () => { it('swaps the values on the stack', () => { - const expected = [...stack] - expected[expected.length - 1] = stack[expected.length - 1 - n] - expected[expected.length - 1 - n] = stack[expected.length - 1] - expectStack(`${assembly} SWAP${n}`, expected) + expectStackTop(`${assembly} SWAP${n}`, stack[stack.length - 1 - n]) + expectStackTop(`${assembly} SWAP${n} POP DUP${n}`, stack[stack.length - 1]) }) it('can cause a stack underflow', () => {