From 33f68c6827e75de3f266199855c34f2c136517e2 Mon Sep 17 00:00:00 2001 From: g11tech Date: Tue, 22 Nov 2022 19:03:46 +0530 Subject: [PATCH] common: Parse post-merge hardfork config in parseGethParams and handle post-merge genesis block (#2427) * correctly set hf order * test specs * fix the merge hf push condition * try placing merge block in better wat * fix test spec for kiln * alternate fix of skipping only pos validation on genesis * move merge validations to non genesis * fix unsupported test case * correctly set hardfork * resolve test genesis poisioning * add comment * cli arg for mergeforkid placement * place merge only after genesis * fix tests * restore test * address another case for merge just post genesis * test case for the new edge case * add comment --- packages/client/bin/cli.ts | 11 ++- packages/client/lib/miner/pendingBlock.ts | 2 +- .../test/rpc/util/CLConnectionManager.spec.ts | 4 +- packages/common/src/common.ts | 4 +- packages/common/src/types.ts | 1 + packages/common/src/utils.ts | 91 ++++++++++++++----- .../common/test/data/post-merge-hardfork.json | 44 +++++++++ packages/common/test/hardforks.spec.ts | 1 + packages/common/test/utils.spec.ts | 90 ++++++++++++++++++ packages/evm/src/evm.ts | 39 ++++---- .../api/EIPs/eip-4895-withdrawals.spec.ts | 9 +- packages/vm/test/api/index.spec.ts | 22 +++++ 12 files changed, 268 insertions(+), 50 deletions(-) create mode 100644 packages/common/test/data/post-merge-hardfork.json diff --git a/packages/client/bin/cli.ts b/packages/client/bin/cli.ts index 5177b24025..810165ea87 100755 --- a/packages/client/bin/cli.ts +++ b/packages/client/bin/cli.ts @@ -70,6 +70,12 @@ const args = yargs(hideBin(process.argv)) describe: 'Import a geth genesis file for running a custom network', coerce: (arg: string) => (arg ? path.resolve(arg) : undefined), }) + .option('mergeForkIdPostMerge', { + describe: + 'Place mergeForkIdTransition hardfork before (false) or after (true) Merge hardfork in the custom gethGenesis', + boolean: true, + default: true, + }) .option('transports', { describe: 'Network transports', default: Config.TRANSPORTS_DEFAULT, @@ -608,7 +614,10 @@ async function run() { // Use geth genesis parameters file if specified const genesisFile = JSON.parse(readFileSync(args.gethGenesis, 'utf-8')) const chainName = path.parse(args.gethGenesis).base.split('.')[0] - common = Common.fromGethGenesis(genesisFile, { chain: chainName }) + common = Common.fromGethGenesis(genesisFile, { + chain: chainName, + mergeForkIdPostMerge: args.mergeForkIdPostMerge, + }) customGenesisState = parseGethGenesisState(genesisFile) } diff --git a/packages/client/lib/miner/pendingBlock.ts b/packages/client/lib/miner/pendingBlock.ts index db03b437bc..c3122f21dc 100644 --- a/packages/client/lib/miner/pendingBlock.ts +++ b/packages/client/lib/miner/pendingBlock.ts @@ -56,7 +56,6 @@ export class PendingBlock { throw new Error('cannot get iterator head: blockchain has no getTotalDifficulty function') } const td = await vm.blockchain.getTotalDifficulty(parentBlock.hash()) - vm._common.setHardforkByBlockNumber(parentBlock.header.number, td) const builder = await vm.buildBlock({ parentBlock, @@ -69,6 +68,7 @@ export class PendingBlock { withdrawals, blockOpts: { putBlockIntoBlockchain: false, + hardforkByTTD: td, }, }) diff --git a/packages/client/test/rpc/util/CLConnectionManager.spec.ts b/packages/client/test/rpc/util/CLConnectionManager.spec.ts index 512cfed100..14035934ab 100644 --- a/packages/client/test/rpc/util/CLConnectionManager.spec.ts +++ b/packages/client/test/rpc/util/CLConnectionManager.spec.ts @@ -42,8 +42,9 @@ tape('[CLConnectionManager]', (t) => { st.ok(manager.running, 'should start') manager.stop() st.ok(!manager.running, 'should stop') + const prevMergeForkBlock = (genesisJSON.config as any).mergeForkBlock ;(genesisJSON.config as any).mergeForkBlock = 0 - const params = parseGethGenesis(genesisJSON, 'post-merge') + const params = parseGethGenesis(genesisJSON, 'post-merge', false) let common = new Common({ chain: params.name, customChains: [params], @@ -53,7 +54,6 @@ tape('[CLConnectionManager]', (t) => { manager = new CLConnectionManager({ config }) st.ok(manager.running, 'starts on instantiation if hardfork is MergeForkBlock') manager.stop() - const prevMergeForkBlock = (genesisJSON.config as any).mergeForkBlock ;(genesisJSON.config as any).mergeForkBlock = 10 common = new Common({ chain: params.name, diff --git a/packages/common/src/common.ts b/packages/common/src/common.ts index a951198873..3577f78df4 100644 --- a/packages/common/src/common.ts +++ b/packages/common/src/common.ts @@ -176,9 +176,9 @@ export class Common extends EventEmitter { */ static fromGethGenesis( genesisJson: any, - { chain, genesisHash, hardfork }: GethConfigOpts + { chain, genesisHash, hardfork, mergeForkIdPostMerge }: GethConfigOpts ): Common { - const genesisParams = parseGethGenesis(genesisJson, chain) + const genesisParams = parseGethGenesis(genesisJson, chain, mergeForkIdPostMerge) const common = new Common({ chain: genesisParams.name ?? 'custom', customChains: [genesisParams], diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index 92488e707d..0b9667b076 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -118,4 +118,5 @@ export interface GethConfigOpts { chain?: string hardfork?: string | Hardfork genesisHash?: Buffer + mergeForkIdPostMerge?: boolean } diff --git a/packages/common/src/utils.ts b/packages/common/src/utils.ts index d4ab9f353c..920aa7f5de 100644 --- a/packages/common/src/utils.ts +++ b/packages/common/src/utils.ts @@ -2,6 +2,7 @@ import { intToHex, isHexPrefixed, stripHexPrefix } from '@ethereumjs/util' import { Hardfork } from './enums' +type ConfigHardfork = { name: string; block: number } /** * Transforms Geth formatted nonce (i.e. hex string) to 8 byte 0x-prefixed string used internally * @param nonce string parsed from the Geth genesis file @@ -20,9 +21,12 @@ function formatNonce(nonce: string): string { /** * Converts Geth genesis parameters to an EthereumJS compatible `CommonOpts` object * @param json object representing the Geth genesis file + * @param optional mergeForkIdPostMerge which clarifies the placement of MergeForkIdTransition + * hardfork, which by default is post merge as with the merged eth networks but could also come + * before merge like in kiln genesis * @returns genesis parameters in a `CommonOpts` compliant object */ -function parseGethParams(json: any) { +function parseGethParams(json: any, mergeForkIdPostMerge: boolean = true) { const { name, config, difficulty, mixHash, gasLimit, coinbase, baseFeePerGas } = json let { extraData, timestamp, nonce } = json const { chainId } = config @@ -83,33 +87,72 @@ function parseGethParams(json: any) { }, } - const forkMap: { [key: string]: string } = { - [Hardfork.Homestead]: 'homesteadBlock', - [Hardfork.Dao]: 'daoForkBlock', - [Hardfork.TangerineWhistle]: 'eip150Block', - [Hardfork.SpuriousDragon]: 'eip155Block', - [Hardfork.Byzantium]: 'byzantiumBlock', - [Hardfork.Constantinople]: 'constantinopleBlock', - [Hardfork.Petersburg]: 'petersburgBlock', - [Hardfork.Istanbul]: 'istanbulBlock', - [Hardfork.MuirGlacier]: 'muirGlacierBlock', - [Hardfork.Berlin]: 'berlinBlock', - [Hardfork.London]: 'londonBlock', - [Hardfork.MergeForkIdTransition]: 'mergeForkBlock', - [Hardfork.Shanghai]: 'shanghaiBlock', + const forkMap: { [key: string]: { name: string; postMerge?: boolean } } = { + [Hardfork.Homestead]: { name: 'homesteadBlock' }, + [Hardfork.Dao]: { name: 'daoForkBlock' }, + [Hardfork.TangerineWhistle]: { name: 'eip150Block' }, + [Hardfork.SpuriousDragon]: { name: 'eip155Block' }, + [Hardfork.Byzantium]: { name: 'byzantiumBlock' }, + [Hardfork.Constantinople]: { name: 'constantinopleBlock' }, + [Hardfork.Petersburg]: { name: 'petersburgBlock' }, + [Hardfork.Istanbul]: { name: 'istanbulBlock' }, + [Hardfork.MuirGlacier]: { name: 'muirGlacierBlock' }, + [Hardfork.Berlin]: { name: 'berlinBlock' }, + [Hardfork.London]: { name: 'londonBlock' }, + [Hardfork.MergeForkIdTransition]: { name: 'mergeForkBlock', postMerge: mergeForkIdPostMerge }, + [Hardfork.Shanghai]: { name: 'shanghaiBlock', postMerge: true }, } - params.hardforks = Object.values(Hardfork) - .map((name) => ({ - name, - block: name === Hardfork.Chainstart ? 0 : config[forkMap[name]] ?? null, + + // forkMapRev is the map from config field name to Hardfork + const forkMapRev = Object.keys(forkMap).reduce((acc, elem) => { + acc[forkMap[elem].name] = elem + return acc + }, {} as { [key: string]: string }) + const configHardforkNames = Object.keys(config).filter((key) => forkMapRev[key] !== undefined) + + params.hardforks = configHardforkNames + .map((nameBlock) => ({ + name: forkMapRev[nameBlock], + block: config[nameBlock], })) - .filter((fork) => fork.block !== null) + .filter((fork) => fork.block !== null && fork.block !== undefined) + + // sort with block + params.hardforks.sort(function (a: ConfigHardfork, b: ConfigHardfork) { + return a.block - b.block + }) + params.hardforks.unshift({ name: Hardfork.Chainstart, block: 0 }) + if (config.terminalTotalDifficulty !== undefined) { - params.hardforks.push({ + // Following points need to be considered for placement of merge hf + // - Merge hardfork can't be placed at genesis + // - Place merge hf before any hardforks that require CL participation for e.g. withdrawals + // - Merge hardfork has to be placed just after genesis if any of the genesis hardforks make CL + // necessary for e.g. withdrawals + const mergeConfig = { name: Hardfork.Merge, ttd: config.terminalTotalDifficulty, block: null, - }) + } + + // If any of the genesis block require merge, then we need merge just right after genesis + const isMergeJustPostGenesis: boolean = params.hardforks + .filter((hf: ConfigHardfork) => hf.block === 0) + .reduce( + (acc: boolean, hf: ConfigHardfork) => acc || forkMap[hf.name]?.postMerge === true, + false + ) + + // Merge hardfork has to be placed before first non-zero block hardfork that is dependent + // on merge or first non zero block hardfork if any of genesis hardforks require merge + const postMergeIndex = params.hardforks.findIndex( + (hf: any) => (isMergeJustPostGenesis || forkMap[hf.name]?.postMerge === true) && hf.block > 0 + ) + if (postMergeIndex !== -1) { + params.hardforks.splice(postMergeIndex, 0, mergeConfig) + } else { + params.hardforks.push(mergeConfig) + } } return params } @@ -120,7 +163,7 @@ function parseGethParams(json: any) { * @param name optional chain name * @returns parsed params */ -export function parseGethGenesis(json: any, name?: string) { +export function parseGethGenesis(json: any, name?: string, mergeForkIdPostMerge?: boolean) { try { if (['config', 'difficulty', 'gasLimit', 'alloc'].some((field) => !(field in json))) { throw new Error('Invalid format, expected geth genesis fields missing') @@ -128,7 +171,7 @@ export function parseGethGenesis(json: any, name?: string) { if (name !== undefined) { json.name = name } - return parseGethParams(json) + return parseGethParams(json, mergeForkIdPostMerge) } catch (e: any) { throw new Error(`Error parsing parameters file: ${e.message}`) } diff --git a/packages/common/test/data/post-merge-hardfork.json b/packages/common/test/data/post-merge-hardfork.json new file mode 100644 index 0000000000..edb8d0435a --- /dev/null +++ b/packages/common/test/data/post-merge-hardfork.json @@ -0,0 +1,44 @@ +{ + "config": { + "chainId": 1, + "homesteadBlock": 0, + "eip150Block": 0, + "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "shanghaiBlock": 8, + "clique": { + "period": 5, + "epoch": 30000 + }, + "terminalTotalDifficulty": 2, + "terminalTotalDifficultyPassed": true + }, + "nonce": "0x42", + "timestamp": "0x0", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x1C9C380", + "difficulty": "0x0", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": { + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x6d6172697573766477000000" + }, + "0x8A04d14125D0FDCDc742F4A05C051De07232EDa4": { + "code": "0x60806040526004361061003f5760003560e01c806301ffc9a714610044578063228951181461008c578063621fd130146101a2578063c5f2892f1461022c575b600080fd5b34801561005057600080fd5b506100786004803603602081101561006757600080fd5b50356001600160e01b031916610253565b604080519115158252519081900360200190f35b6101a0600480360360808110156100a257600080fd5b8101906020810181356401000000008111156100bd57600080fd5b8201836020820111156100cf57600080fd5b803590602001918460018302840111640100000000831117156100f157600080fd5b91939092909160208101903564010000000081111561010f57600080fd5b82018360208201111561012157600080fd5b8035906020019184600183028401116401000000008311171561014357600080fd5b91939092909160208101903564010000000081111561016157600080fd5b82018360208201111561017357600080fd5b8035906020019184600183028401116401000000008311171561019557600080fd5b91935091503561028a565b005b3480156101ae57600080fd5b506101b7610ce6565b6040805160208082528351818301528351919283929083019185019080838360005b838110156101f15781810151838201526020016101d9565b50505050905090810190601f16801561021e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561023857600080fd5b50610241610cf8565b60408051918252519081900360200190f35b60006001600160e01b031982166301ffc9a760e01b148061028457506001600160e01b03198216638564090760e01b145b92915050565b603086146102c95760405162461bcd60e51b81526004018080602001828103825260268152602001806112516026913960400191505060405180910390fd5b602084146103085760405162461bcd60e51b81526004018080602001828103825260368152602001806111e86036913960400191505060405180910390fd5b606082146103475760405162461bcd60e51b81526004018080602001828103825260298152602001806112c46029913960400191505060405180910390fd5b670de0b6b3a764000034101561038e5760405162461bcd60e51b815260040180806020018281038252602681526020018061129e6026913960400191505060405180910390fd5b633b9aca003406156103d15760405162461bcd60e51b815260040180806020018281038252603381526020018061121e6033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff81111561041f5760405162461bcd60e51b81526004018080602001828103825260278152602001806112776027913960400191505060405180910390fd5b606061042a82610fc6565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a61045f602054610fc6565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f01601f191690910187810386528c815260200190508c8c808284376000838201819052601f909101601f191690920188810386528c5181528c51602091820193918e019250908190849084905b838110156104f65781810151838201526020016104de565b50505050905090810190601f1680156105235780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f909101601f19169092018881038452895181528951602091820193918b019250908190849084905b8381101561057f578181015183820152602001610567565b50505050905090810190601f1680156105ac5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284376fffffffffffffffffffffffffffffffff199094169190930190815260408051600f19818403018152601090920190819052815191955093508392506020850191508083835b602083106106415780518252601f199092019160209182019101610622565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610680573d6000803e3d6000fd5b5050506040513d602081101561069557600080fd5b5051905060006002806106ab6040848a8c61114a565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106107015780518252601f1990920191602091820191016106e2565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610740573d6000803e3d6000fd5b5050506040513d602081101561075557600080fd5b50516002610766896040818d61114a565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106107c15780518252601f1990920191602091820191016107a2565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610800573d6000803e3d6000fd5b5050506040513d602081101561081557600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b6020831061086b5780518252601f19909201916020918201910161084c565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa1580156108aa573d6000803e3d6000fd5b5050506040513d60208110156108bf57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b6020831061092e5780518252601f19909201916020918201910161090f565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa15801561096d573d6000803e3d6000fd5b5050506040513d602081101561098257600080fd5b50516040518651600291889160009188916020918201918291908601908083835b602083106109c25780518252601f1990920191602091820191016109a3565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610a495780518252601f199092019160209182019101610a2a565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610a88573d6000803e3d6000fd5b5050506040513d6020811015610a9d57600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610af35780518252601f199092019160209182019101610ad4565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610b32573d6000803e3d6000fd5b5050506040513d6020811015610b4757600080fd5b50519050858114610b895760405162461bcd60e51b81526004018080602001828103825260548152602001806111946054913960600191505060405180910390fd5b60205463ffffffff11610bcd5760405162461bcd60e51b81526004018080602001828103825260218152602001806111736021913960400191505060405180910390fd5b602080546001019081905560005b6020811015610cda578160011660011415610c0d578260008260208110610bfe57fe5b015550610cdd95505050505050565b600260008260208110610c1c57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610c745780518252601f199092019160209182019101610c55565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610cb3573d6000803e3d6000fd5b5050506040513d6020811015610cc857600080fd5b50519250600282049150600101610bdb565b50fe5b50505050505050565b6060610cf3602054610fc6565b905090565b6020546000908190815b6020811015610ea9578160011660011415610ddb57600260008260208110610d2657fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610d7e5780518252601f199092019160209182019101610d5f565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610dbd573d6000803e3d6000fd5b5050506040513d6020811015610dd257600080fd5b50519250610e9b565b60028360218360208110610deb57fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310610e425780518252601f199092019160209182019101610e23565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610e81573d6000803e3d6000fd5b5050506040513d6020811015610e9657600080fd5b505192505b600282049150600101610d02565b50600282610eb8602054610fc6565b600060401b6040516020018084815260200183805190602001908083835b60208310610ef55780518252601f199092019160209182019101610ed6565b51815160209384036101000a600019018019909216911617905267ffffffffffffffff199590951692019182525060408051808303600719018152601890920190819052815191955093508392850191508083835b60208310610f695780518252601f199092019160209182019101610f4a565b51815160209384036101000a60001901801990921691161790526040519190930194509192505080830381855afa158015610fa8573d6000803e3d6000fd5b5050506040513d6020811015610fbd57600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b8260008151811061100057fe5b60200101906001600160f81b031916908160001a9053508060061a60f81b8260018151811061102b57fe5b60200101906001600160f81b031916908160001a9053508060051a60f81b8260028151811061105657fe5b60200101906001600160f81b031916908160001a9053508060041a60f81b8260038151811061108157fe5b60200101906001600160f81b031916908160001a9053508060031a60f81b826004815181106110ac57fe5b60200101906001600160f81b031916908160001a9053508060021a60f81b826005815181106110d757fe5b60200101906001600160f81b031916908160001a9053508060011a60f81b8260068151811061110257fe5b60200101906001600160f81b031916908160001a9053508060001a60f81b8260078151811061112d57fe5b60200101906001600160f81b031916908160001a90535050919050565b60008085851115611159578182fd5b83861115611165578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a164736f6c634300060b000a", + "balance": "0x0" + } + }, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFeePerGas": "0x7" +} diff --git a/packages/common/test/hardforks.spec.ts b/packages/common/test/hardforks.spec.ts index 029cc9f3ec..62039de0a3 100644 --- a/packages/common/test/hardforks.spec.ts +++ b/packages/common/test/hardforks.spec.ts @@ -433,6 +433,7 @@ tape('[Common]: Hardfork logic', function (t: tape.Test) { const json = require(`../../blockchain/test/testdata/geth-genesis-kiln.json`) c = Common.fromGethGenesis(json, { chain: 'kiln', + mergeForkIdPostMerge: false, }) // MergeForkIdTransition change should be before Merge diff --git a/packages/common/test/utils.spec.ts b/packages/common/test/utils.spec.ts index df6c0b9fac..dc4ccd6e84 100644 --- a/packages/common/test/utils.spec.ts +++ b/packages/common/test/utils.spec.ts @@ -1,6 +1,7 @@ import * as tape from 'tape' import { Common } from '../src/common' +import { Hardfork } from '../src/enums' import { parseGethGenesis } from '../src/utils' tape('[Utils/Parse]', (t) => { @@ -65,11 +66,100 @@ tape('[Utils/Parse]', (t) => { '51c7fe41be669f69c45c33a56982cbde405313342d9e2b00d7c91a7b284dd4f8', 'hex' ), + mergeForkIdPostMerge: false, }) + st.deepEqual( + common.hardforks().map((hf) => hf.name), + [ + 'chainstart', + 'homestead', + 'tangerineWhistle', + 'spuriousDragon', + 'byzantium', + 'constantinople', + 'petersburg', + 'istanbul', + 'berlin', + 'london', + 'mergeForkIdTransition', + 'merge', + ], + 'hardfork parse order should be correct' + ) for (const hf of common.hardforks()) { /* eslint-disable @typescript-eslint/no-use-before-define */ st.equal(hf.forkHash, kilnForkHashes[hf.name], `${hf.name} forkHash should match`) } + + // Ok lets schedule shanghai at block 0, this should force merge to be scheduled at just after + // genesis if even mergeForkIdTransition is not confirmed to be post merge + // This will also check if the forks are being correctly sorted based on block + Object.assign(json.config, { shanghaiBlock: 0 }) + const common1 = Common.fromGethGenesis(json, { + chain: 'customChain', + mergeForkIdPostMerge: false, + }) + // merge hardfork is now scheduled just after shanghai even if mergeForkIdTransition is not confirmed + // to be post merge + st.deepEqual( + common1.hardforks().map((hf) => hf.name), + [ + 'chainstart', + 'homestead', + 'tangerineWhistle', + 'spuriousDragon', + 'byzantium', + 'constantinople', + 'petersburg', + 'istanbul', + 'berlin', + 'london', + 'shanghai', + 'merge', + 'mergeForkIdTransition', + ], + 'hardfork parse order should be correct' + ) + }) + + t.test('should successfully parse genesis with hardfork scheduled post merge', async (st) => { + const json = require(`./data/post-merge-hardfork.json`) + const common = Common.fromGethGenesis(json, { + chain: 'customChain', + }) + st.deepEqual( + common.hardforks().map((hf) => hf.name), + [ + 'chainstart', + 'homestead', + 'tangerineWhistle', + 'spuriousDragon', + 'byzantium', + 'constantinople', + 'petersburg', + 'istanbul', + 'muirGlacier', + 'berlin', + 'london', + 'merge', + 'shanghai', + ], + 'hardfork parse order should be correct' + ) + st.equal(common.getHardforkByBlockNumber(0), Hardfork.London, 'london at genesis') + st.equal(common.getHardforkByBlockNumber(1, BigInt(2)), Hardfork.Merge, 'merge at block 1') + // shanghai is at 8 + st.equal(common.getHardforkByBlockNumber(8), Hardfork.Shanghai, 'shanghai at block 8') + // should be post merge at shanghai + st.equal(common.getHardforkByBlockNumber(8, BigInt(2)), Hardfork.Shanghai, 'london at genesis') + // if not post merge, then should error + try { + common.getHardforkByBlockNumber(8, BigInt(1)) + st.fail('should have failed since merge not compeleted before shanghai') + } catch (e) { + st.pass('correctly fails if merge not compeleted before shanghai') + } + st.end() }) }) diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index 8b6b50b01b..d153b8ffe8 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -139,6 +139,25 @@ export interface EVMOpts { * @ignore */ export class EVM implements EVMInterface { + private static supportedHardforks = [ + Hardfork.Chainstart, + Hardfork.Homestead, + Hardfork.Dao, + Hardfork.TangerineWhistle, + Hardfork.SpuriousDragon, + Hardfork.Byzantium, + Hardfork.Constantinople, + Hardfork.Petersburg, + Hardfork.Istanbul, + Hardfork.MuirGlacier, + Hardfork.Berlin, + Hardfork.London, + Hardfork.ArrowGlacier, + Hardfork.GrayGlacier, + Hardfork.MergeForkIdTransition, + Hardfork.Merge, + Hardfork.Shanghai, + ] protected _tx?: { gasPrice: bigint origin: Address @@ -246,25 +265,7 @@ export class EVM implements EVMInterface { } } - const supportedHardforks = [ - Hardfork.Chainstart, - Hardfork.Homestead, - Hardfork.Dao, - Hardfork.TangerineWhistle, - Hardfork.SpuriousDragon, - Hardfork.Byzantium, - Hardfork.Constantinople, - Hardfork.Petersburg, - Hardfork.Istanbul, - Hardfork.MuirGlacier, - Hardfork.Berlin, - Hardfork.London, - Hardfork.ArrowGlacier, - Hardfork.GrayGlacier, - Hardfork.MergeForkIdTransition, - Hardfork.Merge, - ] - if (!supportedHardforks.includes(this._common.hardfork() as Hardfork)) { + if (!EVM.supportedHardforks.includes(this._common.hardfork() as Hardfork)) { throw new Error( `Hardfork ${this._common.hardfork()} not set as supported in supportedHardforks` ) diff --git a/packages/vm/test/api/EIPs/eip-4895-withdrawals.spec.ts b/packages/vm/test/api/EIPs/eip-4895-withdrawals.spec.ts index e280324f5b..175fa5f3df 100644 --- a/packages/vm/test/api/EIPs/eip-4895-withdrawals.spec.ts +++ b/packages/vm/test/api/EIPs/eip-4895-withdrawals.spec.ts @@ -182,12 +182,14 @@ tape('EIP4895 tests', (t) => { t.test('should build a block correctly with withdrawals', async (st) => { const common = Common.fromGethGenesis(genesisJSON, { chain: 'custom' }) + common.setHardforkByBlockNumber(0) const genesisState = parseGethGenesisState(genesisJSON) const blockchain = await Blockchain.create({ common, validateBlocks: false, validateConsensus: false, genesisState, + hardforkByHeadBlockNumber: true, }) const genesisBlock = blockchain.genesisBlock st.equal( @@ -203,11 +205,16 @@ tape('EIP4895 tests', (t) => { const withdrawals = (gethBlockBufferArray[3] as WithdrawalBuffer[]).map((wa) => Withdrawal.fromValuesArray(wa) ) + const td = await blockchain.getTotalDifficulty(genesisBlock.hash()) const blockBuilder = await vm.buildBlock({ parentBlock: genesisBlock, withdrawals, - blockOpts: { calcDifficultyFromHeader: genesisBlock.header, freeze: false }, + blockOpts: { + calcDifficultyFromHeader: genesisBlock.header, + freeze: false, + hardforkByTTD: td, + }, }) const block = await blockBuilder.build() diff --git a/packages/vm/test/api/index.spec.ts b/packages/vm/test/api/index.spec.ts index 07dd69eb18..a9a1b78f48 100644 --- a/packages/vm/test/api/index.spec.ts +++ b/packages/vm/test/api/index.spec.ts @@ -1,6 +1,7 @@ // explicitly import util and buffer, // needed for karma-typescript bundling import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { EVM } from '@ethereumjs/evm' import { Account, Address, KECCAK256_RLP } from '@ethereumjs/util' import { Buffer } from 'buffer' import * as tape from 'tape' @@ -59,12 +60,33 @@ tape('VM -> basic instantiation / boolean switches', (t) => { tape('VM -> supportedHardforks', (t) => { t.test('should throw when common is set to an unsupported hardfork', async (st) => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Shanghai }) + const prevSupported = EVM['supportedHardforks'] + EVM['supportedHardforks'] = [ + Hardfork.Chainstart, + Hardfork.Homestead, + Hardfork.Dao, + Hardfork.TangerineWhistle, + Hardfork.SpuriousDragon, + Hardfork.Byzantium, + Hardfork.Constantinople, + Hardfork.Petersburg, + Hardfork.Istanbul, + Hardfork.MuirGlacier, + Hardfork.Berlin, + Hardfork.London, + Hardfork.ArrowGlacier, + Hardfork.GrayGlacier, + Hardfork.MergeForkIdTransition, + Hardfork.Merge, + ] try { await VM.create({ common }) st.fail('should have failed for unsupported hardfork') } catch (e: any) { st.ok(e.message.includes('supportedHardforks')) } + // restore supported hardforks + EVM['supportedHardforks'] = prevSupported st.end() })