From 2f26e9d297e00c0c6eb3245ac0e24a338ec351fa Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 12 Aug 2024 19:00:55 +0200 Subject: [PATCH 01/89] tx: implement strict 7702 validation --- packages/evm/src/params.ts | 8 ----- packages/tx/src/capabilities/eip7702.ts | 2 +- packages/tx/src/params.ts | 9 +++++ packages/tx/src/types.ts | 4 +-- packages/tx/src/util.ts | 45 +++++++++++++++---------- 5 files changed, 40 insertions(+), 28 deletions(-) diff --git a/packages/evm/src/params.ts b/packages/evm/src/params.ts index f4483d1201..3bff7eaab6 100644 --- a/packages/evm/src/params.ts +++ b/packages/evm/src/params.ts @@ -420,12 +420,4 @@ export const paramsEVM: ParamsDict = { eofcreateGas: 32000, // Base fee of the EOFCREATE opcode (Same as CREATE/CREATE2) returncontractGas: 0, // Base fee of the RETURNCONTRACT opcode }, - /** -. * Set EOA account code for one transaction -. */ - 7702: { - // TODO: Set correct minimum hardfork - // gasPrices - perAuthBaseGas: 2500, // Gas cost of each authority item - }, } diff --git a/packages/tx/src/capabilities/eip7702.ts b/packages/tx/src/capabilities/eip7702.ts index 3839485a0b..a847096333 100644 --- a/packages/tx/src/capabilities/eip7702.ts +++ b/packages/tx/src/capabilities/eip7702.ts @@ -10,7 +10,7 @@ import type { EIP7702CompatibleTx } from '../types.js' export function getDataGas(tx: EIP7702CompatibleTx): bigint { const eip2930Cost = BigInt(AccessLists.getDataGasEIP2930(tx.accessList, tx.common)) const eip7702Cost = BigInt( - tx.authorizationList.length * Number(tx.common.param('perAuthBaseGas')), + tx.authorizationList.length * Number(tx.common.param('perEmptyAccountCost')), ) return Legacy.getDataGas(tx, eip2930Cost + eip7702Cost) } diff --git a/packages/tx/src/params.ts b/packages/tx/src/params.ts index 02f487b583..8eac4ae249 100644 --- a/packages/tx/src/params.ts +++ b/packages/tx/src/params.ts @@ -43,4 +43,13 @@ export const paramsTx: ParamsDict = { 4844: { blobCommitmentVersionKzg: 1, // The number indicated a versioned hash is a KZG commitment }, + /** +. * Set EOA account code for one transaction +. */ + 7702: { + // TODO: Set correct minimum hardfork + // gasPrices + perAuthBaseGas: 2500, // Gas cost of each authority item, provided the authority exists in the trie + perEmptyAccountCost: 25000, // Gas cost of each authority item, in case the authority does not exist in the trie + }, } diff --git a/packages/tx/src/types.ts b/packages/tx/src/types.ts index 348122124f..c34e786836 100644 --- a/packages/tx/src/types.ts +++ b/packages/tx/src/types.ts @@ -600,7 +600,7 @@ export type AccessList = AccessListItem[] export type AuthorizationListItem = { chainId: PrefixedHexString address: PrefixedHexString - nonce: PrefixedHexString[] + nonce: PrefixedHexString yParity: PrefixedHexString r: PrefixedHexString s: PrefixedHexString @@ -610,7 +610,7 @@ export type AuthorizationListItem = { export type AuthorizationListBytesItem = [ Uint8Array, Uint8Array, - Uint8Array[], + Uint8Array, Uint8Array, Uint8Array, Uint8Array, diff --git a/packages/tx/src/util.ts b/packages/tx/src/util.ts index 591c0aa853..3d09f6eb13 100644 --- a/packages/tx/src/util.ts +++ b/packages/tx/src/util.ts @@ -1,5 +1,11 @@ import { + BIGINT_0, + BIGINT_1, + MAX_INTEGER, + MAX_UINT64, type PrefixedHexString, + SECP256K1_ORDER_DIV_2, + bytesToBigInt, bytesToHex, hexToBytes, setLengthLeft, @@ -149,15 +155,12 @@ export class AuthorizationLists { } const chainId = hexToBytes(item.chainId) const addressBytes = hexToBytes(item.address) - const nonceList = [] - for (let j = 0; j < item.nonce.length; j++) { - nonceList.push(hexToBytes(item.nonce[j])) - } + const nonce = hexToBytes(item.nonce) const yParity = hexToBytes(item.yParity) const r = hexToBytes(item.r) const s = hexToBytes(item.s) - newAuthorizationList.push([chainId, addressBytes, nonceList, yParity, r, s]) + newAuthorizationList.push([chainId, addressBytes, nonce, yParity, r, s]) } bufferAuthorizationList = newAuthorizationList } else { @@ -168,18 +171,14 @@ export class AuthorizationLists { const data = bufferAuthorizationList[i] const chainId = bytesToHex(data[0]) const address = bytesToHex(data[1]) - const nonces = data[2] - const nonceList: PrefixedHexString[] = [] - for (let j = 0; j < nonces.length; j++) { - nonceList.push(bytesToHex(nonces[j])) - } + const nonce = bytesToHex(data[2]) const yParity = bytesToHex(data[3]) const r = bytesToHex(data[4]) const s = bytesToHex(data[5]) const jsonItem: AuthorizationListItem = { chainId, address, - nonce: nonceList, + nonce, yParity, r, s, @@ -198,19 +197,31 @@ export class AuthorizationLists { public static verifyAuthorizationList(authorizationList: AuthorizationListBytes) { for (let key = 0; key < authorizationList.length; key++) { const authorizationListItem = authorizationList[key] + const chainId = authorizationListItem[0] const address = authorizationListItem[1] - const nonceList = authorizationListItem[2] + const nonce = authorizationListItem[2] const yParity = authorizationListItem[3] const r = authorizationListItem[4] const s = authorizationListItem[5] - validateNoLeadingZeroes({ yParity, r, s }) + validateNoLeadingZeroes({ yParity, r, s, nonce, chainId }) if (address.length !== 20) { throw new Error('Invalid EIP-7702 transaction: address length should be 20 bytes') } - if (nonceList.length > 1) { - throw new Error('Invalid EIP-7702 transaction: nonce list should consist of at most 1 item') - } else if (nonceList.length === 1) { - validateNoLeadingZeroes({ nonce: nonceList[0] }) + if (bytesToBigInt(chainId) > MAX_INTEGER) { + throw new Error('Invalid EIP-7702 transaction: chainId exceeds 2^256 - 1') + } + if (bytesToBigInt(nonce) > MAX_UINT64) { + throw new Error('Invalid EIP-7702 transaction: nonce exceeds 2^64 - 1') + } + const yParityBigInt = bytesToBigInt(yParity) + if (yParityBigInt !== BIGINT_0 && yParityBigInt !== BIGINT_1) { + throw new Error('Invalid EIP-7702 transaction: yParity should be 0 or 1') + } + if (bytesToBigInt(r) > MAX_INTEGER) { + throw new Error('Invalid EIP-7702 transaction: r exceeds 2^256 - 1') + } + if (bytesToBigInt(s) > SECP256K1_ORDER_DIV_2) { + throw new Error('Invalid EIP-7702 transaction: s > secp256k1n/2') } } } From f59b5a0e15d57b542927907d9eeab9d333dd614a Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 12 Aug 2024 21:51:01 +0200 Subject: [PATCH 02/89] vm: update 7702 tx validation --- packages/vm/src/runTx.ts | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index 6832b64028..c9f5c02b53 100644 --- a/packages/vm/src/runTx.ts +++ b/packages/vm/src/runTx.ts @@ -439,33 +439,45 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { } // Address to take code from const address = data[1] - const nonceList = data[2] + const nonce = data[2] const yParity = bytesToBigInt(data[3]) const r = data[4] const s = data[5] - const rlpdSignedMessage = RLP.encode([chainId, address, nonceList]) + const rlpdSignedMessage = RLP.encode([chainId, address, nonce]) const toSign = keccak256(concatBytes(MAGIC, rlpdSignedMessage)) const pubKey = ecrecover(toSign, yParity, r, s) // Address to set code to const authority = new Address(publicToAddress(pubKey)) - const account = (await vm.stateManager.getAccount(authority)) ?? new Account() + const accountMaybeUndefined = await vm.stateManager.getAccount(authority) + const accountExists = accountMaybeUndefined !== undefined + const account = accountMaybeUndefined ?? new Account() + // Add authority address to warm addresses + vm.evm.journal.addAlwaysWarmAddress(authority.toString()) if (account.isContract()) { - // Note: vm also checks if the code has already been set once by a previous tuple - // So, if there are multiply entires for the same address, then vm is fine - continue + const code = await vm.stateManager.getCode(authority) + if (!equalsBytes(code.slice(0, 3), new Uint8Array([0xef, 0x01, 0x00]))) { + // Account is a "normal" contract + continue + } } - if (nonceList.length !== 0 && account.nonce !== bytesToBigInt(nonceList[0])) { + if (account.nonce !== bytesToBigInt(nonce)) { continue } - const addressConverted = new Address(address) - const addressCode = await vm.stateManager.getCode(addressConverted) + if (accountExists) { + const refund = vm.common.param('perEmptyAccountCost') - vm.common.param('perAuthBaseGas') + fromAccount.balance += refund + } + + fromAccount.nonce++ + await vm.evm.journal.putAccount(caller, fromAccount) + + const addressCode = concatBytes(new Uint8Array([0xef, 0x01, 0x00]), address) await vm.stateManager.putCode(authority, addressCode) writtenAddresses.add(authority.toString()) - vm.evm.journal.addAlwaysWarmAddress(authority.toString()) } } From 5d8ba3959dd71546e2bae89899d837f651718854 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 12 Aug 2024 22:21:41 +0200 Subject: [PATCH 03/89] evm: update 7702 [no ci] --- packages/evm/src/evm.ts | 10 +++++++ packages/evm/src/opcodes/functions.ts | 29 +++++++++++++++----- packages/evm/src/opcodes/gas.ts | 38 +++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index c39855664f..eaaa7354a6 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -1013,6 +1013,16 @@ export class EVM implements EVMInterface { message.isCompiled = true } else { message.code = await this.stateManager.getCode(message.codeAddress) + + // EIP-7702 delegation check + if ( + this.common.isActivatedEIP(7702) && + equalsBytes(message.code.slice(0, 3), new Uint8Array([0xef, 0x01, 0x00])) + ) { + const address = new Address(message.code.slice(3, 24)) + message.code = await this.stateManager.getCode(address) + } + message.isCompiled = false message.chargeCodeAccesses = true } diff --git a/packages/evm/src/opcodes/functions.ts b/packages/evm/src/opcodes/functions.ts index d20b18faa0..a1ed5743a9 100644 --- a/packages/evm/src/opcodes/functions.ts +++ b/packages/evm/src/opcodes/functions.ts @@ -33,6 +33,7 @@ import { setLengthLeft, setLengthRight, } from '@ethereumjs/util' +import { equalBytes } from '@noble/curves/abstract/utils' import { keccak256 } from 'ethereum-cryptography/keccak.js' import { EOFContainer, EOFContainerMode } from '../eof/container.js' @@ -68,6 +69,15 @@ export interface AsyncOpHandler { export type OpHandler = SyncOpHandler | AsyncOpHandler +async function eip7702CodeCheck(runState: RunState, code: Uint8Array) { + if (equalBytes(code, new Uint8Array([0xef, 0x01, 0x00]))) { + const address = new Address(code.slice(3, 24)) + return runState.stateManager.getCode(address) + } + + return code +} + // the opcode functions export const handlers: Map = new Map([ // 0x00: STOP @@ -520,15 +530,17 @@ export const handlers: Map = new Map([ // 0x3b: EXTCODESIZE [ 0x3b, - async function (runState) { + async function (runState, common) { const addressBigInt = runState.stack.pop() const address = createAddressFromStackBigInt(addressBigInt) // EOF check - const code = await runState.stateManager.getCode(address) + let code = await runState.stateManager.getCode(address) if (isEOF(code)) { // In legacy code, the target code is treated as to be "EOFBYTES" code runState.stack.push(BigInt(EOFBYTES.length)) return + } else if (common.isActivatedEIP(7702)) { + code = await eip7702CodeCheck(runState, code) } const size = BigInt( @@ -541,15 +553,18 @@ export const handlers: Map = new Map([ // 0x3c: EXTCODECOPY [ 0x3c, - async function (runState) { + async function (runState, common) { const [addressBigInt, memOffset, codeOffset, dataLength] = runState.stack.popN(4) if (dataLength !== BIGINT_0) { - let code = await runState.stateManager.getCode(createAddressFromStackBigInt(addressBigInt)) + const address = createAddressFromStackBigInt(addressBigInt) + let code = await runState.stateManager.getCode(address) if (isEOF(code)) { // In legacy code, the target code is treated as to be "EOFBYTES" code code = EOFBYTES + } else if (common.isActivatedEIP(7702)) { + code = await eip7702CodeCheck(runState, code) } const data = getDataSlice(code, codeOffset, dataLength) @@ -562,17 +577,19 @@ export const handlers: Map = new Map([ // 0x3f: EXTCODEHASH [ 0x3f, - async function (runState) { + async function (runState, common) { const addressBigInt = runState.stack.pop() const address = createAddressFromStackBigInt(addressBigInt) // EOF check - const code = await runState.stateManager.getCode(address) + let code = await runState.stateManager.getCode(address) if (isEOF(code)) { // In legacy code, the target code is treated as to be "EOFBYTES" code // Therefore, push the hash of EOFBYTES to the stack runState.stack.push(bytesToBigInt(EOFHASH)) return + } else if (common.isActivatedEIP(7702)) { + code = await eip7702CodeCheck(runState, code) } const account = await runState.stateManager.getAccount(address) diff --git a/packages/evm/src/opcodes/gas.ts b/packages/evm/src/opcodes/gas.ts index 6f8cd64252..2a06785f3d 100644 --- a/packages/evm/src/opcodes/gas.ts +++ b/packages/evm/src/opcodes/gas.ts @@ -12,6 +12,7 @@ import { VERKLE_CODE_SIZE_LEAF_KEY, VERKLE_VERSION_LEAF_KEY, bigIntToBytes, + equalsBytes, getVerkleTreeIndexesForStorageSlot, setLengthLeft, } from '@ethereumjs/util' @@ -34,9 +35,18 @@ import { import type { RunState } from '../interpreter.js' import type { Common } from '@ethereumjs/common' +import type { Address } from '@ethereumjs/util' const EXTCALL_TARGET_MAX = BigInt(2) ** BigInt(8 * 20) - BigInt(1) +async function eip7702GasCost(runState: RunState, common: Common, address: Address) { + const code = await runState.stateManager.getCode(address) + if (equalsBytes(code, new Uint8Array([0xef, 0x01, 0x00]))) { + return accessAddressEIP2929(runState, code.slice(3, 24), common) + } + return BIGINT_0 +} + /** * This file returns the dynamic parts of opcodes which have dynamic gas * These are not pure functions: some edit the size of the memory @@ -183,6 +193,10 @@ export const dynamicGasHandlers: Map Date: Mon, 12 Aug 2024 22:52:32 +0200 Subject: [PATCH 04/89] tx: add / fix 7702 tests --- packages/tx/test/eip7702.spec.ts | 34 +++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/packages/tx/test/eip7702.spec.ts b/packages/tx/test/eip7702.spec.ts index ef8e980d51..dc116cb644 100644 --- a/packages/tx/test/eip7702.spec.ts +++ b/packages/tx/test/eip7702.spec.ts @@ -1,5 +1,14 @@ import { Common, Hardfork, Mainnet } from '@ethereumjs/common' -import { createAddressFromPrivateKey, createZeroAddress, hexToBytes } from '@ethereumjs/util' +import { + BIGINT_1, + MAX_INTEGER, + MAX_UINT64, + SECP256K1_ORDER_DIV_2, + bigIntToHex, + createAddressFromPrivateKey, + createZeroAddress, + hexToBytes, +} from '@ethereumjs/util' import { assert, describe, it } from 'vitest' import { create7702EOACodeTx } from '../src/index.js' @@ -19,7 +28,7 @@ function getTxData(override: Partial = {}): TxData { const validAuthorizationList: AuthorizationListItem = { chainId: '0x', address: `0x${'20'.repeat(20)}`, - nonce: ['0x1'], + nonce: '0x1', yParity: '0x1', r: ones32, s: ones32, @@ -65,18 +74,25 @@ describe('[EOACodeEIP7702Transaction]', () => { }, 'address length should be 20 bytes', ], - [ - { - nonce: ['0x1', '0x2'], - }, - 'nonce list should consist of at most 1 item', - ], [{ s: undefined as never }, 's is not defined'], [{ r: undefined as never }, 'r is not defined'], [{ yParity: undefined as never }, 'yParity is not defined'], [{ nonce: undefined as never }, 'nonce is not defined'], [{ address: undefined as never }, 'address is not defined'], [{ chainId: undefined as never }, 'chainId is not defined'], + [{ chainId: bigIntToHex(MAX_INTEGER + BIGINT_1) }, 'chainId exceeds 2^256 - 1'], + [ + { nonce: bigIntToHex(MAX_UINT64 + BIGINT_1) }, + 'Invalid EIP-7702 transaction: nonce exceeds 2^64 - 1', + ], + [{ yParity: '0x2' }, 'yParity should be 0 or 1'], + [{ r: bigIntToHex(MAX_INTEGER + BIGINT_1) }, 'r exceeds 2^256 - 1'], + [{ s: bigIntToHex(SECP256K1_ORDER_DIV_2 + BIGINT_1) }, 's > secp256k1n/2'], + [{ yParity: '0x0002' }, 'yParity cannot have leading zeros'], + [{ r: '0x0001' }, 'r cannot have leading zeros'], + [{ s: '0x0001' }, 's cannot have leading zeros'], + [{ nonce: '0x0001' }, 'nonce cannot have leading zeros'], + [{ chainId: '0x0001' }, 'chainId cannot have leading zeros'], ] for (const test of tests) { @@ -86,7 +102,7 @@ describe('[EOACodeEIP7702Transaction]', () => { create7702EOACodeTx(txData, { common }), testName }) } - + create7702EOACodeTx(getTxData(), { common }) assert.doesNotThrow(() => { create7702EOACodeTx(getTxData(), { common }) }) From ad3effbdaf00b5d5cb2b94ead819a0522e5b2d75 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 12 Aug 2024 23:45:10 +0200 Subject: [PATCH 05/89] vm: fix test encoding of authorization lists [no ci] --- packages/vm/test/api/EIPs/eip-7702.spec.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/vm/test/api/EIPs/eip-7702.spec.ts b/packages/vm/test/api/EIPs/eip-7702.spec.ts index e4300ba371..405db457a1 100644 --- a/packages/vm/test/api/EIPs/eip-7702.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7702.spec.ts @@ -6,7 +6,7 @@ import { Address, BIGINT_1, KECCAK256_NULL, - bigIntToBytes, + bigIntToUnpaddedBytes, concatBytes, createAddressFromString, ecsign, @@ -49,14 +49,22 @@ function getAuthorizationListItem(opts: GetAuthListOpts): AuthorizationListBytes const { chainId, nonce, address, pkey } = actualOpts const chainIdBytes = unpadBytes(hexToBytes(`0x${chainId.toString(16)}`)) - const nonceBytes = nonce !== undefined ? [unpadBytes(hexToBytes(`0x${nonce.toString(16)}`))] : [] + const nonceBytes = + nonce !== undefined ? unpadBytes(hexToBytes(`0x${nonce.toString(16)}`)) : new Uint8Array() const addressBytes = address.toBytes() const rlpdMsg = RLP.encode([chainIdBytes, addressBytes, nonceBytes]) const msgToSign = keccak256(concatBytes(new Uint8Array([5]), rlpdMsg)) const signed = ecsign(msgToSign, pkey) - return [chainIdBytes, addressBytes, nonceBytes, bigIntToBytes(signed.v), signed.r, signed.s] + return [ + chainIdBytes, + addressBytes, + nonceBytes, + bigIntToUnpaddedBytes(signed.v - BigInt(27)), + signed.r, + signed.s, + ] } async function runTest( From f8950b8786bc22569621da830b931e821e89dddf Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 14 Aug 2024 13:51:34 +0200 Subject: [PATCH 06/89] vm: correctly put authority nonce --- packages/vm/src/runTx.ts | 2 +- packages/vm/test/api/EIPs/eip-7702.spec.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index 811666073d..1c0d390e5c 100644 --- a/packages/vm/src/runTx.ts +++ b/packages/vm/src/runTx.ts @@ -472,7 +472,7 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { } fromAccount.nonce++ - await vm.evm.journal.putAccount(caller, fromAccount) + await vm.evm.journal.putAccount(authority, fromAccount) const addressCode = concatBytes(new Uint8Array([0xef, 0x01, 0x00]), address) await vm.stateManager.putCode(authority, addressCode) diff --git a/packages/vm/test/api/EIPs/eip-7702.spec.ts b/packages/vm/test/api/EIPs/eip-7702.spec.ts index 405db457a1..d9411e0c4d 100644 --- a/packages/vm/test/api/EIPs/eip-7702.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7702.spec.ts @@ -20,7 +20,7 @@ import { assert, describe, it } from 'vitest' import { VM, runTx } from '../../../src/index.js' -import type { AuthorizationListBytesItem } from '@ethereumjs/common' +import type { AuthorizationListBytesItem } from '@ethereumjs/tx' const common = new Common({ chain: Mainnet, hardfork: Hardfork.Cancun, eips: [7702] }) @@ -122,7 +122,8 @@ describe('EIP 7702: set code to EOA accounts', () => { ) // Try to set code to two different addresses - // Only the first is valid + // Only the first is valid: the second tuple will have the nonce value 0, but the + // nonce of the account is already set to 1 (by the first tuple) await runTest( [ { From 842db9d1a2ac42724628f8bbb05f8f5838d280d5 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 16 Aug 2024 17:56:11 +0200 Subject: [PATCH 07/89] vm: add test 7702 extcodehash/extcodesize evm: fix extcodehash/extcodesize for delegated accounts --- packages/evm/src/opcodes/functions.ts | 7 ++- packages/vm/test/api/EIPs/eip-7702.spec.ts | 56 ++++++++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/packages/evm/src/opcodes/functions.ts b/packages/evm/src/opcodes/functions.ts index 943a2240ab..6fb0235e25 100644 --- a/packages/evm/src/opcodes/functions.ts +++ b/packages/evm/src/opcodes/functions.ts @@ -61,7 +61,7 @@ export interface AsyncOpHandler { export type OpHandler = SyncOpHandler | AsyncOpHandler async function eip7702CodeCheck(runState: RunState, code: Uint8Array) { - if (equalBytes(code, new Uint8Array([0xef, 0x01, 0x00]))) { + if (equalBytes(code.slice(0, 3), new Uint8Array([0xef, 0x01, 0x00]))) { const address = new Address(code.slice(3, 24)) return runState.stateManager.getCode(address) } @@ -534,9 +534,7 @@ export const handlers: Map = new Map([ code = await eip7702CodeCheck(runState, code) } - const size = BigInt( - await runState.stateManager.getCodeSize(createAddressFromStackBigInt(addressBigInt)), - ) + const size = BigInt(code.length) runState.stack.push(size) }, @@ -581,6 +579,7 @@ export const handlers: Map = new Map([ return } else if (common.isActivatedEIP(7702)) { code = await eip7702CodeCheck(runState, code) + return keccak256(code) } const account = await runState.stateManager.getAccount(address) diff --git a/packages/vm/test/api/EIPs/eip-7702.spec.ts b/packages/vm/test/api/EIPs/eip-7702.spec.ts index d9411e0c4d..d1e8a285bd 100644 --- a/packages/vm/test/api/EIPs/eip-7702.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7702.spec.ts @@ -7,12 +7,15 @@ import { BIGINT_1, KECCAK256_NULL, bigIntToUnpaddedBytes, + bytesToBigInt, concatBytes, createAddressFromString, + createZeroAddress, ecsign, hexToBytes, privateToAddress, unpadBytes, + zeros, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' import { equalsBytes } from 'ethereum-cryptography/utils' @@ -21,6 +24,7 @@ import { assert, describe, it } from 'vitest' import { VM, runTx } from '../../../src/index.js' import type { AuthorizationListBytesItem } from '@ethereumjs/tx' +import type { PrefixedHexString } from '@ethereumjs/util' const common = new Common({ chain: Mainnet, hardfork: Hardfork.Cancun, eips: [7702] }) @@ -277,3 +281,55 @@ describe('EIP 7702: set code to EOA accounts', () => { assert.ok(account === undefined) }) }) + +describe.only('test EIP-7702 opcodes', () => { + it('should correctly report EXTCODE* opcodes', async () => { + // extcodesize and extcodehash + const deploymentAddress = createZeroAddress() + const randomCode = hexToBytes('0x010203040506') + const randomCodeAddress = createAddressFromString('0x' + 'aa'.repeat(20)) + + const opcodes = ['3b', '3f'] + const expected = [BigInt(randomCode.length), bytesToBigInt(keccak256(randomCode))] + + const authTx = create7702EOACodeTx( + { + gasLimit: 100000, + maxFeePerGas: 1000, + authorizationList: [ + getAuthorizationListItem({ + address: randomCodeAddress, + }), + ], + to: deploymentAddress, + value: BIGINT_1, + }, + { common }, + ).sign(defaultSenderPkey) + + // TODO make this more elegant + let i = -1 + for (const opcode of opcodes) { + i++ + const actualCode = hexToBytes( + ('0x73' + defaultAuthAddr.toString().slice(2) + opcode + '5f5500'), + ) + const vm = await VM.create({ common }) + + const acc = (await vm.stateManager.getAccount(defaultSenderAddr)) ?? new Account() + acc.balance = BigInt(1_000_000_000) + await vm.stateManager.putAccount(defaultSenderAddr, acc) + + // The code to either store extcodehash / extcodesize in slot 0 + await vm.stateManager.putCode(deploymentAddress, actualCode) + // The code the authority points to (and should thus be loaded by above script) + await vm.stateManager.putCode(randomCodeAddress, randomCode) + + // Set authority and immediately call into the contract to get the extcodehash / extcodesize + await runTx(vm, { tx: authTx }) + + const result = await vm.stateManager.getStorage(deploymentAddress, zeros(32)) + assert.equal(bytesToBigInt(result), expected[i]) + } + }) +}) From 0b6692f4d588611d6cea9ceee92ddef0a280dad1 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sat, 17 Aug 2024 00:32:16 +0200 Subject: [PATCH 08/89] vm: expand extcode* tests 7702 [no ci] --- packages/vm/test/api/EIPs/eip-7702.spec.ts | 50 +++++++++++++++++----- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/packages/vm/test/api/EIPs/eip-7702.spec.ts b/packages/vm/test/api/EIPs/eip-7702.spec.ts index d1e8a285bd..30453803e3 100644 --- a/packages/vm/test/api/EIPs/eip-7702.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7702.spec.ts @@ -14,6 +14,7 @@ import { ecsign, hexToBytes, privateToAddress, + setLengthRight, unpadBytes, zeros, } from '@ethereumjs/util' @@ -290,7 +291,37 @@ describe.only('test EIP-7702 opcodes', () => { const randomCodeAddress = createAddressFromString('0x' + 'aa'.repeat(20)) const opcodes = ['3b', '3f'] - const expected = [BigInt(randomCode.length), bytesToBigInt(keccak256(randomCode))] + const expected = [bigIntToUnpaddedBytes(BigInt(randomCode.length)), keccak256(randomCode)] + + const tests: { + code: PrefixedHexString + expectedStorage: Uint8Array + name: string + }[] = [ + // EXTCODESIZE + { + // PUSH20 EXTCODESIZE PUSH0 SSTORE STOP + code: ('0x73' + defaultAuthAddr.toString().slice(2) + '3b' + '5f5500'), + expectedStorage: bigIntToUnpaddedBytes(BigInt(randomCode.length)), + name: 'EXTCODESIZE', + }, + // EXTCODEHASH + { + // PUSH20 EXTCODEHASH PUSH0 SSTORE STOP + code: ('0x73' + defaultAuthAddr.toString().slice(2) + '3f' + '5f5500'), + expectedStorage: keccak256(randomCode), + name: 'EXTCODEHASH', + }, + // EXTCODECOPY + { + // PUSH1 32 PUSH0 PUSH0 PUSH20 EXTCODEHASH PUSH0 SSTORE STOP + code: ( + ('0x60205f5f73' + defaultAuthAddr.toString().slice(2) + '3c' + '5f5500') + ), + expectedStorage: setLengthRight(randomCode, 32), + name: 'EXTCODECOPY', + }, + ] const authTx = create7702EOACodeTx( { @@ -307,13 +338,7 @@ describe.only('test EIP-7702 opcodes', () => { { common }, ).sign(defaultSenderPkey) - // TODO make this more elegant - let i = -1 - for (const opcode of opcodes) { - i++ - const actualCode = hexToBytes( - ('0x73' + defaultAuthAddr.toString().slice(2) + opcode + '5f5500'), - ) + async function runOpcodeTest(code: Uint8Array, expectedOutput: Uint8Array, name: string) { const vm = await VM.create({ common }) const acc = (await vm.stateManager.getAccount(defaultSenderAddr)) ?? new Account() @@ -321,7 +346,7 @@ describe.only('test EIP-7702 opcodes', () => { await vm.stateManager.putAccount(defaultSenderAddr, acc) // The code to either store extcodehash / extcodesize in slot 0 - await vm.stateManager.putCode(deploymentAddress, actualCode) + await vm.stateManager.putCode(deploymentAddress, code) // The code the authority points to (and should thus be loaded by above script) await vm.stateManager.putCode(randomCodeAddress, randomCode) @@ -329,7 +354,12 @@ describe.only('test EIP-7702 opcodes', () => { await runTx(vm, { tx: authTx }) const result = await vm.stateManager.getStorage(deploymentAddress, zeros(32)) - assert.equal(bytesToBigInt(result), expected[i]) + console.log(result, expectedOutput) + assert.ok(equalsBytes(result, expectedOutput), `FAIL test: ${name}`) + } + + for (const test of tests) { + await runOpcodeTest(hexToBytes(test.code), test.expectedStorage, test.name) } }) }) From 7a01e8196f1ef1d195d15fdb748a5c40eb5b6466 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sat, 17 Aug 2024 20:18:08 +0200 Subject: [PATCH 09/89] tx/vm: update tests [no ci] --- packages/tx/test/eip7702.spec.ts | 2 +- packages/vm/test/api/EIPs/eip-7702.spec.ts | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/tx/test/eip7702.spec.ts b/packages/tx/test/eip7702.spec.ts index 0eb003caa4..8136e58d1d 100644 --- a/packages/tx/test/eip7702.spec.ts +++ b/packages/tx/test/eip7702.spec.ts @@ -102,7 +102,7 @@ describe('[EOACode7702Transaction]', () => { createEOACode7702Tx(txData, { common }), testName }) } - create7702EOACodeTx(getTxData(), { common }) + assert.doesNotThrow(() => { createEOACode7702Tx(getTxData(), { common }) }) diff --git a/packages/vm/test/api/EIPs/eip-7702.spec.ts b/packages/vm/test/api/EIPs/eip-7702.spec.ts index a968ff5996..2b59998463 100644 --- a/packages/vm/test/api/EIPs/eip-7702.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7702.spec.ts @@ -7,7 +7,6 @@ import { BIGINT_1, KECCAK256_NULL, bigIntToUnpaddedBytes, - bytesToBigInt, concatBytes, createAddressFromString, createZeroAddress, @@ -290,9 +289,6 @@ describe.only('test EIP-7702 opcodes', () => { const randomCode = hexToBytes('0x010203040506') const randomCodeAddress = createAddressFromString('0x' + 'aa'.repeat(20)) - const opcodes = ['3b', '3f'] - const expected = [bigIntToUnpaddedBytes(BigInt(randomCode.length)), keccak256(randomCode)] - const tests: { code: PrefixedHexString expectedStorage: Uint8Array @@ -323,7 +319,7 @@ describe.only('test EIP-7702 opcodes', () => { }, ] - const authTx = create7702EOACodeTx( + const authTx = createEOACode7702Tx( { gasLimit: 100000, maxFeePerGas: 1000, @@ -354,7 +350,6 @@ describe.only('test EIP-7702 opcodes', () => { await runTx(vm, { tx: authTx }) const result = await vm.stateManager.getStorage(deploymentAddress, zeros(32)) - console.log(result, expectedOutput) assert.ok(equalsBytes(result, expectedOutput), `FAIL test: ${name}`) } From 0310175e095cabd2996e3410efc156a6e85c8926 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sat, 17 Aug 2024 21:06:23 +0200 Subject: [PATCH 10/89] evm/vm: update opcodes and fix tests 7702 --- packages/evm/src/opcodes/functions.ts | 3 ++- packages/vm/test/api/EIPs/eip-7702.spec.ts | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/evm/src/opcodes/functions.ts b/packages/evm/src/opcodes/functions.ts index 6fb0235e25..0d29b62bcc 100644 --- a/packages/evm/src/opcodes/functions.ts +++ b/packages/evm/src/opcodes/functions.ts @@ -579,7 +579,8 @@ export const handlers: Map = new Map([ return } else if (common.isActivatedEIP(7702)) { code = await eip7702CodeCheck(runState, code) - return keccak256(code) + runState.stack.push(bytesToBigInt(keccak256(code))) + return } const account = await runState.stateManager.getAccount(address) diff --git a/packages/vm/test/api/EIPs/eip-7702.spec.ts b/packages/vm/test/api/EIPs/eip-7702.spec.ts index 2b59998463..d7204245cb 100644 --- a/packages/vm/test/api/EIPs/eip-7702.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7702.spec.ts @@ -310,9 +310,9 @@ describe.only('test EIP-7702 opcodes', () => { }, // EXTCODECOPY { - // PUSH1 32 PUSH0 PUSH0 PUSH20 EXTCODEHASH PUSH0 SSTORE STOP + // PUSH1 32 PUSH0 PUSH0 PUSH20 EXTCODEHASH PUSH0 MLOAD PUSH0 SSTORE STOP code: ( - ('0x60205f5f73' + defaultAuthAddr.toString().slice(2) + '3c' + '5f5500') + ('0x60205f5f73' + defaultAuthAddr.toString().slice(2) + '3c' + '5f515f5500') ), expectedStorage: setLengthRight(randomCode, 32), name: 'EXTCODECOPY', From d773d2b734a060cd76cc758a5c214babdb3a16bf Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sat, 17 Aug 2024 21:17:54 +0200 Subject: [PATCH 11/89] fix cspell [no ci] --- packages/vm/test/api/EIPs/eip-7702.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/test/api/EIPs/eip-7702.spec.ts b/packages/vm/test/api/EIPs/eip-7702.spec.ts index d7204245cb..4b922d9044 100644 --- a/packages/vm/test/api/EIPs/eip-7702.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7702.spec.ts @@ -283,7 +283,7 @@ describe('EIP 7702: set code to EOA accounts', () => { }) describe.only('test EIP-7702 opcodes', () => { - it('should correctly report EXTCODE* opcodes', async () => { + it('should correctly report EXTCODESIZE/EXTCODEHASH/EXTCODECOPY opcodes', async () => { // extcodesize and extcodehash const deploymentAddress = createZeroAddress() const randomCode = hexToBytes('0x010203040506') From ddea27e26b31ad2fd4f6e1b0a4d7fe8d9097db6c Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 18 Aug 2024 18:41:44 +0200 Subject: [PATCH 12/89] Implement t8n --- packages/vm/test/t8n/README.md | 25 ++ .../test/t8n/clients/default/genesis/.gitkeep | 0 .../vm/test/t8n/clients/ethereumjs/config | 213 ++++++++++++++++++ .../ethereumjs/genesis/ArrowGlacier.json | 9 + .../clients/ethereumjs/genesis/Berlin.json | 9 + .../ethereumjs/genesis/BerlinToLondonAt5.json | 10 + .../clients/ethereumjs/genesis/Byzantium.json | 8 + .../ByzantiumToConstantinopleFixAt5.json | 9 + .../ethereumjs/genesis/Constantinople.json | 9 + .../ethereumjs/genesis/ConstantinopleFix.json | 9 + .../clients/ethereumjs/genesis/EIP150.json | 7 + .../clients/ethereumjs/genesis/EIP158.json | 7 + .../genesis/EIP158ToByzantiumAt5.json | 8 + .../clients/ethereumjs/genesis/Frontier.json | 6 + .../genesis/FrontierToHomesteadAt5.json | 7 + .../clients/ethereumjs/genesis/Homestead.json | 7 + .../ethereumjs/genesis/HomesteadToDaoAt5.json | 7 + .../genesis/HomesteadToEIP150At5.json | 7 + .../clients/ethereumjs/genesis/Istanbul.json | 9 + .../clients/ethereumjs/genesis/London.json | 9 + .../t8n/clients/ethereumjs/genesis/Paris.json | 10 + .../clients/ethereumjs/genesis/Shanghai.json | 10 + .../genesis/correctMiningReward.json | 22 ++ .../vm/test/t8n/clients/ethereumjs/start.sh | 1 + packages/vm/test/t8n/clients/version | 1 + packages/vm/test/t8n/transition-child.cts | 177 +++++++++++++++ packages/vm/test/t8n/transition-cluster.cts | 66 ++++++ 27 files changed, 662 insertions(+) create mode 100644 packages/vm/test/t8n/README.md create mode 100644 packages/vm/test/t8n/clients/default/genesis/.gitkeep create mode 100644 packages/vm/test/t8n/clients/ethereumjs/config create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/ArrowGlacier.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/Berlin.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/BerlinToLondonAt5.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/Byzantium.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/ByzantiumToConstantinopleFixAt5.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/Constantinople.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/ConstantinopleFix.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/EIP150.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/EIP158.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/EIP158ToByzantiumAt5.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/Frontier.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/FrontierToHomesteadAt5.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/Homestead.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/HomesteadToDaoAt5.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/HomesteadToEIP150At5.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/Istanbul.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/London.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/Paris.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/Shanghai.json create mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/correctMiningReward.json create mode 100755 packages/vm/test/t8n/clients/ethereumjs/start.sh create mode 100644 packages/vm/test/t8n/clients/version create mode 100644 packages/vm/test/t8n/transition-child.cts create mode 100644 packages/vm/test/t8n/transition-cluster.cts diff --git a/packages/vm/test/t8n/README.md b/packages/vm/test/t8n/README.md new file mode 100644 index 0000000000..436d366929 --- /dev/null +++ b/packages/vm/test/t8n/README.md @@ -0,0 +1,25 @@ +This folder contains a script to run with `tsx` together with `retesteth`. + +# Instructions + +Clone `ethereum/tests` (either in the `ethereumjs-monorepo` or elsewhere). + +Configure and run `transition-tool`: + +Note: All paths are relative paths to the `VM` package root. + +1. If you change the port number in `transition-cluster.ts` to anything other than 3000 (or run `transition-cluster` on a separate machine or different IP address from retesteth), update `test/vm/retesteth/clients/ethereumjs/start.sh` to reflect the right IP and port. + +2. From VM package root directory, run `tsx test/retesteth/transition-cluster.cts`. (Note: if not ran from root, spawning child processes will error out!) + +Configure and run `retesteth`: + +1. Follow the build instructions to build and install [`retesteth`](https://github.com/ethereum/retesteth) + +2. Run `retesteth` from `[retesteth repository root]/retesteth` using the below command + +`./retesteth -t GeneralStateTests -- --testpath $ETHEREUM_TESTPATH --datadir $RETESTETH_CLIENTS --clients "ethereumjs"` + +Here `$ETHEREUM_TESTPATH` should be set to where `ethereum/tests` root folder is installed (so after cloning it). `$RETESTETH_CLIENTS` should be set to `[ethereumjs-monorepo root]/packages/vm/test/retesteth/clients` (use the absolute path from root when running retesteth from another folder). + +Note: if an error regarding "default client not found" pops up, copy the `test/retesteth/clients/ethereumjs` folder into `test/retesteth/clients/default`. If retesteth complains about Paris missing, then move genesis/Merge.json to genesis/Paris.json diff --git a/packages/vm/test/t8n/clients/default/genesis/.gitkeep b/packages/vm/test/t8n/clients/default/genesis/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/vm/test/t8n/clients/ethereumjs/config b/packages/vm/test/t8n/clients/ethereumjs/config new file mode 100644 index 0000000000..cef98e640e --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/config @@ -0,0 +1,213 @@ +{ + "name":"EthereumJS Transition Tool", + "socketType":"tranition-tool", + "socketAddress":"start.sh", + "forks":[ + "Frontier", + "Homestead", + "EIP150", + "EIP158", + "Byzantium", + "Constantinople", + "ConstantinopleFix", + "Istanbul", + "Berlin", + "London", + "Paris", + "Shanghai" + ], + "additionalForks":[ + "FrontierToHomesteadAt5", + "HomesteadToEIP150At5", + "EIP158ToByzantiumAt5", + "HomesteadToDaoAt5", + "ByzantiumToConstantinopleFixAt5", + "BerlinToLondonAt5", + "ArrowGlacier" + ], + "exceptions":{ + "AddressTooShort":"", + "AddressTooLong":"", + "NonceMax":"", + "NonceTooLong":"", + "InvalidVRS":"", + "InvalidV":"", + "InvalidR":"", + "InvalidS":"", + "InvalidChainID":"", + "ECRecoveryFail":"", + "InvalidStateRoot":"", + "ExtraDataTooBig":"", + "InvalidData":"", + "InvalidDifficulty":"", + "InvalidDifficulty2":"", + "InvalidDifficulty_TooLarge":"", + "InvalidGasLimit":"", + "InvalidGasLimit2":"", + "InvalidGasLimit3":"", + "InvalidGasLimit4":"", + "InvalidGasLimit5":"", + "InvalidValue":"", + "InvalidGasPrice":"", + "InvalidMaxPriorityFeePerGas":"", + "InvalidMaxFeePerGas":"", + "InvalidNonce":"", + "InvalidTo":"", + "GasLimitPriceProductOverflow":"", + "TooMuchGasUsed":"", + "TooMuchGasUsed2":"", + "LeadingZerosGasLimit":"", + "LeadingZerosGasPrice":"", + "LeadingZerosValue":"", + "LeadingZerosNonce":"", + "LeadingZerosR":"", + "LeadingZerosS":"", + "LeadingZerosV":"", + "LeadingZerosDataSize":"", + "LeadingZerosNonceSize":"", + "InvalidNumber":"", + "InvalidTimestampEqualParent":"", + "InvalidTimestampOlderParent":"", + "InvalidLogBloom":"", + "InvalidGasUsed":"", + "InvalidGasUsed2":"", + "InvalidBlockMixHash":"", + "InvalidBlockNonce":"", + "UnknownParent":"", + "UnknownParent2":"", + "InvalidReceiptsStateRoot":"", + "InvalidTransactionsRoot":"", + "InvalidUnclesHash":"", + "InvalidUncleParentHash":"", + "UncleInChain":"", + "UncleIsAncestor":"", + "UncleParentIsNotAncestor":"", + "TooManyUncles":"", + "UncleIsBrother":"", + "OutOfGas":"", + "SenderNotEOA":"", + "IntrinsicGas":"", + "ExtraDataIncorrectDAO":"", + "InvalidTransactionVRS":"", + "BLOCKHEADER_VALUE_TOOLARGE":"", + "TRANSACTION_VALUE_TOOLARGE":"", + "TRANSACTION_VALUE_TOOSHORT":"", + "TR_NonceHasMaxValue":"", + "OVERSIZE_RLP":"", + "RLP_TooFewElements":"", + "RLP_TooManyElements":"", + "RLP_InputContainsMoreThanOneValue":"", + "RLP_VALUESIZE_MORE_AVAILABLEINPUTLENGTH":"", + "RLP_ELEMENT_LARGER_CONTAININGLIST_UNDERSIZE":"", + "RLP_ELEMENT_LARGER_CONTAININGLIST_OVERSIZE":"", + "RLP_ExpectedInputList_EXTBLOCK":"", + "RLP_InvalidArg0_UNMARSHAL_BYTES":"", + "RLP_ExpectedInputList_HEADER_DECODEINTO_BLOCK_EXTBLOCK":"", + "RLP_InputList_TooManyElements_HEADER_DECODEINTO_BLOCK_EXTBLOCK_HEADER":"", + "RLP_InputList_TooManyElements_TXDATA_DECODEINTO_BLOCK_EXTBLOCK_TXS0":"", + "RLP_InputString_TooShort_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_HEADER_COINBASE":"", + "RLP_InputString_TooShort_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_HEADER_COINBASE2":"", + "RLP_InputString_TooShort_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_TXS0_RECIPIENT":"", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_ROOT":"", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_ROOT2":"", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST":"", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST2":"", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH":"", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH2":"", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH":"", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH2":"", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TXHASH":"", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH":"", + "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH2":"", + "RLP_InputString_TooLong_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASLIMIT":"", + "RLP_InputString_TooLong_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASUSED":"", + "RLP_InputString_TooLong_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TIME":"", + "RLP_InputString_TooLong_UINT64_DECODEINTO_BLOCK_EXTBLOCK_TXS0_GASLIMIT":"", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH":"", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH2":"", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_ROOT":"", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST":"", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST2":"", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH":"", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH2":"", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH":"", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH2":"", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TXHASH":"", + "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TXHASH2":"", + "RLP_InputString_TooShort_BLOOM_DECODEINTO_BLOCK_EXTBLOCK_HEADER_BLOOM":"", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_DIFFICULTY":"", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_DIFFICULTY2":"", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASLIMIT":"", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASLIMIT2":"", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASUSED":"", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASUSED2":"", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TIME":"", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TIME2":"", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_TXS0_GASLIMIT":"", + "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_TXS0_GASLIMIT2":"", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NUMBER":"", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NUMBER2":"", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXDATA_PRICE":"", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXDATA_R":"", + "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXDATA_S":"", + "RLP_InputString_TooLong_BLOOM_DECODEINTO_BLOCK_EXTBLOCK_HEADER_BLOOM":"", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH":"", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH":"", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_ROOT":"", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST":"", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TXHASH":"", + "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH":"", + "RLP_ExpectedInputString_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_HEADER_COINBASE":"", + "RLP_ExpectedInputString_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_TX0_RECIPIENT":"", + "RLP_InputString_TooLong_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_HEADER_COINBASE":"", + "RLP_InputString_TooLong_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_TXS0_RECIPIENT":"", + "RLP_ExpectedInputString_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_DIFFICULTY":"", + "RLP_ExpectedInputString_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXR":"", + "RLP_ExpectedInputString_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXS":"", + "RLP_ExpectedInputString_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASLIMIT":"", + "RLP_ExpectedInputString_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASUSED":"", + "RLP_ExpectedInputString_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TIME":"", + "RLP_ExpectedInputString_UINT64_DECODEINTO_BLOCK_EXTBLOCK_TXS0_GASLIMIT":"", + "RLP_ExpectedInputString_NONCE_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NONCE":"", + "RLP_ExpectedInputString_UINT8_DECODEINTO_BLOCK_EXTBLOCK_TXS0_PAYLOAD":"", + "RLP_InputString_TooLong_BLOCKNONCE_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NONCE":"", + "RLP_InputString_TooLong_BLOCKNONCE_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NONCE2":"", + "RLP_NonCanonical_SizeInfo_EXTBLOCK":"", + "RLP_ExpectedInputList_TRANSACTION_DECODEINTO_BLOCK_EXTBLOCK_TXS":"", + "RLP_ExpectedInputList_HEADER_DECODEINTO_BLOCK_EXTBLOCK_UNCLES":"", + "RLP_ExpectedInputList_TXDATA_DECODEINTO_BLOCK_EXTBLOCK_TXS0":"", + "RLP_Error_EOF":"", + "RLP_Error_RLP_Size":"", + "RLP_Error_Size_Information":"", + "LegacyBlockImportImpossible":"", + "LegacyBlockImportImpossible2":"", + "LegacyBlockBaseFeeTransaction":"", + "1559BlockImportImpossible_HeaderIsLegacy":"", + "1559BlockImportImpossible_BaseFeeWrong":"", + "1559BlockImportImpossible_InitialBaseFeeWrong":"", + "1559BlockImportImpossible_TargetGasLow":"", + "1559BlockImportImpossible_TargetGasHigh":"", + "1559BlockImportImpossible_InitialGasLimitInvalid":"", + "TR_IntrinsicGas":"", + "TR_NoFunds":"", + "TR_NoFundsValue":"", + "TR_FeeCapLessThanBlocks":"", + "TR_GasLimitReached":"", + "TR_NonceTooHigh":"", + "TR_NonceTooLow":"", + "TR_TypeNotSupported":"", + "TR_TipGtFeeCap":"", + "TR_TooShort":"", + "1559BaseFeeTooLarge":"", + "1559PriorityFeeGreaterThanBaseFee":"", + "2930AccessListAddressTooLong":"", + "2930AccessListAddressTooShort":"", + "2930AccessListStorageHashTooLong":"", + "1559LeadingZerosBaseFee":"", + "1559LeadingZerosPriorityFee":"", + "2930AccessListStorageHashTooShort":"", + "3675PoWBlockRejected":"", + "3675PoSBlockRejected":"", + "3675PreMerge1559BlockRejected":"" + } + } \ No newline at end of file diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/ArrowGlacier.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/ArrowGlacier.json new file mode 100644 index 0000000000..5acb1cb472 --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/ArrowGlacier.json @@ -0,0 +1,9 @@ +{ + "params": { + "fork": "ArrowGlacier", + "constantinopleForkBlock": "0x00", + "byzantiumForkBlock": "0x00", + "homesteadForkBlock": "0x00" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/Berlin.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/Berlin.json new file mode 100644 index 0000000000..b497948cda --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/Berlin.json @@ -0,0 +1,9 @@ +{ + "params": { + "fork": "Berlin", + "constantinopleForkBlock": "0x00", + "byzantiumForkBlock": "0x00", + "homesteadForkBlock": "0x00" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/BerlinToLondonAt5.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/BerlinToLondonAt5.json new file mode 100644 index 0000000000..8f60badf7e --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/BerlinToLondonAt5.json @@ -0,0 +1,10 @@ +{ + "params": { + "fork": "BerlinToLondonAt5", + "constantinopleForkBlock": "0x00", + "byzantiumForkBlock": "0x00", + "homesteadForkBlock": "0x00", + "londonForkBlock": "0x05" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/Byzantium.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/Byzantium.json new file mode 100644 index 0000000000..6cfb70e484 --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/Byzantium.json @@ -0,0 +1,8 @@ +{ + "params": { + "fork": "Byzantium", + "byzantiumForkBlock": "0x00", + "homesteadForkBlock": "0x00" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/ByzantiumToConstantinopleFixAt5.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/ByzantiumToConstantinopleFixAt5.json new file mode 100644 index 0000000000..a0edf6048c --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/ByzantiumToConstantinopleFixAt5.json @@ -0,0 +1,9 @@ +{ + "params": { + "fork": "ByzantiumToConstantinopleFixAt5", + "constantinopleForkBlock": "0x05", + "byzantiumForkBlock": "0x00", + "homesteadForkBlock": "0x00" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/Constantinople.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/Constantinople.json new file mode 100644 index 0000000000..d664f6348e --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/Constantinople.json @@ -0,0 +1,9 @@ +{ + "params": { + "fork": "Constantinople", + "constantinopleForkBlock": "0x00", + "byzantiumForkBlock": "0x00", + "homesteadForkBlock": "0x00" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/ConstantinopleFix.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/ConstantinopleFix.json new file mode 100644 index 0000000000..02a05e3b38 --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/ConstantinopleFix.json @@ -0,0 +1,9 @@ +{ + "params": { + "fork": "ConstantinopleFix", + "constantinopleForkBlock": "0x00", + "byzantiumForkBlock": "0x00", + "homesteadForkBlock": "0x00" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP150.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP150.json new file mode 100644 index 0000000000..7d8332b7b9 --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP150.json @@ -0,0 +1,7 @@ +{ + "params": { + "fork": "EIP150", + "homesteadForkBlock": "0x00" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP158.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP158.json new file mode 100644 index 0000000000..c91d258ce7 --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP158.json @@ -0,0 +1,7 @@ +{ + "params": { + "fork": "EIP158", + "homesteadForkBlock": "0x00" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP158ToByzantiumAt5.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP158ToByzantiumAt5.json new file mode 100644 index 0000000000..bb1c47dc00 --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP158ToByzantiumAt5.json @@ -0,0 +1,8 @@ +{ + "params": { + "fork": "EIP158ToByzantiumAt5", + "byzantiumForkBlock": "0x05", + "homesteadForkBlock": "0x00" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/Frontier.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/Frontier.json new file mode 100644 index 0000000000..260c08f8a7 --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/Frontier.json @@ -0,0 +1,6 @@ +{ + "params": { + "fork": "Frontier" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/FrontierToHomesteadAt5.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/FrontierToHomesteadAt5.json new file mode 100644 index 0000000000..1f2f7ea78d --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/FrontierToHomesteadAt5.json @@ -0,0 +1,7 @@ +{ + "params": { + "fork": "FrontierToHomesteadAt5", + "homesteadForkBlock": "0x05" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/Homestead.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/Homestead.json new file mode 100644 index 0000000000..99b560a5fe --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/Homestead.json @@ -0,0 +1,7 @@ +{ + "params": { + "fork": "Homestead", + "homesteadForkBlock": "0x00" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/HomesteadToDaoAt5.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/HomesteadToDaoAt5.json new file mode 100644 index 0000000000..6564224adf --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/HomesteadToDaoAt5.json @@ -0,0 +1,7 @@ +{ + "params": { + "fork": "HomesteadToDaoAt5", + "homesteadForkBlock": "0x00" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/HomesteadToEIP150At5.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/HomesteadToEIP150At5.json new file mode 100644 index 0000000000..1d5835c31b --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/HomesteadToEIP150At5.json @@ -0,0 +1,7 @@ +{ + "params": { + "fork": "HomesteadToEIP150At5", + "homesteadForkBlock": "0x00" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/Istanbul.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/Istanbul.json new file mode 100644 index 0000000000..f0f08c7bef --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/Istanbul.json @@ -0,0 +1,9 @@ +{ + "params": { + "fork": "Istanbul", + "constantinopleForkBlock": "0x00", + "byzantiumForkBlock": "0x00", + "homesteadForkBlock": "0x00" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/London.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/London.json new file mode 100644 index 0000000000..900222ae38 --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/London.json @@ -0,0 +1,9 @@ +{ + "params": { + "fork": "London", + "constantinopleForkBlock": "0x00", + "byzantiumForkBlock": "0x00", + "homesteadForkBlock": "0x00" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/Paris.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/Paris.json new file mode 100644 index 0000000000..b2a1ed0a8b --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/Paris.json @@ -0,0 +1,10 @@ +{ + "params": { + "fork": "Merged", + "terminalTotalDifficulty": "0x00", + "constantinopleForkBlock": "0x00", + "byzantiumForkBlock": "0x00", + "homesteadForkBlock": "0x00" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/Shanghai.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/Shanghai.json new file mode 100644 index 0000000000..50161e94b8 --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/Shanghai.json @@ -0,0 +1,10 @@ +{ + "params": { + "fork": "Shanghai", + "terminalTotalDifficulty": "0x00", + "constantinopleForkBlock": "0x00", + "byzantiumForkBlock": "0x00", + "homesteadForkBlock": "0x00" + }, + "accounts": {} +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/correctMiningReward.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/correctMiningReward.json new file mode 100644 index 0000000000..41617b5913 --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/genesis/correctMiningReward.json @@ -0,0 +1,22 @@ +{ + "//comment": "State Tests does not calculate mining reward in post conditions, so when filling a blockchain test out of it, the mining reward must be set", + "Frontier": "5000000000000000000", + "Homestead": "5000000000000000000", + "EIP150": "5000000000000000000", + "EIP158": "5000000000000000000", + "Byzantium": "3000000000000000000", + "Constantinople": "2000000000000000000", + "ConstantinopleFix": "2000000000000000000", + "Istanbul": "2000000000000000000", + "Berlin": "2000000000000000000", + "London": "2000000000000000000", + "Paris": "2000000000000000000", + "Merged": "2000000000000000000", + "Shanghai": "2000000000000000000", + + "//comment": "Retesteth calculate rewards on behalf of the tool when filling state tests", + "YOLOv1": "2000000000000000000", + "YOLOv2": "2000000000000000000", + "YOLOv3": "2000000000000000000", + "Aleut": "2000000000000000000" +} diff --git a/packages/vm/test/t8n/clients/ethereumjs/start.sh b/packages/vm/test/t8n/clients/ethereumjs/start.sh new file mode 100755 index 0000000000..28eeb6380c --- /dev/null +++ b/packages/vm/test/t8n/clients/ethereumjs/start.sh @@ -0,0 +1 @@ +curl -X POST -d "${1} ${2} ${3} ${4} ${5} ${6} ${7} ${8} ${9} ${10} ${11} ${12} ${13} ${14} ${15} ${16} ${17} ${18} ${19} ${20}" --silent http://localhost:3000/ \ No newline at end of file diff --git a/packages/vm/test/t8n/clients/version b/packages/vm/test/t8n/clients/version new file mode 100644 index 0000000000..7207510520 --- /dev/null +++ b/packages/vm/test/t8n/clients/version @@ -0,0 +1 @@ +0.3.0-shanghai \ No newline at end of file diff --git a/packages/vm/test/t8n/transition-child.cts b/packages/vm/test/t8n/transition-child.cts new file mode 100644 index 0000000000..9ddde4511a --- /dev/null +++ b/packages/vm/test/t8n/transition-child.cts @@ -0,0 +1,177 @@ +import { Block, BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' +import { RLP } from '@ethereumjs/rlp' +import { createLegacyTxFromBytesArray, createTxFromSerializedData } from '@ethereumjs/tx' +import { Account, bytesToHex, unprefixedHexToBytes } from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak' +import { readFileSync, writeFileSync } from 'fs' +import { join } from 'path' + +import { VM } from '../../dist/cjs' +import { BlockBuilder } from '../../dist/cjs/buildBlock' +import { getCommon } from '../tester/config' +import { makeBlockFromEnv, setupPreConditions } from '../util' + +import type { TypedTransaction } from '@ethereumjs/tx' +import type { NestedUint8Array } from '@ethereumjs/util' +import type { PostByzantiumTxReceipt } from '../../dist/cjs' + +const yargs = require('yargs/yargs') + +async function runTransition(argsIn: any) { + const args = yargs(argsIn) + .option('state.fork', { + describe: 'Fork to use', + }) + .option('input.alloc', { + describe: 'Initial state allocation', + }) + .option('inputs.txs', { + describe: 'RLP input of txs to run on top of the initial state allocation', + }) + .option('inputs.env', { + describe: 'Input environment (coinbase, difficulty, etc.)', + }) + .option('output.basedir', { + describe: 'Base directory to write output to', + }) + .option('output.result', { + describe: 'File to write output results to (relative to `output.basedir`)', + }) + .option('output.alloc', { + describe: 'File to write output allocation to (after running the transactions)', + }).argv + const alloc = JSON.parse(readFileSync(args.input.alloc).toString()) + const rlpTxs = JSON.parse(readFileSync(args.input.txs).toString()) + const inputEnv = JSON.parse(readFileSync(args.input.env).toString()) + + const common = getCommon(args.state.fork) + + let blockchain + if (args.state.fork === 'Merged') { + const genesisBlockData = { + gasLimit: 5000, + difficulty: 0, + nonce: '0x0000000000000000', + extraData: '0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa', + } + const genesis = createBlockFromBlockData({ header: BlockHeader.fromHeaderData(genesisBlockData) }) + blockchain = await createBlockchain({ common, genesisBlock: genesis }) + } + const vm = blockchain ? await VM.create({ common, blockchain }) : await VM.create({ common }) + await setupPreConditions(vm.stateManager, { pre: alloc }) + + const block = makeBlockFromEnv(inputEnv, { common }) + + const acc = (await vm.stateManager.getAccount(block.header.coinbase)) ?? new Account() + await vm.stateManager.putAccount(block.header.coinbase, acc) + + const txsData = RLP.decode(unprefixedHexToBytes(rlpTxs.slice(2))) + + const headerData = block.header.toJSON() + headerData.difficulty = inputEnv.parentDifficulty + + const builder = new BlockBuilder(vm, { + parentBlock: new Block(), + headerData, + blockOpts: { putBlockIntoBlockchain: false }, + }) + + const receipts: any = [] + + let txCounter = 0 + + vm.events.on('afterTx', async (afterTx, continueFn) => { + const receipt = afterTx.receipt as PostByzantiumTxReceipt + const pushReceipt = { + root: '0x', + status: receipt.status === 0 ? '0x' : '0x1', + cumulativeGasUsed: '0x' + receipt.cumulativeBlockGasUsed.toString(16), + logsBloom: bytesToHex(receipt.bitvector), + logs: null, + transactionHash: bytesToHex(afterTx.transaction.hash()), + contractAddress: '0x0000000000000000000000000000000000000000', + gasUsed: '0x' + afterTx.totalGasSpent.toString(16), + blockHash: '0x0000000000000000000000000000000000000000000000000000000000000000', + transactionIndex: '0x' + txCounter.toString(16), + } + receipts.push(pushReceipt) + txCounter++ + continueFn!(undefined) + }) + + const rejected: any = [] + + let index = 0 + for (const txData of txsData as NestedUint8Array) { + try { + let tx: TypedTransaction + if (txData instanceof Uint8Array) { + tx = createTxFromSerializedData(txData as Uint8Array, { common }) + } else { + tx = createLegacyTxFromBytesArray(txData as Uint8Array[], { common }) + } + await builder.addTransaction(tx) + } catch (e: any) { + rejected.push({ + index, + error: e.message, + }) + } + index++ + } + + const logsBloom = builder.logsBloom() + const logsHash = keccak256(logsBloom) + + await vm.evm.journal.cleanup() + + const output = { + stateRoot: bytesToHex(await vm.stateManager.getStateRoot()), + txRoot: bytesToHex(await builder.transactionsTrie()), + receiptsRoot: bytesToHex(await builder.receiptTrie()), + logsHash: bytesToHex(logsHash), + logsBloom: bytesToHex(logsBloom), + currentDifficulty: '0x20000', + receipts, // TODO fixme + } + + if (rejected.length > 0) { + ;(output as any).rejected = rejected + } + + const outputAlloc = alloc //{} + + const outputResultFilePath = join(args.output.basedir, args.output.result) + const outputAllocFilePath = join(args.output.basedir, args.output.alloc) + + writeFileSync(outputResultFilePath, JSON.stringify(output)) + writeFileSync(outputAllocFilePath, JSON.stringify(outputAlloc)) +} + +let running = false + +process.on('message', async (message) => { + if (running) { + return + } + if (message === 'STOP') { + process.exit() + } else { + running = true + try { + await runTransition(message) + // eslint-disable-next-line no-empty + } catch (e) { + // eslint-disable-next-line no-console + console.log(e) + } + + running = false + + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions + if (process && process.send) { + process.send('done') + } + } +}) diff --git a/packages/vm/test/t8n/transition-cluster.cts b/packages/vm/test/t8n/transition-cluster.cts new file mode 100644 index 0000000000..9cf8ef4fae --- /dev/null +++ b/packages/vm/test/t8n/transition-cluster.cts @@ -0,0 +1,66 @@ +import { fork } from 'child_process' +import { createServer } from 'http' +import { resolve } from 'path' + +const program = resolve('test/retesteth/transition-child.cts') +const parameters: any = [] +const options = { + //stdio: ['pipe', 'pipe', 'pipe', 'ipc'], + execArgv: ['--loader', 'tsx'], +} + +const children: any = [] + +function getChild() { + let index = -1 + for (let i = 0; i < children.length; i++) { + if (children[i].active === false) { + index = i + break + } + } + if (index === -1) { + const child = fork(program, parameters, options) + // eslint-disable-next-line no-console + child.on('error', console.log) + const newChild = { + active: false, + child, + } + children.push(newChild) + return newChild + } + return children[index] +} + +function runTest(message: any) { + return new Promise((resolve) => { + const childObject = getChild() + + childObject.active = true + const child = childObject.child + + child.send(message) + + child.on('message', (_childMessage: any) => { + childObject.active = false + resolve(undefined) + }) + }) +} + +const server = createServer((request: any, result: any) => { + if (request.method === 'POST') { + let message = '' + request.on('data', (data: any) => { + message += data.toString() + }) + request.on('end', async () => { + await runTest(message) + result.end() + }) + } +}) +server.listen(3000) +// eslint-disable-next-line no-console +console.log('Transition cluster ready') From db42d5ead0e7b1f60919ea7ca8b85c52d1d424dd Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 18 Aug 2024 18:44:26 +0200 Subject: [PATCH 13/89] cleanup --- .../vm/test/retesteth/transition-cluster.cts | 2 + .../test/t8n/clients/default/genesis/.gitkeep | 0 .../vm/test/t8n/clients/ethereumjs/config | 213 ------------------ .../ethereumjs/genesis/ArrowGlacier.json | 9 - .../clients/ethereumjs/genesis/Berlin.json | 9 - .../ethereumjs/genesis/BerlinToLondonAt5.json | 10 - .../clients/ethereumjs/genesis/Byzantium.json | 8 - .../ByzantiumToConstantinopleFixAt5.json | 9 - .../ethereumjs/genesis/Constantinople.json | 9 - .../ethereumjs/genesis/ConstantinopleFix.json | 9 - .../clients/ethereumjs/genesis/EIP150.json | 7 - .../clients/ethereumjs/genesis/EIP158.json | 7 - .../genesis/EIP158ToByzantiumAt5.json | 8 - .../clients/ethereumjs/genesis/Frontier.json | 6 - .../genesis/FrontierToHomesteadAt5.json | 7 - .../clients/ethereumjs/genesis/Homestead.json | 7 - .../ethereumjs/genesis/HomesteadToDaoAt5.json | 7 - .../genesis/HomesteadToEIP150At5.json | 7 - .../clients/ethereumjs/genesis/Istanbul.json | 9 - .../clients/ethereumjs/genesis/London.json | 9 - .../t8n/clients/ethereumjs/genesis/Paris.json | 10 - .../clients/ethereumjs/genesis/Shanghai.json | 10 - .../genesis/correctMiningReward.json | 22 -- packages/vm/test/t8n/clients/version | 1 - .../t8n/{clients/ethereumjs => }/start.sh | 0 25 files changed, 2 insertions(+), 393 deletions(-) delete mode 100644 packages/vm/test/t8n/clients/default/genesis/.gitkeep delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/config delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/ArrowGlacier.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/Berlin.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/BerlinToLondonAt5.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/Byzantium.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/ByzantiumToConstantinopleFixAt5.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/Constantinople.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/ConstantinopleFix.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/EIP150.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/EIP158.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/EIP158ToByzantiumAt5.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/Frontier.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/FrontierToHomesteadAt5.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/Homestead.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/HomesteadToDaoAt5.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/HomesteadToEIP150At5.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/Istanbul.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/London.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/Paris.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/Shanghai.json delete mode 100644 packages/vm/test/t8n/clients/ethereumjs/genesis/correctMiningReward.json delete mode 100644 packages/vm/test/t8n/clients/version rename packages/vm/test/t8n/{clients/ethereumjs => }/start.sh (100%) diff --git a/packages/vm/test/retesteth/transition-cluster.cts b/packages/vm/test/retesteth/transition-cluster.cts index 9cf8ef4fae..1224aba5a5 100644 --- a/packages/vm/test/retesteth/transition-cluster.cts +++ b/packages/vm/test/retesteth/transition-cluster.cts @@ -40,6 +40,8 @@ function runTest(message: any) { childObject.active = true const child = childObject.child + console.log(message) + child.send(message) child.on('message', (_childMessage: any) => { diff --git a/packages/vm/test/t8n/clients/default/genesis/.gitkeep b/packages/vm/test/t8n/clients/default/genesis/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/vm/test/t8n/clients/ethereumjs/config b/packages/vm/test/t8n/clients/ethereumjs/config deleted file mode 100644 index cef98e640e..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/config +++ /dev/null @@ -1,213 +0,0 @@ -{ - "name":"EthereumJS Transition Tool", - "socketType":"tranition-tool", - "socketAddress":"start.sh", - "forks":[ - "Frontier", - "Homestead", - "EIP150", - "EIP158", - "Byzantium", - "Constantinople", - "ConstantinopleFix", - "Istanbul", - "Berlin", - "London", - "Paris", - "Shanghai" - ], - "additionalForks":[ - "FrontierToHomesteadAt5", - "HomesteadToEIP150At5", - "EIP158ToByzantiumAt5", - "HomesteadToDaoAt5", - "ByzantiumToConstantinopleFixAt5", - "BerlinToLondonAt5", - "ArrowGlacier" - ], - "exceptions":{ - "AddressTooShort":"", - "AddressTooLong":"", - "NonceMax":"", - "NonceTooLong":"", - "InvalidVRS":"", - "InvalidV":"", - "InvalidR":"", - "InvalidS":"", - "InvalidChainID":"", - "ECRecoveryFail":"", - "InvalidStateRoot":"", - "ExtraDataTooBig":"", - "InvalidData":"", - "InvalidDifficulty":"", - "InvalidDifficulty2":"", - "InvalidDifficulty_TooLarge":"", - "InvalidGasLimit":"", - "InvalidGasLimit2":"", - "InvalidGasLimit3":"", - "InvalidGasLimit4":"", - "InvalidGasLimit5":"", - "InvalidValue":"", - "InvalidGasPrice":"", - "InvalidMaxPriorityFeePerGas":"", - "InvalidMaxFeePerGas":"", - "InvalidNonce":"", - "InvalidTo":"", - "GasLimitPriceProductOverflow":"", - "TooMuchGasUsed":"", - "TooMuchGasUsed2":"", - "LeadingZerosGasLimit":"", - "LeadingZerosGasPrice":"", - "LeadingZerosValue":"", - "LeadingZerosNonce":"", - "LeadingZerosR":"", - "LeadingZerosS":"", - "LeadingZerosV":"", - "LeadingZerosDataSize":"", - "LeadingZerosNonceSize":"", - "InvalidNumber":"", - "InvalidTimestampEqualParent":"", - "InvalidTimestampOlderParent":"", - "InvalidLogBloom":"", - "InvalidGasUsed":"", - "InvalidGasUsed2":"", - "InvalidBlockMixHash":"", - "InvalidBlockNonce":"", - "UnknownParent":"", - "UnknownParent2":"", - "InvalidReceiptsStateRoot":"", - "InvalidTransactionsRoot":"", - "InvalidUnclesHash":"", - "InvalidUncleParentHash":"", - "UncleInChain":"", - "UncleIsAncestor":"", - "UncleParentIsNotAncestor":"", - "TooManyUncles":"", - "UncleIsBrother":"", - "OutOfGas":"", - "SenderNotEOA":"", - "IntrinsicGas":"", - "ExtraDataIncorrectDAO":"", - "InvalidTransactionVRS":"", - "BLOCKHEADER_VALUE_TOOLARGE":"", - "TRANSACTION_VALUE_TOOLARGE":"", - "TRANSACTION_VALUE_TOOSHORT":"", - "TR_NonceHasMaxValue":"", - "OVERSIZE_RLP":"", - "RLP_TooFewElements":"", - "RLP_TooManyElements":"", - "RLP_InputContainsMoreThanOneValue":"", - "RLP_VALUESIZE_MORE_AVAILABLEINPUTLENGTH":"", - "RLP_ELEMENT_LARGER_CONTAININGLIST_UNDERSIZE":"", - "RLP_ELEMENT_LARGER_CONTAININGLIST_OVERSIZE":"", - "RLP_ExpectedInputList_EXTBLOCK":"", - "RLP_InvalidArg0_UNMARSHAL_BYTES":"", - "RLP_ExpectedInputList_HEADER_DECODEINTO_BLOCK_EXTBLOCK":"", - "RLP_InputList_TooManyElements_HEADER_DECODEINTO_BLOCK_EXTBLOCK_HEADER":"", - "RLP_InputList_TooManyElements_TXDATA_DECODEINTO_BLOCK_EXTBLOCK_TXS0":"", - "RLP_InputString_TooShort_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_HEADER_COINBASE":"", - "RLP_InputString_TooShort_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_HEADER_COINBASE2":"", - "RLP_InputString_TooShort_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_TXS0_RECIPIENT":"", - "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_ROOT":"", - "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_ROOT2":"", - "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST":"", - "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST2":"", - "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH":"", - "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH2":"", - "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH":"", - "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH2":"", - "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TXHASH":"", - "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH":"", - "RLP_InputString_TooLong_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH2":"", - "RLP_InputString_TooLong_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASLIMIT":"", - "RLP_InputString_TooLong_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASUSED":"", - "RLP_InputString_TooLong_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TIME":"", - "RLP_InputString_TooLong_UINT64_DECODEINTO_BLOCK_EXTBLOCK_TXS0_GASLIMIT":"", - "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH":"", - "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH2":"", - "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_ROOT":"", - "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST":"", - "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST2":"", - "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH":"", - "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH2":"", - "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH":"", - "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH2":"", - "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TXHASH":"", - "RLP_InputString_TooShort_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TXHASH2":"", - "RLP_InputString_TooShort_BLOOM_DECODEINTO_BLOCK_EXTBLOCK_HEADER_BLOOM":"", - "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_DIFFICULTY":"", - "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_DIFFICULTY2":"", - "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASLIMIT":"", - "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASLIMIT2":"", - "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASUSED":"", - "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASUSED2":"", - "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TIME":"", - "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TIME2":"", - "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_TXS0_GASLIMIT":"", - "RLP_NonCanonicalINT_LeadingZeros_UINT64_DECODEINTO_BLOCK_EXTBLOCK_TXS0_GASLIMIT2":"", - "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NUMBER":"", - "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NUMBER2":"", - "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXDATA_PRICE":"", - "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXDATA_R":"", - "RLP_NonCanonicalINT_LeadingZeros_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXDATA_S":"", - "RLP_InputString_TooLong_BLOOM_DECODEINTO_BLOCK_EXTBLOCK_HEADER_BLOOM":"", - "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_PARENTHASH":"", - "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_RECEIPTHASH":"", - "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_ROOT":"", - "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_MIXDIGEST":"", - "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TXHASH":"", - "RLP_ExpectedInputString_HASH_DECODEINTO_BLOCK_EXTBLOCK_HEADER_UNCLEHASH":"", - "RLP_ExpectedInputString_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_HEADER_COINBASE":"", - "RLP_ExpectedInputString_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_TX0_RECIPIENT":"", - "RLP_InputString_TooLong_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_HEADER_COINBASE":"", - "RLP_InputString_TooLong_ADDRESS_DECODEINTO_BLOCK_EXTBLOCK_TXS0_RECIPIENT":"", - "RLP_ExpectedInputString_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_HEADER_DIFFICULTY":"", - "RLP_ExpectedInputString_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXR":"", - "RLP_ExpectedInputString_BIGINT_DECODEINTO_BLOCK_EXTBLOCK_TXS0_TXS":"", - "RLP_ExpectedInputString_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASLIMIT":"", - "RLP_ExpectedInputString_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_GASUSED":"", - "RLP_ExpectedInputString_UINT64_DECODEINTO_BLOCK_EXTBLOCK_HEADER_TIME":"", - "RLP_ExpectedInputString_UINT64_DECODEINTO_BLOCK_EXTBLOCK_TXS0_GASLIMIT":"", - "RLP_ExpectedInputString_NONCE_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NONCE":"", - "RLP_ExpectedInputString_UINT8_DECODEINTO_BLOCK_EXTBLOCK_TXS0_PAYLOAD":"", - "RLP_InputString_TooLong_BLOCKNONCE_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NONCE":"", - "RLP_InputString_TooLong_BLOCKNONCE_DECODEINTO_BLOCK_EXTBLOCK_HEADER_NONCE2":"", - "RLP_NonCanonical_SizeInfo_EXTBLOCK":"", - "RLP_ExpectedInputList_TRANSACTION_DECODEINTO_BLOCK_EXTBLOCK_TXS":"", - "RLP_ExpectedInputList_HEADER_DECODEINTO_BLOCK_EXTBLOCK_UNCLES":"", - "RLP_ExpectedInputList_TXDATA_DECODEINTO_BLOCK_EXTBLOCK_TXS0":"", - "RLP_Error_EOF":"", - "RLP_Error_RLP_Size":"", - "RLP_Error_Size_Information":"", - "LegacyBlockImportImpossible":"", - "LegacyBlockImportImpossible2":"", - "LegacyBlockBaseFeeTransaction":"", - "1559BlockImportImpossible_HeaderIsLegacy":"", - "1559BlockImportImpossible_BaseFeeWrong":"", - "1559BlockImportImpossible_InitialBaseFeeWrong":"", - "1559BlockImportImpossible_TargetGasLow":"", - "1559BlockImportImpossible_TargetGasHigh":"", - "1559BlockImportImpossible_InitialGasLimitInvalid":"", - "TR_IntrinsicGas":"", - "TR_NoFunds":"", - "TR_NoFundsValue":"", - "TR_FeeCapLessThanBlocks":"", - "TR_GasLimitReached":"", - "TR_NonceTooHigh":"", - "TR_NonceTooLow":"", - "TR_TypeNotSupported":"", - "TR_TipGtFeeCap":"", - "TR_TooShort":"", - "1559BaseFeeTooLarge":"", - "1559PriorityFeeGreaterThanBaseFee":"", - "2930AccessListAddressTooLong":"", - "2930AccessListAddressTooShort":"", - "2930AccessListStorageHashTooLong":"", - "1559LeadingZerosBaseFee":"", - "1559LeadingZerosPriorityFee":"", - "2930AccessListStorageHashTooShort":"", - "3675PoWBlockRejected":"", - "3675PoSBlockRejected":"", - "3675PreMerge1559BlockRejected":"" - } - } \ No newline at end of file diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/ArrowGlacier.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/ArrowGlacier.json deleted file mode 100644 index 5acb1cb472..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/ArrowGlacier.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "params": { - "fork": "ArrowGlacier", - "constantinopleForkBlock": "0x00", - "byzantiumForkBlock": "0x00", - "homesteadForkBlock": "0x00" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/Berlin.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/Berlin.json deleted file mode 100644 index b497948cda..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/Berlin.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "params": { - "fork": "Berlin", - "constantinopleForkBlock": "0x00", - "byzantiumForkBlock": "0x00", - "homesteadForkBlock": "0x00" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/BerlinToLondonAt5.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/BerlinToLondonAt5.json deleted file mode 100644 index 8f60badf7e..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/BerlinToLondonAt5.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "params": { - "fork": "BerlinToLondonAt5", - "constantinopleForkBlock": "0x00", - "byzantiumForkBlock": "0x00", - "homesteadForkBlock": "0x00", - "londonForkBlock": "0x05" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/Byzantium.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/Byzantium.json deleted file mode 100644 index 6cfb70e484..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/Byzantium.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "params": { - "fork": "Byzantium", - "byzantiumForkBlock": "0x00", - "homesteadForkBlock": "0x00" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/ByzantiumToConstantinopleFixAt5.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/ByzantiumToConstantinopleFixAt5.json deleted file mode 100644 index a0edf6048c..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/ByzantiumToConstantinopleFixAt5.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "params": { - "fork": "ByzantiumToConstantinopleFixAt5", - "constantinopleForkBlock": "0x05", - "byzantiumForkBlock": "0x00", - "homesteadForkBlock": "0x00" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/Constantinople.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/Constantinople.json deleted file mode 100644 index d664f6348e..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/Constantinople.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "params": { - "fork": "Constantinople", - "constantinopleForkBlock": "0x00", - "byzantiumForkBlock": "0x00", - "homesteadForkBlock": "0x00" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/ConstantinopleFix.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/ConstantinopleFix.json deleted file mode 100644 index 02a05e3b38..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/ConstantinopleFix.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "params": { - "fork": "ConstantinopleFix", - "constantinopleForkBlock": "0x00", - "byzantiumForkBlock": "0x00", - "homesteadForkBlock": "0x00" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP150.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP150.json deleted file mode 100644 index 7d8332b7b9..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP150.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "params": { - "fork": "EIP150", - "homesteadForkBlock": "0x00" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP158.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP158.json deleted file mode 100644 index c91d258ce7..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP158.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "params": { - "fork": "EIP158", - "homesteadForkBlock": "0x00" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP158ToByzantiumAt5.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP158ToByzantiumAt5.json deleted file mode 100644 index bb1c47dc00..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/EIP158ToByzantiumAt5.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "params": { - "fork": "EIP158ToByzantiumAt5", - "byzantiumForkBlock": "0x05", - "homesteadForkBlock": "0x00" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/Frontier.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/Frontier.json deleted file mode 100644 index 260c08f8a7..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/Frontier.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "params": { - "fork": "Frontier" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/FrontierToHomesteadAt5.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/FrontierToHomesteadAt5.json deleted file mode 100644 index 1f2f7ea78d..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/FrontierToHomesteadAt5.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "params": { - "fork": "FrontierToHomesteadAt5", - "homesteadForkBlock": "0x05" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/Homestead.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/Homestead.json deleted file mode 100644 index 99b560a5fe..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/Homestead.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "params": { - "fork": "Homestead", - "homesteadForkBlock": "0x00" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/HomesteadToDaoAt5.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/HomesteadToDaoAt5.json deleted file mode 100644 index 6564224adf..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/HomesteadToDaoAt5.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "params": { - "fork": "HomesteadToDaoAt5", - "homesteadForkBlock": "0x00" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/HomesteadToEIP150At5.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/HomesteadToEIP150At5.json deleted file mode 100644 index 1d5835c31b..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/HomesteadToEIP150At5.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "params": { - "fork": "HomesteadToEIP150At5", - "homesteadForkBlock": "0x00" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/Istanbul.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/Istanbul.json deleted file mode 100644 index f0f08c7bef..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/Istanbul.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "params": { - "fork": "Istanbul", - "constantinopleForkBlock": "0x00", - "byzantiumForkBlock": "0x00", - "homesteadForkBlock": "0x00" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/London.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/London.json deleted file mode 100644 index 900222ae38..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/London.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "params": { - "fork": "London", - "constantinopleForkBlock": "0x00", - "byzantiumForkBlock": "0x00", - "homesteadForkBlock": "0x00" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/Paris.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/Paris.json deleted file mode 100644 index b2a1ed0a8b..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/Paris.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "params": { - "fork": "Merged", - "terminalTotalDifficulty": "0x00", - "constantinopleForkBlock": "0x00", - "byzantiumForkBlock": "0x00", - "homesteadForkBlock": "0x00" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/Shanghai.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/Shanghai.json deleted file mode 100644 index 50161e94b8..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/Shanghai.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "params": { - "fork": "Shanghai", - "terminalTotalDifficulty": "0x00", - "constantinopleForkBlock": "0x00", - "byzantiumForkBlock": "0x00", - "homesteadForkBlock": "0x00" - }, - "accounts": {} -} diff --git a/packages/vm/test/t8n/clients/ethereumjs/genesis/correctMiningReward.json b/packages/vm/test/t8n/clients/ethereumjs/genesis/correctMiningReward.json deleted file mode 100644 index 41617b5913..0000000000 --- a/packages/vm/test/t8n/clients/ethereumjs/genesis/correctMiningReward.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "//comment": "State Tests does not calculate mining reward in post conditions, so when filling a blockchain test out of it, the mining reward must be set", - "Frontier": "5000000000000000000", - "Homestead": "5000000000000000000", - "EIP150": "5000000000000000000", - "EIP158": "5000000000000000000", - "Byzantium": "3000000000000000000", - "Constantinople": "2000000000000000000", - "ConstantinopleFix": "2000000000000000000", - "Istanbul": "2000000000000000000", - "Berlin": "2000000000000000000", - "London": "2000000000000000000", - "Paris": "2000000000000000000", - "Merged": "2000000000000000000", - "Shanghai": "2000000000000000000", - - "//comment": "Retesteth calculate rewards on behalf of the tool when filling state tests", - "YOLOv1": "2000000000000000000", - "YOLOv2": "2000000000000000000", - "YOLOv3": "2000000000000000000", - "Aleut": "2000000000000000000" -} diff --git a/packages/vm/test/t8n/clients/version b/packages/vm/test/t8n/clients/version deleted file mode 100644 index 7207510520..0000000000 --- a/packages/vm/test/t8n/clients/version +++ /dev/null @@ -1 +0,0 @@ -0.3.0-shanghai \ No newline at end of file diff --git a/packages/vm/test/t8n/clients/ethereumjs/start.sh b/packages/vm/test/t8n/start.sh similarity index 100% rename from packages/vm/test/t8n/clients/ethereumjs/start.sh rename to packages/vm/test/t8n/start.sh From b9e799591e3e5ea34144873b196e1017207dc323 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 18 Aug 2024 18:49:02 +0200 Subject: [PATCH 14/89] make start.sh executable --- packages/vm/test/t8n/start.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vm/test/t8n/start.sh b/packages/vm/test/t8n/start.sh index 28eeb6380c..7924c71ff6 100755 --- a/packages/vm/test/t8n/start.sh +++ b/packages/vm/test/t8n/start.sh @@ -1 +1,2 @@ +#!/bin/bash curl -X POST -d "${1} ${2} ${3} ${4} ${5} ${6} ${7} ${8} ${9} ${10} ${11} ${12} ${13} ${14} ${15} ${16} ${17} ${18} ${19} ${20}" --silent http://localhost:3000/ \ No newline at end of file From 1afdf1b183174ec44265ec5507cd092165563e96 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 18 Aug 2024 18:51:08 +0200 Subject: [PATCH 15/89] remove console.log [no ci] --- packages/vm/test/retesteth/transition-cluster.cts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/vm/test/retesteth/transition-cluster.cts b/packages/vm/test/retesteth/transition-cluster.cts index 1224aba5a5..9cf8ef4fae 100644 --- a/packages/vm/test/retesteth/transition-cluster.cts +++ b/packages/vm/test/retesteth/transition-cluster.cts @@ -40,8 +40,6 @@ function runTest(message: any) { childObject.active = true const child = childObject.child - console.log(message) - child.send(message) child.on('message', (_childMessage: any) => { From 68d70b3e3da4fd469898607222a43b524b79caca Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 18 Aug 2024 18:51:45 +0200 Subject: [PATCH 16/89] change readme [no ci] --- packages/vm/test/t8n/README.md | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/packages/vm/test/t8n/README.md b/packages/vm/test/t8n/README.md index 436d366929..baa40643cf 100644 --- a/packages/vm/test/t8n/README.md +++ b/packages/vm/test/t8n/README.md @@ -1,25 +1 @@ -This folder contains a script to run with `tsx` together with `retesteth`. - -# Instructions - -Clone `ethereum/tests` (either in the `ethereumjs-monorepo` or elsewhere). - -Configure and run `transition-tool`: - -Note: All paths are relative paths to the `VM` package root. - -1. If you change the port number in `transition-cluster.ts` to anything other than 3000 (or run `transition-cluster` on a separate machine or different IP address from retesteth), update `test/vm/retesteth/clients/ethereumjs/start.sh` to reflect the right IP and port. - -2. From VM package root directory, run `tsx test/retesteth/transition-cluster.cts`. (Note: if not ran from root, spawning child processes will error out!) - -Configure and run `retesteth`: - -1. Follow the build instructions to build and install [`retesteth`](https://github.com/ethereum/retesteth) - -2. Run `retesteth` from `[retesteth repository root]/retesteth` using the below command - -`./retesteth -t GeneralStateTests -- --testpath $ETHEREUM_TESTPATH --datadir $RETESTETH_CLIENTS --clients "ethereumjs"` - -Here `$ETHEREUM_TESTPATH` should be set to where `ethereum/tests` root folder is installed (so after cloning it). `$RETESTETH_CLIENTS` should be set to `[ethereumjs-monorepo root]/packages/vm/test/retesteth/clients` (use the absolute path from root when running retesteth from another folder). - -Note: if an error regarding "default client not found" pops up, copy the `test/retesteth/clients/ethereumjs` folder into `test/retesteth/clients/default`. If retesteth complains about Paris missing, then move genesis/Merge.json to genesis/Paris.json +TODO write instructions From 7dc01c6153c79d7e5715d0fc95f2e13260c3743a Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 18 Aug 2024 20:59:48 +0200 Subject: [PATCH 17/89] update t8n [no ci] --- packages/vm/test/t8n/start.sh | 7 +- packages/vm/test/t8n/t8n.ts | 157 ++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 packages/vm/test/t8n/t8n.ts diff --git a/packages/vm/test/t8n/start.sh b/packages/vm/test/t8n/start.sh index 7924c71ff6..0352e14007 100755 --- a/packages/vm/test/t8n/start.sh +++ b/packages/vm/test/t8n/start.sh @@ -1,2 +1,7 @@ #!/bin/bash -curl -X POST -d "${1} ${2} ${3} ${4} ${5} ${6} ${7} ${8} ${9} ${10} ${11} ${12} ${13} ${14} ${15} ${16} ${17} ${18} ${19} ${20}" --silent http://localhost:3000/ \ No newline at end of file +if [[ "$1" == "--version" ]]; then + echo "ethereumjs t8n v1" + exit 0 +fi +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +tsx "$SCRIPT_DIR/t8n.ts" "$@" \ No newline at end of file diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts new file mode 100644 index 0000000000..76d2ee785d --- /dev/null +++ b/packages/vm/test/t8n/t8n.ts @@ -0,0 +1,157 @@ +import { Block, createBlock, createBlockHeader } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' +import { RLP } from '@ethereumjs/rlp' +import { createLegacyTxFromBytesArray, createTxFromSerializedData } from '@ethereumjs/tx' +import { Account, bytesToHex, hexToBytes, unprefixedHexToBytes } from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak' +import { readFileSync, writeFileSync } from 'fs' +import { join } from 'path' +import yargs from 'yargs' +import { hideBin } from 'yargs/helpers' + +import { BlockBuilder } from '../../dist/esm/buildBlock.js' +import { VM } from '../../dist/esm/vm.js' +import { getCommon } from '../tester/config.js' +import { makeBlockFromEnv, setupPreConditions } from '../util.js' + +import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' +import type { TypedTransaction } from '@ethereumjs/tx' +import type { NestedUint8Array } from '@ethereumjs/util' + +const args = yargs(hideBin(process.argv)) + .option('state.fork', { + describe: 'Fork to use', + type: 'string', + }) + .option('input.alloc', { + describe: 'Initial state allocation', + type: 'string', + }) + .option('inputs.txs', { + describe: 'RLP input of txs to run on top of the initial state allocation', + type: 'string', + }) + .option('inputs.env', { + describe: 'Input environment (coinbase, difficulty, etc.)', + type: 'string', + }) + .option('output.basedir', { + describe: 'Base directory to write output to', + type: 'string', + }) + .option('output.result', { + describe: 'File to write output results to (relative to `output.basedir`)', + type: 'string', + }) + .option('output.alloc', { + describe: 'File to write output allocation to (after running the transactions)', + type: 'string', + }).argv as any +const alloc = JSON.parse(readFileSync(args.input.alloc).toString()) +const rlpTxs = JSON.parse(readFileSync(args.input.txs).toString()) +const inputEnv = JSON.parse(readFileSync(args.input.env).toString()) + +const common = getCommon(args.state.fork) + +let blockchain +if (args.state.fork === 'Merged') { + const genesisBlockData = { + gasLimit: 5000, + difficulty: 0, + nonce: hexToBytes('0x0000000000000000'), + extraData: hexToBytes('0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa'), + } + const genesis = createBlock({ header: createBlockHeader(genesisBlockData) }) + blockchain = await createBlockchain({ common, genesisBlock: genesis }) +} +const vm = blockchain ? await VM.create({ common, blockchain }) : await VM.create({ common }) +await setupPreConditions(vm.stateManager, { pre: alloc }) + +console.log(inputEnv) + +const block = makeBlockFromEnv(inputEnv, { common }) + +const acc = (await vm.stateManager.getAccount(block.header.coinbase)) ?? new Account() +await vm.stateManager.putAccount(block.header.coinbase, acc) + +const txsData = RLP.decode(unprefixedHexToBytes(rlpTxs.slice(2))) + +const headerData = block.header.toJSON() +headerData.difficulty = inputEnv.parentDifficulty + +const builder = new BlockBuilder(vm, { + parentBlock: new Block(), + headerData, + blockOpts: { putBlockIntoBlockchain: false }, +}) + +const receipts: any = [] + +let txCounter = 0 + +vm.events.on('afterTx', async (afterTx: any, continueFn: any) => { + const receipt = afterTx.receipt as PostByzantiumTxReceipt + const pushReceipt = { + root: '0x', + status: receipt.status === 0 ? '0x' : '0x1', + cumulativeGasUsed: '0x' + receipt.cumulativeBlockGasUsed.toString(16), + logsBloom: bytesToHex(receipt.bitvector), + logs: null, + transactionHash: bytesToHex(afterTx.transaction.hash()), + contractAddress: '0x0000000000000000000000000000000000000000', + gasUsed: '0x' + afterTx.totalGasSpent.toString(16), + blockHash: '0x0000000000000000000000000000000000000000000000000000000000000000', + transactionIndex: '0x' + txCounter.toString(16), + } + receipts.push(pushReceipt) + txCounter++ + continueFn!(undefined) +}) + +const rejected: any = [] + +let index = 0 +for (const txData of txsData as NestedUint8Array) { + try { + let tx: TypedTransaction + if (txData instanceof Uint8Array) { + tx = createTxFromSerializedData(txData as Uint8Array, { common }) + } else { + tx = createLegacyTxFromBytesArray(txData as Uint8Array[], { common }) + } + await builder.addTransaction(tx) + } catch (e: any) { + rejected.push({ + index, + error: e.message, + }) + } + index++ +} + +const logsBloom = builder.logsBloom() +const logsHash = keccak256(logsBloom) + +await vm.evm.journal.cleanup() + +const output = { + stateRoot: bytesToHex(await vm.stateManager.getStateRoot()), + txRoot: bytesToHex(await builder.transactionsTrie()), + receiptsRoot: bytesToHex(await builder.receiptTrie()), + logsHash: bytesToHex(logsHash), + logsBloom: bytesToHex(logsBloom), + currentDifficulty: '0x20000', + receipts, // TODO fixme +} + +if (rejected.length > 0) { + ;(output as any).rejected = rejected +} + +const outputAlloc = alloc //{} + +const outputResultFilePath = join(args.output.basedir, args.output.result) +const outputAllocFilePath = join(args.output.basedir, args.output.alloc) + +writeFileSync(outputResultFilePath, JSON.stringify(output)) +writeFileSync(outputAllocFilePath, JSON.stringify(outputAlloc)) From b30536d95cde2e0a933fd9eebc27dc9cf4021a28 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 00:33:38 +0200 Subject: [PATCH 18/89] add sample (delete me later) --- packages/vm/test/t8n/sampleGethOutput/args.py | 13 +++++ .../t8n/sampleGethOutput/input/alloc.json | 20 +++++++ .../test/t8n/sampleGethOutput/input/env.json | 20 +++++++ .../test/t8n/sampleGethOutput/input/txs.json | 16 +++++ .../t8n/sampleGethOutput/output/alloc.json | 29 ++++++++++ .../t8n/sampleGethOutput/output/result.json | 24 ++++++++ .../test/t8n/sampleGethOutput/output/txs.rlp | 1 + .../test/t8n/sampleGethOutput/returncode.txt | 1 + .../vm/test/t8n/sampleGethOutput/stderr.txt | 2 + .../vm/test/t8n/sampleGethOutput/stdin.txt | 58 +++++++++++++++++++ .../vm/test/t8n/sampleGethOutput/stdout.txt | 52 +++++++++++++++++ packages/vm/test/t8n/sampleGethOutput/t8n.sh | 4 ++ 12 files changed, 240 insertions(+) create mode 100644 packages/vm/test/t8n/sampleGethOutput/args.py create mode 100644 packages/vm/test/t8n/sampleGethOutput/input/alloc.json create mode 100644 packages/vm/test/t8n/sampleGethOutput/input/env.json create mode 100644 packages/vm/test/t8n/sampleGethOutput/input/txs.json create mode 100644 packages/vm/test/t8n/sampleGethOutput/output/alloc.json create mode 100644 packages/vm/test/t8n/sampleGethOutput/output/result.json create mode 100644 packages/vm/test/t8n/sampleGethOutput/output/txs.rlp create mode 100644 packages/vm/test/t8n/sampleGethOutput/returncode.txt create mode 100644 packages/vm/test/t8n/sampleGethOutput/stderr.txt create mode 100644 packages/vm/test/t8n/sampleGethOutput/stdin.txt create mode 100644 packages/vm/test/t8n/sampleGethOutput/stdout.txt create mode 100755 packages/vm/test/t8n/sampleGethOutput/t8n.sh diff --git a/packages/vm/test/t8n/sampleGethOutput/args.py b/packages/vm/test/t8n/sampleGethOutput/args.py new file mode 100644 index 0000000000..d7de1b7079 --- /dev/null +++ b/packages/vm/test/t8n/sampleGethOutput/args.py @@ -0,0 +1,13 @@ +[ + "/usr/bin/evm", + "t8n", + "--input.alloc=stdin", + "--input.txs=stdin", + "--input.env=stdin", + "--output.result=stdout", + "--output.alloc=stdout", + "--output.body=stdout", + "--state.fork=Shanghai", + "--state.chainid=1", + "--state.reward=0" +] \ No newline at end of file diff --git a/packages/vm/test/t8n/sampleGethOutput/input/alloc.json b/packages/vm/test/t8n/sampleGethOutput/input/alloc.json new file mode 100644 index 0000000000..396b8cdb6c --- /dev/null +++ b/packages/vm/test/t8n/sampleGethOutput/input/alloc.json @@ -0,0 +1,20 @@ +{ + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "nonce": "0x00", + "balance": "0x3635c9adc5dea00000", + "code": "0x", + "storage": {} + }, + "0x0000000000000000000000000000000000001000": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60ff5f5360015ff3", + "storage": {} + }, + "0x0000000000000000000000000000000000001100": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60006000600060006000611000620186a0f1600055600160005560016000601f3e600051600155", + "storage": {} + } +} diff --git a/packages/vm/test/t8n/sampleGethOutput/input/env.json b/packages/vm/test/t8n/sampleGethOutput/input/env.json new file mode 100644 index 0000000000..53c4ad95b2 --- /dev/null +++ b/packages/vm/test/t8n/sampleGethOutput/input/env.json @@ -0,0 +1,20 @@ +{ + "currentCoinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentGasLimit": "100000000000000000", + "currentNumber": "1", + "currentTimestamp": "1000", + "currentRandom": "0", + "currentDifficulty": "0", + "parentDifficulty": "0", + "parentTimestamp": "0", + "parentBaseFee": "7", + "parentGasUsed": "0", + "parentGasLimit": "100000000000000000", + "parentUncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "blockHashes": { + "0": "0xd25d208f3633531c7f285498c7a1bc15f6e921f367e65e978251f88007fbbfbb" + }, + "ommers": [], + "withdrawals": [], + "parentHash": "0xd25d208f3633531c7f285498c7a1bc15f6e921f367e65e978251f88007fbbfbb" +} diff --git a/packages/vm/test/t8n/sampleGethOutput/input/txs.json b/packages/vm/test/t8n/sampleGethOutput/input/txs.json new file mode 100644 index 0000000000..39a5fa1131 --- /dev/null +++ b/packages/vm/test/t8n/sampleGethOutput/input/txs.json @@ -0,0 +1,16 @@ +[ + { + "type": "0x0", + "chainId": "0x1", + "nonce": "0x0", + "gasPrice": "0xa", + "gas": "0x186a0", + "to": "0x0000000000000000000000000000000000001100", + "value": "0x0", + "input": "0x", + "v": "0x25", + "r": "0x450bb2501f58d443b494932d42891374385d2fef15af0090bf88adff0d247d6a", + "s": "0x1f23877ce28d6d6986111de1b5594156a77690f214a2beba16f7556d3bf7a51", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + } +] diff --git a/packages/vm/test/t8n/sampleGethOutput/output/alloc.json b/packages/vm/test/t8n/sampleGethOutput/output/alloc.json new file mode 100644 index 0000000000..fd9ebbc5cb --- /dev/null +++ b/packages/vm/test/t8n/sampleGethOutput/output/alloc.json @@ -0,0 +1,29 @@ +{ + "0x0000000000000000000000000000000000001000": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60ff5f5360015ff3", + "storage": {} + }, + "0x0000000000000000000000000000000000001100": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60006000600060006000611000620186a0f1600055600160005560016000601f3e600051600155", + "storage": { + "0x00": "0x01", + "0x01": "0xff" + } + }, + "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { + "nonce": "0x00", + "balance": "0x031c8f", + "code": "0x", + "storage": {} + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "nonce": "0x01", + "balance": "0x3635c9adc5de95a0ce", + "code": "0x", + "storage": {} + } +} diff --git a/packages/vm/test/t8n/sampleGethOutput/output/result.json b/packages/vm/test/t8n/sampleGethOutput/output/result.json new file mode 100644 index 0000000000..8e106c2ff3 --- /dev/null +++ b/packages/vm/test/t8n/sampleGethOutput/output/result.json @@ -0,0 +1,24 @@ +{ + "stateRoot": "0x187e2ba0dad27c2c7384050e601724bb54673bfb9b7c82ce1217d3432a730c05", + "txRoot": "0x56c81c68f1df70123bad2ce903057438aa4a2a2740a73cfc1d61d4b66ead583d", + "receiptsRoot": "0x64d2b19c0d71c3f3fb78dc292f9c5c3a52eac4fbc43dbcb62c622ba33a9e56c8", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": [ + { + "transactionHash": "0xb8da878f9f5e9334dede83c8ef7bc6831737b2bc1d277670300992fc8c7305ce", + "gasUsed": "0x10985", + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0x10985", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "contractAddress": "0x0000000000000000000000000000000000000000", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionIndex": "0x0" + } + ], + "rejected": [], + "gasUsed": "0x10985", + "currentBaseFee": "0x7", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" +} diff --git a/packages/vm/test/t8n/sampleGethOutput/output/txs.rlp b/packages/vm/test/t8n/sampleGethOutput/output/txs.rlp new file mode 100644 index 0000000000..19ea42279e --- /dev/null +++ b/packages/vm/test/t8n/sampleGethOutput/output/txs.rlp @@ -0,0 +1 @@ +0xf862f860800a830186a0940000000000000000000000000000000000001100808025a0450bb2501f58d443b494932d42891374385d2fef15af0090bf88adff0d247d6aa001f23877ce28d6d6986111de1b5594156a77690f214a2beba16f7556d3bf7a51 \ No newline at end of file diff --git a/packages/vm/test/t8n/sampleGethOutput/returncode.txt b/packages/vm/test/t8n/sampleGethOutput/returncode.txt new file mode 100644 index 0000000000..c227083464 --- /dev/null +++ b/packages/vm/test/t8n/sampleGethOutput/returncode.txt @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/packages/vm/test/t8n/sampleGethOutput/stderr.txt b/packages/vm/test/t8n/sampleGethOutput/stderr.txt new file mode 100644 index 0000000000..aa5f95abc1 --- /dev/null +++ b/packages/vm/test/t8n/sampleGethOutput/stderr.txt @@ -0,0 +1,2 @@ +INFO [08-19|00:30:25.268] Trie dumping started root=187e2b..730c05 +INFO [08-19|00:30:25.268] Trie dumping complete accounts=4 elapsed="105.981µs" diff --git a/packages/vm/test/t8n/sampleGethOutput/stdin.txt b/packages/vm/test/t8n/sampleGethOutput/stdin.txt new file mode 100644 index 0000000000..d7025cd9ad --- /dev/null +++ b/packages/vm/test/t8n/sampleGethOutput/stdin.txt @@ -0,0 +1,58 @@ +{ + "alloc": { + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "nonce": "0x00", + "balance": "0x3635c9adc5dea00000", + "code": "0x", + "storage": {} + }, + "0x0000000000000000000000000000000000001000": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60ff5f5360015ff3", + "storage": {} + }, + "0x0000000000000000000000000000000000001100": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60006000600060006000611000620186a0f1600055600160005560016000601f3e600051600155", + "storage": {} + } + }, + "txs": [ + { + "type": "0x0", + "chainId": "0x1", + "nonce": "0x0", + "gasPrice": "0xa", + "gas": "0x186a0", + "to": "0x0000000000000000000000000000000000001100", + "value": "0x0", + "input": "0x", + "v": "0x25", + "r": "0x450bb2501f58d443b494932d42891374385d2fef15af0090bf88adff0d247d6a", + "s": "0x1f23877ce28d6d6986111de1b5594156a77690f214a2beba16f7556d3bf7a51", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + } + ], + "env": { + "currentCoinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentGasLimit": "100000000000000000", + "currentNumber": "1", + "currentTimestamp": "1000", + "currentRandom": "0", + "currentDifficulty": "0", + "parentDifficulty": "0", + "parentTimestamp": "0", + "parentBaseFee": "7", + "parentGasUsed": "0", + "parentGasLimit": "100000000000000000", + "parentUncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "blockHashes": { + "0": "0xd25d208f3633531c7f285498c7a1bc15f6e921f367e65e978251f88007fbbfbb" + }, + "ommers": [], + "withdrawals": [], + "parentHash": "0xd25d208f3633531c7f285498c7a1bc15f6e921f367e65e978251f88007fbbfbb" + } +} \ No newline at end of file diff --git a/packages/vm/test/t8n/sampleGethOutput/stdout.txt b/packages/vm/test/t8n/sampleGethOutput/stdout.txt new file mode 100644 index 0000000000..38d9132ce7 --- /dev/null +++ b/packages/vm/test/t8n/sampleGethOutput/stdout.txt @@ -0,0 +1,52 @@ +{ + "alloc": { + "0x0000000000000000000000000000000000001000": { + "code": "0x60ff5f5360015ff3", + "balance": "0x0", + "nonce": "0x1" + }, + "0x0000000000000000000000000000000000001100": { + "code": "0x60006000600060006000611000620186a0f1600055600160005560016000601f3e600051600155", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000000ff" + }, + "balance": "0x0", + "nonce": "0x1" + }, + "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { + "balance": "0x31c8f" + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x3635c9adc5de95a0ce", + "nonce": "0x1" + } + }, + "body": "0xf862f860800a830186a0940000000000000000000000000000000000001100808025a0450bb2501f58d443b494932d42891374385d2fef15af0090bf88adff0d247d6aa001f23877ce28d6d6986111de1b5594156a77690f214a2beba16f7556d3bf7a51", + "result": { + "stateRoot": "0x187e2ba0dad27c2c7384050e601724bb54673bfb9b7c82ce1217d3432a730c05", + "txRoot": "0x56c81c68f1df70123bad2ce903057438aa4a2a2740a73cfc1d61d4b66ead583d", + "receiptsRoot": "0x64d2b19c0d71c3f3fb78dc292f9c5c3a52eac4fbc43dbcb62c622ba33a9e56c8", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": [ + { + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0x10985", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": null, + "transactionHash": "0xb8da878f9f5e9334dede83c8ef7bc6831737b2bc1d277670300992fc8c7305ce", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x10985", + "effectiveGasPrice": null, + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionIndex": "0x0" + } + ], + "currentDifficulty": null, + "gasUsed": "0x10985", + "currentBaseFee": "0x7", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" + } +} diff --git a/packages/vm/test/t8n/sampleGethOutput/t8n.sh b/packages/vm/test/t8n/sampleGethOutput/t8n.sh new file mode 100755 index 0000000000..4a15db3b32 --- /dev/null +++ b/packages/vm/test/t8n/sampleGethOutput/t8n.sh @@ -0,0 +1,4 @@ +#!/bin/bash +rm -rf /home/jochem/Documents/ejs/execution-spec-tests/evmdebug/shanghai__eip3855_push0__test_push0__test_push0_contract_during_call_contexts/fork_Shanghai_blockchain_test_call/0/t8n.sh.out # hard-coded to avoid surprises +mkdir /home/jochem/Documents/ejs/execution-spec-tests/evmdebug/shanghai__eip3855_push0__test_push0__test_push0_contract_during_call_contexts/fork_Shanghai_blockchain_test_call/0/t8n.sh.out # unused if tracing is not enabled +/usr/bin/evm t8n --input.alloc=stdin --input.txs=stdin --input.env=stdin --output.result=stdout --output.alloc=stdout --output.body=stdout --state.fork=Shanghai --state.chainid=1 --state.reward=0 < /home/jochem/Documents/ejs/execution-spec-tests/evmdebug/shanghai__eip3855_push0__test_push0__test_push0_contract_during_call_contexts/fork_Shanghai_blockchain_test_call/0/stdin.txt From 4cf454765da0973403232b7fe7563caff9a33b6e Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 00:34:53 +0200 Subject: [PATCH 19/89] update t8n [no ci] --- packages/vm/test/t8n/t8n.ts | 88 +++++++++++++++------ packages/vm/test/t8n/transition-cluster.cts | 4 +- packages/vm/test/util.ts | 7 +- 3 files changed, 75 insertions(+), 24 deletions(-) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 76d2ee785d..e5e547512a 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -1,8 +1,11 @@ import { Block, createBlock, createBlockHeader } from '@ethereumjs/block' import { createBlockchain } from '@ethereumjs/blockchain' -import { RLP } from '@ethereumjs/rlp' -import { createLegacyTxFromBytesArray, createTxFromSerializedData } from '@ethereumjs/tx' -import { Account, bytesToHex, hexToBytes, unprefixedHexToBytes } from '@ethereumjs/util' +import { + createLegacyTxFromBytesArray, + createTxFromSerializedData, + createTxFromTxData, +} from '@ethereumjs/tx' +import { Account, bigIntToHex, bytesToHex, hexToBytes } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' import { readFileSync, writeFileSync } from 'fs' import { join } from 'path' @@ -15,9 +18,36 @@ import { getCommon } from '../tester/config.js' import { makeBlockFromEnv, setupPreConditions } from '../util.js' import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' -import type { TypedTransaction } from '@ethereumjs/tx' +import type { TypedTransaction, TypedTxData } from '@ethereumjs/tx' import type { NestedUint8Array } from '@ethereumjs/util' +function normalizeNumbers(input: any) { + const keys = [ + 'currentGasLimit', + 'currentNumber', + 'currentTimestamp', + 'currentRandom', + 'currentDifficulty', + 'parentDifficulty', + 'parentTimestamp', + 'parentBaseFee', + 'parentGasUsed', + 'parentGasLimit', + 'parentBlobGasUsed', + 'parentExcessBlobGas', + ] + + for (const key of keys) { + const value = input[key] + if (value !== undefined) { + if (value.substring(0, 2) !== '0x') { + input[key] = BigInt(value) + } + } + } + return input +} + const args = yargs(hideBin(process.argv)) .option('state.fork', { describe: 'Fork to use', @@ -47,9 +77,12 @@ const args = yargs(hideBin(process.argv)) describe: 'File to write output allocation to (after running the transactions)', type: 'string', }).argv as any + +console.log('ARGS', args) + const alloc = JSON.parse(readFileSync(args.input.alloc).toString()) -const rlpTxs = JSON.parse(readFileSync(args.input.txs).toString()) -const inputEnv = JSON.parse(readFileSync(args.input.env).toString()) +const txsData = JSON.parse(readFileSync(args.input.txs).toString()) +const inputEnv = normalizeNumbers(JSON.parse(readFileSync(args.input.env).toString())) const common = getCommon(args.state.fork) @@ -65,17 +98,12 @@ if (args.state.fork === 'Merged') { blockchain = await createBlockchain({ common, genesisBlock: genesis }) } const vm = blockchain ? await VM.create({ common, blockchain }) : await VM.create({ common }) -await setupPreConditions(vm.stateManager, { pre: alloc }) -console.log(inputEnv) +console.log('ALLOC', alloc) +await setupPreConditions(vm.stateManager, { pre: alloc }) const block = makeBlockFromEnv(inputEnv, { common }) -const acc = (await vm.stateManager.getAccount(block.header.coinbase)) ?? new Account() -await vm.stateManager.putAccount(block.header.coinbase, acc) - -const txsData = RLP.decode(unprefixedHexToBytes(rlpTxs.slice(2))) - const headerData = block.header.toJSON() headerData.difficulty = inputEnv.parentDifficulty @@ -89,7 +117,7 @@ const receipts: any = [] let txCounter = 0 -vm.events.on('afterTx', async (afterTx: any, continueFn: any) => { +vm.events.on('afterTx', async (afterTx, continueFn: any) => { const receipt = afterTx.receipt as PostByzantiumTxReceipt const pushReceipt = { root: '0x', @@ -111,14 +139,28 @@ vm.events.on('afterTx', async (afterTx: any, continueFn: any) => { const rejected: any = [] let index = 0 -for (const txData of txsData as NestedUint8Array) { + +for (const txData of txsData) { try { - let tx: TypedTransaction - if (txData instanceof Uint8Array) { - tx = createTxFromSerializedData(txData as Uint8Array, { common }) - } else { - tx = createLegacyTxFromBytesArray(txData as Uint8Array[], { common }) + if (txData.v !== undefined) { + txData.yParity = txData.v + } + if (txData.gas !== undefined) { + txData.gasLimit = txData.gas } + + if (txData.authorizationList !== undefined) { + txData.authorizationList.map((e: any) => { + if (e.yParity === undefined) { + e.yParity = e.v + } + }) + } + if (txData.input !== undefined) { + txData.data = txData.input + } + console.log('TXDATA', txData) + const tx = createTxFromTxData(txData, { common }) await builder.addTransaction(tx) } catch (e: any) { rejected.push({ @@ -140,15 +182,17 @@ const output = { receiptsRoot: bytesToHex(await builder.receiptTrie()), logsHash: bytesToHex(logsHash), logsBloom: bytesToHex(logsBloom), - currentDifficulty: '0x20000', receipts, // TODO fixme + gasUsed: bigIntToHex(builder.gasUsed), } if (rejected.length > 0) { ;(output as any).rejected = rejected } -const outputAlloc = alloc //{} +const outputAlloc = alloc + +console.log(output) const outputResultFilePath = join(args.output.basedir, args.output.result) const outputAllocFilePath = join(args.output.basedir, args.output.alloc) diff --git a/packages/vm/test/t8n/transition-cluster.cts b/packages/vm/test/t8n/transition-cluster.cts index 9cf8ef4fae..d789574e59 100644 --- a/packages/vm/test/t8n/transition-cluster.cts +++ b/packages/vm/test/t8n/transition-cluster.cts @@ -2,7 +2,7 @@ import { fork } from 'child_process' import { createServer } from 'http' import { resolve } from 'path' -const program = resolve('test/retesteth/transition-child.cts') +const program = resolve('test/t8n/transition-child.cts') const parameters: any = [] const options = { //stdio: ['pipe', 'pipe', 'pipe', 'ipc'], @@ -50,9 +50,11 @@ function runTest(message: any) { } const server = createServer((request: any, result: any) => { + console.log("IN", request.method) if (request.method === 'POST') { let message = '' request.on('data', (data: any) => { + console.log("DATA", data.toString(), data.toString().length) message += data.toString() }) request.on('end', async () => { diff --git a/packages/vm/test/util.ts b/packages/vm/test/util.ts index e63774d9e3..3437c9eb57 100644 --- a/packages/vm/test/util.ts +++ b/packages/vm/test/util.ts @@ -21,6 +21,8 @@ import { isHexString, setLengthLeft, toBytes, + toType, + TypeOutput, unpadBytes, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' @@ -333,7 +335,10 @@ export function makeBlockHeader(data: any, opts?: BlockOptions) { } } if (opts?.common && opts.common.gteHardfork('paris')) { - headerData['mixHash'] = currentRandom + headerData['mixHash'] = setLengthLeft( + toType(currentRandom, TypeOutput.Uint8Array)!, + 32, + ) headerData['difficulty'] = 0 } if (opts?.common && opts.common.gteHardfork('cancun')) { From c4b702ed448d3473a3062a3f17ffaff34906bff4 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 01:46:13 +0200 Subject: [PATCH 20/89] update t8n to correctly output alloc [no ci] --- packages/vm/test/t8n/t8n.ts | 133 ++++++++++++++++++++++++++++++++---- 1 file changed, 119 insertions(+), 14 deletions(-) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index e5e547512a..46e4f093a0 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -5,7 +5,16 @@ import { createTxFromSerializedData, createTxFromTxData, } from '@ethereumjs/tx' -import { Account, bigIntToHex, bytesToHex, hexToBytes } from '@ethereumjs/util' +import { + Account, + bigIntToHex, + bytesToHex, + createAddressFromString, + hexToBytes, + setLengthLeft, + unpadBytes, + unprefixedHexToBytes, +} from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' import { readFileSync, writeFileSync } from 'fs' import { join } from 'path' @@ -19,7 +28,7 @@ import { makeBlockFromEnv, setupPreConditions } from '../util.js' import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' import type { TypedTransaction, TypedTxData } from '@ethereumjs/tx' -import type { NestedUint8Array } from '@ethereumjs/util' +import type { Address, NestedUint8Array, PrefixedHexString } from '@ethereumjs/util' function normalizeNumbers(input: any) { const keys = [ @@ -124,7 +133,6 @@ vm.events.on('afterTx', async (afterTx, continueFn: any) => { status: receipt.status === 0 ? '0x' : '0x1', cumulativeGasUsed: '0x' + receipt.cumulativeBlockGasUsed.toString(16), logsBloom: bytesToHex(receipt.bitvector), - logs: null, transactionHash: bytesToHex(afterTx.transaction.hash()), contractAddress: '0x0000000000000000000000000000000000000000', gasUsed: '0x' + afterTx.totalGasSpent.toString(16), @@ -136,6 +144,61 @@ vm.events.on('afterTx', async (afterTx, continueFn: any) => { continueFn!(undefined) }) +console.log(alloc) + +// Track the allocation to ensure the output.alloc is correct +const allocTracker: { + [address: string]: { + storage: string[] + } +} = {} + +function addAddress(address: string) { + if (allocTracker[address] === undefined) { + allocTracker[address] = { storage: [] } + } + return allocTracker[address] +} + +function addStorage(address: string, storage: string) { + const storageList = addAddress(address).storage + if (!storageList.includes(storage)) { + storageList.push(storage) + } +} + +const originalPutAccount = vm.stateManager.putAccount +const originalPutCode = vm.stateManager.putCode +const originalPutStorage = vm.stateManager.putStorage + +vm.stateManager.putAccount = async function (...args: any) { + const address =
args[0] + addAddress(address.toString()) + await originalPutAccount.apply(this, args) +} + +vm.stateManager.putAccount = async function (...args: any) { + const address =
args[0] + console.log('PUTACCOUNT', address.toString()) + addAddress(address.toString()) + return await originalPutAccount.apply(this, args) +} + +vm.stateManager.putCode = async function (...args: any) { + const address =
args[0] + console.log('PUTCODE', address.toString()) + addAddress(address.toString()) + return await originalPutCode.apply(this, args) +} + +vm.stateManager.putStorage = async function (...args: any) { + const address =
args[0] + const key = args[1] + console.log('PUTSTORAGE', address.toString(), bytesToHex(key)) + addStorage(address.toString(), bytesToHex(key)) + return await originalPutStorage.apply(this, args) +} + const rejected: any = [] let index = 0 @@ -159,7 +222,6 @@ for (const txData of txsData) { if (txData.input !== undefined) { txData.data = txData.input } - console.log('TXDATA', txData) const tx = createTxFromTxData(txData, { common }) await builder.addTransaction(tx) } catch (e: any) { @@ -171,28 +233,71 @@ for (const txData of txsData) { index++ } -const logsBloom = builder.logsBloom() -const logsHash = keccak256(logsBloom) - await vm.evm.journal.cleanup() +const result = await builder.build() + const output = { - stateRoot: bytesToHex(await vm.stateManager.getStateRoot()), - txRoot: bytesToHex(await builder.transactionsTrie()), - receiptsRoot: bytesToHex(await builder.receiptTrie()), - logsHash: bytesToHex(logsHash), - logsBloom: bytesToHex(logsBloom), - receipts, // TODO fixme + stateRoot: bytesToHex(result.header.stateRoot), + txRoot: bytesToHex(result.header.transactionsTrie), + receiptsRoot: bytesToHex(result.header.receiptTrie), + logsHash: bytesToHex(keccak256(result.header.logsBloom)), + logsBloom: bytesToHex(result.header.logsBloom), + receipts, gasUsed: bigIntToHex(builder.gasUsed), } +if (result.header.baseFeePerGas !== undefined) { + ;(output as any).currentBaseFee = bigIntToHex(result.header.baseFeePerGas) +} + +if (result.header.withdrawalsRoot !== undefined) { + ;(output as any).withdrawalsRoot = bytesToHex(result.header.withdrawalsRoot) +} + if (rejected.length > 0) { ;(output as any).rejected = rejected } +// Build output alloc + +for (const addressString in allocTracker) { + const address = createAddressFromString(addressString) + const account = await vm.stateManager.getAccount(address) + if (account === undefined) { + delete alloc[addressString] + continue + } + if (alloc[addressString] === undefined) { + alloc[addressString] = {} + } + alloc[addressString].nonce = bigIntToHex(account.nonce) + alloc[addressString].balance = bigIntToHex(account.balance) + alloc[addressString].code = bytesToHex(await vm.stateManager.getCode(address)) + + const storage = allocTracker[addressString].storage ?? {} + allocTracker[addressString].storage = storage + + for (const key of storage) { + const keyBytes = hexToBytes(key) + let storageKeyTrimmed = bytesToHex(unpadBytes(keyBytes)) + if (storageKeyTrimmed === '0x') { + storageKeyTrimmed = '0x00' + } + const value = await vm.stateManager.getStorage(address, setLengthLeft(keyBytes, 32)) + if (value.length === 0) { + delete alloc[addressString].storage[storageKeyTrimmed] + // To be sure, also delete any keys which are left-padded to 32 bytes + delete alloc[addressString].storage[key] + continue + } + alloc[addressString].storage[storageKeyTrimmed] = bytesToHex(value) + } +} + const outputAlloc = alloc -console.log(output) +console.log('WRITE', outputAlloc) const outputResultFilePath = join(args.output.basedir, args.output.result) const outputAllocFilePath = join(args.output.basedir, args.output.alloc) From 7f4dc6adc6709f982f11969ee874c4537c6a4e18 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 01:49:00 +0200 Subject: [PATCH 21/89] remove console.logs [no ci] --- packages/vm/test/t8n/t8n.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 46e4f093a0..744b4fc6fc 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -87,8 +87,6 @@ const args = yargs(hideBin(process.argv)) type: 'string', }).argv as any -console.log('ARGS', args) - const alloc = JSON.parse(readFileSync(args.input.alloc).toString()) const txsData = JSON.parse(readFileSync(args.input.txs).toString()) const inputEnv = normalizeNumbers(JSON.parse(readFileSync(args.input.env).toString())) @@ -108,7 +106,6 @@ if (args.state.fork === 'Merged') { } const vm = blockchain ? await VM.create({ common, blockchain }) : await VM.create({ common }) -console.log('ALLOC', alloc) await setupPreConditions(vm.stateManager, { pre: alloc }) const block = makeBlockFromEnv(inputEnv, { common }) @@ -144,8 +141,6 @@ vm.events.on('afterTx', async (afterTx, continueFn: any) => { continueFn!(undefined) }) -console.log(alloc) - // Track the allocation to ensure the output.alloc is correct const allocTracker: { [address: string]: { @@ -179,14 +174,12 @@ vm.stateManager.putAccount = async function (...args: any) { vm.stateManager.putAccount = async function (...args: any) { const address =
args[0] - console.log('PUTACCOUNT', address.toString()) addAddress(address.toString()) return await originalPutAccount.apply(this, args) } vm.stateManager.putCode = async function (...args: any) { const address =
args[0] - console.log('PUTCODE', address.toString()) addAddress(address.toString()) return await originalPutCode.apply(this, args) } @@ -194,7 +187,6 @@ vm.stateManager.putCode = async function (...args: any) { vm.stateManager.putStorage = async function (...args: any) { const address =
args[0] const key = args[1] - console.log('PUTSTORAGE', address.toString(), bytesToHex(key)) addStorage(address.toString(), bytesToHex(key)) return await originalPutStorage.apply(this, args) } @@ -297,8 +289,6 @@ for (const addressString in allocTracker) { const outputAlloc = alloc -console.log('WRITE', outputAlloc) - const outputResultFilePath = join(args.output.basedir, args.output.result) const outputAllocFilePath = join(args.output.basedir, args.output.alloc) From e556de99769ae03088bb0d3a002c337d35d47ed4 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 02:05:07 +0200 Subject: [PATCH 22/89] fix certain values for expected output [no ci] --- packages/vm/test/t8n/t8n.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 744b4fc6fc..cc64886c01 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -37,6 +37,7 @@ function normalizeNumbers(input: any) { 'currentTimestamp', 'currentRandom', 'currentDifficulty', + 'currentBaseFee', 'parentDifficulty', 'parentTimestamp', 'parentBaseFee', @@ -127,7 +128,7 @@ vm.events.on('afterTx', async (afterTx, continueFn: any) => { const receipt = afterTx.receipt as PostByzantiumTxReceipt const pushReceipt = { root: '0x', - status: receipt.status === 0 ? '0x' : '0x1', + status: receipt.status === 0 ? '0x0' : '0x1', cumulativeGasUsed: '0x' + receipt.cumulativeBlockGasUsed.toString(16), logsBloom: bytesToHex(receipt.bitvector), transactionHash: bytesToHex(afterTx.transaction.hash()), From e83e414cccd65d1adc048bc0e2d6272799ad1901 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 02:33:49 +0200 Subject: [PATCH 23/89] some t8n fixes regarding output --- packages/vm/test/t8n/t8n.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index cc64886c01..0696839bf7 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -38,6 +38,8 @@ function normalizeNumbers(input: any) { 'currentRandom', 'currentDifficulty', 'currentBaseFee', + 'currentBlobGasUsed', + 'currentExcessBlobGas', 'parentDifficulty', 'parentTimestamp', 'parentBaseFee', @@ -248,6 +250,14 @@ if (result.header.withdrawalsRoot !== undefined) { ;(output as any).withdrawalsRoot = bytesToHex(result.header.withdrawalsRoot) } +if (result.header.blobGasUsed !== undefined) { + ;(output as any).blobGasUsed = bigIntToHex(result.header.blobGasUsed) +} + +if (result.header.excessBlobGas !== undefined) { + ;(output as any).currentExcessBlobGas = bigIntToHex(result.header.excessBlobGas) +} + if (rejected.length > 0) { ;(output as any).rejected = rejected } @@ -268,8 +278,8 @@ for (const addressString in allocTracker) { alloc[addressString].balance = bigIntToHex(account.balance) alloc[addressString].code = bytesToHex(await vm.stateManager.getCode(address)) - const storage = allocTracker[addressString].storage ?? {} - allocTracker[addressString].storage = storage + const storage = allocTracker[addressString].storage + alloc[addressString].storage = alloc[addressString].storage ?? {} for (const key of storage) { const keyBytes = hexToBytes(key) @@ -279,6 +289,8 @@ for (const addressString in allocTracker) { } const value = await vm.stateManager.getStorage(address, setLengthLeft(keyBytes, 32)) if (value.length === 0) { + console.log(storageKeyTrimmed) + console.log(alloc[addressString]) delete alloc[addressString].storage[storageKeyTrimmed] // To be sure, also delete any keys which are left-padded to 32 bytes delete alloc[addressString].storage[key] From 121ed0b783aeced59551121a970fc34ed7e6158a Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 02:34:34 +0200 Subject: [PATCH 24/89] lint [no ci] --- packages/vm/test/t8n/t8n.ts | 19 +++++-------------- packages/vm/test/util.ts | 2 +- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 0696839bf7..f63ef9ef25 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -1,19 +1,13 @@ import { Block, createBlock, createBlockHeader } from '@ethereumjs/block' import { createBlockchain } from '@ethereumjs/blockchain' +import { createTxFromTxData } from '@ethereumjs/tx' import { - createLegacyTxFromBytesArray, - createTxFromSerializedData, - createTxFromTxData, -} from '@ethereumjs/tx' -import { - Account, bigIntToHex, bytesToHex, createAddressFromString, hexToBytes, setLengthLeft, unpadBytes, - unprefixedHexToBytes, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' import { readFileSync, writeFileSync } from 'fs' @@ -27,8 +21,7 @@ import { getCommon } from '../tester/config.js' import { makeBlockFromEnv, setupPreConditions } from '../util.js' import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' -import type { TypedTransaction, TypedTxData } from '@ethereumjs/tx' -import type { Address, NestedUint8Array, PrefixedHexString } from '@ethereumjs/util' +import type { Address, PrefixedHexString } from '@ethereumjs/util' function normalizeNumbers(input: any) { const keys = [ @@ -178,20 +171,20 @@ vm.stateManager.putAccount = async function (...args: any) { vm.stateManager.putAccount = async function (...args: any) { const address =
args[0] addAddress(address.toString()) - return await originalPutAccount.apply(this, args) + return originalPutAccount.apply(this, args) } vm.stateManager.putCode = async function (...args: any) { const address =
args[0] addAddress(address.toString()) - return await originalPutCode.apply(this, args) + return originalPutCode.apply(this, args) } vm.stateManager.putStorage = async function (...args: any) { const address =
args[0] const key = args[1] addStorage(address.toString(), bytesToHex(key)) - return await originalPutStorage.apply(this, args) + return originalPutStorage.apply(this, args) } const rejected: any = [] @@ -289,8 +282,6 @@ for (const addressString in allocTracker) { } const value = await vm.stateManager.getStorage(address, setLengthLeft(keyBytes, 32)) if (value.length === 0) { - console.log(storageKeyTrimmed) - console.log(alloc[addressString]) delete alloc[addressString].storage[storageKeyTrimmed] // To be sure, also delete any keys which are left-padded to 32 bytes delete alloc[addressString].storage[key] diff --git a/packages/vm/test/util.ts b/packages/vm/test/util.ts index 3437c9eb57..bdc82a1a45 100644 --- a/packages/vm/test/util.ts +++ b/packages/vm/test/util.ts @@ -11,6 +11,7 @@ import { import { Account, Address, + TypeOutput, bigIntToBytes, bytesToBigInt, bytesToHex, @@ -22,7 +23,6 @@ import { setLengthLeft, toBytes, toType, - TypeOutput, unpadBytes, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' From 528612b50daa6cc34ebd4c699d83dfea7ca2daff Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 03:01:19 +0200 Subject: [PATCH 25/89] t8n fixes for paris format [no ci] --- packages/vm/test/t8n/t8n.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index f63ef9ef25..4f6f16ed7e 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -205,6 +205,15 @@ for (const txData of txsData) { if (e.yParity === undefined) { e.yParity = e.v } + if (e.yParity === '0x0') { + e.yParity = '0x' + } + if (e.nonce === '0x0') { + e.nonce = '0x' + } + if (e.chainId === '0x0') { + e.chainId = '0x' + } }) } if (txData.input !== undefined) { @@ -251,6 +260,10 @@ if (result.header.excessBlobGas !== undefined) { ;(output as any).currentExcessBlobGas = bigIntToHex(result.header.excessBlobGas) } +if (result.header.requestsRoot !== undefined) { + ;(output as any).requestsRoot = bytesToHex(result.header.requestsRoot) +} + if (rejected.length > 0) { ;(output as any).rejected = rejected } From d4d3100fe2ba47c4be92c7b6cdcacd7512d5ec37 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 03:13:23 +0200 Subject: [PATCH 26/89] vm: get params from tx for 7702 [no ci] --- packages/vm/src/runTx.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index 66d8bf0eec..4a712eca80 100644 --- a/packages/vm/src/runTx.ts +++ b/packages/vm/src/runTx.ts @@ -467,7 +467,7 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { } if (accountExists) { - const refund = vm.common.param('perEmptyAccountCost') - vm.common.param('perAuthBaseGas') + const refund = tx.common.param('perEmptyAccountCost') - tx.common.param('perAuthBaseGas') fromAccount.balance += refund } From d0a4dfb5edd0fb576e97a26e11f79d4b0278dad3 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 03:43:24 +0200 Subject: [PATCH 27/89] t8n console.log dumps [no ci] --- packages/vm/test/t8n/t8n.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 4f6f16ed7e..1feffe8ec8 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -119,6 +119,8 @@ const receipts: any = [] let txCounter = 0 +let log = true + vm.events.on('afterTx', async (afterTx, continueFn: any) => { const receipt = afterTx.receipt as PostByzantiumTxReceipt const pushReceipt = { @@ -132,11 +134,24 @@ vm.events.on('afterTx', async (afterTx, continueFn: any) => { blockHash: '0x0000000000000000000000000000000000000000000000000000000000000000', transactionIndex: '0x' + txCounter.toString(16), } + console.log('TX DONE') + log = false receipts.push(pushReceipt) txCounter++ continueFn!(undefined) }) +vm.events.on('beforeTx', (e) => { + console.log('!---! NEW TX') + console.log('-------------------------------------------------') +}) + +vm.evm.events?.on('step', (e) => { + if (log) { + console.log(e.address.toString(), e.opcode.name) + } +}) + // Track the allocation to ensure the output.alloc is correct const allocTracker: { [address: string]: { From 0fa00d333a5b67dae475ea226106651c74b3e07c Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 03:44:00 +0200 Subject: [PATCH 28/89] vm: 7702 correctly apply the refund [no ci] --- packages/vm/src/runTx.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index 4a712eca80..1fce1d8b6a 100644 --- a/packages/vm/src/runTx.ts +++ b/packages/vm/src/runTx.ts @@ -423,6 +423,8 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { } await vm.evm.journal.putAccount(caller, fromAccount) + let gasRefund = BIGINT_0 + const writtenAddresses = new Set() if (tx.supports(Capability.EIP7702EOACode)) { // Add contract code for authority tuples provided by EIP 7702 tx @@ -468,11 +470,12 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { if (accountExists) { const refund = tx.common.param('perEmptyAccountCost') - tx.common.param('perAuthBaseGas') - fromAccount.balance += refund + gasRefund += refund + await vm.evm.journal.putAccount(caller, fromAccount) } - fromAccount.nonce++ - await vm.evm.journal.putAccount(authority, fromAccount) + account.nonce++ + await vm.evm.journal.putAccount(authority, account) const addressCode = concatBytes(new Uint8Array([0xef, 0x01, 0x00]), address) await vm.stateManager.putCode(authority, addressCode) @@ -566,7 +569,7 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { } // Process any gas refund - let gasRefund = results.execResult.gasRefund ?? BIGINT_0 + gasRefund += results.execResult.gasRefund ?? BIGINT_0 results.gasRefund = gasRefund const maxRefundQuotient = vm.common.param('maxRefundQuotient') if (gasRefund !== BIGINT_0) { From 1a21f86ac743d6b3006f8cf5bcaa90bf77e1a083 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 04:22:02 +0200 Subject: [PATCH 29/89] vm: 7702: correctly handle self-sponsored txs [no ci] --- packages/vm/src/runTx.ts | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index 1fce1d8b6a..c9f5f8a3a1 100644 --- a/packages/vm/src/runTx.ts +++ b/packages/vm/src/runTx.ts @@ -7,6 +7,7 @@ import { Account, Address, BIGINT_0, + BIGINT_1, KECCAK256_NULL, bytesToBigInt, bytesToHex, @@ -292,8 +293,21 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { } // EIP-3607: Reject transactions from senders with deployed code if (vm.common.isActivatedEIP(3607) && !equalsBytes(fromAccount.codeHash, KECCAK256_NULL)) { - const msg = _errorMsg('invalid sender address, address is not EOA (EIP-3607)', vm, block, tx) - throw new Error(msg) + if (vm.common.isActivatedEIP(7702)) { + const code = await state.getCode(caller) + if (!equalsBytes(code.slice(0, 3), new Uint8Array([0xef, 0x01, 0x00]))) { + const msg = _errorMsg( + 'invalid sender address, address is not EOA (EIP-3607)', + vm, + block, + tx, + ) + throw new Error(msg) + } + } else { + const msg = _errorMsg('invalid sender address, address is not EOA (EIP-3607)', vm, block, tx) + throw new Error(msg) + } } // Check balance against upfront tx cost @@ -464,14 +478,23 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { continue } } - if (account.nonce !== bytesToBigInt(nonce)) { + + // Nonce check + if (caller.toString() === authority.toString()) { + if (account.nonce + BIGINT_1 !== bytesToBigInt(nonce)) { + // Edge case: caller is the authority, so is self-signing the delegation + // In this case, we "virtually" bump the account nonce by one + // We CANNOT put this updated nonce into the account trie, because then + // the EVM will bump the nonce once again, thus resulting in a wrong nonce + continue + } + } else if (account.nonce !== bytesToBigInt(nonce)) { continue } if (accountExists) { const refund = tx.common.param('perEmptyAccountCost') - tx.common.param('perAuthBaseGas') gasRefund += refund - await vm.evm.journal.putAccount(caller, fromAccount) } account.nonce++ From 61629571f542d6569bb7fc95e14352a53ec2e904 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 06:25:19 +0200 Subject: [PATCH 30/89] tx: throw if authorization list is empty --- packages/tx/src/util.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/tx/src/util.ts b/packages/tx/src/util.ts index 6fdc9eeb5e..284b46d560 100644 --- a/packages/tx/src/util.ts +++ b/packages/tx/src/util.ts @@ -199,6 +199,9 @@ export class AuthorizationLists { } public static verifyAuthorizationList(authorizationList: AuthorizationListBytes) { + if (authorizationList.length === 0) { + throw new Error('Invalid EIP-7702 transaction: authorization list is empty') + } for (let key = 0; key < authorizationList.length; key++) { const authorizationListItem = authorizationList[key] const chainId = authorizationListItem[0] From 2043d1e79a0b6d9e11ce84a196289f3cb994ca0d Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 06:25:39 +0200 Subject: [PATCH 31/89] vm: requests do not throw if code is non-existant --- packages/vm/src/requests.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/vm/src/requests.ts b/packages/vm/src/requests.ts index 52baccb219..e035b9db8d 100644 --- a/packages/vm/src/requests.ts +++ b/packages/vm/src/requests.ts @@ -69,9 +69,9 @@ const accumulateEIP7002Requests = async ( const code = await vm.stateManager.getCode(withdrawalsAddress) if (code.length === 0) { - throw new Error( + /*throw new Error( 'Attempt to accumulate EIP-7002 requests failed: the contract does not exist. Ensure the deployment tx has been run, or that the required contract code is stored', - ) + )*/ } const systemAddressBytes = bigIntToAddressBytes(vm.common.param('systemAddress')) @@ -119,9 +119,9 @@ const accumulateEIP7251Requests = async ( const code = await vm.stateManager.getCode(consolidationsAddress) if (code.length === 0) { - throw new Error( + /*throw new Error( 'Attempt to accumulate EIP-7251 requests failed: the contract does not exist. Ensure the deployment tx has been run, or that the required contract code is stored', - ) + )*/ } const systemAddressBytes = bigIntToAddressBytes(vm.common.param('systemAddress')) From 9bd46adfeaa996c437b52fb6085dffd8cb7dfa99 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 16:49:42 +0200 Subject: [PATCH 32/89] evm: ensure correct extcodehash reporting if account is delegated to a non-existing account --- packages/evm/src/opcodes/functions.ts | 28 +++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/packages/evm/src/opcodes/functions.ts b/packages/evm/src/opcodes/functions.ts index 0d29b62bcc..a79204eb98 100644 --- a/packages/evm/src/opcodes/functions.ts +++ b/packages/evm/src/opcodes/functions.ts @@ -60,9 +60,15 @@ export interface AsyncOpHandler { export type OpHandler = SyncOpHandler | AsyncOpHandler -async function eip7702CodeCheck(runState: RunState, code: Uint8Array) { +function getEIP7702DelegatedAddress(code: Uint8Array) { if (equalBytes(code.slice(0, 3), new Uint8Array([0xef, 0x01, 0x00]))) { - const address = new Address(code.slice(3, 24)) + return new Address(code.slice(3, 24)) + } +} + +async function eip7702CodeCheck(runState: RunState, code: Uint8Array) { + const address = getEIP7702DelegatedAddress(code) + if (address !== undefined) { return runState.stateManager.getCode(address) } @@ -571,16 +577,26 @@ export const handlers: Map = new Map([ const address = createAddressFromStackBigInt(addressBigInt) // EOF check - let code = await runState.stateManager.getCode(address) + const code = await runState.stateManager.getCode(address) if (isEOF(code)) { // In legacy code, the target code is treated as to be "EOFBYTES" code // Therefore, push the hash of EOFBYTES to the stack runState.stack.push(bytesToBigInt(EOFHASH)) return } else if (common.isActivatedEIP(7702)) { - code = await eip7702CodeCheck(runState, code) - runState.stack.push(bytesToBigInt(keccak256(code))) - return + const possibleDelegatedAddress = getEIP7702DelegatedAddress(code) + if (possibleDelegatedAddress !== undefined) { + const account = await runState.stateManager.getAccount(possibleDelegatedAddress) + if (!account || account.isEmpty()) { + runState.stack.push(BIGINT_0) + return + } + + runState.stack.push(BigInt(bytesToHex(account.codeHash))) + } else { + runState.stack.push(bytesToBigInt(keccak256(code))) + return + } } const account = await runState.stateManager.getAccount(address) From 1623bc766c495658bf3970d1ac27992395a27caf Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 19:34:50 +0200 Subject: [PATCH 33/89] update t8n to generate logs [no ci] --- packages/evm/src/opcodes/functions.ts | 1 + packages/vm/test/t8n/t8n.ts | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/evm/src/opcodes/functions.ts b/packages/evm/src/opcodes/functions.ts index a79204eb98..4b20d46c29 100644 --- a/packages/evm/src/opcodes/functions.ts +++ b/packages/evm/src/opcodes/functions.ts @@ -593,6 +593,7 @@ export const handlers: Map = new Map([ } runState.stack.push(BigInt(bytesToHex(account.codeHash))) + return } else { runState.stack.push(bytesToBigInt(keccak256(code))) return diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 1feffe8ec8..c21cee0c81 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -1,5 +1,6 @@ import { Block, createBlock, createBlockHeader } from '@ethereumjs/block' import { createBlockchain } from '@ethereumjs/blockchain' +import { RLP } from '@ethereumjs/rlp' import { createTxFromTxData } from '@ethereumjs/tx' import { bigIntToHex, @@ -106,6 +107,10 @@ await setupPreConditions(vm.stateManager, { pre: alloc }) const block = makeBlockFromEnv(inputEnv, { common }) +// TODO: add state.reward +//const acc = (await vm.stateManager.getAccount(block.header.coinbase)) ?? new Account() +//await vm.stateManager.putAccount(block.header.coinbase, acc) + const headerData = block.header.toJSON() headerData.difficulty = inputEnv.parentDifficulty @@ -128,6 +133,7 @@ vm.events.on('afterTx', async (afterTx, continueFn: any) => { status: receipt.status === 0 ? '0x0' : '0x1', cumulativeGasUsed: '0x' + receipt.cumulativeBlockGasUsed.toString(16), logsBloom: bytesToHex(receipt.bitvector), + logs: receipt.logs, transactionHash: bytesToHex(afterTx.transaction.hash()), contractAddress: '0x0000000000000000000000000000000000000000', gasUsed: '0x' + afterTx.totalGasSpent.toString(16), @@ -141,7 +147,7 @@ vm.events.on('afterTx', async (afterTx, continueFn: any) => { continueFn!(undefined) }) -vm.events.on('beforeTx', (e) => { +vm.events.on('beforeTx', () => { console.log('!---! NEW TX') console.log('-------------------------------------------------') }) @@ -249,11 +255,19 @@ await vm.evm.journal.cleanup() const result = await builder.build() +const logsBuilder = [] + +for (const receipt of receipts) { + for (const log of receipt.logs) { + logsBuilder.push(log) + } +} + const output = { stateRoot: bytesToHex(result.header.stateRoot), txRoot: bytesToHex(result.header.transactionsTrie), receiptsRoot: bytesToHex(result.header.receiptTrie), - logsHash: bytesToHex(keccak256(result.header.logsBloom)), + logsHash: bytesToHex(keccak256(RLP.encode(logsBuilder))), logsBloom: bytesToHex(result.header.logsBloom), receipts, gasUsed: bigIntToHex(builder.gasUsed), From b28ff8407709f5f98ac26d931da9e7ae40794769 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 20:49:09 +0200 Subject: [PATCH 34/89] t8n correctly output log format [no ci] --- packages/vm/test/t8n/t8n.ts | 38 ++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index c21cee0c81..42a0e08ab7 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -3,12 +3,15 @@ import { createBlockchain } from '@ethereumjs/blockchain' import { RLP } from '@ethereumjs/rlp' import { createTxFromTxData } from '@ethereumjs/tx' import { + BIGINT_1, bigIntToHex, bytesToHex, createAddressFromString, hexToBytes, setLengthLeft, + toBytes, unpadBytes, + zeros, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' import { readFileSync, writeFileSync } from 'fs' @@ -22,6 +25,7 @@ import { getCommon } from '../tester/config.js' import { makeBlockFromEnv, setupPreConditions } from '../util.js' import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' +import type { Log } from '@ethereumjs/evm' import type { Address, PrefixedHexString } from '@ethereumjs/util' function normalizeNumbers(input: any) { @@ -126,14 +130,38 @@ let txCounter = 0 let log = true +const logsBuilder: Log[] = [] + +let txIndex = -BIGINT_1 + vm.events.on('afterTx', async (afterTx, continueFn: any) => { + txIndex++ const receipt = afterTx.receipt as PostByzantiumTxReceipt + + const formattedLogs = [] + for (const log of receipt.logs) { + logsBuilder.push(log) + + const entry: any = { + address: bytesToHex(log[0]), + topics: log[1].map((e) => bytesToHex(e)), + data: bytesToHex(log[2]), + blockNumber: bytesToHex(toBytes(builder['headerData'].number)), + transactionHash: bytesToHex(afterTx.transaction.hash()), + transactionIndex: bigIntToHex(txIndex), + blockHash: bytesToHex(zeros(32)), + logIndex: bigIntToHex(BigInt(formattedLogs.length)), + removed: 'false', + } + formattedLogs.push(entry) + } + const pushReceipt = { root: '0x', status: receipt.status === 0 ? '0x0' : '0x1', cumulativeGasUsed: '0x' + receipt.cumulativeBlockGasUsed.toString(16), logsBloom: bytesToHex(receipt.bitvector), - logs: receipt.logs, + logs: formattedLogs, transactionHash: bytesToHex(afterTx.transaction.hash()), contractAddress: '0x0000000000000000000000000000000000000000', gasUsed: '0x' + afterTx.totalGasSpent.toString(16), @@ -255,14 +283,6 @@ await vm.evm.journal.cleanup() const result = await builder.build() -const logsBuilder = [] - -for (const receipt of receipts) { - for (const log of receipt.logs) { - logsBuilder.push(log) - } -} - const output = { stateRoot: bytesToHex(result.header.stateRoot), txRoot: bytesToHex(result.header.transactionsTrie), From d5ece8b314454d6d268322f62c471678bb1e81f4 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 19 Aug 2024 22:19:42 +0200 Subject: [PATCH 35/89] change t8ntool start script name [no ci] --- packages/vm/test/t8n/{start.sh => ethereumjs-t8ntool.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/vm/test/t8n/{start.sh => ethereumjs-t8ntool.sh} (100%) diff --git a/packages/vm/test/t8n/start.sh b/packages/vm/test/t8n/ethereumjs-t8ntool.sh similarity index 100% rename from packages/vm/test/t8n/start.sh rename to packages/vm/test/t8n/ethereumjs-t8ntool.sh From 0da810841e3d97858bd4f32ac049737832faf4c2 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 20 Aug 2024 16:23:16 +0200 Subject: [PATCH 36/89] putcode log --- packages/vm/test/t8n/t8n.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 42a0e08ab7..9883540a2e 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -224,6 +224,7 @@ vm.stateManager.putAccount = async function (...args: any) { } vm.stateManager.putCode = async function (...args: any) { + console.log('PUTCODE', args[0].toString(), bytesToHex(args[1])) const address =
args[0] addAddress(address.toString()) return originalPutCode.apply(this, args) From d4f9c604e89f73956cd3509ad5d9d2c174d0d3c1 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 20 Aug 2024 16:45:11 +0200 Subject: [PATCH 37/89] vm: 7702 ensure delegated accounts are not deleted [no ci] --- packages/vm/src/runTx.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index 8748748b38..05c0c2f5e1 100644 --- a/packages/vm/src/runTx.ts +++ b/packages/vm/src/runTx.ts @@ -12,7 +12,6 @@ import { bytesToHex, bytesToUnprefixedHex, concatBytes, - createAddressFromString, ecrecover, equalsBytes, hexToBytes, @@ -426,7 +425,6 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { let gasRefund = BIGINT_0 - const writtenAddresses = new Set() if (tx.supports(Capability.EIP7702EOACode)) { // Add contract code for authority tuples provided by EIP 7702 tx const authorizationList = (tx).authorizationList @@ -489,8 +487,6 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { const addressCode = concatBytes(new Uint8Array([0xef, 0x01, 0x00]), address) await vm.stateManager.putCode(authority, addressCode) - - writtenAddresses.add(authority.toString()) } } @@ -673,15 +669,6 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { } } - /** - * Cleanup code of accounts written to in a 7702 transaction - */ - - for (const str of writtenAddresses) { - const address = createAddressFromString(str) - await vm.stateManager.putCode(address, new Uint8Array()) - } - if (enableProfiler) { // eslint-disable-next-line no-console console.timeEnd(accountsCleanUpLabel) From 678c7cf18abd12a4f51ff4e15aeceddbbdb38e96 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 20 Aug 2024 18:01:35 +0200 Subject: [PATCH 38/89] t8n: output CLrequests [no ci] --- packages/vm/test/t8n/t8n.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 9883540a2e..101b774e3e 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -4,6 +4,7 @@ import { RLP } from '@ethereumjs/rlp' import { createTxFromTxData } from '@ethereumjs/tx' import { BIGINT_1, + CLRequestType, bigIntToHex, bytesToHex, createAddressFromString, @@ -314,6 +315,30 @@ if (result.header.requestsRoot !== undefined) { ;(output as any).requestsRoot = bytesToHex(result.header.requestsRoot) } +if (result.requests !== undefined) { + if (common.isActivatedEIP(6110)) { + ;(output as any).depositRequests = [] + } + + if (common.isActivatedEIP(7002)) { + ;(output as any).withdrawalRequests = [] + } + + if (common.isActivatedEIP(7251)) { + ;(output as any).consolidationRequests = [] + } + + for (const request of result.requests) { + if (request.type === CLRequestType.Deposit) { + ;(output as any).depositRequests.push(request.toJSON()) + } else if (request.type === CLRequestType.Withdrawal) { + ;(output as any).withdrawalRequests.push(request.toJSON()) + } else if (request.type === CLRequestType.Consolidation) { + ;(output as any).consolidationRequests.push(request.toJSON()) + } + } +} + if (rejected.length > 0) { ;(output as any).rejected = rejected } From ae01bcafddd80e3cb0bebb0fca776f7cab2c25bd Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 20 Aug 2024 21:53:02 +0200 Subject: [PATCH 39/89] t8n: add initKzg / blob tx support --- packages/vm/src/buildBlock.ts | 9 +++++++-- packages/vm/test/t8n/t8n.ts | 5 +++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/vm/src/buildBlock.ts b/packages/vm/src/buildBlock.ts index 5ce32f1ddb..22967392de 100644 --- a/packages/vm/src/buildBlock.ts +++ b/packages/vm/src/buildBlock.ts @@ -212,7 +212,10 @@ export class BlockBuilder { */ async addTransaction( tx: TypedTransaction, - { skipHardForkValidation }: { skipHardForkValidation?: boolean } = {}, + { + skipHardForkValidation, + allowNoBlobs, + }: { skipHardForkValidation?: boolean; allowNoBlobs?: boolean } = {}, ) { this.checkStatus() @@ -241,7 +244,9 @@ export class BlockBuilder { // Guard against the case if a tx came into the pool without blobs i.e. network wrapper payload if (blobTx.blobs === undefined) { - throw new Error('blobs missing for 4844 transaction') + if (allowNoBlobs !== true) { + throw new Error('blobs missing for 4844 transaction') + } } if (this.blobGasUsed + BigInt(blobTx.numBlobs()) * blobGasPerBlob > blobGasLimit) { diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 101b774e3e..d58b552f6b 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -16,6 +16,7 @@ import { } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' import { readFileSync, writeFileSync } from 'fs' +import { loadKZG } from 'kzg-wasm' import { join } from 'path' import yargs from 'yargs' import { hideBin } from 'yargs/helpers' @@ -93,7 +94,7 @@ const alloc = JSON.parse(readFileSync(args.input.alloc).toString()) const txsData = JSON.parse(readFileSync(args.input.txs).toString()) const inputEnv = normalizeNumbers(JSON.parse(readFileSync(args.input.env).toString())) -const common = getCommon(args.state.fork) +const common = getCommon(args.state.fork, await loadKZG()) let blockchain if (args.state.fork === 'Merged') { @@ -271,7 +272,7 @@ for (const txData of txsData) { txData.data = txData.input } const tx = createTxFromTxData(txData, { common }) - await builder.addTransaction(tx) + await builder.addTransaction(tx, { allowNoBlobs: true }) } catch (e: any) { rejected.push({ index, From 8f9eddb6986a4dab0fa377e06aa36eb35179bbcb Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 21 Aug 2024 11:16:29 +0200 Subject: [PATCH 40/89] t8n: add blockhash support --- packages/vm/test/t8n/t8n.ts | 38 ++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index d58b552f6b..1b0a1288d1 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -1,5 +1,4 @@ -import { Block, createBlock, createBlockHeader } from '@ethereumjs/block' -import { createBlockchain } from '@ethereumjs/blockchain' +import { Block } from '@ethereumjs/block' import { RLP } from '@ethereumjs/rlp' import { createTxFromTxData } from '@ethereumjs/tx' import { @@ -21,13 +20,13 @@ import { join } from 'path' import yargs from 'yargs' import { hideBin } from 'yargs/helpers' -import { BlockBuilder } from '../../dist/esm/buildBlock.js' +import { BlockBuilder, buildBlock } from '../../dist/esm/buildBlock.js' import { VM } from '../../dist/esm/vm.js' import { getCommon } from '../tester/config.js' import { makeBlockFromEnv, setupPreConditions } from '../util.js' import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' -import type { Log } from '@ethereumjs/evm' +import { EVMMockBlockchain, type Log } from '@ethereumjs/evm' import type { Address, PrefixedHexString } from '@ethereumjs/util' function normalizeNumbers(input: any) { @@ -96,18 +95,26 @@ const inputEnv = normalizeNumbers(JSON.parse(readFileSync(args.input.env).toStri const common = getCommon(args.state.fork, await loadKZG()) -let blockchain -if (args.state.fork === 'Merged') { - const genesisBlockData = { - gasLimit: 5000, - difficulty: 0, - nonce: hexToBytes('0x0000000000000000'), - extraData: hexToBytes('0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa'), +const blockchain = new EVMMockBlockchain() + +blockchain.getBlock = async function (number?: Number) { + for (const key in inputEnv.blockHashes) { + if (Number(key) === number) { + return { + hash() { + return hexToBytes(inputEnv.blockHashes[key]) + }, + } + } + } + return { + hash() { + return zeros(32) + }, } - const genesis = createBlock({ header: createBlockHeader(genesisBlockData) }) - blockchain = await createBlockchain({ common, genesisBlock: genesis }) } -const vm = blockchain ? await VM.create({ common, blockchain }) : await VM.create({ common }) + +const vm = await VM.create({ common, blockchain }) await setupPreConditions(vm.stateManager, { pre: alloc }) @@ -120,7 +127,7 @@ const block = makeBlockFromEnv(inputEnv, { common }) const headerData = block.header.toJSON() headerData.difficulty = inputEnv.parentDifficulty -const builder = new BlockBuilder(vm, { +const builder = await buildBlock(vm, { parentBlock: new Block(), headerData, blockOpts: { putBlockIntoBlockchain: false }, @@ -235,6 +242,7 @@ vm.stateManager.putCode = async function (...args: any) { vm.stateManager.putStorage = async function (...args: any) { const address =
args[0] const key = args[1] + console.log('PUTSTORAGE', address.toString(), bytesToHex(key), bytesToHex(args[2])) addStorage(address.toString(), bytesToHex(key)) return originalPutStorage.apply(this, args) } From 4edea49ef00197f17cb47ffd0965bb8eabcb6c00 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 21 Aug 2024 11:36:31 +0200 Subject: [PATCH 41/89] t8n: produce allocation for system contracts --- packages/vm/test/t8n/t8n.ts | 104 ++++++++++++++++++------------------ packages/vm/test/util.ts | 3 +- 2 files changed, 54 insertions(+), 53 deletions(-) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 1b0a1288d1..68d716d02e 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -120,6 +120,58 @@ await setupPreConditions(vm.stateManager, { pre: alloc }) const block = makeBlockFromEnv(inputEnv, { common }) +// Track the allocation to ensure the output.alloc is correct +const allocTracker: { + [address: string]: { + storage: string[] + } +} = {} + +function addAddress(address: string) { + if (allocTracker[address] === undefined) { + allocTracker[address] = { storage: [] } + } + return allocTracker[address] +} + +function addStorage(address: string, storage: string) { + const storageList = addAddress(address).storage + if (!storageList.includes(storage)) { + storageList.push(storage) + } +} + +const originalPutAccount = vm.stateManager.putAccount +const originalPutCode = vm.stateManager.putCode +const originalPutStorage = vm.stateManager.putStorage + +vm.stateManager.putAccount = async function (...args: any) { + const address =
args[0] + addAddress(address.toString()) + await originalPutAccount.apply(this, args) +} + +vm.stateManager.putAccount = async function (...args: any) { + const address =
args[0] + addAddress(address.toString()) + return originalPutAccount.apply(this, args) +} + +vm.stateManager.putCode = async function (...args: any) { + console.log('PUTCODE', args[0].toString(), bytesToHex(args[1])) + const address =
args[0] + addAddress(address.toString()) + return originalPutCode.apply(this, args) +} + +vm.stateManager.putStorage = async function (...args: any) { + const address =
args[0] + const key = args[1] + console.log('PUTSTORAGE', address.toString(), bytesToHex(key), bytesToHex(args[2])) + addStorage(address.toString(), bytesToHex(key)) + return originalPutStorage.apply(this, args) +} + // TODO: add state.reward //const acc = (await vm.stateManager.getAccount(block.header.coinbase)) ?? new Account() //await vm.stateManager.putAccount(block.header.coinbase, acc) @@ -195,58 +247,6 @@ vm.evm.events?.on('step', (e) => { } }) -// Track the allocation to ensure the output.alloc is correct -const allocTracker: { - [address: string]: { - storage: string[] - } -} = {} - -function addAddress(address: string) { - if (allocTracker[address] === undefined) { - allocTracker[address] = { storage: [] } - } - return allocTracker[address] -} - -function addStorage(address: string, storage: string) { - const storageList = addAddress(address).storage - if (!storageList.includes(storage)) { - storageList.push(storage) - } -} - -const originalPutAccount = vm.stateManager.putAccount -const originalPutCode = vm.stateManager.putCode -const originalPutStorage = vm.stateManager.putStorage - -vm.stateManager.putAccount = async function (...args: any) { - const address =
args[0] - addAddress(address.toString()) - await originalPutAccount.apply(this, args) -} - -vm.stateManager.putAccount = async function (...args: any) { - const address =
args[0] - addAddress(address.toString()) - return originalPutAccount.apply(this, args) -} - -vm.stateManager.putCode = async function (...args: any) { - console.log('PUTCODE', args[0].toString(), bytesToHex(args[1])) - const address =
args[0] - addAddress(address.toString()) - return originalPutCode.apply(this, args) -} - -vm.stateManager.putStorage = async function (...args: any) { - const address =
args[0] - const key = args[1] - console.log('PUTSTORAGE', address.toString(), bytesToHex(key), bytesToHex(args[2])) - addStorage(address.toString(), bytesToHex(key)) - return originalPutStorage.apply(this, args) -} - const rejected: any = [] let index = 0 diff --git a/packages/vm/test/util.ts b/packages/vm/test/util.ts index bdc82a1a45..c2b8497a2b 100644 --- a/packages/vm/test/util.ts +++ b/packages/vm/test/util.ts @@ -302,6 +302,7 @@ export function makeBlockHeader(data: any, opts?: BlockOptions) { currentTimestamp, currentGasLimit, previousHash, + parentHash, currentCoinbase, currentDifficulty, currentExcessBlobGas, @@ -315,7 +316,7 @@ export function makeBlockHeader(data: any, opts?: BlockOptions) { const headerData: any = { number: currentNumber, coinbase: currentCoinbase, - parentHash: previousHash, + parentHash: previousHash ?? parentHash, difficulty: currentDifficulty, gasLimit: currentGasLimit, timestamp: currentTimestamp, From 57a98cc77af5e4cc54f28b02cf3190efdd920b1b Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 21 Aug 2024 11:57:52 +0200 Subject: [PATCH 42/89] vm/buildBlock: take parentHash from headerData if present --- packages/vm/src/buildBlock.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/src/buildBlock.ts b/packages/vm/src/buildBlock.ts index 22967392de..c1c2662740 100644 --- a/packages/vm/src/buildBlock.ts +++ b/packages/vm/src/buildBlock.ts @@ -89,7 +89,7 @@ export class BlockBuilder { this.headerData = { ...opts.headerData, - parentHash: opts.parentBlock.hash(), + parentHash: opts.headerData?.parentHash ?? opts.parentBlock.hash(), number: opts.headerData?.number ?? opts.parentBlock.header.number + BIGINT_1, gasLimit: opts.headerData?.gasLimit ?? opts.parentBlock.header.gasLimit, timestamp: opts.headerData?.timestamp ?? Math.round(Date.now() / 1000), From 3685ab43d136aab4c21c596323401773b96dd38d Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 21 Aug 2024 11:58:08 +0200 Subject: [PATCH 43/89] t8n: lint [no ci] --- packages/vm/test/t8n/t8n.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 68d716d02e..4b89244e99 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -1,4 +1,5 @@ import { Block } from '@ethereumjs/block' +import { EVMMockBlockchain, type Log } from '@ethereumjs/evm' import { RLP } from '@ethereumjs/rlp' import { createTxFromTxData } from '@ethereumjs/tx' import { @@ -26,7 +27,6 @@ import { getCommon } from '../tester/config.js' import { makeBlockFromEnv, setupPreConditions } from '../util.js' import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' -import { EVMMockBlockchain, type Log } from '@ethereumjs/evm' import type { Address, PrefixedHexString } from '@ethereumjs/util' function normalizeNumbers(input: any) { From db3d24345f71139ea78fd07ffd966d80e43e02f9 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 21 Aug 2024 18:13:05 +0200 Subject: [PATCH 44/89] vm: exit early if system contract has no code [no ci] --- packages/vm/src/requests.ts | 16 ---------------- packages/vm/src/runBlock.ts | 33 ++++++++++++--------------------- 2 files changed, 12 insertions(+), 37 deletions(-) diff --git a/packages/vm/src/requests.ts b/packages/vm/src/requests.ts index e035b9db8d..5e6b79a6bf 100644 --- a/packages/vm/src/requests.ts +++ b/packages/vm/src/requests.ts @@ -66,14 +66,6 @@ const accumulateEIP7002Requests = async ( ) const withdrawalsAddress = createAddressFromString(bytesToHex(addressBytes)) - const code = await vm.stateManager.getCode(withdrawalsAddress) - - if (code.length === 0) { - /*throw new Error( - 'Attempt to accumulate EIP-7002 requests failed: the contract does not exist. Ensure the deployment tx has been run, or that the required contract code is stored', - )*/ - } - const systemAddressBytes = bigIntToAddressBytes(vm.common.param('systemAddress')) const systemAddress = createAddressFromString(bytesToHex(systemAddressBytes)) @@ -116,14 +108,6 @@ const accumulateEIP7251Requests = async ( ) const consolidationsAddress = createAddressFromString(bytesToHex(addressBytes)) - const code = await vm.stateManager.getCode(consolidationsAddress) - - if (code.length === 0) { - /*throw new Error( - 'Attempt to accumulate EIP-7251 requests failed: the contract does not exist. Ensure the deployment tx has been run, or that the required contract code is stored', - )*/ - } - const systemAddressBytes = bigIntToAddressBytes(vm.common.param('systemAddress')) const systemAddress = createAddressFromString(bytesToHex(systemAddressBytes)) diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index 409b844728..4295bf2d1e 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -498,14 +498,12 @@ export async function accumulateParentBlockHash( // getAccount with historyAddress will throw error as witnesses are not bundled // but we need to put account so as to query later for slot - try { - if ((await vm.stateManager.getAccount(historyAddress)) === undefined) { - const emptyHistoryAcc = new Account(BigInt(1)) - await vm.evm.journal.putAccount(historyAddress, emptyHistoryAcc) - } - } catch (_e) { - const emptyHistoryAcc = new Account(BigInt(1)) - await vm.evm.journal.putAccount(historyAddress, emptyHistoryAcc) + const code = await vm.stateManager.getCode(historyAddress) + + if (code.length === 0) { + // Exit early, system contract has no code so no storage is written + // TODO: verify with Gabriel that this is fine regarding verkle (should we put an empty account?) + return } async function putBlockHash(vm: VM, hash: Uint8Array, number: bigint) { @@ -540,24 +538,17 @@ export async function accumulateParentBeaconBlockRoot(vm: VM, root: Uint8Array, const timestampIndex = timestamp % historicalRootsLength const timestampExtended = timestampIndex + historicalRootsLength - /** - * Note: (by Jochem) - * If we don't do vm (put account if undefined / non-existent), block runner crashes because the beacon root address does not exist - * vm is hence (for me) again a reason why it should /not/ throw if the address does not exist - * All ethereum accounts have empty storage by default - */ - /** * Note: (by Gabriel) * Get account will throw an error in stateless execution b/c witnesses are not bundled * But we do need an account so we are able to put the storage */ - try { - if ((await vm.stateManager.getAccount(parentBeaconBlockRootAddress)) === undefined) { - await vm.evm.journal.putAccount(parentBeaconBlockRootAddress, new Account()) - } - } catch (_) { - await vm.evm.journal.putAccount(parentBeaconBlockRootAddress, new Account()) + const code = await vm.stateManager.getCode(parentBeaconBlockRootAddress) + + if (code.length === 0) { + // Exit early, system contract has no code so no storage is written + // TODO: verify with Gabriel that this is fine regarding verkle (should we put an empty account?) + return } await vm.stateManager.putStorage( From d95e598fbc469d86da64e68d285914b9050c2ab2 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 21 Aug 2024 19:06:10 +0200 Subject: [PATCH 45/89] t8n: use mcl instead of noble for bls [no ci] --- packages/vm/test/t8n/t8n.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 4b89244e99..f2f055c124 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -17,11 +17,13 @@ import { import { keccak256 } from 'ethereum-cryptography/keccak' import { readFileSync, writeFileSync } from 'fs' import { loadKZG } from 'kzg-wasm' +import * as mcl from 'mcl-wasm' import { join } from 'path' import yargs from 'yargs' import { hideBin } from 'yargs/helpers' -import { BlockBuilder, buildBlock } from '../../dist/esm/buildBlock.js' +import { MCLBLS } from '../../../evm/dist/cjs/index.js' +import { buildBlock } from '../../dist/esm/buildBlock.js' import { VM } from '../../dist/esm/vm.js' import { getCommon } from '../tester/config.js' import { makeBlockFromEnv, setupPreConditions } from '../util.js' @@ -114,7 +116,13 @@ blockchain.getBlock = async function (number?: Number) { } } -const vm = await VM.create({ common, blockchain }) +await mcl.init(mcl.BLS12_381) +const bls = new MCLBLS(mcl) +const evmOpts = { + bls, +} + +const vm = await VM.create({ common, blockchain, evmOpts }) await setupPreConditions(vm.stateManager, { pre: alloc }) From 993684d365ec7a92a53567bd3c1eeece4aa52722 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 23 Aug 2024 16:25:52 +0200 Subject: [PATCH 46/89] remove console.logs --- packages/vm/test/t8n/t8n.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index f2f055c124..abfcbd4272 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -166,7 +166,6 @@ vm.stateManager.putAccount = async function (...args: any) { } vm.stateManager.putCode = async function (...args: any) { - console.log('PUTCODE', args[0].toString(), bytesToHex(args[1])) const address =
args[0] addAddress(address.toString()) return originalPutCode.apply(this, args) @@ -175,7 +174,6 @@ vm.stateManager.putCode = async function (...args: any) { vm.stateManager.putStorage = async function (...args: any) { const address =
args[0] const key = args[1] - console.log('PUTSTORAGE', address.toString(), bytesToHex(key), bytesToHex(args[2])) addStorage(address.toString(), bytesToHex(key)) return originalPutStorage.apply(this, args) } From a41ef16d259d553369417e2ded04c7955ff97e06 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 23 Aug 2024 16:26:27 +0200 Subject: [PATCH 47/89] evm: 7702 correctly check for gas on delegated code --- packages/evm/src/opcodes/gas.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/evm/src/opcodes/gas.ts b/packages/evm/src/opcodes/gas.ts index b2a7841165..c3efc512a8 100644 --- a/packages/evm/src/opcodes/gas.ts +++ b/packages/evm/src/opcodes/gas.ts @@ -40,7 +40,7 @@ const EXTCALL_TARGET_MAX = BigInt(2) ** BigInt(8 * 20) - BigInt(1) async function eip7702GasCost(runState: RunState, common: Common, address: Address) { const code = await runState.stateManager.getCode(address) - if (equalsBytes(code, new Uint8Array([0xef, 0x01, 0x00]))) { + if (equalsBytes(code.slice(0, 3), new Uint8Array([0xef, 0x01, 0x00]))) { return accessAddressEIP2929(runState, code.slice(3, 24), common) } return BIGINT_0 From 30072e25dacf7d91c1a15803f13bcaa6ff7d438c Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 23 Aug 2024 16:28:33 +0200 Subject: [PATCH 48/89] evm: add verkle gas logic for 7702 --- packages/evm/src/opcodes/gas.ts | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/evm/src/opcodes/gas.ts b/packages/evm/src/opcodes/gas.ts index c3efc512a8..c7b6fbbeea 100644 --- a/packages/evm/src/opcodes/gas.ts +++ b/packages/evm/src/opcodes/gas.ts @@ -38,10 +38,15 @@ import type { Address } from '@ethereumjs/util' const EXTCALL_TARGET_MAX = BigInt(2) ** BigInt(8 * 20) - BigInt(1) -async function eip7702GasCost(runState: RunState, common: Common, address: Address) { +async function eip7702GasCost( + runState: RunState, + common: Common, + address: Address, + charge2929Gas: boolean, +) { const code = await runState.stateManager.getCode(address) if (equalsBytes(code.slice(0, 3), new Uint8Array([0xef, 0x01, 0x00]))) { - return accessAddressEIP2929(runState, code.slice(3, 24), common) + return accessAddressEIP2929(runState, code.slice(3, 24), common, charge2929Gas) } return BIGINT_0 } @@ -193,7 +198,7 @@ export const dynamicGasHandlers: Map Date: Sun, 25 Aug 2024 14:22:42 +0200 Subject: [PATCH 49/89] t8n: delete unwanted files [no ci] --- packages/vm/test/t8n/sampleGethOutput/args.py | 13 -- .../t8n/sampleGethOutput/input/alloc.json | 20 -- .../test/t8n/sampleGethOutput/input/env.json | 20 -- .../test/t8n/sampleGethOutput/input/txs.json | 16 -- .../t8n/sampleGethOutput/output/alloc.json | 29 --- .../t8n/sampleGethOutput/output/result.json | 24 --- .../test/t8n/sampleGethOutput/output/txs.rlp | 1 - .../test/t8n/sampleGethOutput/returncode.txt | 1 - .../vm/test/t8n/sampleGethOutput/stderr.txt | 2 - .../vm/test/t8n/sampleGethOutput/stdin.txt | 58 ------ .../vm/test/t8n/sampleGethOutput/stdout.txt | 52 ----- packages/vm/test/t8n/sampleGethOutput/t8n.sh | 4 - packages/vm/test/t8n/transition-child.cts | 177 ------------------ packages/vm/test/t8n/transition-cluster.cts | 68 ------- 14 files changed, 485 deletions(-) delete mode 100644 packages/vm/test/t8n/sampleGethOutput/args.py delete mode 100644 packages/vm/test/t8n/sampleGethOutput/input/alloc.json delete mode 100644 packages/vm/test/t8n/sampleGethOutput/input/env.json delete mode 100644 packages/vm/test/t8n/sampleGethOutput/input/txs.json delete mode 100644 packages/vm/test/t8n/sampleGethOutput/output/alloc.json delete mode 100644 packages/vm/test/t8n/sampleGethOutput/output/result.json delete mode 100644 packages/vm/test/t8n/sampleGethOutput/output/txs.rlp delete mode 100644 packages/vm/test/t8n/sampleGethOutput/returncode.txt delete mode 100644 packages/vm/test/t8n/sampleGethOutput/stderr.txt delete mode 100644 packages/vm/test/t8n/sampleGethOutput/stdin.txt delete mode 100644 packages/vm/test/t8n/sampleGethOutput/stdout.txt delete mode 100755 packages/vm/test/t8n/sampleGethOutput/t8n.sh delete mode 100644 packages/vm/test/t8n/transition-child.cts delete mode 100644 packages/vm/test/t8n/transition-cluster.cts diff --git a/packages/vm/test/t8n/sampleGethOutput/args.py b/packages/vm/test/t8n/sampleGethOutput/args.py deleted file mode 100644 index d7de1b7079..0000000000 --- a/packages/vm/test/t8n/sampleGethOutput/args.py +++ /dev/null @@ -1,13 +0,0 @@ -[ - "/usr/bin/evm", - "t8n", - "--input.alloc=stdin", - "--input.txs=stdin", - "--input.env=stdin", - "--output.result=stdout", - "--output.alloc=stdout", - "--output.body=stdout", - "--state.fork=Shanghai", - "--state.chainid=1", - "--state.reward=0" -] \ No newline at end of file diff --git a/packages/vm/test/t8n/sampleGethOutput/input/alloc.json b/packages/vm/test/t8n/sampleGethOutput/input/alloc.json deleted file mode 100644 index 396b8cdb6c..0000000000 --- a/packages/vm/test/t8n/sampleGethOutput/input/alloc.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { - "nonce": "0x00", - "balance": "0x3635c9adc5dea00000", - "code": "0x", - "storage": {} - }, - "0x0000000000000000000000000000000000001000": { - "nonce": "0x01", - "balance": "0x00", - "code": "0x60ff5f5360015ff3", - "storage": {} - }, - "0x0000000000000000000000000000000000001100": { - "nonce": "0x01", - "balance": "0x00", - "code": "0x60006000600060006000611000620186a0f1600055600160005560016000601f3e600051600155", - "storage": {} - } -} diff --git a/packages/vm/test/t8n/sampleGethOutput/input/env.json b/packages/vm/test/t8n/sampleGethOutput/input/env.json deleted file mode 100644 index 53c4ad95b2..0000000000 --- a/packages/vm/test/t8n/sampleGethOutput/input/env.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "currentCoinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", - "currentGasLimit": "100000000000000000", - "currentNumber": "1", - "currentTimestamp": "1000", - "currentRandom": "0", - "currentDifficulty": "0", - "parentDifficulty": "0", - "parentTimestamp": "0", - "parentBaseFee": "7", - "parentGasUsed": "0", - "parentGasLimit": "100000000000000000", - "parentUncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "blockHashes": { - "0": "0xd25d208f3633531c7f285498c7a1bc15f6e921f367e65e978251f88007fbbfbb" - }, - "ommers": [], - "withdrawals": [], - "parentHash": "0xd25d208f3633531c7f285498c7a1bc15f6e921f367e65e978251f88007fbbfbb" -} diff --git a/packages/vm/test/t8n/sampleGethOutput/input/txs.json b/packages/vm/test/t8n/sampleGethOutput/input/txs.json deleted file mode 100644 index 39a5fa1131..0000000000 --- a/packages/vm/test/t8n/sampleGethOutput/input/txs.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "type": "0x0", - "chainId": "0x1", - "nonce": "0x0", - "gasPrice": "0xa", - "gas": "0x186a0", - "to": "0x0000000000000000000000000000000000001100", - "value": "0x0", - "input": "0x", - "v": "0x25", - "r": "0x450bb2501f58d443b494932d42891374385d2fef15af0090bf88adff0d247d6a", - "s": "0x1f23877ce28d6d6986111de1b5594156a77690f214a2beba16f7556d3bf7a51", - "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" - } -] diff --git a/packages/vm/test/t8n/sampleGethOutput/output/alloc.json b/packages/vm/test/t8n/sampleGethOutput/output/alloc.json deleted file mode 100644 index fd9ebbc5cb..0000000000 --- a/packages/vm/test/t8n/sampleGethOutput/output/alloc.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "0x0000000000000000000000000000000000001000": { - "nonce": "0x01", - "balance": "0x00", - "code": "0x60ff5f5360015ff3", - "storage": {} - }, - "0x0000000000000000000000000000000000001100": { - "nonce": "0x01", - "balance": "0x00", - "code": "0x60006000600060006000611000620186a0f1600055600160005560016000601f3e600051600155", - "storage": { - "0x00": "0x01", - "0x01": "0xff" - } - }, - "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { - "nonce": "0x00", - "balance": "0x031c8f", - "code": "0x", - "storage": {} - }, - "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { - "nonce": "0x01", - "balance": "0x3635c9adc5de95a0ce", - "code": "0x", - "storage": {} - } -} diff --git a/packages/vm/test/t8n/sampleGethOutput/output/result.json b/packages/vm/test/t8n/sampleGethOutput/output/result.json deleted file mode 100644 index 8e106c2ff3..0000000000 --- a/packages/vm/test/t8n/sampleGethOutput/output/result.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "stateRoot": "0x187e2ba0dad27c2c7384050e601724bb54673bfb9b7c82ce1217d3432a730c05", - "txRoot": "0x56c81c68f1df70123bad2ce903057438aa4a2a2740a73cfc1d61d4b66ead583d", - "receiptsRoot": "0x64d2b19c0d71c3f3fb78dc292f9c5c3a52eac4fbc43dbcb62c622ba33a9e56c8", - "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "receipts": [ - { - "transactionHash": "0xb8da878f9f5e9334dede83c8ef7bc6831737b2bc1d277670300992fc8c7305ce", - "gasUsed": "0x10985", - "root": "0x", - "status": "0x1", - "cumulativeGasUsed": "0x10985", - "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "contractAddress": "0x0000000000000000000000000000000000000000", - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "transactionIndex": "0x0" - } - ], - "rejected": [], - "gasUsed": "0x10985", - "currentBaseFee": "0x7", - "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" -} diff --git a/packages/vm/test/t8n/sampleGethOutput/output/txs.rlp b/packages/vm/test/t8n/sampleGethOutput/output/txs.rlp deleted file mode 100644 index 19ea42279e..0000000000 --- a/packages/vm/test/t8n/sampleGethOutput/output/txs.rlp +++ /dev/null @@ -1 +0,0 @@ -0xf862f860800a830186a0940000000000000000000000000000000000001100808025a0450bb2501f58d443b494932d42891374385d2fef15af0090bf88adff0d247d6aa001f23877ce28d6d6986111de1b5594156a77690f214a2beba16f7556d3bf7a51 \ No newline at end of file diff --git a/packages/vm/test/t8n/sampleGethOutput/returncode.txt b/packages/vm/test/t8n/sampleGethOutput/returncode.txt deleted file mode 100644 index c227083464..0000000000 --- a/packages/vm/test/t8n/sampleGethOutput/returncode.txt +++ /dev/null @@ -1 +0,0 @@ -0 \ No newline at end of file diff --git a/packages/vm/test/t8n/sampleGethOutput/stderr.txt b/packages/vm/test/t8n/sampleGethOutput/stderr.txt deleted file mode 100644 index aa5f95abc1..0000000000 --- a/packages/vm/test/t8n/sampleGethOutput/stderr.txt +++ /dev/null @@ -1,2 +0,0 @@ -INFO [08-19|00:30:25.268] Trie dumping started root=187e2b..730c05 -INFO [08-19|00:30:25.268] Trie dumping complete accounts=4 elapsed="105.981µs" diff --git a/packages/vm/test/t8n/sampleGethOutput/stdin.txt b/packages/vm/test/t8n/sampleGethOutput/stdin.txt deleted file mode 100644 index d7025cd9ad..0000000000 --- a/packages/vm/test/t8n/sampleGethOutput/stdin.txt +++ /dev/null @@ -1,58 +0,0 @@ -{ - "alloc": { - "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { - "nonce": "0x00", - "balance": "0x3635c9adc5dea00000", - "code": "0x", - "storage": {} - }, - "0x0000000000000000000000000000000000001000": { - "nonce": "0x01", - "balance": "0x00", - "code": "0x60ff5f5360015ff3", - "storage": {} - }, - "0x0000000000000000000000000000000000001100": { - "nonce": "0x01", - "balance": "0x00", - "code": "0x60006000600060006000611000620186a0f1600055600160005560016000601f3e600051600155", - "storage": {} - } - }, - "txs": [ - { - "type": "0x0", - "chainId": "0x1", - "nonce": "0x0", - "gasPrice": "0xa", - "gas": "0x186a0", - "to": "0x0000000000000000000000000000000000001100", - "value": "0x0", - "input": "0x", - "v": "0x25", - "r": "0x450bb2501f58d443b494932d42891374385d2fef15af0090bf88adff0d247d6a", - "s": "0x1f23877ce28d6d6986111de1b5594156a77690f214a2beba16f7556d3bf7a51", - "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" - } - ], - "env": { - "currentCoinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", - "currentGasLimit": "100000000000000000", - "currentNumber": "1", - "currentTimestamp": "1000", - "currentRandom": "0", - "currentDifficulty": "0", - "parentDifficulty": "0", - "parentTimestamp": "0", - "parentBaseFee": "7", - "parentGasUsed": "0", - "parentGasLimit": "100000000000000000", - "parentUncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "blockHashes": { - "0": "0xd25d208f3633531c7f285498c7a1bc15f6e921f367e65e978251f88007fbbfbb" - }, - "ommers": [], - "withdrawals": [], - "parentHash": "0xd25d208f3633531c7f285498c7a1bc15f6e921f367e65e978251f88007fbbfbb" - } -} \ No newline at end of file diff --git a/packages/vm/test/t8n/sampleGethOutput/stdout.txt b/packages/vm/test/t8n/sampleGethOutput/stdout.txt deleted file mode 100644 index 38d9132ce7..0000000000 --- a/packages/vm/test/t8n/sampleGethOutput/stdout.txt +++ /dev/null @@ -1,52 +0,0 @@ -{ - "alloc": { - "0x0000000000000000000000000000000000001000": { - "code": "0x60ff5f5360015ff3", - "balance": "0x0", - "nonce": "0x1" - }, - "0x0000000000000000000000000000000000001100": { - "code": "0x60006000600060006000611000620186a0f1600055600160005560016000601f3e600051600155", - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000000000000000ff" - }, - "balance": "0x0", - "nonce": "0x1" - }, - "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { - "balance": "0x31c8f" - }, - "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { - "balance": "0x3635c9adc5de95a0ce", - "nonce": "0x1" - } - }, - "body": "0xf862f860800a830186a0940000000000000000000000000000000000001100808025a0450bb2501f58d443b494932d42891374385d2fef15af0090bf88adff0d247d6aa001f23877ce28d6d6986111de1b5594156a77690f214a2beba16f7556d3bf7a51", - "result": { - "stateRoot": "0x187e2ba0dad27c2c7384050e601724bb54673bfb9b7c82ce1217d3432a730c05", - "txRoot": "0x56c81c68f1df70123bad2ce903057438aa4a2a2740a73cfc1d61d4b66ead583d", - "receiptsRoot": "0x64d2b19c0d71c3f3fb78dc292f9c5c3a52eac4fbc43dbcb62c622ba33a9e56c8", - "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "receipts": [ - { - "root": "0x", - "status": "0x1", - "cumulativeGasUsed": "0x10985", - "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "logs": null, - "transactionHash": "0xb8da878f9f5e9334dede83c8ef7bc6831737b2bc1d277670300992fc8c7305ce", - "contractAddress": "0x0000000000000000000000000000000000000000", - "gasUsed": "0x10985", - "effectiveGasPrice": null, - "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "transactionIndex": "0x0" - } - ], - "currentDifficulty": null, - "gasUsed": "0x10985", - "currentBaseFee": "0x7", - "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" - } -} diff --git a/packages/vm/test/t8n/sampleGethOutput/t8n.sh b/packages/vm/test/t8n/sampleGethOutput/t8n.sh deleted file mode 100755 index 4a15db3b32..0000000000 --- a/packages/vm/test/t8n/sampleGethOutput/t8n.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -rm -rf /home/jochem/Documents/ejs/execution-spec-tests/evmdebug/shanghai__eip3855_push0__test_push0__test_push0_contract_during_call_contexts/fork_Shanghai_blockchain_test_call/0/t8n.sh.out # hard-coded to avoid surprises -mkdir /home/jochem/Documents/ejs/execution-spec-tests/evmdebug/shanghai__eip3855_push0__test_push0__test_push0_contract_during_call_contexts/fork_Shanghai_blockchain_test_call/0/t8n.sh.out # unused if tracing is not enabled -/usr/bin/evm t8n --input.alloc=stdin --input.txs=stdin --input.env=stdin --output.result=stdout --output.alloc=stdout --output.body=stdout --state.fork=Shanghai --state.chainid=1 --state.reward=0 < /home/jochem/Documents/ejs/execution-spec-tests/evmdebug/shanghai__eip3855_push0__test_push0__test_push0_contract_during_call_contexts/fork_Shanghai_blockchain_test_call/0/stdin.txt diff --git a/packages/vm/test/t8n/transition-child.cts b/packages/vm/test/t8n/transition-child.cts deleted file mode 100644 index 9ddde4511a..0000000000 --- a/packages/vm/test/t8n/transition-child.cts +++ /dev/null @@ -1,177 +0,0 @@ -import { Block, BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' -import { createBlockchain } from '@ethereumjs/blockchain' -import { RLP } from '@ethereumjs/rlp' -import { createLegacyTxFromBytesArray, createTxFromSerializedData } from '@ethereumjs/tx' -import { Account, bytesToHex, unprefixedHexToBytes } from '@ethereumjs/util' -import { keccak256 } from 'ethereum-cryptography/keccak' -import { readFileSync, writeFileSync } from 'fs' -import { join } from 'path' - -import { VM } from '../../dist/cjs' -import { BlockBuilder } from '../../dist/cjs/buildBlock' -import { getCommon } from '../tester/config' -import { makeBlockFromEnv, setupPreConditions } from '../util' - -import type { TypedTransaction } from '@ethereumjs/tx' -import type { NestedUint8Array } from '@ethereumjs/util' -import type { PostByzantiumTxReceipt } from '../../dist/cjs' - -const yargs = require('yargs/yargs') - -async function runTransition(argsIn: any) { - const args = yargs(argsIn) - .option('state.fork', { - describe: 'Fork to use', - }) - .option('input.alloc', { - describe: 'Initial state allocation', - }) - .option('inputs.txs', { - describe: 'RLP input of txs to run on top of the initial state allocation', - }) - .option('inputs.env', { - describe: 'Input environment (coinbase, difficulty, etc.)', - }) - .option('output.basedir', { - describe: 'Base directory to write output to', - }) - .option('output.result', { - describe: 'File to write output results to (relative to `output.basedir`)', - }) - .option('output.alloc', { - describe: 'File to write output allocation to (after running the transactions)', - }).argv - const alloc = JSON.parse(readFileSync(args.input.alloc).toString()) - const rlpTxs = JSON.parse(readFileSync(args.input.txs).toString()) - const inputEnv = JSON.parse(readFileSync(args.input.env).toString()) - - const common = getCommon(args.state.fork) - - let blockchain - if (args.state.fork === 'Merged') { - const genesisBlockData = { - gasLimit: 5000, - difficulty: 0, - nonce: '0x0000000000000000', - extraData: '0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa', - } - const genesis = createBlockFromBlockData({ header: BlockHeader.fromHeaderData(genesisBlockData) }) - blockchain = await createBlockchain({ common, genesisBlock: genesis }) - } - const vm = blockchain ? await VM.create({ common, blockchain }) : await VM.create({ common }) - await setupPreConditions(vm.stateManager, { pre: alloc }) - - const block = makeBlockFromEnv(inputEnv, { common }) - - const acc = (await vm.stateManager.getAccount(block.header.coinbase)) ?? new Account() - await vm.stateManager.putAccount(block.header.coinbase, acc) - - const txsData = RLP.decode(unprefixedHexToBytes(rlpTxs.slice(2))) - - const headerData = block.header.toJSON() - headerData.difficulty = inputEnv.parentDifficulty - - const builder = new BlockBuilder(vm, { - parentBlock: new Block(), - headerData, - blockOpts: { putBlockIntoBlockchain: false }, - }) - - const receipts: any = [] - - let txCounter = 0 - - vm.events.on('afterTx', async (afterTx, continueFn) => { - const receipt = afterTx.receipt as PostByzantiumTxReceipt - const pushReceipt = { - root: '0x', - status: receipt.status === 0 ? '0x' : '0x1', - cumulativeGasUsed: '0x' + receipt.cumulativeBlockGasUsed.toString(16), - logsBloom: bytesToHex(receipt.bitvector), - logs: null, - transactionHash: bytesToHex(afterTx.transaction.hash()), - contractAddress: '0x0000000000000000000000000000000000000000', - gasUsed: '0x' + afterTx.totalGasSpent.toString(16), - blockHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - transactionIndex: '0x' + txCounter.toString(16), - } - receipts.push(pushReceipt) - txCounter++ - continueFn!(undefined) - }) - - const rejected: any = [] - - let index = 0 - for (const txData of txsData as NestedUint8Array) { - try { - let tx: TypedTransaction - if (txData instanceof Uint8Array) { - tx = createTxFromSerializedData(txData as Uint8Array, { common }) - } else { - tx = createLegacyTxFromBytesArray(txData as Uint8Array[], { common }) - } - await builder.addTransaction(tx) - } catch (e: any) { - rejected.push({ - index, - error: e.message, - }) - } - index++ - } - - const logsBloom = builder.logsBloom() - const logsHash = keccak256(logsBloom) - - await vm.evm.journal.cleanup() - - const output = { - stateRoot: bytesToHex(await vm.stateManager.getStateRoot()), - txRoot: bytesToHex(await builder.transactionsTrie()), - receiptsRoot: bytesToHex(await builder.receiptTrie()), - logsHash: bytesToHex(logsHash), - logsBloom: bytesToHex(logsBloom), - currentDifficulty: '0x20000', - receipts, // TODO fixme - } - - if (rejected.length > 0) { - ;(output as any).rejected = rejected - } - - const outputAlloc = alloc //{} - - const outputResultFilePath = join(args.output.basedir, args.output.result) - const outputAllocFilePath = join(args.output.basedir, args.output.alloc) - - writeFileSync(outputResultFilePath, JSON.stringify(output)) - writeFileSync(outputAllocFilePath, JSON.stringify(outputAlloc)) -} - -let running = false - -process.on('message', async (message) => { - if (running) { - return - } - if (message === 'STOP') { - process.exit() - } else { - running = true - try { - await runTransition(message) - // eslint-disable-next-line no-empty - } catch (e) { - // eslint-disable-next-line no-console - console.log(e) - } - - running = false - - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - if (process && process.send) { - process.send('done') - } - } -}) diff --git a/packages/vm/test/t8n/transition-cluster.cts b/packages/vm/test/t8n/transition-cluster.cts deleted file mode 100644 index d789574e59..0000000000 --- a/packages/vm/test/t8n/transition-cluster.cts +++ /dev/null @@ -1,68 +0,0 @@ -import { fork } from 'child_process' -import { createServer } from 'http' -import { resolve } from 'path' - -const program = resolve('test/t8n/transition-child.cts') -const parameters: any = [] -const options = { - //stdio: ['pipe', 'pipe', 'pipe', 'ipc'], - execArgv: ['--loader', 'tsx'], -} - -const children: any = [] - -function getChild() { - let index = -1 - for (let i = 0; i < children.length; i++) { - if (children[i].active === false) { - index = i - break - } - } - if (index === -1) { - const child = fork(program, parameters, options) - // eslint-disable-next-line no-console - child.on('error', console.log) - const newChild = { - active: false, - child, - } - children.push(newChild) - return newChild - } - return children[index] -} - -function runTest(message: any) { - return new Promise((resolve) => { - const childObject = getChild() - - childObject.active = true - const child = childObject.child - - child.send(message) - - child.on('message', (_childMessage: any) => { - childObject.active = false - resolve(undefined) - }) - }) -} - -const server = createServer((request: any, result: any) => { - console.log("IN", request.method) - if (request.method === 'POST') { - let message = '' - request.on('data', (data: any) => { - console.log("DATA", data.toString(), data.toString().length) - message += data.toString() - }) - request.on('end', async () => { - await runTest(message) - result.end() - }) - } -}) -server.listen(3000) -// eslint-disable-next-line no-console -console.log('Transition cluster ready') From 33b8523628a84a747e1055b1c8d71a5380a1ff13 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 25 Aug 2024 14:37:25 +0200 Subject: [PATCH 50/89] vm/tx: fix 7702 tests --- packages/vm/src/runTx.ts | 4 ++++ packages/vm/test/api/runBlock.spec.ts | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index 05c0c2f5e1..0283b2a39d 100644 --- a/packages/vm/src/runTx.ts +++ b/packages/vm/src/runTx.ts @@ -429,13 +429,16 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { // Add contract code for authority tuples provided by EIP 7702 tx const authorizationList = (tx).authorizationList const MAGIC = new Uint8Array([5]) + console.log('CHECK7702') for (let i = 0; i < authorizationList.length; i++) { + console.log('check auth') // Authority tuple validation const data = authorizationList[i] const chainId = data[0] const chainIdBN = bytesToBigInt(chainId) if (chainIdBN !== BIGINT_0 && chainIdBN !== vm.common.chainId()) { // Chain id does not match, continue + console.log('chainid') continue } // Address to take code from @@ -460,6 +463,7 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { const code = await vm.stateManager.getCode(authority) if (!equalsBytes(code.slice(0, 3), new Uint8Array([0xef, 0x01, 0x00]))) { // Account is a "normal" contract + console.log('contract') continue } } diff --git a/packages/vm/test/api/runBlock.spec.ts b/packages/vm/test/api/runBlock.spec.ts index 2ec22c5ef8..629a433c49 100644 --- a/packages/vm/test/api/runBlock.spec.ts +++ b/packages/vm/test/api/runBlock.spec.ts @@ -20,7 +20,6 @@ import { Address, BIGINT_1, KECCAK256_RLP, - bigIntToBytes, concatBytes, createAddressFromString, createZeroAddress, @@ -616,7 +615,9 @@ describe('runBlock() -> tx types', async () => { const msgToSign = keccak256(concatBytes(new Uint8Array([5]), rlpdMsg)) const signed = ecsign(msgToSign, pkey) - return [chainIdBytes, addressBytes, nonceBytes, bigIntToBytes(signed.v), signed.r, signed.s] + const yParity = signed.v === BigInt(27) ? new Uint8Array() : new Uint8Array([1]) + + return [chainIdBytes, addressBytes, nonceBytes, yParity, signed.r, signed.s] } const common = new Common({ @@ -641,6 +642,7 @@ describe('runBlock() -> tx types', async () => { const authorizationListOpts2 = [ { address: code2Addr, + nonce: 1, }, ] From 5b4313ad626d7990825ac8583f09f36d83dd2e70 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 25 Aug 2024 14:58:23 +0200 Subject: [PATCH 51/89] tx: throw if 7702-tx has no `to` field --- packages/tx/src/7702/tx.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/tx/src/7702/tx.ts b/packages/tx/src/7702/tx.ts index c22449fa0e..8c7239c861 100644 --- a/packages/tx/src/7702/tx.ts +++ b/packages/tx/src/7702/tx.ts @@ -117,6 +117,13 @@ export class EOACode7702Transaction extends BaseTransaction Date: Sun, 25 Aug 2024 15:09:00 +0200 Subject: [PATCH 52/89] vm/tx: fix 7702 tests --- packages/tx/test/eip7702.spec.ts | 3 +- packages/vm/src/runTx.ts | 4 -- packages/vm/test/api/EIPs/eip-7702.spec.ts | 62 +++------------------- 3 files changed, 8 insertions(+), 61 deletions(-) diff --git a/packages/tx/test/eip7702.spec.ts b/packages/tx/test/eip7702.spec.ts index 8136e58d1d..48ee891292 100644 --- a/packages/tx/test/eip7702.spec.ts +++ b/packages/tx/test/eip7702.spec.ts @@ -41,6 +41,7 @@ function getTxData(override: Partial = {}): TxData { ...override, }, ], + to: createZeroAddress(), } } @@ -52,7 +53,7 @@ describe('[EOACode7702Transaction]', () => { maxFeePerGas: 1, maxPriorityFeePerGas: 1, accessList: [], - authorizationList: [], + ...getTxData(), chainId: 1, gasLimit: 100000, to: createZeroAddress(), diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index 0283b2a39d..05c0c2f5e1 100644 --- a/packages/vm/src/runTx.ts +++ b/packages/vm/src/runTx.ts @@ -429,16 +429,13 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { // Add contract code for authority tuples provided by EIP 7702 tx const authorizationList = (tx).authorizationList const MAGIC = new Uint8Array([5]) - console.log('CHECK7702') for (let i = 0; i < authorizationList.length; i++) { - console.log('check auth') // Authority tuple validation const data = authorizationList[i] const chainId = data[0] const chainIdBN = bytesToBigInt(chainId) if (chainIdBN !== BIGINT_0 && chainIdBN !== vm.common.chainId()) { // Chain id does not match, continue - console.log('chainid') continue } // Address to take code from @@ -463,7 +460,6 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { const code = await vm.stateManager.getCode(authority) if (!equalsBytes(code.slice(0, 3), new Uint8Array([0xef, 0x01, 0x00]))) { // Account is a "normal" contract - console.log('contract') continue } } diff --git a/packages/vm/test/api/EIPs/eip-7702.spec.ts b/packages/vm/test/api/EIPs/eip-7702.spec.ts index 4b922d9044..0cdf2183b6 100644 --- a/packages/vm/test/api/EIPs/eip-7702.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7702.spec.ts @@ -5,7 +5,6 @@ import { Account, Address, BIGINT_1, - KECCAK256_NULL, bigIntToUnpaddedBytes, concatBytes, createAddressFromString, @@ -71,12 +70,7 @@ function getAuthorizationListItem(opts: GetAuthListOpts): AuthorizationListBytes ] } -async function runTest( - authorizationListOpts: GetAuthListOpts[], - expect: Uint8Array, - vm?: VM, - skipEmptyCode?: boolean, -) { +async function runTest(authorizationListOpts: GetAuthListOpts[], expect: Uint8Array, vm?: VM) { vm = vm ?? (await VM.create({ common })) const authList = authorizationListOpts.map((opt) => getAuthorizationListItem(opt)) const tx = createEOACode7702Tx( @@ -105,12 +99,6 @@ async function runTest( const slot = hexToBytes(`0x${'00'.repeat(31)}01`) const value = await vm.stateManager.getStorage(defaultAuthAddr, slot) assert.ok(equalsBytes(unpadBytes(expect), value)) - - if (skipEmptyCode === undefined) { - // Check that the code is cleaned after the `runTx` - const account = (await vm.stateManager.getAccount(defaultAuthAddr)) ?? new Account() - assert.ok(equalsBytes(account.codeHash, KECCAK256_NULL)) - } } describe('EIP 7702: set code to EOA accounts', () => { @@ -195,7 +183,6 @@ describe('EIP 7702: set code to EOA accounts', () => { ], new Uint8Array(), vm, - true, ) }) @@ -212,8 +199,9 @@ describe('EIP 7702: set code to EOA accounts', () => { // 5 * PUSH0: 10 // 1 * PUSH20: 3 // 1 * GAS: 2 - // 1x warm call: 100 - // Total: 115 + // 1x warm call: 100 (to auth address) + // --> This calls into the cold code1Addr, so add 2600 cold account gas cost + // Total: 2715 const checkAddressWarmCode = hexToBytes( `0x5F5F5F5F5F73${defaultAuthAddr.toString().slice(2)}5AF1`, ) @@ -240,49 +228,11 @@ describe('EIP 7702: set code to EOA accounts', () => { await vm.stateManager.putAccount(defaultSenderAddr, acc) const res = await runTx(vm, { tx }) - assert.ok(res.execResult.executionGasUsed === BigInt(115)) - }) - - // This test shows, that due to EIP-161, if an EOA has 0 nonce and 0 balance, - // if EIP-7702 code is being ran which sets storage on this EOA, - // the account is still deleted after the tx (and thus also the storage is wiped) - it('EIP-161 test case', async () => { - const vm = await VM.create({ common }) - const authList = [ - getAuthorizationListItem({ - address: code1Addr, - }), - ] - const tx = createEOACode7702Tx( - { - gasLimit: 100000, - maxFeePerGas: 1000, - authorizationList: authList, - to: defaultAuthAddr, - // value: BIGINT_1 // Note, by enabling this line, the account will not get deleted - // Therefore, this test will pass - }, - { common }, - ).sign(defaultSenderPkey) - - // Store value 1 in storage slot 1 - // PUSH1 PUSH1 SSTORE STOP - const code = hexToBytes('0x600160015500') - await vm.stateManager.putCode(code1Addr, code) - - const acc = (await vm.stateManager.getAccount(defaultSenderAddr)) ?? new Account() - acc.balance = BigInt(1_000_000_000) - await vm.stateManager.putAccount(defaultSenderAddr, acc) - - await runTx(vm, { tx }) - - // Note: due to EIP-161, defaultAuthAddr is now deleted - const account = await vm.stateManager.getAccount(defaultAuthAddr) - assert.ok(account === undefined) + assert.ok(res.execResult.executionGasUsed === BigInt(2715)) }) }) -describe.only('test EIP-7702 opcodes', () => { +describe('test EIP-7702 opcodes', () => { it('should correctly report EXTCODESIZE/EXTCODEHASH/EXTCODECOPY opcodes', async () => { // extcodesize and extcodehash const deploymentAddress = createZeroAddress() From e65100fb4519959c806c77fa36e11c936578ab8c Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 25 Aug 2024 16:34:34 +0200 Subject: [PATCH 53/89] t8n: first cleanup --- packages/vm/test/t8n/helpers.ts | 85 +++++++++++++++++++++++++++++++++ packages/vm/test/t8n/t8n.ts | 34 ++----------- packages/vm/test/t8n/types.ts | 18 +++++++ 3 files changed, 106 insertions(+), 31 deletions(-) create mode 100644 packages/vm/test/t8n/helpers.ts create mode 100644 packages/vm/test/t8n/types.ts diff --git a/packages/vm/test/t8n/helpers.ts b/packages/vm/test/t8n/helpers.ts new file mode 100644 index 0000000000..70825fe05c --- /dev/null +++ b/packages/vm/test/t8n/helpers.ts @@ -0,0 +1,85 @@ +import yargs from 'yargs' +import { hideBin } from 'yargs/helpers' + +import type { T8NOptions } from './types.js' + +export function getArguments() { + const argsParsed = yargs(hideBin(process.argv)) + .parserConfiguration({ + 'dot-notation': false, + }) + .option('state.fork', { + describe: 'Fork to use', + type: 'string', + demandOption: true, + }) + .option('state.chainid', { + describe: 'ChainID to use', + type: 'string', + default: '1', + }) + .option('state.reward', { + describe: + 'Coinbase reward after running txs. If 0: coinbase account is touched and rewarded 0 wei. If -1, the coinbase account is not touched (default)', + type: 'string', + default: '-1', + }) + .option('input.alloc', { + describe: 'Initial state allocation', + type: 'string', + demandOption: true, + }) + .option('input.txs', { + describe: 'JSON input of txs to run on top of the initial state allocation', + type: 'string', + demandOption: true, + }) + .option('input.env', { + describe: 'Input environment (coinbase, difficulty, etc.)', + type: 'string', + demandOption: true, + }) + .option('output.basedir', { + describe: 'Base directory to write output to', + type: 'string', + demandOption: true, + }) + .option('output.result', { + describe: 'File to write output results to (relative to `output.basedir`)', + type: 'string', + demandOption: true, + }) + .option('output.alloc', { + describe: 'File to write output allocation to (after running the transactions)', + type: 'string', + demandOption: true, + }) + .option('output.body', { + describe: 'File to write the txs RLP to', + type: 'string', + default: 'txs.rlp', + }) + .strict() + .help().argv + + const args = argsParsed as any as T8NOptions + + args.input = { + alloc: (args)['input.alloc'], + txs: (args)['input.txs'], + env: (args)['input.env'], + } + args.output = { + basedir: (args)['output.basedir'], + result: (args)['output.result'], + alloc: (args)['output.alloc'], + body: (args)['output.body'], + } + args.state = { + fork: (args)['state.fork'], + reward: BigInt((args)['state.reward']), + chainid: BigInt((args)['state.chainid']), + } + + return args +} diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index abfcbd4272..3abd21c16d 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -19,8 +19,6 @@ import { readFileSync, writeFileSync } from 'fs' import { loadKZG } from 'kzg-wasm' import * as mcl from 'mcl-wasm' import { join } from 'path' -import yargs from 'yargs' -import { hideBin } from 'yargs/helpers' import { MCLBLS } from '../../../evm/dist/cjs/index.js' import { buildBlock } from '../../dist/esm/buildBlock.js' @@ -28,6 +26,8 @@ import { VM } from '../../dist/esm/vm.js' import { getCommon } from '../tester/config.js' import { makeBlockFromEnv, setupPreConditions } from '../util.js' +import { getArguments } from './helpers.js' + import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' import type { Address, PrefixedHexString } from '@ethereumjs/util' @@ -61,35 +61,7 @@ function normalizeNumbers(input: any) { return input } -const args = yargs(hideBin(process.argv)) - .option('state.fork', { - describe: 'Fork to use', - type: 'string', - }) - .option('input.alloc', { - describe: 'Initial state allocation', - type: 'string', - }) - .option('inputs.txs', { - describe: 'RLP input of txs to run on top of the initial state allocation', - type: 'string', - }) - .option('inputs.env', { - describe: 'Input environment (coinbase, difficulty, etc.)', - type: 'string', - }) - .option('output.basedir', { - describe: 'Base directory to write output to', - type: 'string', - }) - .option('output.result', { - describe: 'File to write output results to (relative to `output.basedir`)', - type: 'string', - }) - .option('output.alloc', { - describe: 'File to write output allocation to (after running the transactions)', - type: 'string', - }).argv as any +const args = getArguments() const alloc = JSON.parse(readFileSync(args.input.alloc).toString()) const txsData = JSON.parse(readFileSync(args.input.txs).toString()) diff --git a/packages/vm/test/t8n/types.ts b/packages/vm/test/t8n/types.ts new file mode 100644 index 0000000000..ff59f0079c --- /dev/null +++ b/packages/vm/test/t8n/types.ts @@ -0,0 +1,18 @@ +export type T8NOptions = { + state: { + fork: string + reward: BigInt + chainid: BigInt + } + input: { + alloc: string + txs: string + env: string + } + output: { + basedir: string + result: string + alloc: string + body: string + } +} From 757395b368d1a4befc7481b51eca3e39efbe6733 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 26 Aug 2024 15:32:22 +0200 Subject: [PATCH 54/89] t8ntool: WIP [no ci] --- packages/vm/test/t8n/helpers.ts | 37 ++++++++ packages/vm/test/t8n/t8n.ts | 12 ++- packages/vm/test/t8n/transitionTool.ts | 126 +++++++++++++++++++++++++ packages/vm/test/t8n/types.ts | 4 + 4 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 packages/vm/test/t8n/transitionTool.ts diff --git a/packages/vm/test/t8n/helpers.ts b/packages/vm/test/t8n/helpers.ts index 70825fe05c..2b952e4691 100644 --- a/packages/vm/test/t8n/helpers.ts +++ b/packages/vm/test/t8n/helpers.ts @@ -83,3 +83,40 @@ export function getArguments() { return args } + +/** + * This function accepts an `inputs.env` which converts non-hex-prefixed numbers + * to a BigInt value, to avoid errors when converting non-prefixed hex strings to + * numbers + * @param input + * @returns converted input + */ +export function normalizeNumbers(input: any) { + const keys = [ + 'currentGasLimit', + 'currentNumber', + 'currentTimestamp', + 'currentRandom', + 'currentDifficulty', + 'currentBaseFee', + 'currentBlobGasUsed', + 'currentExcessBlobGas', + 'parentDifficulty', + 'parentTimestamp', + 'parentBaseFee', + 'parentGasUsed', + 'parentGasLimit', + 'parentBlobGasUsed', + 'parentExcessBlobGas', + ] + + for (const key of keys) { + const value = input[key] + if (value !== undefined) { + if (value.substring(0, 2) !== '0x') { + input[key] = BigInt(value) + } + } + } + return input +} diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 3abd21c16d..18cf87ef72 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -3,6 +3,7 @@ import { EVMMockBlockchain, type Log } from '@ethereumjs/evm' import { RLP } from '@ethereumjs/rlp' import { createTxFromTxData } from '@ethereumjs/tx' import { + Account, BIGINT_1, CLRequestType, bigIntToHex, @@ -30,6 +31,7 @@ import { getArguments } from './helpers.js' import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' import type { Address, PrefixedHexString } from '@ethereumjs/util' +import { TransitionTool } from './transitionTool.js' function normalizeNumbers(input: any) { const keys = [ @@ -63,6 +65,8 @@ function normalizeNumbers(input: any) { const args = getArguments() +await TransitionTool.init(args) + const alloc = JSON.parse(readFileSync(args.input.alloc).toString()) const txsData = JSON.parse(readFileSync(args.input.txs).toString()) const inputEnv = normalizeNumbers(JSON.parse(readFileSync(args.input.env).toString())) @@ -221,7 +225,11 @@ vm.events.on('beforeTx', () => { vm.evm.events?.on('step', (e) => { if (log) { - console.log(e.address.toString(), e.opcode.name) + console.log({ + gas: Number(e.gasLeft).toString(16), + stack: e.stack.map((a) => a.toString(16)), + opName: e.opcode.name, + }) } }) @@ -270,6 +278,8 @@ for (const txData of txsData) { await vm.evm.journal.cleanup() +vm.stateManager.deleteAccount(block.header.coinbase) + const result = await builder.build() const output = { diff --git a/packages/vm/test/t8n/transitionTool.ts b/packages/vm/test/t8n/transitionTool.ts new file mode 100644 index 0000000000..c7e4578e6e --- /dev/null +++ b/packages/vm/test/t8n/transitionTool.ts @@ -0,0 +1,126 @@ +import { readFileSync } from 'fs' +import { T8NOptions, T8NAlloc, T8NEnv } from './types.js' +import { normalizeNumbers } from './helpers.js' +import { TransactionType, TxData, TypedTxData } from '@ethereumjs/tx' +import { Common } from '@ethereumjs/common' +import { getCommon } from '../tester/config.js' +import { loadKZG } from 'kzg-wasm' +import { VM } from '../../src/vm.js' +import { Address, hexToBytes, zeros } from '@ethereumjs/util' +import { bytesToHex } from 'ethereum-cryptography/utils' +import { EVMMockBlockchain, MCLBLS } from '@ethereumjs/evm' +import * as mcl from 'mcl-wasm' +import { makeBlockFromEnv, setupPreConditions } from '../util.js' + +export class TransitionTool { + public options: T8NOptions + + public alloc: T8NAlloc + public txsData: TypedTxData[] + public inputEnv: T8NEnv + + public common!: Common + + private constructor(args: T8NOptions) { + this.options = args + + this.alloc = JSON.parse(readFileSync(args.input.alloc).toString()) + this.txsData = JSON.parse(readFileSync(args.input.txs).toString()) + this.inputEnv = normalizeNumbers(JSON.parse(readFileSync(args.input.env).toString())) + } + + static async init(args: T8NOptions) { + const t8nTool = new TransitionTool(args) + t8nTool.common = getCommon(args.state.fork, await loadKZG()) + + const blockchain = new EVMMockBlockchain() + + // Override `getBlock` to ensure `BLOCKHASH` works as expected (reads from `inputEnv.blockHashes`) + blockchain.getBlock = async function (number?: Number) { + for (const key in t8nTool.inputEnv.blockHashes) { + if (Number(key) === number) { + return { + hash() { + return hexToBytes(t8nTool.inputEnv.blockHashes[key]) + }, + } + } + } + // Hash not found, so return the zero hash + return { + hash() { + return zeros(32) + }, + } + } + + await mcl.init(mcl.BLS12_381) + const bls = new MCLBLS(mcl) + const evmOpts = { + bls, + } + + const vm = await VM.create({ common: t8nTool.common, blockchain, evmOpts }) + + await setupPreConditions(vm.stateManager, { pre: t8nTool.alloc }) + + const block = makeBlockFromEnv(t8nTool.inputEnv, { common: t8nTool.common }) + } +} + +class StateTracker { + private allocTracker: { + [address: string]: { + storage: string[] + } + } = {} + + private constructor(vm: VM) { + const originalPutAccount = vm.stateManager.putAccount + const originalPutCode = vm.stateManager.putCode + const originalPutStorage = vm.stateManager.putStorage + + const self = this + + vm.stateManager.putAccount = async function (...args: any) { + const address =
args[0] + self.addAddress(address.toString()) + await originalPutAccount.apply(this, args) + } + + vm.stateManager.putAccount = async function (...args: any) { + const address =
args[0] + self.addAddress(address.toString()) + return originalPutAccount.apply(this, args) + } + + vm.stateManager.putCode = async function (...args: any) { + const address =
args[0] + self.addAddress(address.toString()) + return originalPutCode.apply(this, args) + } + + vm.stateManager.putStorage = async function (...args: any) { + const address =
args[0] + const key = args[1] + self.addStorage(address.toString(), bytesToHex(key)) + return originalPutStorage.apply(this, args) + } + } + + static async init(vm: VM) {} + + addAddress(address: string) { + if (this.allocTracker[address] === undefined) { + this.allocTracker[address] = { storage: [] } + } + return this.allocTracker[address] + } + + addStorage(address: string, storage: string) { + const storageList = this.addAddress(address).storage + if (!storageList.includes(storage)) { + storageList.push(storage) + } + } +} diff --git a/packages/vm/test/t8n/types.ts b/packages/vm/test/t8n/types.ts index ff59f0079c..203c695541 100644 --- a/packages/vm/test/t8n/types.ts +++ b/packages/vm/test/t8n/types.ts @@ -16,3 +16,7 @@ export type T8NOptions = { body: string } } + +export type T8NAlloc = {} + +export type T8NEnv = {} From f68fa9c01e16ab2039656af68f1b696629f00fbe Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 26 Aug 2024 16:01:12 +0200 Subject: [PATCH 55/89] VM: exit early on non-existing system contracts --- packages/vm/src/requests.ts | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/vm/src/requests.ts b/packages/vm/src/requests.ts index 5e6b79a6bf..a4dbddc62a 100644 --- a/packages/vm/src/requests.ts +++ b/packages/vm/src/requests.ts @@ -71,6 +71,14 @@ const accumulateEIP7002Requests = async ( const originalAccount = await vm.stateManager.getAccount(systemAddress) + if (originalAccount === undefined) { + await vm.stateManager.deleteAccount(systemAddress) + return + } else { + // Restore the original account (the `runCall` updates the nonce) + await vm.stateManager.putAccount(systemAddress, originalAccount) + } + const results = await vm.evm.runCall({ caller: systemAddress, gasLimit: BigInt(1_000_000), @@ -88,13 +96,6 @@ const accumulateEIP7002Requests = async ( requests.push(WithdrawalRequest.fromRequestData({ sourceAddress, validatorPubkey, amount })) } } - - if (originalAccount === undefined) { - await vm.stateManager.deleteAccount(systemAddress) - } else { - // Restore the original account (the `runCall` updates the nonce) - await vm.stateManager.putAccount(systemAddress, originalAccount) - } } const accumulateEIP7251Requests = async ( @@ -113,6 +114,14 @@ const accumulateEIP7251Requests = async ( const originalAccount = await vm.stateManager.getAccount(systemAddress) + if (originalAccount === undefined) { + await vm.stateManager.deleteAccount(systemAddress) + return + } else { + // Restore the original account (the `runCall` updates the nonce) + await vm.stateManager.putAccount(systemAddress, originalAccount) + } + const results = await vm.evm.runCall({ caller: systemAddress, gasLimit: BigInt(1_000_000), @@ -132,13 +141,6 @@ const accumulateEIP7251Requests = async ( ) } } - - if (originalAccount === undefined) { - await vm.stateManager.deleteAccount(systemAddress) - } else { - // Restore the original account (the `runCall` updates the nonce) - await vm.stateManager.putAccount(systemAddress, originalAccount) - } } const accumulateDeposits = async ( From 2f0d1abd5e288591972d1df7d72524d43ba482ae Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 26 Aug 2024 16:01:12 +0200 Subject: [PATCH 56/89] VM: exit early on non-existing system contracts --- packages/vm/src/requests.ts | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/vm/src/requests.ts b/packages/vm/src/requests.ts index e035b9db8d..5b6558d2c3 100644 --- a/packages/vm/src/requests.ts +++ b/packages/vm/src/requests.ts @@ -79,6 +79,14 @@ const accumulateEIP7002Requests = async ( const originalAccount = await vm.stateManager.getAccount(systemAddress) + if (originalAccount === undefined) { + await vm.stateManager.deleteAccount(systemAddress) + return + } else { + // Restore the original account (the `runCall` updates the nonce) + await vm.stateManager.putAccount(systemAddress, originalAccount) + } + const results = await vm.evm.runCall({ caller: systemAddress, gasLimit: BigInt(1_000_000), @@ -96,13 +104,6 @@ const accumulateEIP7002Requests = async ( requests.push(WithdrawalRequest.fromRequestData({ sourceAddress, validatorPubkey, amount })) } } - - if (originalAccount === undefined) { - await vm.stateManager.deleteAccount(systemAddress) - } else { - // Restore the original account (the `runCall` updates the nonce) - await vm.stateManager.putAccount(systemAddress, originalAccount) - } } const accumulateEIP7251Requests = async ( @@ -129,6 +130,14 @@ const accumulateEIP7251Requests = async ( const originalAccount = await vm.stateManager.getAccount(systemAddress) + if (originalAccount === undefined) { + await vm.stateManager.deleteAccount(systemAddress) + return + } else { + // Restore the original account (the `runCall` updates the nonce) + await vm.stateManager.putAccount(systemAddress, originalAccount) + } + const results = await vm.evm.runCall({ caller: systemAddress, gasLimit: BigInt(1_000_000), @@ -148,13 +157,6 @@ const accumulateEIP7251Requests = async ( ) } } - - if (originalAccount === undefined) { - await vm.stateManager.deleteAccount(systemAddress) - } else { - // Restore the original account (the `runCall` updates the nonce) - await vm.stateManager.putAccount(systemAddress, originalAccount) - } } const accumulateDeposits = async ( From 4ed53d48b5bfd6988d797263e23ec3f47d521a09 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 26 Aug 2024 17:52:37 +0200 Subject: [PATCH 57/89] backup [no ci] --- packages/vm/test/t8n/t8n.ts | 2 +- packages/vm/test/t8n/transitionTool.ts | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 18cf87ef72..404a650542 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -28,10 +28,10 @@ import { getCommon } from '../tester/config.js' import { makeBlockFromEnv, setupPreConditions } from '../util.js' import { getArguments } from './helpers.js' +import { TransitionTool } from './transitionTool.js' import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' import type { Address, PrefixedHexString } from '@ethereumjs/util' -import { TransitionTool } from './transitionTool.js' function normalizeNumbers(input: any) { const keys = [ diff --git a/packages/vm/test/t8n/transitionTool.ts b/packages/vm/test/t8n/transitionTool.ts index c7e4578e6e..98a4c19b9e 100644 --- a/packages/vm/test/t8n/transitionTool.ts +++ b/packages/vm/test/t8n/transitionTool.ts @@ -1,17 +1,21 @@ -import { readFileSync } from 'fs' -import { T8NOptions, T8NAlloc, T8NEnv } from './types.js' -import { normalizeNumbers } from './helpers.js' -import { TransactionType, TxData, TypedTxData } from '@ethereumjs/tx' -import { Common } from '@ethereumjs/common' -import { getCommon } from '../tester/config.js' -import { loadKZG } from 'kzg-wasm' -import { VM } from '../../src/vm.js' import { Address, hexToBytes, zeros } from '@ethereumjs/util' import { bytesToHex } from 'ethereum-cryptography/utils' import { EVMMockBlockchain, MCLBLS } from '@ethereumjs/evm' +import { TransactionType, TxData } from '@ethereumjs/tx' +import { readFileSync } from 'fs' +import { loadKZG } from 'kzg-wasm' import * as mcl from 'mcl-wasm' + +import { VM } from '../../src/vm.js' +import { getCommon } from '../tester/config.js' import { makeBlockFromEnv, setupPreConditions } from '../util.js' +import { normalizeNumbers } from './helpers.js' + +import type { T8NAlloc, T8NEnv, T8NOptions } from './types.js' +import type { Common } from '@ethereumjs/common' +import type { TypedTxData } from '@ethereumjs/tx' + export class TransitionTool { public options: T8NOptions From 93b1d13b4f31fecacbe3a483d63019c208a18f6f Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 27 Aug 2024 17:54:40 +0200 Subject: [PATCH 58/89] t8ntool: do not delete coinbase --- packages/vm/test/t8n/t8n.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 404a650542..5800f54966 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -65,7 +65,7 @@ function normalizeNumbers(input: any) { const args = getArguments() -await TransitionTool.init(args) +//await TransitionTool.init(args) const alloc = JSON.parse(readFileSync(args.input.alloc).toString()) const txsData = JSON.parse(readFileSync(args.input.txs).toString()) @@ -278,8 +278,6 @@ for (const txData of txsData) { await vm.evm.journal.cleanup() -vm.stateManager.deleteAccount(block.header.coinbase) - const result = await builder.build() const output = { From eb3063c0ade1143f7dd0e6b9dba5bb33eae6663b Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 27 Aug 2024 18:28:19 +0200 Subject: [PATCH 59/89] correctly exit early on requests --- packages/vm/src/requests.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/vm/src/requests.ts b/packages/vm/src/requests.ts index a4dbddc62a..36cf4db8e3 100644 --- a/packages/vm/src/requests.ts +++ b/packages/vm/src/requests.ts @@ -69,14 +69,10 @@ const accumulateEIP7002Requests = async ( const systemAddressBytes = bigIntToAddressBytes(vm.common.param('systemAddress')) const systemAddress = createAddressFromString(bytesToHex(systemAddressBytes)) - const originalAccount = await vm.stateManager.getAccount(systemAddress) + const originalAccount = await vm.stateManager.getAccount(withdrawalsAddress) if (originalAccount === undefined) { - await vm.stateManager.deleteAccount(systemAddress) return - } else { - // Restore the original account (the `runCall` updates the nonce) - await vm.stateManager.putAccount(systemAddress, originalAccount) } const results = await vm.evm.runCall({ @@ -112,14 +108,10 @@ const accumulateEIP7251Requests = async ( const systemAddressBytes = bigIntToAddressBytes(vm.common.param('systemAddress')) const systemAddress = createAddressFromString(bytesToHex(systemAddressBytes)) - const originalAccount = await vm.stateManager.getAccount(systemAddress) + const originalAccount = await vm.stateManager.getAccount(consolidationsAddress) if (originalAccount === undefined) { - await vm.stateManager.deleteAccount(systemAddress) return - } else { - // Restore the original account (the `runCall` updates the nonce) - await vm.stateManager.putAccount(systemAddress, originalAccount) } const results = await vm.evm.runCall({ From d133f9d44fc4eacc79b06f0beae48dca7d69bfaa Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 27 Aug 2024 18:28:39 +0200 Subject: [PATCH 60/89] backup --- packages/vm/test/t8n/transitionTool.ts | 264 ++++++++++++++++++++++++- packages/vm/test/t8n/types.ts | 45 ++++- 2 files changed, 296 insertions(+), 13 deletions(-) diff --git a/packages/vm/test/t8n/transitionTool.ts b/packages/vm/test/t8n/transitionTool.ts index 98a4c19b9e..7737aa95f3 100644 --- a/packages/vm/test/t8n/transitionTool.ts +++ b/packages/vm/test/t8n/transitionTool.ts @@ -1,21 +1,39 @@ -import { Address, hexToBytes, zeros } from '@ethereumjs/util' -import { bytesToHex } from 'ethereum-cryptography/utils' +import { Block } from '@ethereumjs/block' import { EVMMockBlockchain, MCLBLS } from '@ethereumjs/evm' -import { TransactionType, TxData } from '@ethereumjs/tx' +import { RLP } from '@ethereumjs/rlp' +import { TransactionType, TxData, createTxFromTxData } from '@ethereumjs/tx' +import { + Account, + BIGINT_1, + CLRequestType, + bigIntToHex, + createAddressFromString, + hexToBytes, + toBytes, + zeros, +} from '@ethereumjs/util' + +import { keccak256 } from 'ethereum-cryptography/keccak' +import { bytesToHex } from 'ethereum-cryptography/utils' import { readFileSync } from 'fs' import { loadKZG } from 'kzg-wasm' import * as mcl from 'mcl-wasm' -import { VM } from '../../src/vm.js' +import { buildBlock } from '../../dist/esm/buildBlock.js' +import { VM } from '../../dist/esm/vm.js' import { getCommon } from '../tester/config.js' import { makeBlockFromEnv, setupPreConditions } from '../util.js' import { normalizeNumbers } from './helpers.js' -import type { T8NAlloc, T8NEnv, T8NOptions } from './types.js' +import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' +import type { RunnerOptions, T8NAlloc, T8NEnv, T8NOptions } from './types.js' import type { Common } from '@ethereumjs/common' +import type { Log } from '@ethereumjs/evm' import type { TypedTxData } from '@ethereumjs/tx' +import type { Address } from '@ethereumjs/util' + export class TransitionTool { public options: T8NOptions @@ -24,6 +42,7 @@ export class TransitionTool { public inputEnv: T8NEnv public common!: Common + public stateTracker!: StateTracker private constructor(args: T8NOptions) { this.options = args @@ -33,10 +52,12 @@ export class TransitionTool { this.inputEnv = normalizeNumbers(JSON.parse(readFileSync(args.input.env).toString())) } - static async init(args: T8NOptions) { + static async init(args: T8NOptions, runnerOptions: RunnerOptions = {}) { const t8nTool = new TransitionTool(args) t8nTool.common = getCommon(args.state.fork, await loadKZG()) + const common = t8nTool.common + const blockchain = new EVMMockBlockchain() // Override `getBlock` to ensure `BLOCKHASH` works as expected (reads from `inputEnv.blockHashes`) @@ -66,9 +87,191 @@ export class TransitionTool { const vm = await VM.create({ common: t8nTool.common, blockchain, evmOpts }) + t8nTool.stateTracker = new StateTracker(vm, t8nTool.alloc) + await setupPreConditions(vm.stateManager, { pre: t8nTool.alloc }) const block = makeBlockFromEnv(t8nTool.inputEnv, { common: t8nTool.common }) + + const headerData = block.header.toJSON() + headerData.difficulty = bigIntToHex(BigInt(t8nTool.inputEnv.parentDifficulty)) + + const builder = await buildBlock(vm, { + parentBlock: new Block(), + headerData, + blockOpts: { putBlockIntoBlockchain: false }, + }) + + // TODO: add state.reward + //const acc = (await vm.stateManager.getAccount(block.header.coinbase)) ?? new Account() + //await vm.stateManager.putAccount(block.header.coinbase, acc) + + const receipts: any = [] + + const log = runnerOptions.log === true + + const logsBuilder: Log[] = [] + + let txIndex = -BIGINT_1 + + vm.events.on('afterTx', async (afterTx, continueFn: any) => { + txIndex++ + const receipt = afterTx.receipt as PostByzantiumTxReceipt + + const formattedLogs = [] + for (const log of receipt.logs) { + logsBuilder.push(log) + + const entry: any = { + address: bytesToHex(log[0]), + topics: log[1].map((e) => bytesToHex(e)), + data: bytesToHex(log[2]), + blockNumber: bytesToHex(toBytes(builder['headerData'].number)), + transactionHash: bytesToHex(afterTx.transaction.hash()), + transactionIndex: bigIntToHex(txIndex), + blockHash: bytesToHex(zeros(32)), + logIndex: bigIntToHex(BigInt(formattedLogs.length)), + removed: 'false', + } + formattedLogs.push(entry) + } + + const pushReceipt = { + root: '0x', + status: receipt.status === 0 ? '0x0' : '0x1', + cumulativeGasUsed: '0x' + receipt.cumulativeBlockGasUsed.toString(16), + logsBloom: bytesToHex(receipt.bitvector), + logs: formattedLogs, + transactionHash: bytesToHex(afterTx.transaction.hash()), + contractAddress: '0x0000000000000000000000000000000000000000', + gasUsed: '0x' + afterTx.totalGasSpent.toString(16), + blockHash: '0x0000000000000000000000000000000000000000000000000000000000000000', + transactionIndex: bigIntToHex(txIndex), + } + receipts.push(pushReceipt) + continueFn!(undefined) + }) + + vm.evm.events?.on('step', (e) => { + if (log) { + console.log({ + gas: Number(e.gasLeft).toString(16), + stack: e.stack.map((a) => a.toString(16)), + opName: e.opcode.name, + }) + } + }) + + const rejected: any = [] + + let index = 0 + + for (const txData of t8nTool.txsData) { + try { + if (txData.v !== undefined) { + ;(txData).yParity = txData.v + } + if ((txData).gas !== undefined) { + txData.gasLimit = (txData).txData.gas + } + + if ((txData).txData.authorizationList !== undefined) { + ;(txData).txData.authorizationList.map((e: any) => { + if (e.yParity === undefined) { + e.yParity = e.v + } + if (e.yParity === '0x0') { + e.yParity = '0x' + } + if (e.nonce === '0x0') { + e.nonce = '0x' + } + if (e.chainId === '0x0') { + e.chainId = '0x' + } + }) + } + if ((txData).txData.input !== undefined) { + txData.data = (txData).input + } + const tx = createTxFromTxData(txData, { common: t8nTool.common }) + await builder.addTransaction(tx, { allowNoBlobs: true }) + } catch (e: any) { + rejected.push({ + index, + error: e.message, + }) + } + index++ + } + + await vm.evm.journal.cleanup() + + if (t8nTool.options.state.reward !== -BIGINT_1) { + const coinbase = (await vm.stateManager.getAccount(block.header.coinbase)) ?? new Account() + coinbase.balance += t8nTool.options.state.reward + await vm.stateManager.putAccount(block.header.coinbase, coinbase) + } + + const result = await builder.build() + + const output = { + stateRoot: bytesToHex(result.header.stateRoot), + txRoot: bytesToHex(result.header.transactionsTrie), + receiptsRoot: bytesToHex(result.header.receiptTrie), + logsHash: bytesToHex(keccak256(RLP.encode(logsBuilder))), + logsBloom: bytesToHex(result.header.logsBloom), + receipts, + gasUsed: bigIntToHex(builder.gasUsed), + } + + if (result.header.baseFeePerGas !== undefined) { + ;(output as any).currentBaseFee = bigIntToHex(result.header.baseFeePerGas) + } + + if (result.header.withdrawalsRoot !== undefined) { + ;(output as any).withdrawalsRoot = bytesToHex(result.header.withdrawalsRoot) + } + + if (result.header.blobGasUsed !== undefined) { + ;(output as any).blobGasUsed = bigIntToHex(result.header.blobGasUsed) + } + + if (result.header.excessBlobGas !== undefined) { + ;(output as any).currentExcessBlobGas = bigIntToHex(result.header.excessBlobGas) + } + + if (result.header.requestsRoot !== undefined) { + ;(output as any).requestsRoot = bytesToHex(result.header.requestsRoot) + } + + if (result.requests !== undefined) { + if (common.isActivatedEIP(6110)) { + ;(output as any).depositRequests = [] + } + + if (common.isActivatedEIP(7002)) { + ;(output as any).withdrawalRequests = [] + } + + if (common.isActivatedEIP(7251)) { + ;(output as any).consolidationRequests = [] + } + + for (const request of result.requests) { + if (request.type === CLRequestType.Deposit) { + ;(output as any).depositRequests.push(request.toJSON()) + } else if (request.type === CLRequestType.Withdrawal) { + ;(output as any).withdrawalRequests.push(request.toJSON()) + } else if (request.type === CLRequestType.Consolidation) { + ;(output as any).consolidationRequests.push(request.toJSON()) + } + } + } + + if (rejected.length > 0) { + ;(output as any).rejected = rejected + } } } @@ -79,11 +282,18 @@ class StateTracker { } } = {} - private constructor(vm: VM) { + private alloc: T8NAlloc + + private vm: VM + + constructor(vm: VM, alloc: T8NAlloc) { + this.alloc = alloc const originalPutAccount = vm.stateManager.putAccount const originalPutCode = vm.stateManager.putCode const originalPutStorage = vm.stateManager.putStorage + this.vm = vm + const self = this vm.stateManager.putAccount = async function (...args: any) { @@ -112,8 +322,6 @@ class StateTracker { } } - static async init(vm: VM) {} - addAddress(address: string) { if (this.allocTracker[address] === undefined) { this.allocTracker[address] = { storage: [] } @@ -127,4 +335,42 @@ class StateTracker { storageList.push(storage) } } + + public async dumpAlloc() { + // Build output alloc + const alloc = this.alloc + for (const addressString in this.allocTracker) { + const address = createAddressFromString(addressString) + const account = await this.vm.stateManager.getAccount(address) + if (account === undefined) { + delete alloc[addressString] + continue + } + if (alloc[addressString] === undefined) { + alloc[addressString] = { balance: '0x0 ' } + } + alloc[addressString].nonce = bigIntToHex(account.nonce) + alloc[addressString].balance = bigIntToHex(account.balance) + alloc[addressString].code = bytesToHex(await vm.stateManager.getCode(address)) + + const storage = allocTracker[addressString].storage + alloc[addressString].storage = alloc[addressString].storage ?? {} + + for (const key of storage) { + const keyBytes = hexToBytes(key) + let storageKeyTrimmed = bytesToHex(unpadBytes(keyBytes)) + if (storageKeyTrimmed === '0x') { + storageKeyTrimmed = '0x00' + } + const value = await vm.stateManager.getStorage(address, setLengthLeft(keyBytes, 32)) + if (value.length === 0) { + delete alloc[addressString].storage[storageKeyTrimmed] + // To be sure, also delete any keys which are left-padded to 32 bytes + delete alloc[addressString].storage[key] + continue + } + alloc[addressString].storage[storageKeyTrimmed] = bytesToHex(value) + } + } + } } diff --git a/packages/vm/test/t8n/types.ts b/packages/vm/test/t8n/types.ts index 203c695541..af0f24a955 100644 --- a/packages/vm/test/t8n/types.ts +++ b/packages/vm/test/t8n/types.ts @@ -1,8 +1,10 @@ +import type { PrefixedHexString } from '@ethereumjs/util' + export type T8NOptions = { state: { fork: string - reward: BigInt - chainid: BigInt + reward: bigint + chainid: bigint } input: { alloc: string @@ -17,6 +19,41 @@ export type T8NOptions = { } } -export type T8NAlloc = {} +export type T8NAlloc = { + [address: string]: { + nonce?: string + balance: string + code?: string + storage: { + [key: string]: string + } + } +} -export type T8NEnv = {} +export type T8NEnv = { + currentCoinbase: PrefixedHexString + currentGasLimit: string + currentNumber: string + currentTimestamp: string + currentRandom?: string + currentDifficulty: string + parentDifficulty: string + parentTimestamp: string + parentBaseFee: string + parentGasUsed: string + parentGasLimit: string + parentUncleHash: PrefixedHexString + parentBlobGasUsed?: string + parentExcessBlobGas?: string + parentBeaconBlockRoot?: PrefixedHexString + blockHashes: { + [number: string]: PrefixedHexString + } + ommers: PrefixedHexString[] + withdrawals: string[] + parentHash: PrefixedHexString +} + +export type RunnerOptions = { + log?: boolean +} From 80788a1279ffd1c44e10ded250474debf590c714 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 27 Aug 2024 18:53:09 +0200 Subject: [PATCH 61/89] 7702: add delegated account to warm address --- packages/evm/src/evm.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index b7482febdf..d73d12c6a1 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -1020,6 +1020,9 @@ export class EVM implements EVMInterface { ) { const address = new Address(message.code.slice(3, 24)) message.code = await this.stateManager.getCode(address) + if (message.depth === 0) { + this.journal.addAlwaysWarmAddress(address.toString()) + } } message.isCompiled = false From ec85a45ccf99d5c0bb8951f89f2b9805de4c0903 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 27 Aug 2024 18:53:09 +0200 Subject: [PATCH 62/89] 7702: add delegated account to warm address --- packages/evm/src/evm.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index b7482febdf..d73d12c6a1 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -1020,6 +1020,9 @@ export class EVM implements EVMInterface { ) { const address = new Address(message.code.slice(3, 24)) message.code = await this.stateManager.getCode(address) + if (message.depth === 0) { + this.journal.addAlwaysWarmAddress(address.toString()) + } } message.isCompiled = false From 2ba2b34a5670ea5b9b5ea50269867ce1132a1a7f Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 27 Aug 2024 19:04:11 +0200 Subject: [PATCH 63/89] increase memory limit --- packages/vm/test/t8n/ethereumjs-t8ntool.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/test/t8n/ethereumjs-t8ntool.sh b/packages/vm/test/t8n/ethereumjs-t8ntool.sh index 0352e14007..440377af24 100755 --- a/packages/vm/test/t8n/ethereumjs-t8ntool.sh +++ b/packages/vm/test/t8n/ethereumjs-t8ntool.sh @@ -4,4 +4,4 @@ if [[ "$1" == "--version" ]]; then exit 0 fi SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -tsx "$SCRIPT_DIR/t8n.ts" "$@" \ No newline at end of file +NODE_OPTIONS="--max-old-space-size=4096" tsx "$SCRIPT_DIR/t8n.ts" "$@" \ No newline at end of file From 332799a5a7fc1383ef384a9b82d4f1ffddaec21e Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 27 Aug 2024 20:22:41 +0200 Subject: [PATCH 64/89] export node options --- packages/vm/test/t8n/ethereumjs-t8ntool.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/vm/test/t8n/ethereumjs-t8ntool.sh b/packages/vm/test/t8n/ethereumjs-t8ntool.sh index 440377af24..06cf1d62c2 100755 --- a/packages/vm/test/t8n/ethereumjs-t8ntool.sh +++ b/packages/vm/test/t8n/ethereumjs-t8ntool.sh @@ -4,4 +4,5 @@ if [[ "$1" == "--version" ]]; then exit 0 fi SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -NODE_OPTIONS="--max-old-space-size=4096" tsx "$SCRIPT_DIR/t8n.ts" "$@" \ No newline at end of file +export NODE_OPTIONS="--max-old-space-size=4096" +tsx "$SCRIPT_DIR/t8n.ts" "$@" \ No newline at end of file From fc165137dbe750af754b5d7a34ea02b1571c35a6 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 28 Aug 2024 13:01:59 +0200 Subject: [PATCH 65/89] vm: requests do restore system account --- packages/vm/src/requests.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/vm/src/requests.ts b/packages/vm/src/requests.ts index 36cf4db8e3..e617ec1520 100644 --- a/packages/vm/src/requests.ts +++ b/packages/vm/src/requests.ts @@ -68,6 +68,7 @@ const accumulateEIP7002Requests = async ( const systemAddressBytes = bigIntToAddressBytes(vm.common.param('systemAddress')) const systemAddress = createAddressFromString(bytesToHex(systemAddressBytes)) + const systemAccount = await vm.stateManager.getAccount(systemAddress) const originalAccount = await vm.stateManager.getAccount(withdrawalsAddress) @@ -81,6 +82,12 @@ const accumulateEIP7002Requests = async ( to: withdrawalsAddress, }) + if (systemAccount === undefined) { + await vm.stateManager.deleteAccount(systemAddress) + } else { + await vm.stateManager.putAccount(systemAddress, systemAccount) + } + const resultsBytes = results.execResult.returnValue if (resultsBytes.length > 0) { // Each request is 76 bytes @@ -107,6 +114,7 @@ const accumulateEIP7251Requests = async ( const systemAddressBytes = bigIntToAddressBytes(vm.common.param('systemAddress')) const systemAddress = createAddressFromString(bytesToHex(systemAddressBytes)) + const systemAccount = await vm.stateManager.getAccount(systemAddress) const originalAccount = await vm.stateManager.getAccount(consolidationsAddress) @@ -120,6 +128,12 @@ const accumulateEIP7251Requests = async ( to: consolidationsAddress, }) + if (systemAccount === undefined) { + await vm.stateManager.deleteAccount(systemAddress) + } else { + await vm.stateManager.putAccount(systemAddress, systemAccount) + } + const resultsBytes = results.execResult.returnValue if (resultsBytes.length > 0) { // Each request is 116 bytes From 8b0cc5c1648ee81f49d4845b9c6b893d2c995964 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 28 Aug 2024 13:01:59 +0200 Subject: [PATCH 66/89] vm: requests do restore system account --- packages/vm/src/requests.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/vm/src/requests.ts b/packages/vm/src/requests.ts index 5b6558d2c3..934cb316dd 100644 --- a/packages/vm/src/requests.ts +++ b/packages/vm/src/requests.ts @@ -76,6 +76,7 @@ const accumulateEIP7002Requests = async ( const systemAddressBytes = bigIntToAddressBytes(vm.common.param('systemAddress')) const systemAddress = createAddressFromString(bytesToHex(systemAddressBytes)) + const systemAccount = await vm.stateManager.getAccount(systemAddress) const originalAccount = await vm.stateManager.getAccount(systemAddress) @@ -93,6 +94,12 @@ const accumulateEIP7002Requests = async ( to: withdrawalsAddress, }) + if (systemAccount === undefined) { + await vm.stateManager.deleteAccount(systemAddress) + } else { + await vm.stateManager.putAccount(systemAddress, systemAccount) + } + const resultsBytes = results.execResult.returnValue if (resultsBytes.length > 0) { // Each request is 76 bytes @@ -127,6 +134,7 @@ const accumulateEIP7251Requests = async ( const systemAddressBytes = bigIntToAddressBytes(vm.common.param('systemAddress')) const systemAddress = createAddressFromString(bytesToHex(systemAddressBytes)) + const systemAccount = await vm.stateManager.getAccount(systemAddress) const originalAccount = await vm.stateManager.getAccount(systemAddress) @@ -144,6 +152,12 @@ const accumulateEIP7251Requests = async ( to: consolidationsAddress, }) + if (systemAccount === undefined) { + await vm.stateManager.deleteAccount(systemAddress) + } else { + await vm.stateManager.putAccount(systemAddress, systemAccount) + } + const resultsBytes = results.execResult.returnValue if (resultsBytes.length > 0) { // Each request is 116 bytes From 097f7be8328ba83a4c53cace0a6730263d3d488a Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 28 Aug 2024 19:30:26 +0200 Subject: [PATCH 67/89] t8ntool: convert edge cases to correct values --- packages/vm/test/t8n/t8n.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 5800f54966..308ca2f0a0 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -260,6 +260,12 @@ for (const txData of txsData) { if (e.chainId === '0x0') { e.chainId = '0x' } + if (e.r === '0x0') { + e.r = '0x' + } + if (e.s === '0x0') { + e.s = '0x' + } }) } if (txData.input !== undefined) { From e710e78d5194199d96005b7984aff811fd763c4a Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 28 Aug 2024 19:34:39 +0200 Subject: [PATCH 68/89] 7702: continue processing once auth ecrecover is invalid --- packages/vm/src/runTx.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index 05c0c2f5e1..dc90d3b9e8 100644 --- a/packages/vm/src/runTx.ts +++ b/packages/vm/src/runTx.ts @@ -447,7 +447,13 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { const rlpdSignedMessage = RLP.encode([chainId, address, nonce]) const toSign = keccak256(concatBytes(MAGIC, rlpdSignedMessage)) - const pubKey = ecrecover(toSign, yParity, r, s) + let pubKey + try { + pubKey = ecrecover(toSign, yParity, r, s) + } catch (e) { + // Invalid signature, continue + continue + } // Address to set code to const authority = new Address(publicToAddress(pubKey)) const accountMaybeUndefined = await vm.stateManager.getAccount(authority) From 8cc575524e4e9b800260f18003044dd0f40ad6d0 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 2 Sep 2024 16:40:18 +0200 Subject: [PATCH 69/89] evm/vm: add 7702 delegation constant --- packages/evm/src/evm.ts | 31 ++++++++++++++------------- packages/evm/src/opcodes/functions.ts | 3 ++- packages/evm/src/opcodes/gas.ts | 3 ++- packages/evm/src/types.ts | 3 +++ packages/vm/src/runTx.ts | 9 +++++--- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index d73d12c6a1..4841528c34 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -30,6 +30,21 @@ import { getOpcodesForHF } from './opcodes/index.js' import { paramsEVM } from './params.js' import { NobleBLS, getActivePrecompiles, getPrecompileName } from './precompiles/index.js' import { TransientStorage } from './transientStorage.js' +import { + type Block, + type CustomOpcode, + DELEGATION_7702_FLAG, + type EVMBLSInterface, + type EVMBN254Interface, + type EVMEvents, + type EVMInterface, + type EVMMockBlockchainInterface, + type EVMOpts, + type EVMResult, + type EVMRunCallOpts, + type EVMRunCodeOpts, + type ExecResult, +} from './types.js' import type { InterpreterOpts } from './interpreter.js' import type { Timer } from './logger.js' @@ -37,20 +52,6 @@ import type { MessageWithTo } from './message.js' import type { AsyncDynamicGasHandler, SyncDynamicGasHandler } from './opcodes/gas.js' import type { OpHandler, OpcodeList, OpcodeMap } from './opcodes/index.js' import type { CustomPrecompile, PrecompileFunc } from './precompiles/index.js' -import type { - Block, - CustomOpcode, - EVMBLSInterface, - EVMBN254Interface, - EVMEvents, - EVMInterface, - EVMMockBlockchainInterface, - EVMOpts, - EVMResult, - EVMRunCallOpts, - EVMRunCodeOpts, - ExecResult, -} from './types.js' import type { Common, StateManagerInterface } from '@ethereumjs/common' const debug = debugDefault('evm:evm') @@ -1016,7 +1017,7 @@ export class EVM implements EVMInterface { // EIP-7702 delegation check if ( this.common.isActivatedEIP(7702) && - equalsBytes(message.code.slice(0, 3), new Uint8Array([0xef, 0x01, 0x00])) + equalsBytes(message.code.slice(0, 3), DELEGATION_7702_FLAG) ) { const address = new Address(message.code.slice(3, 24)) message.code = await this.stateManager.getCode(address) diff --git a/packages/evm/src/opcodes/functions.ts b/packages/evm/src/opcodes/functions.ts index 4b20d46c29..29f9fb8e45 100644 --- a/packages/evm/src/opcodes/functions.ts +++ b/packages/evm/src/opcodes/functions.ts @@ -33,6 +33,7 @@ import { EOFContainer, EOFContainerMode } from '../eof/container.js' import { EOFError } from '../eof/errors.js' import { EOFBYTES, EOFHASH, isEOF } from '../eof/util.js' import { ERROR } from '../exceptions.js' +import { DELEGATION_7702_FLAG } from '../types.js' import { createAddressFromStackBigInt, @@ -61,7 +62,7 @@ export interface AsyncOpHandler { export type OpHandler = SyncOpHandler | AsyncOpHandler function getEIP7702DelegatedAddress(code: Uint8Array) { - if (equalBytes(code.slice(0, 3), new Uint8Array([0xef, 0x01, 0x00]))) { + if (equalBytes(code.slice(0, 3), DELEGATION_7702_FLAG)) { return new Address(code.slice(3, 24)) } } diff --git a/packages/evm/src/opcodes/gas.ts b/packages/evm/src/opcodes/gas.ts index c7b6fbbeea..9d3aa2570e 100644 --- a/packages/evm/src/opcodes/gas.ts +++ b/packages/evm/src/opcodes/gas.ts @@ -18,6 +18,7 @@ import { import { EOFError } from '../eof/errors.js' import { ERROR } from '../exceptions.js' +import { DELEGATION_7702_FLAG } from '../types.js' import { updateSstoreGasEIP1283 } from './EIP1283.js' import { updateSstoreGasEIP2200 } from './EIP2200.js' @@ -45,7 +46,7 @@ async function eip7702GasCost( charge2929Gas: boolean, ) { const code = await runState.stateManager.getCode(address) - if (equalsBytes(code.slice(0, 3), new Uint8Array([0xef, 0x01, 0x00]))) { + if (equalsBytes(code.slice(0, 3), DELEGATION_7702_FLAG)) { return accessAddressEIP2929(runState, code.slice(3, 24), common, charge2929Gas) } return BIGINT_0 diff --git a/packages/evm/src/types.ts b/packages/evm/src/types.ts index eb4a690002..b4281c52a8 100644 --- a/packages/evm/src/types.ts +++ b/packages/evm/src/types.ts @@ -507,3 +507,6 @@ export type EOFEnv = { returnStack: number[] } } + +// EIP-7702 flag: if contract code starts with these 3 bytes, it is a 7702-delegated EOA +export const DELEGATION_7702_FLAG = new Uint8Array([0xef, 0x01, 0x00]) diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index dc90d3b9e8..8fd6ed5a23 100644 --- a/packages/vm/src/runTx.ts +++ b/packages/vm/src/runTx.ts @@ -64,6 +64,9 @@ const journalCacheCleanUpLabel = 'Journal/cache cleanup' const receiptsLabel = 'Receipts' const entireTxLabel = 'Entire tx' +// EIP-7702 flag: if contract code starts with these 3 bytes, it is a 7702-delegated EOA +const DELEGATION_7702_FLAG = new Uint8Array([0xef, 0x01, 0x00]) + /** * @ignore */ @@ -290,7 +293,7 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { if (vm.common.isActivatedEIP(3607) && !equalsBytes(fromAccount.codeHash, KECCAK256_NULL)) { if (vm.common.isActivatedEIP(7702)) { const code = await state.getCode(caller) - if (!equalsBytes(code.slice(0, 3), new Uint8Array([0xef, 0x01, 0x00]))) { + if (!equalsBytes(code.slice(0, 3), DELEGATION_7702_FLAG)) { const msg = _errorMsg( 'invalid sender address, address is not EOA (EIP-3607)', vm, @@ -464,7 +467,7 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { vm.evm.journal.addAlwaysWarmAddress(authority.toString()) if (account.isContract()) { const code = await vm.stateManager.getCode(authority) - if (!equalsBytes(code.slice(0, 3), new Uint8Array([0xef, 0x01, 0x00]))) { + if (!equalsBytes(code.slice(0, 3), DELEGATION_7702_FLAG)) { // Account is a "normal" contract continue } @@ -491,7 +494,7 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { account.nonce++ await vm.evm.journal.putAccount(authority, account) - const addressCode = concatBytes(new Uint8Array([0xef, 0x01, 0x00]), address) + const addressCode = concatBytes(DELEGATION_7702_FLAG, address) await vm.stateManager.putCode(authority, addressCode) } } From 6325bc3393f1dd9be8765304b9b1a3acfc27bd00 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 2 Sep 2024 17:03:53 +0200 Subject: [PATCH 70/89] vm: fix requests --- packages/vm/src/requests.ts | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/packages/vm/src/requests.ts b/packages/vm/src/requests.ts index da9135f9d0..54230e18bc 100644 --- a/packages/vm/src/requests.ts +++ b/packages/vm/src/requests.ts @@ -66,26 +66,14 @@ const accumulateEIP7002Requests = async ( ) const withdrawalsAddress = createAddressFromString(bytesToHex(addressBytes)) - const code = await vm.stateManager.getCode(withdrawalsAddress) - - if (code.length === 0) { - /*throw new Error( - 'Attempt to accumulate EIP-7002 requests failed: the contract does not exist. Ensure the deployment tx has been run, or that the required contract code is stored', - )*/ - } - const systemAddressBytes = bigIntToAddressBytes(vm.common.param('systemAddress')) const systemAddress = createAddressFromString(bytesToHex(systemAddressBytes)) const systemAccount = await vm.stateManager.getAccount(systemAddress) - const originalAccount = await vm.stateManager.getAccount(systemAddress) + const originalAccount = await vm.stateManager.getAccount(withdrawalsAddress) if (originalAccount === undefined) { - await vm.stateManager.deleteAccount(systemAddress) return - } else { - // Restore the original account (the `runCall` updates the nonce) - await vm.stateManager.putAccount(systemAddress, originalAccount) } const results = await vm.evm.runCall({ @@ -124,26 +112,14 @@ const accumulateEIP7251Requests = async ( ) const consolidationsAddress = createAddressFromString(bytesToHex(addressBytes)) - const code = await vm.stateManager.getCode(consolidationsAddress) - - if (code.length === 0) { - /*throw new Error( - 'Attempt to accumulate EIP-7251 requests failed: the contract does not exist. Ensure the deployment tx has been run, or that the required contract code is stored', - )*/ - } - const systemAddressBytes = bigIntToAddressBytes(vm.common.param('systemAddress')) const systemAddress = createAddressFromString(bytesToHex(systemAddressBytes)) const systemAccount = await vm.stateManager.getAccount(systemAddress) - const originalAccount = await vm.stateManager.getAccount(systemAddress) + const originalAccount = await vm.stateManager.getAccount(consolidationsAddress) if (originalAccount === undefined) { - await vm.stateManager.deleteAccount(systemAddress) return - } else { - // Restore the original account (the `runCall` updates the nonce) - await vm.stateManager.putAccount(systemAddress, originalAccount) } const results = await vm.evm.runCall({ From 4287e95b91da6220ecb17124bb2b9c7d10fdf127 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 2 Sep 2024 20:09:45 +0200 Subject: [PATCH 71/89] vm: unduplify 3607 error msg --- packages/vm/src/runTx.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts index 8fd6ed5a23..1036c6e30d 100644 --- a/packages/vm/src/runTx.ts +++ b/packages/vm/src/runTx.ts @@ -291,9 +291,15 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { } // EIP-3607: Reject transactions from senders with deployed code if (vm.common.isActivatedEIP(3607) && !equalsBytes(fromAccount.codeHash, KECCAK256_NULL)) { - if (vm.common.isActivatedEIP(7702)) { - const code = await state.getCode(caller) - if (!equalsBytes(code.slice(0, 3), DELEGATION_7702_FLAG)) { + const isActive7702 = vm.common.isActivatedEIP(7702) + switch (isActive7702) { + case true: { + const code = await state.getCode(caller) + // If the EOA is 7702-delegated, sending txs from this EOA is fine + if (equalsBytes(code.slice(0, 3), DELEGATION_7702_FLAG)) break + // Trying to send TX from account with code (which is not 7702-delegated), falls through and throws + } + default: { const msg = _errorMsg( 'invalid sender address, address is not EOA (EIP-3607)', vm, @@ -302,9 +308,6 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise { ) throw new Error(msg) } - } else { - const msg = _errorMsg('invalid sender address, address is not EOA (EIP-3607)', vm, block, tx) - throw new Error(msg) } } From d0e5105f30f15c7cef3c87698ceeafcd460d6484 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 2 Sep 2024 22:02:41 +0200 Subject: [PATCH 72/89] update wip t8n cleanup [no ci] --- packages/vm/test/t8n/transitionTool.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/vm/test/t8n/transitionTool.ts b/packages/vm/test/t8n/transitionTool.ts index 7737aa95f3..6ef69eff83 100644 --- a/packages/vm/test/t8n/transitionTool.ts +++ b/packages/vm/test/t8n/transitionTool.ts @@ -189,6 +189,12 @@ export class TransitionTool { if (e.chainId === '0x0') { e.chainId = '0x' } + if (e.r === '0x0') { + e.r = '0x' + } + if (e.s === '0x0') { + e.s = '0x' + } }) } if ((txData).txData.input !== undefined) { From 1b7dfbf81a2cf48ddde2619a8f8a3a34665321a7 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 2 Sep 2024 22:09:52 +0200 Subject: [PATCH 73/89] add TODO to buildblock --- packages/vm/src/buildBlock.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/vm/src/buildBlock.ts b/packages/vm/src/buildBlock.ts index fc725ce446..119b3b144d 100644 --- a/packages/vm/src/buildBlock.ts +++ b/packages/vm/src/buildBlock.ts @@ -245,6 +245,8 @@ export class BlockBuilder { // Guard against the case if a tx came into the pool without blobs i.e. network wrapper payload if (blobTx.blobs === undefined) { + // TODO: verify if we want this, do we want to allow the block builder to accept blob txs without the actual blobs? + // (these must have at least one `blobVersionedHashes`, this is verified at tx-level) if (allowNoBlobs !== true) { throw new Error('blobs missing for 4844 transaction') } From e98cf8c00c0d1e2f6e52e048b120e875543d6133 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 3 Sep 2024 15:23:51 +0200 Subject: [PATCH 74/89] update t8ntool to use createVM [no ci] --- packages/vm/test/t8n/t8n.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 308ca2f0a0..8b3b798135 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -24,6 +24,7 @@ import { join } from 'path' import { MCLBLS } from '../../../evm/dist/cjs/index.js' import { buildBlock } from '../../dist/esm/buildBlock.js' import { VM } from '../../dist/esm/vm.js' +import { createVM } from '../../dist/esm/constructors.js' import { getCommon } from '../tester/config.js' import { makeBlockFromEnv, setupPreConditions } from '../util.js' @@ -98,7 +99,7 @@ const evmOpts = { bls, } -const vm = await VM.create({ common, blockchain, evmOpts }) +const vm = await createVM({ common, blockchain, evmOpts }) await setupPreConditions(vm.stateManager, { pre: alloc }) From aceb8ef6c1d1f6379667ef8efaa56796eeb7f1b2 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 3 Sep 2024 15:24:20 +0200 Subject: [PATCH 75/89] update clean version as well [no ci] --- packages/vm/test/t8n/transitionTool.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/vm/test/t8n/transitionTool.ts b/packages/vm/test/t8n/transitionTool.ts index 6ef69eff83..7ddefeef23 100644 --- a/packages/vm/test/t8n/transitionTool.ts +++ b/packages/vm/test/t8n/transitionTool.ts @@ -21,6 +21,7 @@ import * as mcl from 'mcl-wasm' import { buildBlock } from '../../dist/esm/buildBlock.js' import { VM } from '../../dist/esm/vm.js' +import { createVM } from '../../dist/esm/constructors.js' import { getCommon } from '../tester/config.js' import { makeBlockFromEnv, setupPreConditions } from '../util.js' @@ -85,7 +86,7 @@ export class TransitionTool { bls, } - const vm = await VM.create({ common: t8nTool.common, blockchain, evmOpts }) + const vm = await createVM({ common: t8nTool.common, blockchain, evmOpts }) t8nTool.stateTracker = new StateTracker(vm, t8nTool.alloc) From 88dfdd0b69c75759cb834ed5d64fbf7d6e564e23 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Wed, 11 Sep 2024 14:30:29 -0400 Subject: [PATCH 76/89] fix example --- packages/tx/examples/EOACodeTx.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/tx/examples/EOACodeTx.ts b/packages/tx/examples/EOACodeTx.ts index 482600669b..d23700ae5c 100644 --- a/packages/tx/examples/EOACodeTx.ts +++ b/packages/tx/examples/EOACodeTx.ts @@ -1,7 +1,6 @@ import { Common, Hardfork, Mainnet } from '@ethereumjs/common' import { createEOACode7702Tx } from '@ethereumjs/tx' - -import type { PrefixedHexString } from '@ethereumjs/util' +import { type PrefixedHexString, createAddressFromPrivateKey, randomBytes } from '@ethereumjs/util' const ones32 = `0x${'01'.repeat(32)}` as PrefixedHexString @@ -12,12 +11,13 @@ const tx = createEOACode7702Tx( { chainId: '0x2', address: `0x${'20'.repeat(20)}`, - nonce: ['0x1'], + nonce: '0x1', yParity: '0x1', r: ones32, s: ones32, }, ], + to: createAddressFromPrivateKey(randomBytes(32)), }, { common }, ) From aad5ce616ab4d131f56e7b21d16d63c9ea080f77 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 12 Sep 2024 23:16:08 +0200 Subject: [PATCH 77/89] t8ntool attempt to cleanup --- packages/vm/test/t8n/ethereumjs-t8ntool.sh | 2 +- packages/vm/test/t8n/launchT8N.ts | 6 + packages/vm/test/t8n/stateTracker.ts | 110 ++++++++ packages/vm/test/t8n/t8n.ts | 4 +- packages/vm/test/t8n/t8ntool.ts | 284 +++++++++++++++++++++ packages/vm/test/t8n/transitionTool.ts | 4 +- packages/vm/test/t8n/types.ts | 60 ++++- 7 files changed, 455 insertions(+), 15 deletions(-) create mode 100644 packages/vm/test/t8n/launchT8N.ts create mode 100644 packages/vm/test/t8n/stateTracker.ts create mode 100644 packages/vm/test/t8n/t8ntool.ts diff --git a/packages/vm/test/t8n/ethereumjs-t8ntool.sh b/packages/vm/test/t8n/ethereumjs-t8ntool.sh index 06cf1d62c2..bde03526eb 100755 --- a/packages/vm/test/t8n/ethereumjs-t8ntool.sh +++ b/packages/vm/test/t8n/ethereumjs-t8ntool.sh @@ -5,4 +5,4 @@ if [[ "$1" == "--version" ]]; then fi SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) export NODE_OPTIONS="--max-old-space-size=4096" -tsx "$SCRIPT_DIR/t8n.ts" "$@" \ No newline at end of file +tsx "$SCRIPT_DIR/launchT8N.ts" "$@" \ No newline at end of file diff --git a/packages/vm/test/t8n/launchT8N.ts b/packages/vm/test/t8n/launchT8N.ts new file mode 100644 index 0000000000..f4e14b66e8 --- /dev/null +++ b/packages/vm/test/t8n/launchT8N.ts @@ -0,0 +1,6 @@ +import { getArguments } from './helpers.js' +import { TransitionTool } from './t8ntool.js' + +const args = getArguments() + +await TransitionTool.run(args) diff --git a/packages/vm/test/t8n/stateTracker.ts b/packages/vm/test/t8n/stateTracker.ts new file mode 100644 index 0000000000..eb62d51855 --- /dev/null +++ b/packages/vm/test/t8n/stateTracker.ts @@ -0,0 +1,110 @@ +import { + bigIntToHex, + bytesToHex, + createAddressFromString, + hexToBytes, + setLengthLeft, + unpadBytes, +} from '@ethereumjs/util' + +import type { VM } from '../../src/vm.js' +import type { T8NAlloc } from './types.js' +import type { Account, Address, PrefixedHexString } from '@ethereumjs/util' + +export class StateTracker { + private allocTracker: { + // TODO these are all PrefixexHexString + [address: string]: { + storage: string[] + } + } = {} + + private alloc: T8NAlloc + + private vm: VM + + constructor(vm: VM, alloc: T8NAlloc) { + this.alloc = alloc + const originalPutAccount = vm.stateManager.putAccount + const originalPutCode = vm.stateManager.putCode + const originalPutStorage = vm.stateManager.putStorage + + this.vm = vm + + const self = this + + vm.stateManager.putAccount = async function (...args: [Address, Account?]) { + const address = args[0] + self.addAddress(address.toString()) + await originalPutAccount.apply(this, args) + } + + vm.stateManager.putCode = async function (...args: [Address, Uint8Array]) { + const address = args[0] + self.addAddress(address.toString()) + return originalPutCode.apply(this, args) + } + + vm.stateManager.putStorage = async function (...args: [Address, Uint8Array, Uint8Array]) { + const address = args[0] + const key = args[1] + self.addStorage(address.toString(), bytesToHex(key)) + return originalPutStorage.apply(this, args) + } + } + + private addAddress(address: PrefixedHexString) { + if (this.allocTracker[address] === undefined) { + this.allocTracker[address] = { storage: [] } + } + return this.allocTracker[address] + } + + private addStorage(address: PrefixedHexString, storage: PrefixedHexString) { + const storageList = this.addAddress(address).storage + if (!storageList.includes(storage)) { + storageList.push(storage) + } + } + + public async dumpAlloc() { + // Build output alloc + const outputAlloc = this.alloc + for (const addressString in this.allocTracker) { + const address = createAddressFromString(addressString) + const account = await this.vm.stateManager.getAccount(address) + if (account === undefined) { + delete outputAlloc[addressString] + continue + } + if (outputAlloc[addressString] === undefined) { + outputAlloc[addressString] = { + balance: '0x0', + } + } + outputAlloc[addressString].nonce = bigIntToHex(account.nonce) + outputAlloc[addressString].balance = bigIntToHex(account.balance) + outputAlloc[addressString].code = bytesToHex(await this.vm.stateManager.getCode(address)) + + const storage = this.allocTracker[addressString].storage + outputAlloc[addressString].storage = outputAlloc[addressString].storage ?? {} + + for (const key of storage) { + const keyBytes = hexToBytes(key) + let storageKeyTrimmed = bytesToHex(unpadBytes(keyBytes)) + if (storageKeyTrimmed === '0x') { + storageKeyTrimmed = '0x00' + } + const value = await this.vm.stateManager.getStorage(address, setLengthLeft(keyBytes, 32)) + if (value.length === 0) { + delete outputAlloc[addressString].storage[storageKeyTrimmed] + // To be sure, also delete any keys which are left-padded to 32 bytes + delete outputAlloc[addressString].storage[key] + continue + } + outputAlloc[addressString].storage[storageKeyTrimmed] = bytesToHex(value) + } + } + return outputAlloc + } +} diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts index 8b3b798135..663479c9b0 100644 --- a/packages/vm/test/t8n/t8n.ts +++ b/packages/vm/test/t8n/t8n.ts @@ -23,8 +23,8 @@ import { join } from 'path' import { MCLBLS } from '../../../evm/dist/cjs/index.js' import { buildBlock } from '../../dist/esm/buildBlock.js' -import { VM } from '../../dist/esm/vm.js' import { createVM } from '../../dist/esm/constructors.js' +import { VM } from '../../dist/esm/vm.js' import { getCommon } from '../tester/config.js' import { makeBlockFromEnv, setupPreConditions } from '../util.js' @@ -178,7 +178,7 @@ const logsBuilder: Log[] = [] let txIndex = -BIGINT_1 -vm.events.on('afterTx', async (afterTx, continueFn: any) => { +vm.events.on('afterTx', (afterTx, continueFn: any) => { txIndex++ const receipt = afterTx.receipt as PostByzantiumTxReceipt diff --git a/packages/vm/test/t8n/t8ntool.ts b/packages/vm/test/t8n/t8ntool.ts new file mode 100644 index 0000000000..e793f0dd77 --- /dev/null +++ b/packages/vm/test/t8n/t8ntool.ts @@ -0,0 +1,284 @@ +import { Block } from '@ethereumjs/block' +import { EVMMockBlockchain, MCLBLS } from '@ethereumjs/evm' +import { RLP } from '@ethereumjs/rlp' +import { createTxFromTxData } from '@ethereumjs/tx' +import { bigIntToHex, hexToBytes, toBytes, zeros } from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak' +import { bytesToHex } from 'ethereum-cryptography/utils' +import { readFileSync, writeFileSync } from 'fs' +import { loadKZG } from 'kzg-wasm' +import * as mcl from 'mcl-wasm' +import { join } from 'path' + +import { buildBlock, createVM } from '../../src/index.js' +import { rewardAccount } from '../../src/runBlock.js' +import { getCommon } from '../tester/config.js' +import { makeBlockFromEnv, setupPreConditions } from '../util.js' + +import { normalizeNumbers } from './helpers.js' + +import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' +import type { BlockBuilder, VM } from '../../src/index.js' +import type { AfterTxEvent } from '../../src/types.js' +import { StateTracker } from './stateTracker.js' +import type { RunnerOptions, T8NAlloc, T8NEnv, T8NOptions, T8NOutput, T8NReceipt } from './types.js' +import type { Common } from '@ethereumjs/common' +import type { Log } from '@ethereumjs/evm' +import type { TypedTxData } from '@ethereumjs/tx' +import type { PrefixedHexString } from '@ethereumjs/util' + +export class TransitionTool { + public options: T8NOptions + + public alloc: T8NAlloc + public txsData: TypedTxData[] + public inputEnv: T8NEnv + + public common!: Common + public vm!: VM + + public stateTracker!: StateTracker + + // Array of rejected txs + // These are rejected in case of: + // (1) The transaction is invalid (for instance, an Authorization List tx does not contain an authorization list) + // (2) The transaction is rejected by the block builder (for instance, if the sender does not have enough funds to pay) + public rejected: { index: number; error: string }[] + + // Logs tracker (for logsHash) + public logs: Log[] + + // Receipt tracker (in t8n format) + public receipts: T8NReceipt[] + + private constructor(args: T8NOptions) { + this.options = args + + this.alloc = JSON.parse(readFileSync(args.input.alloc).toString()) + this.txsData = normalizeTxData(JSON.parse(readFileSync(args.input.txs).toString())) + this.inputEnv = normalizeNumbers(JSON.parse(readFileSync(args.input.env).toString())) + + this.rejected = [] + this.logs = [] + this.receipts = [] + } + + static async run(args: T8NOptions, runnerOptions: RunnerOptions = {}) { + await new TransitionTool(args).run(args, runnerOptions) + } + + private async run(args: T8NOptions, runnerOptions: RunnerOptions = {}) { + await this.setup(args, runnerOptions) + + const block = makeBlockFromEnv(this.inputEnv, { common: this.common }) + + const headerData = block.header.toJSON() + headerData.difficulty = this.inputEnv.parentDifficulty + + const builder = await buildBlock(this.vm, { + parentBlock: new Block(), + headerData, + blockOpts: { putBlockIntoBlockchain: false }, + }) + + let index = 0 + + this.vm.events.on('afterTx', (event) => this.afterTx(event, index, builder)) + + for (const txData of this.txsData) { + try { + const tx = createTxFromTxData(txData, { common: this.common }) + // Set `allowNoBlobs` to `true`, since the test might not have the blob + // The 4844-tx at this should still be valid, since it has the `blobHashes` field + await builder.addTransaction(tx, { allowNoBlobs: true }) + } catch (e: any) { + this.rejected.push({ + index, + error: e.message, + }) + } + index++ + } + + // Reward miner + + if (args.state.reward !== BigInt(-1)) { + await rewardAccount(this.vm.evm, block.header.coinbase, args.state.reward, this.vm.common) + } + + const result = await builder.build() + + const convertedOutput = this.getOutput(result) + const alloc = await this.stateTracker.dumpAlloc() + + this.writeOutput(args, convertedOutput, alloc) + } + + private async setup(args: T8NOptions, runnerOptions: RunnerOptions = {}) { + this.common = getCommon(args.state.fork, await loadKZG()) + + const blockchain = getBlockchain(this.inputEnv) + + // Setup BLS + // TODO: use Noble + await mcl.init(mcl.BLS12_381) + const bls = new MCLBLS(mcl) + const evmOpts = { + bls, + } + this.vm = await createVM({ common: this.common, blockchain, evmOpts }) + await setupPreConditions(this.vm.stateManager, { pre: this.alloc }) + + this.stateTracker = new StateTracker(this.vm, this.alloc) + + if (runnerOptions.log === true) { + this.vm.events.on('beforeTx', () => { + // eslint-disable-next-line no-console + console.log('Processing new transaction...') + }) + this.vm.events.on('afterTx', () => { + // eslint-disable-next-line no-console + console.log('Done processing transaction (system operations might follow next)') + }) + this.vm.evm.events?.on('step', (e) => { + // eslint-disable-next-line no-console + console.log({ + gasLeft: e.gasLeft.toString(), + stack: e.stack.map((a) => bigIntToHex(a)), + opName: e.opcode.name, + depth: e.depth, + address: e.address.toString(), + }) + }) + } + } + + private afterTx(event: AfterTxEvent, txIndex: number, builder: BlockBuilder) { + const receipt = event.receipt as PostByzantiumTxReceipt + + const formattedLogs = [] + for (const log of receipt.logs) { + this.logs.push(log) + + const entry: any = { + address: bytesToHex(log[0]), + topics: log[1].map((e) => bytesToHex(e)), + data: bytesToHex(log[2]), + blockNumber: bytesToHex(toBytes(builder['headerData'].number)), + transactionHash: bytesToHex(event.transaction.hash()), + transactionIndex: bigIntToHex(BigInt(txIndex)), + blockHash: bytesToHex(zeros(32)), + logIndex: bigIntToHex(BigInt(formattedLogs.length)), + removed: 'false', + } + formattedLogs.push(entry) + } + + this.receipts.push({ + root: '0x', + status: receipt.status === 0 ? '0x0' : '0x1', + cumulativeGasUsed: '0x' + receipt.cumulativeBlockGasUsed.toString(16), + logsBloom: bytesToHex(receipt.bitvector), + logs: formattedLogs, + transactionHash: bytesToHex(event.transaction.hash()), + contractAddress: '0x0000000000000000000000000000000000000000', + gasUsed: '0x' + event.totalGasSpent.toString(16), + blockHash: '0x0000000000000000000000000000000000000000000000000000000000000000', + transactionIndex: bigIntToHex(BigInt(txIndex)), + }) + } + + private getOutput(block: Block): T8NOutput { + const output: T8NOutput = { + stateRoot: bytesToHex(block.header.stateRoot), + txRoot: bytesToHex(block.header.transactionsTrie), + receiptsRoot: bytesToHex(block.header.receiptTrie), + logsHash: bytesToHex(keccak256(RLP.encode(this.logs))), + logsBloom: bytesToHex(block.header.logsBloom), + receipts: this.receipts, + gasUsed: bigIntToHex(block.header.gasUsed), + } + + return output + } + + private writeOutput(args: T8NOptions, output: T8NOutput, outputAlloc: T8NAlloc) { + const outputResultFilePath = join(args.output.basedir, args.output.result) + const outputAllocFilePath = join(args.output.basedir, args.output.alloc) + + writeFileSync(outputResultFilePath, JSON.stringify(output)) + writeFileSync(outputAllocFilePath, JSON.stringify(outputAlloc)) + } +} + +// Helper methods + +/** + * Returns a blockchain with an overridden "getBlock" method to return the correct block hash + * @param inputEnv the T8NEnv input, which contains a `blockHashes` list containing the respective block hashes + * @returns + */ +function getBlockchain(inputEnv: T8NEnv) { + const blockchain = new EVMMockBlockchain() + + blockchain.getBlock = async function (number?: Number) { + for (const key in inputEnv.blockHashes) { + if (Number(key) === number) { + return { + hash() { + return hexToBytes(inputEnv.blockHashes[key]) + }, + } + } + } + return { + hash() { + return zeros(32) + }, + } + } + return blockchain +} + +/** + * Normalizes txData to use with EthereumJS keywords. For instance, 1559-txs have `v` fields on the inputs, where EthereumJS expects `yParity` + * @param txData Array of txData + * @returns Normalized array of txData + */ +function normalizeTxData(txData: TypedTxData[]) { + return txData.map((data: any) => { + if (data.v !== undefined) { + data.yParity = data.v + } + if (data.gas !== undefined) { + data.gasLimit = data.gas + } + + if (data.authorizationList !== undefined) { + data.authorizationList.map((e: any) => { + if (e.yParity === undefined) { + e.yParity = e.v + } + if (e.yParity === '0x0') { + e.yParity = '0x' + } + if (e.nonce === '0x0') { + e.nonce = '0x' + } + if (e.chainId === '0x0') { + e.chainId = '0x' + } + if (e.r === '0x0') { + e.r = '0x' + } + if (e.s === '0x0') { + e.s = '0x' + } + }) + } + if (data.input !== undefined) { + data.data = data.input + } + return data as TypedTxData + }) +} diff --git a/packages/vm/test/t8n/transitionTool.ts b/packages/vm/test/t8n/transitionTool.ts index 7ddefeef23..8a78b79741 100644 --- a/packages/vm/test/t8n/transitionTool.ts +++ b/packages/vm/test/t8n/transitionTool.ts @@ -12,7 +12,6 @@ import { toBytes, zeros, } from '@ethereumjs/util' - import { keccak256 } from 'ethereum-cryptography/keccak' import { bytesToHex } from 'ethereum-cryptography/utils' import { readFileSync } from 'fs' @@ -20,7 +19,6 @@ import { loadKZG } from 'kzg-wasm' import * as mcl from 'mcl-wasm' import { buildBlock } from '../../dist/esm/buildBlock.js' -import { VM } from '../../dist/esm/vm.js' import { createVM } from '../../dist/esm/constructors.js' import { getCommon } from '../tester/config.js' import { makeBlockFromEnv, setupPreConditions } from '../util.js' @@ -28,11 +26,11 @@ import { makeBlockFromEnv, setupPreConditions } from '../util.js' import { normalizeNumbers } from './helpers.js' import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' +import type { VM } from '../../dist/esm/vm.js' import type { RunnerOptions, T8NAlloc, T8NEnv, T8NOptions } from './types.js' import type { Common } from '@ethereumjs/common' import type { Log } from '@ethereumjs/evm' import type { TypedTxData } from '@ethereumjs/tx' - import type { Address } from '@ethereumjs/util' export class TransitionTool { diff --git a/packages/vm/test/t8n/types.ts b/packages/vm/test/t8n/types.ts index af0f24a955..f603184b14 100644 --- a/packages/vm/test/t8n/types.ts +++ b/packages/vm/test/t8n/types.ts @@ -1,5 +1,3 @@ -import type { PrefixedHexString } from '@ethereumjs/util' - export type T8NOptions = { state: { fork: string @@ -20,18 +18,19 @@ export type T8NOptions = { } export type T8NAlloc = { + // TODO these are all string [address: string]: { nonce?: string balance: string code?: string - storage: { + storage?: { [key: string]: string } } } export type T8NEnv = { - currentCoinbase: PrefixedHexString + currentCoinbase: string currentGasLimit: string currentNumber: string currentTimestamp: string @@ -42,18 +41,61 @@ export type T8NEnv = { parentBaseFee: string parentGasUsed: string parentGasLimit: string - parentUncleHash: PrefixedHexString + parentUncleHash: string parentBlobGasUsed?: string parentExcessBlobGas?: string - parentBeaconBlockRoot?: PrefixedHexString + parentBeaconBlockRoot?: string blockHashes: { - [number: string]: PrefixedHexString + [number: string]: string } - ommers: PrefixedHexString[] + ommers: string[] withdrawals: string[] - parentHash: PrefixedHexString + parentHash: string } export type RunnerOptions = { log?: boolean } + +export type T8NOutput = { + stateRoot: string + txRoot: string + receiptsRoot: string + logsHash: string + logsBloom: string + receipts: T8NReceipt[] + gasUsed: string + currentBaseFee?: string + withdrawalsRoot?: string + blobGasUsed?: string + currentExcessBlobGas?: string + requestsRoot?: string + depositRequests?: string[] + withdrawalRequests?: string[] + consolidationRequests?: string[] +} + +type T8NLog = { + address: string + topics: string[] + data: string + blockNumber: string + transactionHash: string + transactionIndex: string + blockHash: string + logIndex: string + removed: 'false' +} + +export type T8NReceipt = { + root: string + status: string + cumulativeGasUsed: string + logsBloom: string + logs: T8NLog[] + transactionHash: string + contractAddress: string + gasUsed: string + blockHash: string + transactionIndex: string +} From f97d56b7e0889e1997a3aa9bc785e1a8121e39c7 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 12 Sep 2024 23:21:12 +0200 Subject: [PATCH 78/89] t8ntool: cleanup --- packages/vm/test/t8n/t8ntool.ts | 64 ++++++++++++++++++++++++++++++--- packages/vm/test/t8n/types.ts | 3 ++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/packages/vm/test/t8n/t8ntool.ts b/packages/vm/test/t8n/t8ntool.ts index e793f0dd77..346753b9c9 100644 --- a/packages/vm/test/t8n/t8ntool.ts +++ b/packages/vm/test/t8n/t8ntool.ts @@ -2,7 +2,7 @@ import { Block } from '@ethereumjs/block' import { EVMMockBlockchain, MCLBLS } from '@ethereumjs/evm' import { RLP } from '@ethereumjs/rlp' import { createTxFromTxData } from '@ethereumjs/tx' -import { bigIntToHex, hexToBytes, toBytes, zeros } from '@ethereumjs/util' +import { CLRequestType, bigIntToHex, hexToBytes, toBytes, zeros } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' import { bytesToHex } from 'ethereum-cryptography/utils' import { readFileSync, writeFileSync } from 'fs' @@ -16,12 +16,20 @@ import { getCommon } from '../tester/config.js' import { makeBlockFromEnv, setupPreConditions } from '../util.js' import { normalizeNumbers } from './helpers.js' +import { StateTracker } from './stateTracker.js' import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' import type { BlockBuilder, VM } from '../../src/index.js' import type { AfterTxEvent } from '../../src/types.js' -import { StateTracker } from './stateTracker.js' -import type { RunnerOptions, T8NAlloc, T8NEnv, T8NOptions, T8NOutput, T8NReceipt } from './types.js' +import type { + RunnerOptions, + T8NAlloc, + T8NEnv, + T8NOptions, + T8NOutput, + T8NReceipt, + T8NRejectedTx, +} from './types.js' import type { Common } from '@ethereumjs/common' import type { Log } from '@ethereumjs/evm' import type { TypedTxData } from '@ethereumjs/tx' @@ -43,7 +51,7 @@ export class TransitionTool { // These are rejected in case of: // (1) The transaction is invalid (for instance, an Authorization List tx does not contain an authorization list) // (2) The transaction is rejected by the block builder (for instance, if the sender does not have enough funds to pay) - public rejected: { index: number; error: string }[] + public rejected: T8NRejectedTx[] // Logs tracker (for logsHash) public logs: Log[] @@ -199,6 +207,54 @@ export class TransitionTool { gasUsed: bigIntToHex(block.header.gasUsed), } + if (block.header.baseFeePerGas !== undefined) { + output.currentBaseFee = bigIntToHex(block.header.baseFeePerGas) + } + + if (block.header.withdrawalsRoot !== undefined) { + output.withdrawalsRoot = bytesToHex(block.header.withdrawalsRoot) + } + + if (block.header.blobGasUsed !== undefined) { + output.blobGasUsed = bigIntToHex(block.header.blobGasUsed) + } + + if (block.header.excessBlobGas !== undefined) { + output.currentExcessBlobGas = bigIntToHex(block.header.excessBlobGas) + } + + if (block.header.requestsRoot !== undefined) { + output.requestsRoot = bytesToHex(block.header.requestsRoot) + } + + if (block.requests !== undefined) { + if (this.common.isActivatedEIP(6110)) { + output.depositRequests = [] + } + + if (this.common.isActivatedEIP(7002)) { + output.withdrawalRequests = [] + } + + if (this.common.isActivatedEIP(7251)) { + output.consolidationRequests = [] + } + + for (const request of block.requests) { + if (request.type === CLRequestType.Deposit) { + output.depositRequests!.push(request.toJSON()) + } else if (request.type === CLRequestType.Withdrawal) { + output.withdrawalRequests!.push(request.toJSON()) + } else if (request.type === CLRequestType.Consolidation) { + output.consolidationRequests!.push(request.toJSON()) + } + } + } + + if (this.rejected.length > 0) { + output.rejected = this.rejected + } + return output } diff --git a/packages/vm/test/t8n/types.ts b/packages/vm/test/t8n/types.ts index f603184b14..b9b3ebb844 100644 --- a/packages/vm/test/t8n/types.ts +++ b/packages/vm/test/t8n/types.ts @@ -57,6 +57,8 @@ export type RunnerOptions = { log?: boolean } +export type T8NRejectedTx = { index: number; error: string } + export type T8NOutput = { stateRoot: string txRoot: string @@ -73,6 +75,7 @@ export type T8NOutput = { depositRequests?: string[] withdrawalRequests?: string[] consolidationRequests?: string[] + rejected?: T8NRejectedTx[] } type T8NLog = { From 632ee30c84df43bf1a13373f82d90d936ada9d67 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 13 Sep 2024 14:07:09 +0200 Subject: [PATCH 79/89] t8ntool fix import --- packages/vm/test/t8n/t8ntool.ts | 23 +++++++++++++++++------ packages/vm/test/t8n/types.ts | 12 +++++++++--- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/packages/vm/test/t8n/t8ntool.ts b/packages/vm/test/t8n/t8ntool.ts index 346753b9c9..14cb494e89 100644 --- a/packages/vm/test/t8n/t8ntool.ts +++ b/packages/vm/test/t8n/t8ntool.ts @@ -2,9 +2,15 @@ import { Block } from '@ethereumjs/block' import { EVMMockBlockchain, MCLBLS } from '@ethereumjs/evm' import { RLP } from '@ethereumjs/rlp' import { createTxFromTxData } from '@ethereumjs/tx' -import { CLRequestType, bigIntToHex, hexToBytes, toBytes, zeros } from '@ethereumjs/util' +import { + CLRequestType, + bigIntToHex, + bytesToHex, + hexToBytes, + toBytes, + zeros, +} from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' -import { bytesToHex } from 'ethereum-cryptography/utils' import { readFileSync, writeFileSync } from 'fs' import { loadKZG } from 'kzg-wasm' import * as mcl from 'mcl-wasm' @@ -33,7 +39,12 @@ import type { import type { Common } from '@ethereumjs/common' import type { Log } from '@ethereumjs/evm' import type { TypedTxData } from '@ethereumjs/tx' -import type { PrefixedHexString } from '@ethereumjs/util' +import type { + ConsolidationRequestV1, + DepositRequestV1, + PrefixedHexString, + WithdrawalRequestV1, +} from '@ethereumjs/util' export class TransitionTool { public options: T8NOptions @@ -242,11 +253,11 @@ export class TransitionTool { for (const request of block.requests) { if (request.type === CLRequestType.Deposit) { - output.depositRequests!.push(request.toJSON()) + output.depositRequests!.push(request.toJSON()) } else if (request.type === CLRequestType.Withdrawal) { - output.withdrawalRequests!.push(request.toJSON()) + output.withdrawalRequests!.push(request.toJSON()) } else if (request.type === CLRequestType.Consolidation) { - output.consolidationRequests!.push(request.toJSON()) + output.consolidationRequests!.push(request.toJSON()) } } } diff --git a/packages/vm/test/t8n/types.ts b/packages/vm/test/t8n/types.ts index b9b3ebb844..6dbedc7862 100644 --- a/packages/vm/test/t8n/types.ts +++ b/packages/vm/test/t8n/types.ts @@ -1,3 +1,9 @@ +import type { + ConsolidationRequestV1, + DepositRequestV1, + WithdrawalRequestV1, +} from '@ethereumjs/util' + export type T8NOptions = { state: { fork: string @@ -72,9 +78,9 @@ export type T8NOutput = { blobGasUsed?: string currentExcessBlobGas?: string requestsRoot?: string - depositRequests?: string[] - withdrawalRequests?: string[] - consolidationRequests?: string[] + depositRequests?: DepositRequestV1[] + withdrawalRequests?: WithdrawalRequestV1[] + consolidationRequests?: ConsolidationRequestV1[] rejected?: T8NRejectedTx[] } From f75624cffde57b5298f8f9092e2706fbd6e6aa20 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 13 Sep 2024 14:08:00 +0200 Subject: [PATCH 80/89] remove old files --- packages/vm/test/t8n/t8n.ts | 390 ------------------------- packages/vm/test/t8n/transitionTool.ts | 381 ------------------------ 2 files changed, 771 deletions(-) delete mode 100644 packages/vm/test/t8n/t8n.ts delete mode 100644 packages/vm/test/t8n/transitionTool.ts diff --git a/packages/vm/test/t8n/t8n.ts b/packages/vm/test/t8n/t8n.ts deleted file mode 100644 index 663479c9b0..0000000000 --- a/packages/vm/test/t8n/t8n.ts +++ /dev/null @@ -1,390 +0,0 @@ -import { Block } from '@ethereumjs/block' -import { EVMMockBlockchain, type Log } from '@ethereumjs/evm' -import { RLP } from '@ethereumjs/rlp' -import { createTxFromTxData } from '@ethereumjs/tx' -import { - Account, - BIGINT_1, - CLRequestType, - bigIntToHex, - bytesToHex, - createAddressFromString, - hexToBytes, - setLengthLeft, - toBytes, - unpadBytes, - zeros, -} from '@ethereumjs/util' -import { keccak256 } from 'ethereum-cryptography/keccak' -import { readFileSync, writeFileSync } from 'fs' -import { loadKZG } from 'kzg-wasm' -import * as mcl from 'mcl-wasm' -import { join } from 'path' - -import { MCLBLS } from '../../../evm/dist/cjs/index.js' -import { buildBlock } from '../../dist/esm/buildBlock.js' -import { createVM } from '../../dist/esm/constructors.js' -import { VM } from '../../dist/esm/vm.js' -import { getCommon } from '../tester/config.js' -import { makeBlockFromEnv, setupPreConditions } from '../util.js' - -import { getArguments } from './helpers.js' -import { TransitionTool } from './transitionTool.js' - -import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' -import type { Address, PrefixedHexString } from '@ethereumjs/util' - -function normalizeNumbers(input: any) { - const keys = [ - 'currentGasLimit', - 'currentNumber', - 'currentTimestamp', - 'currentRandom', - 'currentDifficulty', - 'currentBaseFee', - 'currentBlobGasUsed', - 'currentExcessBlobGas', - 'parentDifficulty', - 'parentTimestamp', - 'parentBaseFee', - 'parentGasUsed', - 'parentGasLimit', - 'parentBlobGasUsed', - 'parentExcessBlobGas', - ] - - for (const key of keys) { - const value = input[key] - if (value !== undefined) { - if (value.substring(0, 2) !== '0x') { - input[key] = BigInt(value) - } - } - } - return input -} - -const args = getArguments() - -//await TransitionTool.init(args) - -const alloc = JSON.parse(readFileSync(args.input.alloc).toString()) -const txsData = JSON.parse(readFileSync(args.input.txs).toString()) -const inputEnv = normalizeNumbers(JSON.parse(readFileSync(args.input.env).toString())) - -const common = getCommon(args.state.fork, await loadKZG()) - -const blockchain = new EVMMockBlockchain() - -blockchain.getBlock = async function (number?: Number) { - for (const key in inputEnv.blockHashes) { - if (Number(key) === number) { - return { - hash() { - return hexToBytes(inputEnv.blockHashes[key]) - }, - } - } - } - return { - hash() { - return zeros(32) - }, - } -} - -await mcl.init(mcl.BLS12_381) -const bls = new MCLBLS(mcl) -const evmOpts = { - bls, -} - -const vm = await createVM({ common, blockchain, evmOpts }) - -await setupPreConditions(vm.stateManager, { pre: alloc }) - -const block = makeBlockFromEnv(inputEnv, { common }) - -// Track the allocation to ensure the output.alloc is correct -const allocTracker: { - [address: string]: { - storage: string[] - } -} = {} - -function addAddress(address: string) { - if (allocTracker[address] === undefined) { - allocTracker[address] = { storage: [] } - } - return allocTracker[address] -} - -function addStorage(address: string, storage: string) { - const storageList = addAddress(address).storage - if (!storageList.includes(storage)) { - storageList.push(storage) - } -} - -const originalPutAccount = vm.stateManager.putAccount -const originalPutCode = vm.stateManager.putCode -const originalPutStorage = vm.stateManager.putStorage - -vm.stateManager.putAccount = async function (...args: any) { - const address =
args[0] - addAddress(address.toString()) - await originalPutAccount.apply(this, args) -} - -vm.stateManager.putAccount = async function (...args: any) { - const address =
args[0] - addAddress(address.toString()) - return originalPutAccount.apply(this, args) -} - -vm.stateManager.putCode = async function (...args: any) { - const address =
args[0] - addAddress(address.toString()) - return originalPutCode.apply(this, args) -} - -vm.stateManager.putStorage = async function (...args: any) { - const address =
args[0] - const key = args[1] - addStorage(address.toString(), bytesToHex(key)) - return originalPutStorage.apply(this, args) -} - -// TODO: add state.reward -//const acc = (await vm.stateManager.getAccount(block.header.coinbase)) ?? new Account() -//await vm.stateManager.putAccount(block.header.coinbase, acc) - -const headerData = block.header.toJSON() -headerData.difficulty = inputEnv.parentDifficulty - -const builder = await buildBlock(vm, { - parentBlock: new Block(), - headerData, - blockOpts: { putBlockIntoBlockchain: false }, -}) - -const receipts: any = [] - -let txCounter = 0 - -let log = true - -const logsBuilder: Log[] = [] - -let txIndex = -BIGINT_1 - -vm.events.on('afterTx', (afterTx, continueFn: any) => { - txIndex++ - const receipt = afterTx.receipt as PostByzantiumTxReceipt - - const formattedLogs = [] - for (const log of receipt.logs) { - logsBuilder.push(log) - - const entry: any = { - address: bytesToHex(log[0]), - topics: log[1].map((e) => bytesToHex(e)), - data: bytesToHex(log[2]), - blockNumber: bytesToHex(toBytes(builder['headerData'].number)), - transactionHash: bytesToHex(afterTx.transaction.hash()), - transactionIndex: bigIntToHex(txIndex), - blockHash: bytesToHex(zeros(32)), - logIndex: bigIntToHex(BigInt(formattedLogs.length)), - removed: 'false', - } - formattedLogs.push(entry) - } - - const pushReceipt = { - root: '0x', - status: receipt.status === 0 ? '0x0' : '0x1', - cumulativeGasUsed: '0x' + receipt.cumulativeBlockGasUsed.toString(16), - logsBloom: bytesToHex(receipt.bitvector), - logs: formattedLogs, - transactionHash: bytesToHex(afterTx.transaction.hash()), - contractAddress: '0x0000000000000000000000000000000000000000', - gasUsed: '0x' + afterTx.totalGasSpent.toString(16), - blockHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - transactionIndex: '0x' + txCounter.toString(16), - } - console.log('TX DONE') - log = false - receipts.push(pushReceipt) - txCounter++ - continueFn!(undefined) -}) - -vm.events.on('beforeTx', () => { - console.log('!---! NEW TX') - console.log('-------------------------------------------------') -}) - -vm.evm.events?.on('step', (e) => { - if (log) { - console.log({ - gas: Number(e.gasLeft).toString(16), - stack: e.stack.map((a) => a.toString(16)), - opName: e.opcode.name, - }) - } -}) - -const rejected: any = [] - -let index = 0 - -for (const txData of txsData) { - try { - if (txData.v !== undefined) { - txData.yParity = txData.v - } - if (txData.gas !== undefined) { - txData.gasLimit = txData.gas - } - - if (txData.authorizationList !== undefined) { - txData.authorizationList.map((e: any) => { - if (e.yParity === undefined) { - e.yParity = e.v - } - if (e.yParity === '0x0') { - e.yParity = '0x' - } - if (e.nonce === '0x0') { - e.nonce = '0x' - } - if (e.chainId === '0x0') { - e.chainId = '0x' - } - if (e.r === '0x0') { - e.r = '0x' - } - if (e.s === '0x0') { - e.s = '0x' - } - }) - } - if (txData.input !== undefined) { - txData.data = txData.input - } - const tx = createTxFromTxData(txData, { common }) - await builder.addTransaction(tx, { allowNoBlobs: true }) - } catch (e: any) { - rejected.push({ - index, - error: e.message, - }) - } - index++ -} - -await vm.evm.journal.cleanup() - -const result = await builder.build() - -const output = { - stateRoot: bytesToHex(result.header.stateRoot), - txRoot: bytesToHex(result.header.transactionsTrie), - receiptsRoot: bytesToHex(result.header.receiptTrie), - logsHash: bytesToHex(keccak256(RLP.encode(logsBuilder))), - logsBloom: bytesToHex(result.header.logsBloom), - receipts, - gasUsed: bigIntToHex(builder.gasUsed), -} - -if (result.header.baseFeePerGas !== undefined) { - ;(output as any).currentBaseFee = bigIntToHex(result.header.baseFeePerGas) -} - -if (result.header.withdrawalsRoot !== undefined) { - ;(output as any).withdrawalsRoot = bytesToHex(result.header.withdrawalsRoot) -} - -if (result.header.blobGasUsed !== undefined) { - ;(output as any).blobGasUsed = bigIntToHex(result.header.blobGasUsed) -} - -if (result.header.excessBlobGas !== undefined) { - ;(output as any).currentExcessBlobGas = bigIntToHex(result.header.excessBlobGas) -} - -if (result.header.requestsRoot !== undefined) { - ;(output as any).requestsRoot = bytesToHex(result.header.requestsRoot) -} - -if (result.requests !== undefined) { - if (common.isActivatedEIP(6110)) { - ;(output as any).depositRequests = [] - } - - if (common.isActivatedEIP(7002)) { - ;(output as any).withdrawalRequests = [] - } - - if (common.isActivatedEIP(7251)) { - ;(output as any).consolidationRequests = [] - } - - for (const request of result.requests) { - if (request.type === CLRequestType.Deposit) { - ;(output as any).depositRequests.push(request.toJSON()) - } else if (request.type === CLRequestType.Withdrawal) { - ;(output as any).withdrawalRequests.push(request.toJSON()) - } else if (request.type === CLRequestType.Consolidation) { - ;(output as any).consolidationRequests.push(request.toJSON()) - } - } -} - -if (rejected.length > 0) { - ;(output as any).rejected = rejected -} - -// Build output alloc - -for (const addressString in allocTracker) { - const address = createAddressFromString(addressString) - const account = await vm.stateManager.getAccount(address) - if (account === undefined) { - delete alloc[addressString] - continue - } - if (alloc[addressString] === undefined) { - alloc[addressString] = {} - } - alloc[addressString].nonce = bigIntToHex(account.nonce) - alloc[addressString].balance = bigIntToHex(account.balance) - alloc[addressString].code = bytesToHex(await vm.stateManager.getCode(address)) - - const storage = allocTracker[addressString].storage - alloc[addressString].storage = alloc[addressString].storage ?? {} - - for (const key of storage) { - const keyBytes = hexToBytes(key) - let storageKeyTrimmed = bytesToHex(unpadBytes(keyBytes)) - if (storageKeyTrimmed === '0x') { - storageKeyTrimmed = '0x00' - } - const value = await vm.stateManager.getStorage(address, setLengthLeft(keyBytes, 32)) - if (value.length === 0) { - delete alloc[addressString].storage[storageKeyTrimmed] - // To be sure, also delete any keys which are left-padded to 32 bytes - delete alloc[addressString].storage[key] - continue - } - alloc[addressString].storage[storageKeyTrimmed] = bytesToHex(value) - } -} - -const outputAlloc = alloc - -const outputResultFilePath = join(args.output.basedir, args.output.result) -const outputAllocFilePath = join(args.output.basedir, args.output.alloc) - -writeFileSync(outputResultFilePath, JSON.stringify(output)) -writeFileSync(outputAllocFilePath, JSON.stringify(outputAlloc)) diff --git a/packages/vm/test/t8n/transitionTool.ts b/packages/vm/test/t8n/transitionTool.ts deleted file mode 100644 index 8a78b79741..0000000000 --- a/packages/vm/test/t8n/transitionTool.ts +++ /dev/null @@ -1,381 +0,0 @@ -import { Block } from '@ethereumjs/block' -import { EVMMockBlockchain, MCLBLS } from '@ethereumjs/evm' -import { RLP } from '@ethereumjs/rlp' -import { TransactionType, TxData, createTxFromTxData } from '@ethereumjs/tx' -import { - Account, - BIGINT_1, - CLRequestType, - bigIntToHex, - createAddressFromString, - hexToBytes, - toBytes, - zeros, -} from '@ethereumjs/util' -import { keccak256 } from 'ethereum-cryptography/keccak' -import { bytesToHex } from 'ethereum-cryptography/utils' -import { readFileSync } from 'fs' -import { loadKZG } from 'kzg-wasm' -import * as mcl from 'mcl-wasm' - -import { buildBlock } from '../../dist/esm/buildBlock.js' -import { createVM } from '../../dist/esm/constructors.js' -import { getCommon } from '../tester/config.js' -import { makeBlockFromEnv, setupPreConditions } from '../util.js' - -import { normalizeNumbers } from './helpers.js' - -import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' -import type { VM } from '../../dist/esm/vm.js' -import type { RunnerOptions, T8NAlloc, T8NEnv, T8NOptions } from './types.js' -import type { Common } from '@ethereumjs/common' -import type { Log } from '@ethereumjs/evm' -import type { TypedTxData } from '@ethereumjs/tx' -import type { Address } from '@ethereumjs/util' - -export class TransitionTool { - public options: T8NOptions - - public alloc: T8NAlloc - public txsData: TypedTxData[] - public inputEnv: T8NEnv - - public common!: Common - public stateTracker!: StateTracker - - private constructor(args: T8NOptions) { - this.options = args - - this.alloc = JSON.parse(readFileSync(args.input.alloc).toString()) - this.txsData = JSON.parse(readFileSync(args.input.txs).toString()) - this.inputEnv = normalizeNumbers(JSON.parse(readFileSync(args.input.env).toString())) - } - - static async init(args: T8NOptions, runnerOptions: RunnerOptions = {}) { - const t8nTool = new TransitionTool(args) - t8nTool.common = getCommon(args.state.fork, await loadKZG()) - - const common = t8nTool.common - - const blockchain = new EVMMockBlockchain() - - // Override `getBlock` to ensure `BLOCKHASH` works as expected (reads from `inputEnv.blockHashes`) - blockchain.getBlock = async function (number?: Number) { - for (const key in t8nTool.inputEnv.blockHashes) { - if (Number(key) === number) { - return { - hash() { - return hexToBytes(t8nTool.inputEnv.blockHashes[key]) - }, - } - } - } - // Hash not found, so return the zero hash - return { - hash() { - return zeros(32) - }, - } - } - - await mcl.init(mcl.BLS12_381) - const bls = new MCLBLS(mcl) - const evmOpts = { - bls, - } - - const vm = await createVM({ common: t8nTool.common, blockchain, evmOpts }) - - t8nTool.stateTracker = new StateTracker(vm, t8nTool.alloc) - - await setupPreConditions(vm.stateManager, { pre: t8nTool.alloc }) - - const block = makeBlockFromEnv(t8nTool.inputEnv, { common: t8nTool.common }) - - const headerData = block.header.toJSON() - headerData.difficulty = bigIntToHex(BigInt(t8nTool.inputEnv.parentDifficulty)) - - const builder = await buildBlock(vm, { - parentBlock: new Block(), - headerData, - blockOpts: { putBlockIntoBlockchain: false }, - }) - - // TODO: add state.reward - //const acc = (await vm.stateManager.getAccount(block.header.coinbase)) ?? new Account() - //await vm.stateManager.putAccount(block.header.coinbase, acc) - - const receipts: any = [] - - const log = runnerOptions.log === true - - const logsBuilder: Log[] = [] - - let txIndex = -BIGINT_1 - - vm.events.on('afterTx', async (afterTx, continueFn: any) => { - txIndex++ - const receipt = afterTx.receipt as PostByzantiumTxReceipt - - const formattedLogs = [] - for (const log of receipt.logs) { - logsBuilder.push(log) - - const entry: any = { - address: bytesToHex(log[0]), - topics: log[1].map((e) => bytesToHex(e)), - data: bytesToHex(log[2]), - blockNumber: bytesToHex(toBytes(builder['headerData'].number)), - transactionHash: bytesToHex(afterTx.transaction.hash()), - transactionIndex: bigIntToHex(txIndex), - blockHash: bytesToHex(zeros(32)), - logIndex: bigIntToHex(BigInt(formattedLogs.length)), - removed: 'false', - } - formattedLogs.push(entry) - } - - const pushReceipt = { - root: '0x', - status: receipt.status === 0 ? '0x0' : '0x1', - cumulativeGasUsed: '0x' + receipt.cumulativeBlockGasUsed.toString(16), - logsBloom: bytesToHex(receipt.bitvector), - logs: formattedLogs, - transactionHash: bytesToHex(afterTx.transaction.hash()), - contractAddress: '0x0000000000000000000000000000000000000000', - gasUsed: '0x' + afterTx.totalGasSpent.toString(16), - blockHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - transactionIndex: bigIntToHex(txIndex), - } - receipts.push(pushReceipt) - continueFn!(undefined) - }) - - vm.evm.events?.on('step', (e) => { - if (log) { - console.log({ - gas: Number(e.gasLeft).toString(16), - stack: e.stack.map((a) => a.toString(16)), - opName: e.opcode.name, - }) - } - }) - - const rejected: any = [] - - let index = 0 - - for (const txData of t8nTool.txsData) { - try { - if (txData.v !== undefined) { - ;(txData).yParity = txData.v - } - if ((txData).gas !== undefined) { - txData.gasLimit = (txData).txData.gas - } - - if ((txData).txData.authorizationList !== undefined) { - ;(txData).txData.authorizationList.map((e: any) => { - if (e.yParity === undefined) { - e.yParity = e.v - } - if (e.yParity === '0x0') { - e.yParity = '0x' - } - if (e.nonce === '0x0') { - e.nonce = '0x' - } - if (e.chainId === '0x0') { - e.chainId = '0x' - } - if (e.r === '0x0') { - e.r = '0x' - } - if (e.s === '0x0') { - e.s = '0x' - } - }) - } - if ((txData).txData.input !== undefined) { - txData.data = (txData).input - } - const tx = createTxFromTxData(txData, { common: t8nTool.common }) - await builder.addTransaction(tx, { allowNoBlobs: true }) - } catch (e: any) { - rejected.push({ - index, - error: e.message, - }) - } - index++ - } - - await vm.evm.journal.cleanup() - - if (t8nTool.options.state.reward !== -BIGINT_1) { - const coinbase = (await vm.stateManager.getAccount(block.header.coinbase)) ?? new Account() - coinbase.balance += t8nTool.options.state.reward - await vm.stateManager.putAccount(block.header.coinbase, coinbase) - } - - const result = await builder.build() - - const output = { - stateRoot: bytesToHex(result.header.stateRoot), - txRoot: bytesToHex(result.header.transactionsTrie), - receiptsRoot: bytesToHex(result.header.receiptTrie), - logsHash: bytesToHex(keccak256(RLP.encode(logsBuilder))), - logsBloom: bytesToHex(result.header.logsBloom), - receipts, - gasUsed: bigIntToHex(builder.gasUsed), - } - - if (result.header.baseFeePerGas !== undefined) { - ;(output as any).currentBaseFee = bigIntToHex(result.header.baseFeePerGas) - } - - if (result.header.withdrawalsRoot !== undefined) { - ;(output as any).withdrawalsRoot = bytesToHex(result.header.withdrawalsRoot) - } - - if (result.header.blobGasUsed !== undefined) { - ;(output as any).blobGasUsed = bigIntToHex(result.header.blobGasUsed) - } - - if (result.header.excessBlobGas !== undefined) { - ;(output as any).currentExcessBlobGas = bigIntToHex(result.header.excessBlobGas) - } - - if (result.header.requestsRoot !== undefined) { - ;(output as any).requestsRoot = bytesToHex(result.header.requestsRoot) - } - - if (result.requests !== undefined) { - if (common.isActivatedEIP(6110)) { - ;(output as any).depositRequests = [] - } - - if (common.isActivatedEIP(7002)) { - ;(output as any).withdrawalRequests = [] - } - - if (common.isActivatedEIP(7251)) { - ;(output as any).consolidationRequests = [] - } - - for (const request of result.requests) { - if (request.type === CLRequestType.Deposit) { - ;(output as any).depositRequests.push(request.toJSON()) - } else if (request.type === CLRequestType.Withdrawal) { - ;(output as any).withdrawalRequests.push(request.toJSON()) - } else if (request.type === CLRequestType.Consolidation) { - ;(output as any).consolidationRequests.push(request.toJSON()) - } - } - } - - if (rejected.length > 0) { - ;(output as any).rejected = rejected - } - } -} - -class StateTracker { - private allocTracker: { - [address: string]: { - storage: string[] - } - } = {} - - private alloc: T8NAlloc - - private vm: VM - - constructor(vm: VM, alloc: T8NAlloc) { - this.alloc = alloc - const originalPutAccount = vm.stateManager.putAccount - const originalPutCode = vm.stateManager.putCode - const originalPutStorage = vm.stateManager.putStorage - - this.vm = vm - - const self = this - - vm.stateManager.putAccount = async function (...args: any) { - const address =
args[0] - self.addAddress(address.toString()) - await originalPutAccount.apply(this, args) - } - - vm.stateManager.putAccount = async function (...args: any) { - const address =
args[0] - self.addAddress(address.toString()) - return originalPutAccount.apply(this, args) - } - - vm.stateManager.putCode = async function (...args: any) { - const address =
args[0] - self.addAddress(address.toString()) - return originalPutCode.apply(this, args) - } - - vm.stateManager.putStorage = async function (...args: any) { - const address =
args[0] - const key = args[1] - self.addStorage(address.toString(), bytesToHex(key)) - return originalPutStorage.apply(this, args) - } - } - - addAddress(address: string) { - if (this.allocTracker[address] === undefined) { - this.allocTracker[address] = { storage: [] } - } - return this.allocTracker[address] - } - - addStorage(address: string, storage: string) { - const storageList = this.addAddress(address).storage - if (!storageList.includes(storage)) { - storageList.push(storage) - } - } - - public async dumpAlloc() { - // Build output alloc - const alloc = this.alloc - for (const addressString in this.allocTracker) { - const address = createAddressFromString(addressString) - const account = await this.vm.stateManager.getAccount(address) - if (account === undefined) { - delete alloc[addressString] - continue - } - if (alloc[addressString] === undefined) { - alloc[addressString] = { balance: '0x0 ' } - } - alloc[addressString].nonce = bigIntToHex(account.nonce) - alloc[addressString].balance = bigIntToHex(account.balance) - alloc[addressString].code = bytesToHex(await vm.stateManager.getCode(address)) - - const storage = allocTracker[addressString].storage - alloc[addressString].storage = alloc[addressString].storage ?? {} - - for (const key of storage) { - const keyBytes = hexToBytes(key) - let storageKeyTrimmed = bytesToHex(unpadBytes(keyBytes)) - if (storageKeyTrimmed === '0x') { - storageKeyTrimmed = '0x00' - } - const value = await vm.stateManager.getStorage(address, setLengthLeft(keyBytes, 32)) - if (value.length === 0) { - delete alloc[addressString].storage[storageKeyTrimmed] - // To be sure, also delete any keys which are left-padded to 32 bytes - delete alloc[addressString].storage[key] - continue - } - alloc[addressString].storage[storageKeyTrimmed] = bytesToHex(value) - } - } - } -} From cc2abf71925e3c1fd117563043cf21c132a1a241 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 13 Sep 2024 14:11:05 +0200 Subject: [PATCH 81/89] use noble bls t8ntool --- packages/vm/test/t8n/t8ntool.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/vm/test/t8n/t8ntool.ts b/packages/vm/test/t8n/t8ntool.ts index 14cb494e89..0fe44c222e 100644 --- a/packages/vm/test/t8n/t8ntool.ts +++ b/packages/vm/test/t8n/t8ntool.ts @@ -1,5 +1,5 @@ import { Block } from '@ethereumjs/block' -import { EVMMockBlockchain, MCLBLS } from '@ethereumjs/evm' +import { EVMMockBlockchain, MCLBLS, NobleBLS } from '@ethereumjs/evm' import { RLP } from '@ethereumjs/rlp' import { createTxFromTxData } from '@ethereumjs/tx' import { @@ -139,11 +139,8 @@ export class TransitionTool { const blockchain = getBlockchain(this.inputEnv) // Setup BLS - // TODO: use Noble - await mcl.init(mcl.BLS12_381) - const bls = new MCLBLS(mcl) const evmOpts = { - bls, + bls: new NobleBLS(), } this.vm = await createVM({ common: this.common, blockchain, evmOpts }) await setupPreConditions(this.vm.stateManager, { pre: this.alloc }) From 7a26e260389c2b169b713b23704dbd470c4c7f56 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 13 Sep 2024 14:28:26 +0200 Subject: [PATCH 82/89] add loggers to t8n args --- packages/vm/test/t8n/helpers.ts | 5 +++++ packages/vm/test/t8n/launchT8N.ts | 4 +--- packages/vm/test/t8n/t8ntool.ts | 29 ++++++++++++----------------- packages/vm/test/t8n/types.ts | 7 ++----- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/packages/vm/test/t8n/helpers.ts b/packages/vm/test/t8n/helpers.ts index 2b952e4691..7c43d9a13c 100644 --- a/packages/vm/test/t8n/helpers.ts +++ b/packages/vm/test/t8n/helpers.ts @@ -59,6 +59,11 @@ export function getArguments() { type: 'string', default: 'txs.rlp', }) + .option('log', { + describe: 'Optionally write light-trace logs to stdout', + type: 'boolean', + default: false, + }) .strict() .help().argv diff --git a/packages/vm/test/t8n/launchT8N.ts b/packages/vm/test/t8n/launchT8N.ts index f4e14b66e8..4a25543e2c 100644 --- a/packages/vm/test/t8n/launchT8N.ts +++ b/packages/vm/test/t8n/launchT8N.ts @@ -1,6 +1,4 @@ import { getArguments } from './helpers.js' import { TransitionTool } from './t8ntool.js' -const args = getArguments() - -await TransitionTool.run(args) +await TransitionTool.run(getArguments()) diff --git a/packages/vm/test/t8n/t8ntool.ts b/packages/vm/test/t8n/t8ntool.ts index 0fe44c222e..e8cdaf0fc3 100644 --- a/packages/vm/test/t8n/t8ntool.ts +++ b/packages/vm/test/t8n/t8ntool.ts @@ -1,5 +1,5 @@ import { Block } from '@ethereumjs/block' -import { EVMMockBlockchain, MCLBLS, NobleBLS } from '@ethereumjs/evm' +import { EVMMockBlockchain, NobleBLS } from '@ethereumjs/evm' import { RLP } from '@ethereumjs/rlp' import { createTxFromTxData } from '@ethereumjs/tx' import { @@ -13,7 +13,6 @@ import { import { keccak256 } from 'ethereum-cryptography/keccak' import { readFileSync, writeFileSync } from 'fs' import { loadKZG } from 'kzg-wasm' -import * as mcl from 'mcl-wasm' import { join } from 'path' import { buildBlock, createVM } from '../../src/index.js' @@ -27,15 +26,7 @@ import { StateTracker } from './stateTracker.js' import type { PostByzantiumTxReceipt } from '../../dist/esm/types.js' import type { BlockBuilder, VM } from '../../src/index.js' import type { AfterTxEvent } from '../../src/types.js' -import type { - RunnerOptions, - T8NAlloc, - T8NEnv, - T8NOptions, - T8NOutput, - T8NReceipt, - T8NRejectedTx, -} from './types.js' +import type { T8NAlloc, T8NEnv, T8NOptions, T8NOutput, T8NReceipt, T8NRejectedTx } from './types.js' import type { Common } from '@ethereumjs/common' import type { Log } from '@ethereumjs/evm' import type { TypedTxData } from '@ethereumjs/tx' @@ -46,6 +37,10 @@ import type { WithdrawalRequestV1, } from '@ethereumjs/util' +/** + * This is the TransitionTool class to run transitions. The entire class is marked `private` since + * it is only intended to be used **once**. To use it, use the single public entrypoint TransitionTool.run(args) + */ export class TransitionTool { public options: T8NOptions @@ -82,12 +77,12 @@ export class TransitionTool { this.receipts = [] } - static async run(args: T8NOptions, runnerOptions: RunnerOptions = {}) { - await new TransitionTool(args).run(args, runnerOptions) + static async run(args: T8NOptions) { + await new TransitionTool(args).run(args) } - private async run(args: T8NOptions, runnerOptions: RunnerOptions = {}) { - await this.setup(args, runnerOptions) + private async run(args: T8NOptions) { + await this.setup(args) const block = makeBlockFromEnv(this.inputEnv, { common: this.common }) @@ -133,7 +128,7 @@ export class TransitionTool { this.writeOutput(args, convertedOutput, alloc) } - private async setup(args: T8NOptions, runnerOptions: RunnerOptions = {}) { + private async setup(args: T8NOptions) { this.common = getCommon(args.state.fork, await loadKZG()) const blockchain = getBlockchain(this.inputEnv) @@ -147,7 +142,7 @@ export class TransitionTool { this.stateTracker = new StateTracker(this.vm, this.alloc) - if (runnerOptions.log === true) { + if (args.log === true) { this.vm.events.on('beforeTx', () => { // eslint-disable-next-line no-console console.log('Processing new transaction...') diff --git a/packages/vm/test/t8n/types.ts b/packages/vm/test/t8n/types.ts index 6dbedc7862..7f26d26d62 100644 --- a/packages/vm/test/t8n/types.ts +++ b/packages/vm/test/t8n/types.ts @@ -21,10 +21,11 @@ export type T8NOptions = { alloc: string body: string } + log: boolean } export type T8NAlloc = { - // TODO these are all string + // These are all PrefixedHexString, but TypeScript fails to coerce these in some places for some reason [address: string]: { nonce?: string balance: string @@ -59,10 +60,6 @@ export type T8NEnv = { parentHash: string } -export type RunnerOptions = { - log?: boolean -} - export type T8NRejectedTx = { index: number; error: string } export type T8NOutput = { From 6d913ecf34544f8fb537c26edff04aa51ab36d3e Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 13 Sep 2024 14:45:05 +0200 Subject: [PATCH 83/89] add readme [no ci] --- packages/vm/test/t8n/README.md | 41 +++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/vm/test/t8n/README.md b/packages/vm/test/t8n/README.md index baa40643cf..30ec38e9f3 100644 --- a/packages/vm/test/t8n/README.md +++ b/packages/vm/test/t8n/README.md @@ -1 +1,40 @@ -TODO write instructions +# EVM T8NTool + +T8NTool, or Transition Tool, is a tool used to "fill tests" by test runners, for instance . These files take an input allocation (the pre-state), which contains the accounts (their balances, nonces, storage and code). It also provides an environment, which holds relevant data as current timestamp, previous block hashes, current gas limit, etc. Finally, it also provides a transactions file, which are the transactions to run on top of this pre-state and environment. It outputs the post-state and relevant other artifacts, such as tx receipts and their logs. Test fillers will take this output to generate relevant tests, such as Blockchain tests or State tests, which can then be directly ran in other clients, or using EthereumJS `npm run test:blockchain` or `npm run test:state`. + +## Using T8Ntool to fill `execution-spec-tests` + +To fill `execution-spec-tests` (or write own tests, and test those against the monorepo), follow these steps: + +1. Clone . +2. Follow the installation steps: . + +To fill tests, such as the EIP-1153 TSTORE/TLOAD tests, run: + +- `fill -vv -x --fork Cancun tests/cancun/eip1153_tstore/ --evm-bin=../ethereumjs-monorepo/packages/vm/test/t8n/ethereumjs-t8ntool.sh` + +Breaking down these arguments: + +- `-vv`: Verbose output +- `-x`: Fail early if any of the test fillers fails +- `--fork`: Fork to fill for +- `--evm-bin`: relative/absolute path to t8ns `ethereumjs-t8ntool.sh` + +Optionally, it is also possible to add the `-k ` option which will only fill this certain test. + +## Debugging T8NTool with `execution-spec-tests` + +Sometimes it is unclear why a test fails, and one wants more verbose output (from the EthereumJS side). To do so, raw output from `execution-spec-tests` can be dumped by adding the `evm-dump-dir=` flag to the `fill` command above. This will output `stdout`, `stderr`, the raw output allocation and the raw results (logs, receipts, etc.) to the `evm-dump-dir`. Additionally, if traces are wanted in `stdout`, add the `--log` flag to `ethereumjs-t8ntool.sh`, i.e. `tsx "$SCRIPT_DIR/launchT8N.ts" "$@" --log`. + +This will produce small EVM traces, like this: + +```typescript +Processing new transaction... +{ + gasLeft: '9976184', + stack: [], + opName: 'CALLDATASIZE', + depth: 0, + address: '0x0000000000000000000000000000000000001000' +} +``` From df2d0b9c33eef0dd51a3e78087158aa5651ae737 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 13 Sep 2024 15:32:11 +0200 Subject: [PATCH 84/89] add t8ntool test --- packages/vm/.gitignore | 2 + packages/vm/test/api/t8ntool/t8ntool.spec.ts | 44 +++++++++++++++++++ packages/vm/test/t8n/helpers.ts | 6 --- .../vm/test/t8n/testdata/input/alloc.json | 14 ++++++ packages/vm/test/t8n/testdata/input/env.json | 20 +++++++++ packages/vm/test/t8n/testdata/input/txs.json | 16 +++++++ .../vm/test/t8n/testdata/output/alloc.json | 20 +++++++++ .../vm/test/t8n/testdata/output/result.json | 24 ++++++++++ packages/vm/test/t8n/types.ts | 1 - 9 files changed, 140 insertions(+), 7 deletions(-) create mode 100644 packages/vm/test/api/t8ntool/t8ntool.spec.ts create mode 100644 packages/vm/test/t8n/testdata/input/alloc.json create mode 100644 packages/vm/test/t8n/testdata/input/env.json create mode 100644 packages/vm/test/t8n/testdata/input/txs.json create mode 100644 packages/vm/test/t8n/testdata/output/alloc.json create mode 100644 packages/vm/test/t8n/testdata/output/result.json diff --git a/packages/vm/.gitignore b/packages/vm/.gitignore index 3ed4090b20..f7c4be695a 100644 --- a/packages/vm/.gitignore +++ b/packages/vm/.gitignore @@ -1,2 +1,4 @@ .cachedb benchmarks/*.js +test/t8n/testdata/output/allocTEST.json +test/t8n/testdata/output/resultTEST.json \ No newline at end of file diff --git a/packages/vm/test/api/t8ntool/t8ntool.spec.ts b/packages/vm/test/api/t8ntool/t8ntool.spec.ts new file mode 100644 index 0000000000..9c34937aa7 --- /dev/null +++ b/packages/vm/test/api/t8ntool/t8ntool.spec.ts @@ -0,0 +1,44 @@ +import { readFileSync } from 'fs' +import { assert, describe, it } from 'vitest' + +import { TransitionTool } from '../../t8n/t8ntool.js' + +import type { T8NOptions } from '../../t8n/types.js' + +const t8nDir = 'test/t8n/testdata/' + +const args: T8NOptions = { + state: { + fork: 'shanghai', + reward: BigInt(0), + chainid: BigInt(1), + }, + input: { + alloc: `${t8nDir}input/alloc.json`, + txs: `${t8nDir}input/txs.json`, + env: `${t8nDir}input/env.json`, + }, + output: { + basedir: t8nDir, + result: `output/resultTEST.json`, + alloc: `output/allocTEST.json`, + }, + log: false, +} + +// This test is generated using `execution-spec-tests` commit 88cab2521322191b2ec7ef7d548740c0b0a264fc, running: +// fill -k test_push0_contracts[fork_Shanghai-blockchain_test-key_sstore] --fork Shanghai tests/shanghai/eip3855_push0 --evm-bin= + +// The test will run the TransitionTool using the inputs, and then compare if the output matches + +describe('test runner config tests', () => { + it('should run t8ntool with inputs and report the expected output', async () => { + await TransitionTool.run(args) + const expectedResult = JSON.parse(readFileSync(`${t8nDir}/output/result.json`).toString()) + const expectedAlloc = JSON.parse(readFileSync(`${t8nDir}/output/alloc.json`).toString()) + const reportedResult = JSON.parse(readFileSync(`${t8nDir}/output/resultTEST.json`).toString()) + const reportedAlloc = JSON.parse(readFileSync(`${t8nDir}/output/allocTEST.json`).toString()) + assert.deepStrictEqual(reportedResult, expectedResult, 'result matches expected result') + assert.deepStrictEqual(reportedAlloc, expectedAlloc, 'alloc matches expected alloc') + }) +}) diff --git a/packages/vm/test/t8n/helpers.ts b/packages/vm/test/t8n/helpers.ts index 7c43d9a13c..3e2ee56752 100644 --- a/packages/vm/test/t8n/helpers.ts +++ b/packages/vm/test/t8n/helpers.ts @@ -54,11 +54,6 @@ export function getArguments() { type: 'string', demandOption: true, }) - .option('output.body', { - describe: 'File to write the txs RLP to', - type: 'string', - default: 'txs.rlp', - }) .option('log', { describe: 'Optionally write light-trace logs to stdout', type: 'boolean', @@ -78,7 +73,6 @@ export function getArguments() { basedir: (args)['output.basedir'], result: (args)['output.result'], alloc: (args)['output.alloc'], - body: (args)['output.body'], } args.state = { fork: (args)['state.fork'], diff --git a/packages/vm/test/t8n/testdata/input/alloc.json b/packages/vm/test/t8n/testdata/input/alloc.json new file mode 100644 index 0000000000..5636d0fe76 --- /dev/null +++ b/packages/vm/test/t8n/testdata/input/alloc.json @@ -0,0 +1,14 @@ +{ + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "nonce": "0x00", + "balance": "0x3635c9adc5dea00000", + "code": "0x", + "storage": {} + }, + "0x0000000000000000000000000000000000001000": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60015f55", + "storage": {} + } +} diff --git a/packages/vm/test/t8n/testdata/input/env.json b/packages/vm/test/t8n/testdata/input/env.json new file mode 100644 index 0000000000..856a1bb845 --- /dev/null +++ b/packages/vm/test/t8n/testdata/input/env.json @@ -0,0 +1,20 @@ +{ + "currentCoinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentGasLimit": "100000000000000000", + "currentNumber": "1", + "currentTimestamp": "1000", + "currentRandom": "0", + "currentDifficulty": "0", + "parentDifficulty": "0", + "parentTimestamp": "0", + "parentBaseFee": "7", + "parentGasUsed": "0", + "parentGasLimit": "100000000000000000", + "parentUncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "blockHashes": { + "0": "0xb8e2278dcbf8c5a9a75efba398d65f11b8021b5729b52b7cb29ab2c911b2ad0e" + }, + "ommers": [], + "withdrawals": [], + "parentHash": "0xb8e2278dcbf8c5a9a75efba398d65f11b8021b5729b52b7cb29ab2c911b2ad0e" +} diff --git a/packages/vm/test/t8n/testdata/input/txs.json b/packages/vm/test/t8n/testdata/input/txs.json new file mode 100644 index 0000000000..25d90d019d --- /dev/null +++ b/packages/vm/test/t8n/testdata/input/txs.json @@ -0,0 +1,16 @@ +[ + { + "type": "0x0", + "chainId": "0x1", + "nonce": "0x0", + "gasPrice": "0xa", + "gas": "0x186a0", + "to": "0x0000000000000000000000000000000000001000", + "value": "0x0", + "input": "0x", + "v": "0x25", + "r": "0x9069fab60fe5c8a970860130c49d2295646da4fff858330a1fd5d260cd01e562", + "s": "0x7a31960780931801bb34d06f89f19a529357f833c6cfda63135440a743949717", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + } +] diff --git a/packages/vm/test/t8n/testdata/output/alloc.json b/packages/vm/test/t8n/testdata/output/alloc.json new file mode 100644 index 0000000000..978d91deef --- /dev/null +++ b/packages/vm/test/t8n/testdata/output/alloc.json @@ -0,0 +1,20 @@ +{ + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "nonce": "0x1", + "balance": "0x3635c9adc5de996c36", + "code": "0x", + "storage": {} + }, + "0x0000000000000000000000000000000000001000": { + "nonce": "0x1", + "balance": "0x0", + "code": "0x60015f55", + "storage": { "0x00": "0x01" } + }, + "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { + "balance": "0x1f923", + "nonce": "0x0", + "code": "0x", + "storage": {} + } +} diff --git a/packages/vm/test/t8n/testdata/output/result.json b/packages/vm/test/t8n/testdata/output/result.json new file mode 100644 index 0000000000..0472f7077a --- /dev/null +++ b/packages/vm/test/t8n/testdata/output/result.json @@ -0,0 +1,24 @@ +{ + "stateRoot": "0x3fa1fe742a29d9b11867fae5d162645b828bf24c533a1f3c6483b5765a5fb517", + "txRoot": "0x73b4cec7dd450173155fcf22cd9a4bbac4e41c118b3d6e9dc22202a1966e9483", + "receiptsRoot": "0xc598f69a5674cae9337261b669970e24abc0b46e6d284372a239ec8ccbf20b0a", + "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "receipts": [ + { + "root": "0x", + "status": "0x1", + "cumulativeGasUsed": "0xa861", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "logs": [], + "transactionHash": "0xdec8f52ec4ec24aefca6b6a1fbd8bd36bd3670ac31dc7e4dd74a1cc490a57306", + "contractAddress": "0x0000000000000000000000000000000000000000", + "gasUsed": "0xa861", + "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "transactionIndex": "0x0" + } + ], + "gasUsed": "0xa861", + "currentBaseFee": "0x7", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" +} diff --git a/packages/vm/test/t8n/types.ts b/packages/vm/test/t8n/types.ts index 7f26d26d62..527be50049 100644 --- a/packages/vm/test/t8n/types.ts +++ b/packages/vm/test/t8n/types.ts @@ -19,7 +19,6 @@ export type T8NOptions = { basedir: string result: string alloc: string - body: string } log: boolean } From 40638cc74af8187e600cdb62ba5648215bf72db7 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 13 Sep 2024 15:39:31 +0200 Subject: [PATCH 85/89] fix cspell --- config/cspell-md.json | 2 ++ config/cspell-ts.json | 1 + packages/vm/test/t8n/stateTracker.ts | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/config/cspell-md.json b/config/cspell-md.json index a00979bf8d..70a5cb1d33 100644 --- a/config/cspell-md.json +++ b/config/cspell-md.json @@ -2,6 +2,8 @@ "language": "en-US", "ignoreRegExpList": ["/0x[0-9A-Fa-f]+/"], "words": [ + "t8ntool", + "calldatasize", "Dencun", "Hardfork", "acolytec", diff --git a/config/cspell-ts.json b/config/cspell-ts.json index ad6c4d2ea8..79b934d298 100644 --- a/config/cspell-ts.json +++ b/config/cspell-ts.json @@ -12,6 +12,7 @@ } ], "words": [ + "t8ntool", "!Json", "!Rpc", "Hardfork", diff --git a/packages/vm/test/t8n/stateTracker.ts b/packages/vm/test/t8n/stateTracker.ts index eb62d51855..b999668ba5 100644 --- a/packages/vm/test/t8n/stateTracker.ts +++ b/packages/vm/test/t8n/stateTracker.ts @@ -13,7 +13,7 @@ import type { Account, Address, PrefixedHexString } from '@ethereumjs/util' export class StateTracker { private allocTracker: { - // TODO these are all PrefixexHexString + // TODO these are all PrefixedHexString [address: string]: { storage: string[] } From 552b9ed8912228e0934289407cc585fefd051c05 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 13 Sep 2024 16:09:49 +0200 Subject: [PATCH 86/89] add deprecated output.body --- packages/vm/test/t8n/helpers.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/vm/test/t8n/helpers.ts b/packages/vm/test/t8n/helpers.ts index 3e2ee56752..54a8f697f8 100644 --- a/packages/vm/test/t8n/helpers.ts +++ b/packages/vm/test/t8n/helpers.ts @@ -54,6 +54,11 @@ export function getArguments() { type: 'string', demandOption: true, }) + .option('output.body', { + deprecate: true, + description: 'File to write transaction RLPs to (currently unused)', + type: 'string', + }) .option('log', { describe: 'Optionally write light-trace logs to stdout', type: 'boolean', From fb676712ceb3cbce24604d80b5c911199a05d996 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 13 Sep 2024 18:00:23 +0200 Subject: [PATCH 87/89] make tsc happy --- packages/vm/test/t8n/stateTracker.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vm/test/t8n/stateTracker.ts b/packages/vm/test/t8n/stateTracker.ts index b999668ba5..8e8376da1c 100644 --- a/packages/vm/test/t8n/stateTracker.ts +++ b/packages/vm/test/t8n/stateTracker.ts @@ -97,12 +97,12 @@ export class StateTracker { } const value = await this.vm.stateManager.getStorage(address, setLengthLeft(keyBytes, 32)) if (value.length === 0) { - delete outputAlloc[addressString].storage[storageKeyTrimmed] + delete outputAlloc[addressString].storage![storageKeyTrimmed] // To be sure, also delete any keys which are left-padded to 32 bytes - delete outputAlloc[addressString].storage[key] + delete outputAlloc[addressString].storage![key] continue } - outputAlloc[addressString].storage[storageKeyTrimmed] = bytesToHex(value) + outputAlloc[addressString].storage![storageKeyTrimmed] = bytesToHex(value) } } return outputAlloc From f27218468925be6e161ebeb730038a04ba776f04 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 13 Sep 2024 18:08:44 +0200 Subject: [PATCH 88/89] vm: fix 2935 test --- .../vm/test/api/EIPs/eip-2935-historical-block-hashes.spec.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/vm/test/api/EIPs/eip-2935-historical-block-hashes.spec.ts b/packages/vm/test/api/EIPs/eip-2935-historical-block-hashes.spec.ts index b95b7b58cf..7b97d3390f 100644 --- a/packages/vm/test/api/EIPs/eip-2935-historical-block-hashes.spec.ts +++ b/packages/vm/test/api/EIPs/eip-2935-historical-block-hashes.spec.ts @@ -178,6 +178,8 @@ describe('EIP 2935: historical block hashes', () => { validateConsensus: false, }) const vm = await createVM({ common: commonGenesis, blockchain }) + // Ensure 2935 system code exists + await vm.stateManager.putCode(historyAddress, contract2935Code) commonGenesis.setHardforkBy({ timestamp: 1, }) @@ -216,6 +218,8 @@ describe('EIP 2935: historical block hashes', () => { validateConsensus: false, }) const vm = await createVM({ common, blockchain }) + // Ensure 2935 system code exists + await vm.stateManager.putCode(historyAddress, contract2935Code) let lastBlock = (await vm.blockchain.getBlock(0)) as Block for (let i = 1; i <= blocksToBuild; i++) { lastBlock = await ( From 335967aec5411504e398050cb71016587bc4cbf5 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Fri, 13 Sep 2024 22:18:33 -0400 Subject: [PATCH 89/89] Skip t8n tests in browser --- packages/vm/vitest.config.browser.mts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/vm/vitest.config.browser.mts b/packages/vm/vitest.config.browser.mts index a3b19fe16a..73613ccbc2 100644 --- a/packages/vm/vitest.config.browser.mts +++ b/packages/vm/vitest.config.browser.mts @@ -11,7 +11,9 @@ export default mergeConfig( // path.resolve is not a function 'test/api/tester/tester.config.spec.ts', // Cannot read properties of undefined (reading 'pedersen_hash') - 'test/api/EIPs/eip-6800-verkle.spec.ts' + 'test/api/EIPs/eip-6800-verkle.spec.ts', + // Uses NodeJS builtins and we don't need to fill tests in browser anyway + 'test/api/t8ntool/t8ntool.spec.ts' ], }, }