diff --git a/.gitignore b/.gitignore index 2ed037f436..4616e4fc08 100644 --- a/.gitignore +++ b/.gitignore @@ -109,5 +109,9 @@ Temporary Items # ethereumjs datadir* +## Vite +stats.html +*bundle*.js + ## Vitest __snapshots__ diff --git a/FUNDING.json b/FUNDING.json deleted file mode 100644 index 19d3e55010..0000000000 --- a/FUNDING.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "drips": { - "ethereum": { - "ownedBy": "0xa6f9685867e15785d7E90523776A6bD8a2989D79" - } - } -} diff --git a/README.md b/README.md index 648e06ecf8..a1345460de 100644 --- a/README.md +++ b/README.md @@ -43,12 +43,13 @@ Below you can find a list of the packages included in this repository. The following are our currently active branches: -| Branch | Release Series | Status |  Description | -| --------------------------------------------------------------------------------------- | -------------- | ------------- | ---------------------------------- | -| [master](https://github.com/ethereumjs/ethereumjs-monorepo) | v7 | `Active` | Current release and working branch | -| [maintenance-v6](https://github.com/ethereumjs/ethereumjs-monorepo/tree/maintenance-v6) | v6 | `Maintenance` | Maintenance for v6 releases | +| Branch | Release Series | Status |  Description | +| --------------------------------------------------------------------------------------- | ---------------------- | ------------- | ---------------------------------------------- | +| [master](https://github.com/ethereumjs/ethereumjs-monorepo) | Upcoming (Autumn 2024) | `Develop` | Breaking release work | +| [maintenance-v8](https://github.com/ethereumjs/ethereumjs-monorepo/tree/maintenance-v8) | v7/v8 | `Maintenance` | Maintenance for v8 releases (v7 also included) | +| [maintenance-v6](https://github.com/ethereumjs/ethereumjs-monorepo/tree/maintenance-v6) | v6 | `Maintenance` | Maintenance for v6 releases | -Breaking releases are done in sync for all libraries, and release cycles are named after the `@ethereumjs/vm` version. In most cases PRs should be opened towards the current working branch. +Breaking releases are mostly done in sync for all libraries (latest exceptions: VM v8, EVM v3), and release cycles are currently named after the `@ethereumjs/vm` version. In most cases PRs should be opened towards the current working branch. If there is no current working branch, please ask! 🙂 To inspect code related to a specific package version, refer to the [tags](https://github.com/ethereumjs/ethereumjs-monorepo/tags). diff --git a/config/tsconfig.json b/config/tsconfig.json index e0f4ed1545..974902f212 100644 --- a/config/tsconfig.json +++ b/config/tsconfig.json @@ -3,8 +3,8 @@ "sourceMap": true, "declaration": true, "declarationMap": true, - "module": "Node16", - "moduleResolution": "node", + "module": "NodeNext", + "moduleResolution": "NodeNext", "emitDecoratorMetadata": true, "experimentalDecorators": true, "esModuleInterop": false, diff --git a/config/tsconfig.prod.cjs.json b/config/tsconfig.prod.cjs.json index fc8520e737..fe0aa2cc49 100644 --- a/config/tsconfig.prod.cjs.json +++ b/config/tsconfig.prod.cjs.json @@ -1,3 +1,7 @@ { - "extends": "./tsconfig.json" + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "Node16", + "moduleResolution": "node" + } } diff --git a/config/tsconfig.prod.esm.json b/config/tsconfig.prod.esm.json index fec8b26791..fc8520e737 100644 --- a/config/tsconfig.prod.esm.json +++ b/config/tsconfig.prod.esm.json @@ -1,6 +1,3 @@ { - "extends": "./tsconfig.json", - "compilerOptions": { - "module": "esnext" - } + "extends": "./tsconfig.json" } diff --git a/package-lock.json b/package-lock.json index 8fb0c5fe17..7d78164924 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16787,7 +16787,8 @@ "version": "4.3.0", "license": "MIT", "dependencies": { - "@ethereumjs/util": "^9.0.3" + "@ethereumjs/util": "^9.0.3", + "ethereum-cryptography": "^2.2.1" }, "devDependencies": { "@polkadot/util": "^12.6.2", diff --git a/packages/block/examples/1559.ts b/packages/block/examples/1559.ts index 7e53606116..a77afbc323 100644 --- a/packages/block/examples/1559.ts +++ b/packages/block/examples/1559.ts @@ -1,8 +1,8 @@ -import { Block } from '@ethereumjs/block' +import { Block, createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) -const block = Block.fromBlockData( +const block = createBlockFromBlockData( { header: { baseFeePerGas: BigInt(10), @@ -19,7 +19,7 @@ console.log(Number(block.header.calcNextBaseFee())) // 11 // So for creating a block with a matching base fee in a certain // chain context you can do: -const blockWithMatchingBaseFee = Block.fromBlockData( +const blockWithMatchingBaseFee = createBlockFromBlockData( { header: { baseFeePerGas: block.header.calcNextBaseFee(), diff --git a/packages/block/examples/4844.ts b/packages/block/examples/4844.ts index 189bead7c8..7039c84189 100644 --- a/packages/block/examples/4844.ts +++ b/packages/block/examples/4844.ts @@ -1,5 +1,5 @@ import { Common, Chain, Hardfork } from '@ethereumjs/common' -import { Block } from '@ethereumjs/block' +import { Block, createBlockFromBlockData } from '@ethereumjs/block' import { BlobEIP4844Transaction } from '@ethereumjs/tx' import { Address } from '@ethereumjs/util' import { loadKZG } from 'kzg-wasm' @@ -20,7 +20,7 @@ const main = async () => { { common } ) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { excessBlobGas: 0n, diff --git a/packages/block/examples/clique.ts b/packages/block/examples/clique.ts index b8e948117d..ba32b58cc1 100644 --- a/packages/block/examples/clique.ts +++ b/packages/block/examples/clique.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { Block, createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' const common = new Common({ chain: Chain.Goerli, hardfork: Hardfork.Chainstart }) @@ -6,5 +6,5 @@ const common = new Common({ chain: Chain.Goerli, hardfork: Hardfork.Chainstart } console.log(common.consensusType()) // 'poa' console.log(common.consensusAlgorithm()) // 'clique' -Block.fromBlockData({ header: { extraData: new Uint8Array(97) } }, { common }) +createBlockFromBlockData({ header: { extraData: new Uint8Array(97) } }, { common }) console.log(`Old Clique Proof-of-Authority block created`) diff --git a/packages/block/examples/pos.ts b/packages/block/examples/pos.ts index 5996471553..1096669d55 100644 --- a/packages/block/examples/pos.ts +++ b/packages/block/examples/pos.ts @@ -1,9 +1,9 @@ -import { Block } from '@ethereumjs/block' +import { Block, createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common } from '@ethereumjs/common' const common = new Common({ chain: Chain.Mainnet }) -const block = Block.fromBlockData( +const block = createBlockFromBlockData( { // Provide your block data here or use default values }, diff --git a/packages/block/examples/pow.ts b/packages/block/examples/pow.ts index cfbce77814..997e47c26c 100644 --- a/packages/block/examples/pow.ts +++ b/packages/block/examples/pow.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { Block, createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) @@ -6,5 +6,5 @@ const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart console.log(common.consensusType()) // 'pow' console.log(common.consensusAlgorithm()) // 'ethash' -Block.fromBlockData({}, { common }) +createBlockFromBlockData({}, { common }) console.log(`Old Proof-of-Work block created`) diff --git a/packages/block/examples/withdrawals.ts b/packages/block/examples/withdrawals.ts index 6f1991a97b..73e5e7dfc1 100644 --- a/packages/block/examples/withdrawals.ts +++ b/packages/block/examples/withdrawals.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { Block, createBlockFromBlockData } from '@ethereumjs/block' import { Common, Chain } from '@ethereumjs/common' import { Address, hexToBytes } from '@ethereumjs/util' import type { WithdrawalData } from '@ethereumjs/util' @@ -12,7 +12,7 @@ const withdrawal = { amount: BigInt(1000), } -const block = Block.fromBlockData( +const block = createBlockFromBlockData( { header: { withdrawalsRoot: hexToBytes( diff --git a/packages/block/package.json b/packages/block/package.json index f883e4259d..4591a64184 100644 --- a/packages/block/package.json +++ b/packages/block/package.json @@ -16,7 +16,8 @@ }, "license": "MPL-2.0", "author": "mjbecze (mb@ethdev.com)", - "type": "commonjs", + "type": "module", + "sideEffects": false, "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "exports": { diff --git a/packages/block/src/block.ts b/packages/block/src/block.ts index 1461a662ea..66bc14e7db 100644 --- a/packages/block/src/block.ts +++ b/packages/block/src/block.ts @@ -1,64 +1,67 @@ import { ConsensusType } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { Trie } from '@ethereumjs/trie' -import { BlobEIP4844Transaction, Capability, TransactionFactory } from '@ethereumjs/tx' +import { BlobEIP4844Transaction, Capability } from '@ethereumjs/tx' import { BIGINT_0, - CLRequestFactory, CLRequestType, - ConsolidationRequest, - DepositRequest, KECCAK256_RLP, KECCAK256_RLP_ARRAY, - Withdrawal, - WithdrawalRequest, - bigIntToHex, bytesToHex, - bytesToUtf8, equalsBytes, - fetchFromProvider, - getProvider, - hexToBytes, - intToHex, - isHexString, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak.js' -import { executionPayloadFromBeaconPayload } from './from-beacon-payload.js' -import { blockFromRpc } from './from-rpc.js' import { BlockHeader } from './header.js' - -import type { BeaconPayloadJson } from './from-beacon-payload.js' +import { genRequestsTrieRoot, genTransactionsTrieRoot, genWithdrawalsTrieRoot } from './helpers.js' + +/* eslint-disable */ +// This is to allow for a proper and linked collection of constructors for the class header. +// For tree shaking/code size this should be no problem since types go away on transpilation. +// TODO: See if there is an easier way to achieve the same result. +// See: https://github.com/microsoft/TypeScript/issues/47558 +// (situation will eventually improve on Typescript and/or Eslint update) import type { - BlockBytes, - BlockData, - BlockOptions, - ExecutionPayload, - ExecutionWitnessBytes, - HeaderData, - JsonBlock, - JsonRpcBlock, - RequestsBytes, - WithdrawalsBytes, -} from './types.js' + createBlockFromBeaconPayloadJson, + createBlockFromBlockData, + createBlockFromExecutionPayload, + createBlockFromJsonRpcProvider, + createBlockFromRLPSerializedBlock, + createBlockFromRPC, + createBlockFromValuesArray, +} from './index.js' +/* eslint-enable */ +import type { BlockBytes, BlockOptions, ExecutionPayload, JsonBlock } from './types.js' import type { Common } from '@ethereumjs/common' import type { FeeMarketEIP1559Transaction, LegacyTransaction, - TxOptions, TypedTransaction, } from '@ethereumjs/tx' import type { CLRequest, - EthersProvider, - PrefixedHexString, - RequestBytes, + ConsolidationRequest, + DepositRequest, VerkleExecutionWitness, - WithdrawalBytes, + Withdrawal, + WithdrawalRequest, } from '@ethereumjs/util' /** - * An object that represents the block. + * Class representing a block in the Ethereum network. The {@link BlockHeader} has its own + * class and can be used independently, for a block it is included in the form of the + * {@link Block.header} property. + * + * A block object can be created with one of the following constructor methods + * (separate from the Block class to allow for tree shaking): + * + * - {@link createBlockFromBlockData } + * - {@link createBlockFromValuesArray } + * - {@link createBlockFromRLPSerializedBlock } + * - {@link createBlockFromRPC } + * - {@link createBlockFromJsonRpcProvider } + * - {@link createBlockFromExecutionPayload } + * - {@link createBlockFromBeaconPayloadJson } */ export class Block { public readonly header: BlockHeader @@ -82,458 +85,11 @@ export class Block { requestsRoot?: Uint8Array } = {} - /** - * Returns the withdrawals trie root for array of Withdrawal. - * @param wts array of Withdrawal to compute the root of - * @param optional emptyTrie to use to generate the root - */ - public static async genWithdrawalsTrieRoot(wts: Withdrawal[], emptyTrie?: Trie) { - const trie = emptyTrie ?? new Trie() - for (const [i, wt] of wts.entries()) { - await trie.put(RLP.encode(i), RLP.encode(wt.raw())) - } - return trie.root() - } - - /** - * Returns the txs trie root for array of TypedTransaction - * @param txs array of TypedTransaction to compute the root of - * @param optional emptyTrie to use to generate the root - */ - public static async genTransactionsTrieRoot(txs: TypedTransaction[], emptyTrie?: Trie) { - const trie = emptyTrie ?? new Trie() - for (const [i, tx] of txs.entries()) { - await trie.put(RLP.encode(i), tx.serialize()) - } - return trie.root() - } - - /** - * Returns the requests trie root for an array of CLRequests - * @param requests - an array of CLRequests - * @param emptyTrie optional empty trie used to generate the root - * @returns a 32 byte Uint8Array representing the requests trie root - */ - public static async genRequestsTrieRoot(requests: CLRequest[], emptyTrie?: Trie) { - // Requests should be sorted in monotonically ascending order based on type - // and whatever internal sorting logic is defined by each request type - if (requests.length > 1) { - for (let x = 1; x < requests.length; x++) { - if (requests[x].type < requests[x - 1].type) - throw new Error('requests are not sorted in ascending order') - } - } - const trie = emptyTrie ?? new Trie() - for (const [i, req] of requests.entries()) { - await trie.put(RLP.encode(i), req.serialize()) - } - return trie.root() - } - - /** - * Static constructor to create a block from a block data dictionary - * - * @param blockData - * @param opts - */ - public static fromBlockData(blockData: BlockData = {}, opts?: BlockOptions) { - const { - header: headerData, - transactions: txsData, - uncleHeaders: uhsData, - withdrawals: withdrawalsData, - executionWitness: executionWitnessData, - requests: clRequests, - } = blockData - - const header = BlockHeader.fromHeaderData(headerData, opts) - - // parse transactions - const transactions = [] - for (const txData of txsData ?? []) { - const tx = TransactionFactory.fromTxData(txData, { - ...opts, - // Use header common in case of setHardfork being activated - common: header.common, - } as TxOptions) - transactions.push(tx) - } - - // parse uncle headers - const uncleHeaders = [] - const uncleOpts: BlockOptions = { - ...opts, - // Use header common in case of setHardfork being activated - common: header.common, - // Disable this option here (all other options carried over), since this overwrites the provided Difficulty to an incorrect value - calcDifficultyFromHeader: undefined, - } - // Uncles are obsolete post-merge, any hardfork by option implies setHardfork - if (opts?.setHardfork !== undefined) { - uncleOpts.setHardfork = true - } - for (const uhData of uhsData ?? []) { - const uh = BlockHeader.fromHeaderData(uhData, uncleOpts) - uncleHeaders.push(uh) - } - - const withdrawals = withdrawalsData?.map(Withdrawal.fromWithdrawalData) - // The witness data is planned to come in rlp serialized bytes so leave this - // stub till that time - const executionWitness = executionWitnessData - - return new Block( - header, - transactions, - uncleHeaders, - withdrawals, - opts, - clRequests, - executionWitness - ) - } - - /** - * Static constructor to create a block from a RLP-serialized block - * - * @param serialized - * @param opts - */ - public static fromRLPSerializedBlock(serialized: Uint8Array, opts?: BlockOptions) { - const values = RLP.decode(Uint8Array.from(serialized)) as BlockBytes - - if (!Array.isArray(values)) { - throw new Error('Invalid serialized block input. Must be array') - } - - return Block.fromValuesArray(values, opts) - } - - /** - * Static constructor to create a block from an array of Bytes values - * - * @param values - * @param opts - */ - public static fromValuesArray(values: BlockBytes, opts?: BlockOptions) { - if (values.length > 5) { - throw new Error( - `invalid block. More values=${values.length} than expected were received (at most 5)` - ) - } - - // First try to load header so that we can use its common (in case of setHardfork being activated) - // to correctly make checks on the hardforks - const [headerData, txsData, uhsData, ...valuesTail] = values - const header = BlockHeader.fromValuesArray(headerData, opts) - - // conditional assignment of rest of values and splicing them out from the valuesTail - const withdrawalBytes = header.common.isActivatedEIP(4895) - ? (valuesTail.splice(0, 1)[0] as WithdrawalsBytes) - : undefined - const requestBytes = header.common.isActivatedEIP(7685) - ? (valuesTail.splice(0, 1)[0] as RequestsBytes) - : undefined - // if witness bytes are not present that we should assume that witness has not been provided - // in that scenario pass null as undefined is used for default witness assignment - const executionWitnessBytes = header.common.isActivatedEIP(6800) - ? (valuesTail.splice(0, 1)[0] as ExecutionWitnessBytes) - : null - - if ( - header.common.isActivatedEIP(4895) && - (withdrawalBytes === undefined || !Array.isArray(withdrawalBytes)) - ) { - throw new Error( - 'Invalid serialized block input: EIP-4895 is active, and no withdrawals were provided as array' - ) - } - - if ( - header.common.isActivatedEIP(7685) && - (requestBytes === undefined || !Array.isArray(requestBytes)) - ) { - throw new Error( - 'Invalid serialized block input: EIP-7685 is active, and no requestBytes were provided as array' - ) - } - - if (header.common.isActivatedEIP(6800) && executionWitnessBytes === undefined) { - throw new Error( - 'Invalid serialized block input: EIP-6800 is active, and execution witness is undefined' - ) - } - - // parse transactions - const transactions = [] - for (const txData of txsData ?? []) { - transactions.push( - TransactionFactory.fromBlockBodyData(txData, { - ...opts, - // Use header common in case of setHardfork being activated - common: header.common, - }) - ) - } - - // parse uncle headers - const uncleHeaders = [] - const uncleOpts: BlockOptions = { - ...opts, - // Use header common in case of setHardfork being activated - common: header.common, - // Disable this option here (all other options carried over), since this overwrites the provided Difficulty to an incorrect value - calcDifficultyFromHeader: undefined, - } - // Uncles are obsolete post-merge, any hardfork by option implies setHardfork - if (opts?.setHardfork !== undefined) { - uncleOpts.setHardfork = true - } - for (const uncleHeaderData of uhsData ?? []) { - uncleHeaders.push(BlockHeader.fromValuesArray(uncleHeaderData, uncleOpts)) - } - - const withdrawals = (withdrawalBytes as WithdrawalBytes[]) - ?.map(([index, validatorIndex, address, amount]) => ({ - index, - validatorIndex, - address, - amount, - })) - ?.map(Withdrawal.fromWithdrawalData) - - let requests - if (header.common.isActivatedEIP(7685)) { - requests = (requestBytes as RequestBytes[]).map((bytes) => - CLRequestFactory.fromSerializedRequest(bytes) - ) - } - // executionWitness are not part of the EL fetched blocks via eth_ bodies method - // they are currently only available via the engine api constructed blocks - let executionWitness - if (header.common.isActivatedEIP(6800)) { - if (executionWitnessBytes !== undefined) { - executionWitness = JSON.parse(bytesToUtf8(RLP.decode(executionWitnessBytes) as Uint8Array)) - } else if (opts?.executionWitness !== undefined) { - executionWitness = opts.executionWitness - } else { - // don't assign default witness if eip 6800 is implemented as it leads to incorrect - // assumptions while executing the block. if not present in input implies its unavailable - executionWitness = null - } - } - - return new Block( - header, - transactions, - uncleHeaders, - withdrawals, - opts, - requests, - executionWitness - ) - } - - /** - * Creates a new block object from Ethereum JSON RPC. - * - * @param blockParams - Ethereum JSON RPC of block (eth_getBlockByNumber) - * @param uncles - Optional list of Ethereum JSON RPC of uncles (eth_getUncleByBlockHashAndIndex) - * @param opts - An object describing the blockchain - */ - public static fromRPC(blockData: JsonRpcBlock, uncles?: any[], opts?: BlockOptions) { - return blockFromRpc(blockData, uncles, opts) - } - - /** - * Method to retrieve a block from a JSON-RPC provider and format as a {@link Block} - * @param provider either a url for a remote provider or an Ethers JsonRpcProvider object - * @param blockTag block hash or block number to be run - * @param opts {@link BlockOptions} - * @returns the block specified by `blockTag` - */ - public static fromJsonRpcProvider = async ( - provider: string | EthersProvider, - blockTag: string | bigint, - opts: BlockOptions - ) => { - let blockData - const providerUrl = getProvider(provider) - - if (typeof blockTag === 'string' && blockTag.length === 66) { - blockData = await fetchFromProvider(providerUrl, { - method: 'eth_getBlockByHash', - params: [blockTag, true], - }) - } else if (typeof blockTag === 'bigint') { - blockData = await fetchFromProvider(providerUrl, { - method: 'eth_getBlockByNumber', - params: [bigIntToHex(blockTag), true], - }) - } else if ( - isHexString(blockTag) || - blockTag === 'latest' || - blockTag === 'earliest' || - blockTag === 'pending' || - blockTag === 'finalized' || - blockTag === 'safe' - ) { - blockData = await fetchFromProvider(providerUrl, { - method: 'eth_getBlockByNumber', - params: [blockTag, true], - }) - } else { - throw new Error( - `expected blockTag to be block hash, bigint, hex prefixed string, or earliest/latest/pending; got ${blockTag}` - ) - } - - if (blockData === null) { - throw new Error('No block data returned from provider') - } - - const uncleHeaders = [] - if (blockData.uncles.length > 0) { - for (let x = 0; x < blockData.uncles.length; x++) { - const headerData = await fetchFromProvider(providerUrl, { - method: 'eth_getUncleByBlockHashAndIndex', - params: [blockData.hash, intToHex(x)], - }) - uncleHeaders.push(headerData) - } - } - - return blockFromRpc(blockData, uncleHeaders, opts) - } - - /** - * Method to retrieve a block from an execution payload - * @param execution payload constructed from beacon payload - * @param opts {@link BlockOptions} - * @returns the block constructed block - */ - public static async fromExecutionPayload( - payload: ExecutionPayload, - opts?: BlockOptions - ): Promise { - const { - blockNumber: number, - receiptsRoot: receiptTrie, - prevRandao: mixHash, - feeRecipient: coinbase, - transactions, - withdrawals: withdrawalsData, - depositRequests, - withdrawalRequests, - consolidationRequests, - executionWitness, - } = payload - - const txs = [] - for (const [index, serializedTx] of transactions.entries()) { - try { - const tx = TransactionFactory.fromSerializedData( - hexToBytes(serializedTx as PrefixedHexString), - { - common: opts?.common, - } - ) - txs.push(tx) - } catch (error) { - const validationError = `Invalid tx at index ${index}: ${error}` - throw validationError - } - } - - const transactionsTrie = await Block.genTransactionsTrieRoot( - txs, - new Trie({ common: opts?.common }) - ) - const withdrawals = withdrawalsData?.map((wData) => Withdrawal.fromWithdrawalData(wData)) - const withdrawalsRoot = withdrawals - ? await Block.genWithdrawalsTrieRoot(withdrawals, new Trie({ common: opts?.common })) - : undefined - - const hasDepositRequests = depositRequests !== undefined && depositRequests !== null - const hasWithdrawalRequests = withdrawalRequests !== undefined && withdrawalRequests !== null - const hasConsolidationRequests = - consolidationRequests !== undefined && consolidationRequests !== null - - const requests = - hasDepositRequests || hasWithdrawalRequests || hasConsolidationRequests - ? ([] as CLRequest[]) - : undefined - - if (depositRequests !== undefined && depositRequests !== null) { - for (const dJson of depositRequests) { - requests!.push(DepositRequest.fromJSON(dJson)) - } - } - if (withdrawalRequests !== undefined && withdrawalRequests !== null) { - for (const wJson of withdrawalRequests) { - requests!.push(WithdrawalRequest.fromJSON(wJson)) - } - } - if (consolidationRequests !== undefined && consolidationRequests !== null) { - for (const cJson of consolidationRequests) { - requests!.push(ConsolidationRequest.fromJSON(cJson)) - } - } - - const requestsRoot = requests - ? await Block.genRequestsTrieRoot(requests, new Trie({ common: opts?.common })) - : undefined - - const header: HeaderData = { - ...payload, - number, - receiptTrie, - transactionsTrie, - withdrawalsRoot, - mixHash, - coinbase, - requestsRoot, - } - - // we are not setting setHardfork as common is already set to the correct hf - const block = Block.fromBlockData( - { header, transactions: txs, withdrawals, executionWitness, requests }, - opts - ) - if ( - block.common.isActivatedEIP(6800) && - (executionWitness === undefined || executionWitness === null) - ) { - throw Error('Missing executionWitness for EIP-6800 activated executionPayload') - } - // Verify blockHash matches payload - if (!equalsBytes(block.hash(), hexToBytes(payload.blockHash as PrefixedHexString))) { - const validationError = `Invalid blockHash, expected: ${ - payload.blockHash - }, received: ${bytesToHex(block.hash())}` - throw Error(validationError) - } - - return block - } - - /** - * Method to retrieve a block from a beacon payload json - * @param payload json of a beacon beacon fetched from beacon apis - * @param opts {@link BlockOptions} - * @returns the block constructed block - */ - public static async fromBeaconPayloadJson( - payload: BeaconPayloadJson, - opts?: BlockOptions - ): Promise { - const executionPayload = executionPayloadFromBeaconPayload(payload) - return Block.fromExecutionPayload(executionPayload, opts) - } - /** * This constructor takes the values, validates them, assigns them and freezes the object. - * Use the static factory methods to assist in creating a Block object from varying data types and options. + * + * @deprecated Use the static factory methods (see {@link Block} for an overview) to assist in creating + * a Block object from varying data types and options. */ constructor( header?: BlockHeader, @@ -673,7 +229,7 @@ export class Block { * Generates transaction trie for validation. */ async genTxTrie(): Promise { - return Block.genTransactionsTrieRoot(this.transactions, new Trie({ common: this.common })) + return genTransactionsTrieRoot(this.transactions, new Trie({ common: this.common })) } /** @@ -708,11 +264,11 @@ export class Block { if (requestsInput === undefined) { if (this.cache.requestsRoot === undefined) { - this.cache.requestsRoot = await Block.genRequestsTrieRoot(this.requests!) + this.cache.requestsRoot = await genRequestsTrieRoot(this.requests!) } return equalsBytes(this.cache.requestsRoot, this.header.requestsRoot!) } else { - const reportedRoot = await Block.genRequestsTrieRoot(requests) + const reportedRoot = await genRequestsTrieRoot(requests) return equalsBytes(reportedRoot, this.header.requestsRoot!) } } @@ -916,7 +472,7 @@ export class Block { } if (this.cache.withdrawalsTrieRoot === undefined) { - this.cache.withdrawalsTrieRoot = await Block.genWithdrawalsTrieRoot( + this.cache.withdrawalsTrieRoot = await genWithdrawalsTrieRoot( this.withdrawals!, new Trie({ common: this.common }) ) diff --git a/packages/block/src/constructors.ts b/packages/block/src/constructors.ts new file mode 100644 index 0000000000..f06fbc359e --- /dev/null +++ b/packages/block/src/constructors.ts @@ -0,0 +1,442 @@ +import { RLP } from '@ethereumjs/rlp' +import { Trie } from '@ethereumjs/trie' +import { TransactionFactory } from '@ethereumjs/tx' +import { + CLRequestFactory, + ConsolidationRequest, + DepositRequest, + Withdrawal, + WithdrawalRequest, + bigIntToHex, + bytesToHex, + bytesToUtf8, + equalsBytes, + fetchFromProvider, + getProvider, + hexToBytes, + intToHex, + isHexString, +} from '@ethereumjs/util' + +import { createBlockFromRpc } from './from-rpc.js' +import { genRequestsTrieRoot, genTransactionsTrieRoot, genWithdrawalsTrieRoot } from './helpers.js' + +import { Block, BlockHeader, executionPayloadFromBeaconPayload } from './index.js' + +import type { BeaconPayloadJson } from './from-beacon-payload.js' +import type { + BlockBytes, + BlockData, + BlockOptions, + ExecutionPayload, + ExecutionWitnessBytes, + HeaderData, + JsonRpcBlock, + RequestsBytes, + WithdrawalsBytes, +} from './types.js' +import type { TxOptions } from '@ethereumjs/tx' +import type { + CLRequest, + CLRequestType, + EthersProvider, + PrefixedHexString, + RequestBytes, + WithdrawalBytes, +} from '@ethereumjs/util' + +/** + * Static constructor to create a block from a block data dictionary + * + * @param blockData + * @param opts + */ +export function createBlockFromBlockData(blockData: BlockData = {}, opts?: BlockOptions) { + const { + header: headerData, + transactions: txsData, + uncleHeaders: uhsData, + withdrawals: withdrawalsData, + executionWitness: executionWitnessData, + requests: clRequests, + } = blockData + + const header = BlockHeader.fromHeaderData(headerData, opts) + + // parse transactions + const transactions = [] + for (const txData of txsData ?? []) { + const tx = TransactionFactory.fromTxData(txData, { + ...opts, + // Use header common in case of setHardfork being activated + common: header.common, + } as TxOptions) + transactions.push(tx) + } + + // parse uncle headers + const uncleHeaders = [] + const uncleOpts: BlockOptions = { + ...opts, + // Use header common in case of setHardfork being activated + common: header.common, + // Disable this option here (all other options carried over), since this overwrites the provided Difficulty to an incorrect value + calcDifficultyFromHeader: undefined, + } + // Uncles are obsolete post-merge, any hardfork by option implies setHardfork + if (opts?.setHardfork !== undefined) { + uncleOpts.setHardfork = true + } + for (const uhData of uhsData ?? []) { + const uh = BlockHeader.fromHeaderData(uhData, uncleOpts) + uncleHeaders.push(uh) + } + + const withdrawals = withdrawalsData?.map(Withdrawal.fromWithdrawalData) + // The witness data is planned to come in rlp serialized bytes so leave this + // stub till that time + const executionWitness = executionWitnessData + + return new Block( + header, + transactions, + uncleHeaders, + withdrawals, + opts, + clRequests, + executionWitness + ) +} + +/** + * Static constructor to create a block from an array of Bytes values + * + * @param values + * @param opts + */ +export function createBlockFromValuesArray(values: BlockBytes, opts?: BlockOptions) { + if (values.length > 5) { + throw new Error(`invalid More values=${values.length} than expected were received (at most 5)`) + } + + // First try to load header so that we can use its common (in case of setHardfork being activated) + // to correctly make checks on the hardforks + const [headerData, txsData, uhsData, ...valuesTail] = values + const header = BlockHeader.fromValuesArray(headerData, opts) + + // conditional assignment of rest of values and splicing them out from the valuesTail + const withdrawalBytes = header.common.isActivatedEIP(4895) + ? (valuesTail.splice(0, 1)[0] as WithdrawalsBytes) + : undefined + const requestBytes = header.common.isActivatedEIP(7685) + ? (valuesTail.splice(0, 1)[0] as RequestsBytes) + : undefined + // if witness bytes are not present that we should assume that witness has not been provided + // in that scenario pass null as undefined is used for default witness assignment + const executionWitnessBytes = header.common.isActivatedEIP(6800) + ? (valuesTail.splice(0, 1)[0] as ExecutionWitnessBytes) + : null + + if ( + header.common.isActivatedEIP(4895) && + (withdrawalBytes === undefined || !Array.isArray(withdrawalBytes)) + ) { + throw new Error( + 'Invalid serialized block input: EIP-4895 is active, and no withdrawals were provided as array' + ) + } + + if ( + header.common.isActivatedEIP(7685) && + (requestBytes === undefined || !Array.isArray(requestBytes)) + ) { + throw new Error( + 'Invalid serialized block input: EIP-7685 is active, and no requestBytes were provided as array' + ) + } + + if (header.common.isActivatedEIP(6800) && executionWitnessBytes === undefined) { + throw new Error( + 'Invalid serialized block input: EIP-6800 is active, and execution witness is undefined' + ) + } + + // parse transactions + const transactions = [] + for (const txData of txsData ?? []) { + transactions.push( + TransactionFactory.fromBlockBodyData(txData, { + ...opts, + // Use header common in case of setHardfork being activated + common: header.common, + }) + ) + } + + // parse uncle headers + const uncleHeaders = [] + const uncleOpts: BlockOptions = { + ...opts, + // Use header common in case of setHardfork being activated + common: header.common, + // Disable this option here (all other options carried over), since this overwrites the provided Difficulty to an incorrect value + calcDifficultyFromHeader: undefined, + } + // Uncles are obsolete post-merge, any hardfork by option implies setHardfork + if (opts?.setHardfork !== undefined) { + uncleOpts.setHardfork = true + } + for (const uncleHeaderData of uhsData ?? []) { + uncleHeaders.push(BlockHeader.fromValuesArray(uncleHeaderData, uncleOpts)) + } + + const withdrawals = (withdrawalBytes as WithdrawalBytes[]) + ?.map(([index, validatorIndex, address, amount]) => ({ + index, + validatorIndex, + address, + amount, + })) + ?.map(Withdrawal.fromWithdrawalData) + + let requests + if (header.common.isActivatedEIP(7685)) { + requests = (requestBytes as RequestBytes[]).map((bytes) => + CLRequestFactory.fromSerializedRequest(bytes) + ) + } + // executionWitness are not part of the EL fetched blocks via eth_ bodies method + // they are currently only available via the engine api constructed blocks + let executionWitness + if (header.common.isActivatedEIP(6800)) { + if (executionWitnessBytes !== undefined) { + executionWitness = JSON.parse(bytesToUtf8(RLP.decode(executionWitnessBytes) as Uint8Array)) + } else if (opts?.executionWitness !== undefined) { + executionWitness = opts.executionWitness + } else { + // don't assign default witness if eip 6800 is implemented as it leads to incorrect + // assumptions while executing the if not present in input implies its unavailable + executionWitness = null + } + } + + return new Block( + header, + transactions, + uncleHeaders, + withdrawals, + opts, + requests, + executionWitness + ) +} + +/** + * Static constructor to create a block from a RLP-serialized block + * + * @param serialized + * @param opts + */ +export function createBlockFromRLPSerializedBlock(serialized: Uint8Array, opts?: BlockOptions) { + const values = RLP.decode(Uint8Array.from(serialized)) as BlockBytes + + if (!Array.isArray(values)) { + throw new Error('Invalid serialized block input. Must be array') + } + + return createBlockFromValuesArray(values, opts) +} + +/** + * Creates a new block object from Ethereum JSON RPC. + * + * @param blockParams - Ethereum JSON RPC of block (eth_getBlockByNumber) + * @param uncles - Optional list of Ethereum JSON RPC of uncles (eth_getUncleByBlockHashAndIndex) + * @param opts - An object describing the blockchain + */ +export function createBlockFromRPC(blockData: JsonRpcBlock, uncles?: any[], opts?: BlockOptions) { + return createBlockFromRpc(blockData, uncles, opts) +} + +/** + * Method to retrieve a block from a JSON-RPC provider and format as a {@link Block} + * @param provider either a url for a remote provider or an Ethers JsonRpcProvider object + * @param blockTag block hash or block number to be run + * @param opts {@link BlockOptions} + * @returns the block specified by `blockTag` + */ +export const createBlockFromJsonRpcProvider = async ( + provider: string | EthersProvider, + blockTag: string | bigint, + opts: BlockOptions +) => { + let blockData + const providerUrl = getProvider(provider) + + if (typeof blockTag === 'string' && blockTag.length === 66) { + blockData = await fetchFromProvider(providerUrl, { + method: 'eth_getBlockByHash', + params: [blockTag, true], + }) + } else if (typeof blockTag === 'bigint') { + blockData = await fetchFromProvider(providerUrl, { + method: 'eth_getBlockByNumber', + params: [bigIntToHex(blockTag), true], + }) + } else if ( + isHexString(blockTag) || + blockTag === 'latest' || + blockTag === 'earliest' || + blockTag === 'pending' || + blockTag === 'finalized' || + blockTag === 'safe' + ) { + blockData = await fetchFromProvider(providerUrl, { + method: 'eth_getBlockByNumber', + params: [blockTag, true], + }) + } else { + throw new Error( + `expected blockTag to be block hash, bigint, hex prefixed string, or earliest/latest/pending; got ${blockTag}` + ) + } + + if (blockData === null) { + throw new Error('No block data returned from provider') + } + + const uncleHeaders = [] + if (blockData.uncles.length > 0) { + for (let x = 0; x < blockData.uncles.length; x++) { + const headerData = await fetchFromProvider(providerUrl, { + method: 'eth_getUncleByBlockHashAndIndex', + params: [blockData.hash, intToHex(x)], + }) + uncleHeaders.push(headerData) + } + } + + return createBlockFromRpc(blockData, uncleHeaders, opts) +} + +/** + * Method to retrieve a block from an execution payload + * @param execution payload constructed from beacon payload + * @param opts {@link BlockOptions} + * @returns the block constructed block + */ +export async function createBlockFromExecutionPayload( + payload: ExecutionPayload, + opts?: BlockOptions +): Promise { + const { + blockNumber: number, + receiptsRoot: receiptTrie, + prevRandao: mixHash, + feeRecipient: coinbase, + transactions, + withdrawals: withdrawalsData, + depositRequests, + withdrawalRequests, + consolidationRequests, + executionWitness, + } = payload + + const txs = [] + for (const [index, serializedTx] of transactions.entries()) { + try { + const tx = TransactionFactory.fromSerializedData( + hexToBytes(serializedTx as PrefixedHexString), + { + common: opts?.common, + } + ) + txs.push(tx) + } catch (error) { + const validationError = `Invalid tx at index ${index}: ${error}` + throw validationError + } + } + + const transactionsTrie = await genTransactionsTrieRoot(txs, new Trie({ common: opts?.common })) + const withdrawals = withdrawalsData?.map((wData) => Withdrawal.fromWithdrawalData(wData)) + const withdrawalsRoot = withdrawals + ? await genWithdrawalsTrieRoot(withdrawals, new Trie({ common: opts?.common })) + : undefined + + const hasDepositRequests = depositRequests !== undefined && depositRequests !== null + const hasWithdrawalRequests = withdrawalRequests !== undefined && withdrawalRequests !== null + const hasConsolidationRequests = + consolidationRequests !== undefined && consolidationRequests !== null + + const requests = + hasDepositRequests || hasWithdrawalRequests || hasConsolidationRequests + ? ([] as CLRequest[]) + : undefined + + if (depositRequests !== undefined && depositRequests !== null) { + for (const dJson of depositRequests) { + requests!.push(DepositRequest.fromJSON(dJson)) + } + } + if (withdrawalRequests !== undefined && withdrawalRequests !== null) { + for (const wJson of withdrawalRequests) { + requests!.push(WithdrawalRequest.fromJSON(wJson)) + } + } + if (consolidationRequests !== undefined && consolidationRequests !== null) { + for (const cJson of consolidationRequests) { + requests!.push(ConsolidationRequest.fromJSON(cJson)) + } + } + + const requestsRoot = requests + ? await genRequestsTrieRoot(requests, new Trie({ common: opts?.common })) + : undefined + + const header: HeaderData = { + ...payload, + number, + receiptTrie, + transactionsTrie, + withdrawalsRoot, + mixHash, + coinbase, + requestsRoot, + } + + // we are not setting setHardfork as common is already set to the correct hf + const block = createBlockFromBlockData( + { header, transactions: txs, withdrawals, executionWitness, requests }, + opts + ) + if ( + block.common.isActivatedEIP(6800) && + (executionWitness === undefined || executionWitness === null) + ) { + throw Error('Missing executionWitness for EIP-6800 activated executionPayload') + } + // Verify blockHash matches payload + if (!equalsBytes(block.hash(), hexToBytes(payload.blockHash as PrefixedHexString))) { + const validationError = `Invalid blockHash, expected: ${ + payload.blockHash + }, received: ${bytesToHex(block.hash())}` + throw Error(validationError) + } + + return block +} + +/** + * Method to retrieve a block from a beacon payload json + * @param payload json of a beacon beacon fetched from beacon apis + * @param opts {@link BlockOptions} + * @returns the block constructed block + */ +export async function createBlockFromBeaconPayloadJson( + payload: BeaconPayloadJson, + opts?: BlockOptions +): Promise { + const executionPayload = executionPayloadFromBeaconPayload(payload) + return createBlockFromExecutionPayload(executionPayload, opts) +} diff --git a/packages/block/src/from-rpc.ts b/packages/block/src/from-rpc.ts index c8646ebc28..20ec9cfc5c 100644 --- a/packages/block/src/from-rpc.ts +++ b/packages/block/src/from-rpc.ts @@ -8,10 +8,9 @@ import { toType, } from '@ethereumjs/util' +import { createBlockFromBlockData } from './constructors.js' import { blockHeaderFromRpc } from './header-from-rpc.js' -import { Block } from './index.js' - import type { BlockOptions, JsonRpcBlock } from './index.js' import type { TypedTransaction } from '@ethereumjs/tx' import type { PrefixedHexString } from '@ethereumjs/util' @@ -45,7 +44,7 @@ function normalizeTxParams(_txParams: any) { * @param options - An object describing the blockchain * @deprecated */ -export function blockFromRpc( +export function createBlockFromRpc( blockParams: JsonRpcBlock, uncles: any[] = [], options?: BlockOptions @@ -66,7 +65,7 @@ export function blockFromRpc( const bytes = hexToBytes(req as PrefixedHexString) return CLRequestFactory.fromSerializedRequest(bytes) }) - return Block.fromBlockData( + return createBlockFromBlockData( { header, transactions, uncleHeaders, withdrawals: blockParams.withdrawals, requests }, options ) diff --git a/packages/block/src/helpers.ts b/packages/block/src/helpers.ts index 3668794321..145d051bb7 100644 --- a/packages/block/src/helpers.ts +++ b/packages/block/src/helpers.ts @@ -1,9 +1,11 @@ +import { RLP } from '@ethereumjs/rlp' +import { Trie } from '@ethereumjs/trie' import { BlobEIP4844Transaction } from '@ethereumjs/tx' import { BIGINT_0, BIGINT_1, TypeOutput, isHexString, toType } from '@ethereumjs/util' import type { BlockHeaderBytes, HeaderData } from './types.js' import type { TypedTransaction } from '@ethereumjs/tx' -import type { PrefixedHexString } from '@ethereumjs/util' +import type { CLRequest, CLRequestType, PrefixedHexString, Withdrawal } from '@ethereumjs/util' /** * Returns a 0x-prefixed hex number string from a hex string or string integer. @@ -116,3 +118,51 @@ export const fakeExponential = (factor: bigint, numerator: bigint, denominator: return output / denominator } + +/** + * Returns the withdrawals trie root for array of Withdrawal. + * @param wts array of Withdrawal to compute the root of + * @param optional emptyTrie to use to generate the root + */ +export async function genWithdrawalsTrieRoot(wts: Withdrawal[], emptyTrie?: Trie) { + const trie = emptyTrie ?? new Trie() + for (const [i, wt] of wts.entries()) { + await trie.put(RLP.encode(i), RLP.encode(wt.raw())) + } + return trie.root() +} + +/** + * Returns the txs trie root for array of TypedTransaction + * @param txs array of TypedTransaction to compute the root of + * @param optional emptyTrie to use to generate the root + */ +export async function genTransactionsTrieRoot(txs: TypedTransaction[], emptyTrie?: Trie) { + const trie = emptyTrie ?? new Trie() + for (const [i, tx] of txs.entries()) { + await trie.put(RLP.encode(i), tx.serialize()) + } + return trie.root() +} + +/** + * Returns the requests trie root for an array of CLRequests + * @param requests - an array of CLRequests + * @param emptyTrie optional empty trie used to generate the root + * @returns a 32 byte Uint8Array representing the requests trie root + */ +export async function genRequestsTrieRoot(requests: CLRequest[], emptyTrie?: Trie) { + // Requests should be sorted in monotonically ascending order based on type + // and whatever internal sorting logic is defined by each request type + if (requests.length > 1) { + for (let x = 1; x < requests.length; x++) { + if (requests[x].type < requests[x - 1].type) + throw new Error('requests are not sorted in ascending order') + } + } + const trie = emptyTrie ?? new Trie() + for (const [i, req] of requests.entries()) { + await trie.put(RLP.encode(i), req.serialize()) + } + return trie.root() +} diff --git a/packages/block/src/index.ts b/packages/block/src/index.ts index f6c5da85a1..a4e2bacd4d 100644 --- a/packages/block/src/index.ts +++ b/packages/block/src/index.ts @@ -1,5 +1,12 @@ export { Block } from './block.js' +export * from './constructors.js' export { type BeaconPayloadJson, executionPayloadFromBeaconPayload } from './from-beacon-payload.js' export { BlockHeader } from './header.js' -export { getDifficulty, valuesArrayToHeaderData } from './helpers.js' +export { + genRequestsTrieRoot, + genTransactionsTrieRoot, + genWithdrawalsTrieRoot, + getDifficulty, + valuesArrayToHeaderData, +} from './helpers.js' export * from './types.js' diff --git a/packages/block/test/block.spec.ts b/packages/block/test/block.spec.ts index 258c45859e..8bccdd1d63 100644 --- a/packages/block/test/block.spec.ts +++ b/packages/block/test/block.spec.ts @@ -11,8 +11,13 @@ import { } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { blockFromRpc } from '../src/from-rpc.js' -import { Block } from '../src/index.js' +import { + createBlockFromBlockData, + createBlockFromRLPSerializedBlock, + createBlockFromValuesArray, +} from '../src/constructors.js' +import { createBlockFromRpc } from '../src/from-rpc.js' +import { genTransactionsTrieRoot } from '../src/helpers.js' import * as testDataGenesis from './testdata/genesishashestest.json' import * as testDataFromRpcGoerli from './testdata/testdata-from-rpc-goerli.json' @@ -20,32 +25,32 @@ import * as testDataPreLondon2 from './testdata/testdata_pre-london-2.json' import * as testDataPreLondon from './testdata/testdata_pre-london.json' import * as testnetMerge from './testdata/testnetMerge.json' -import type { BlockBytes, JsonRpcBlock } from '../src/index.js' +import type { Block, BlockBytes, JsonRpcBlock } from '../src/index.js' import type { ChainConfig } from '@ethereumjs/common' import type { NestedUint8Array, PrefixedHexString } from '@ethereumjs/util' describe('[Block]: block functions', () => { it('should test block initialization', () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - const genesis = Block.fromBlockData({}, { common }) + const genesis = createBlockFromBlockData({}, { common }) assert.ok(bytesToHex(genesis.hash()), 'block should initialize') // test default freeze values // also test if the options are carried over to the constructor - let block = Block.fromBlockData({}) + let block = createBlockFromBlockData({}) assert.ok(Object.isFrozen(block), 'block should be frozen by default') - block = Block.fromBlockData({}, { freeze: false }) + block = createBlockFromBlockData({}, { freeze: false }) assert.ok( !Object.isFrozen(block), 'block should not be frozen when freeze deactivated in options' ) const rlpBlock = block.serialize() - block = Block.fromRLPSerializedBlock(rlpBlock) + block = createBlockFromRLPSerializedBlock(rlpBlock) assert.ok(Object.isFrozen(block), 'block should be frozen by default') - block = Block.fromRLPSerializedBlock(rlpBlock, { freeze: false }) + block = createBlockFromRLPSerializedBlock(rlpBlock, { freeze: false }) assert.ok( !Object.isFrozen(block), 'block should not be frozen when freeze deactivated in options' @@ -68,10 +73,10 @@ describe('[Block]: block functions', () => { const valuesArray = [headerArray, [], []] - block = Block.fromValuesArray(valuesArray, { common }) + block = createBlockFromValuesArray(valuesArray, { common }) assert.ok(Object.isFrozen(block), 'block should be frozen by default') - block = Block.fromValuesArray(valuesArray, { common, freeze: false }) + block = createBlockFromValuesArray(valuesArray, { common, freeze: false }) assert.ok( !Object.isFrozen(block), 'block should not be frozen when freeze deactivated in options' @@ -86,7 +91,7 @@ describe('[Block]: block functions', () => { customChains: customChains as ChainConfig[], }) - let block = Block.fromBlockData( + let block = createBlockFromBlockData( { header: { number: 12, // Berlin block @@ -97,7 +102,7 @@ describe('[Block]: block functions', () => { ) assert.equal(block.common.hardfork(), Hardfork.Berlin, 'should use setHardfork option') - block = Block.fromBlockData( + block = createBlockFromBlockData( { header: { number: 20, // Future block @@ -111,7 +116,7 @@ describe('[Block]: block functions', () => { 'should use setHardfork option (td > threshold)' ) - block = Block.fromBlockData( + block = createBlockFromBlockData( { header: { number: 12, // Berlin block, @@ -129,7 +134,7 @@ describe('[Block]: block functions', () => { it('should initialize with undefined parameters without throwing', () => { assert.doesNotThrow(function () { - Block.fromBlockData() + createBlockFromBlockData() }) }) @@ -137,18 +142,18 @@ describe('[Block]: block functions', () => { const common = new Common({ chain: Chain.Goerli }) const opts = { common } assert.doesNotThrow(function () { - Block.fromBlockData({}, opts) + createBlockFromBlockData({}, opts) }) }) it('should throw when trying to initialize with uncle headers on a PoA network', () => { const common = new Common({ chain: Chain.Mainnet }) - const uncleBlock = Block.fromBlockData( + const uncleBlock = createBlockFromBlockData( { header: { extraData: new Uint8Array(117) } }, { common } ) assert.throws(function () { - Block.fromBlockData({ uncleHeaders: [uncleBlock.header] }, { common }) + createBlockFromBlockData({ uncleHeaders: [uncleBlock.header] }, { common }) }) }) @@ -156,7 +161,7 @@ describe('[Block]: block functions', () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) const blockRlp = hexToBytes(testDataPreLondon.blocks[0].rlp as PrefixedHexString) try { - Block.fromRLPSerializedBlock(blockRlp, { common }) + createBlockFromRLPSerializedBlock(blockRlp, { common }) assert.ok(true, 'should pass') } catch (error: any) { assert.fail('should not throw') @@ -167,7 +172,7 @@ describe('[Block]: block functions', () => { const common = new Common({ chain: Chain.Goerli, hardfork: Hardfork.Chainstart }) try { - blockFromRpc(testDataFromRpcGoerli as JsonRpcBlock, [], { common }) + createBlockFromRpc(testDataFromRpcGoerli as JsonRpcBlock, [], { common }) assert.ok(true, 'does not throw') } catch (error: any) { assert.fail('error thrown') @@ -182,7 +187,7 @@ describe('[Block]: block functions', () => { it('should test transaction validation - invalid tx trie', async () => { const blockRlp = hexToBytes(testDataPreLondon.blocks[0].rlp as PrefixedHexString) const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) - const block = Block.fromRLPSerializedBlock(blockRlp, { common, freeze: false }) + const block = createBlockFromRLPSerializedBlock(blockRlp, { common, freeze: false }) await testTransactionValidation(block) ;(block.header as any).transactionsTrie = new Uint8Array(32) try { @@ -198,9 +203,9 @@ describe('[Block]: block functions', () => { gasLimit: 53000, gasPrice: 7, }) - const blockTest = Block.fromBlockData({ transactions: [tx] }) + const blockTest = createBlockFromBlockData({ transactions: [tx] }) const txTrie = await blockTest.genTxTrie() - const block = Block.fromBlockData({ + const block = createBlockFromBlockData({ header: { transactionsTrie: txTrie, }, @@ -215,14 +220,14 @@ describe('[Block]: block functions', () => { }) it('should test transaction validation with empty transaction list', async () => { - const block = Block.fromBlockData({}) + const block = createBlockFromBlockData({}) await testTransactionValidation(block) }) it('should test transaction validation with legacy tx in london', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) const blockRlp = hexToBytes(testDataPreLondon.blocks[0].rlp as PrefixedHexString) - const block = Block.fromRLPSerializedBlock(blockRlp, { common, freeze: false }) + const block = createBlockFromRLPSerializedBlock(blockRlp, { common, freeze: false }) await testTransactionValidation(block) ;(block.transactions[0] as any).gasPrice = BigInt(0) const result = block.getTransactionsValidationErrors() @@ -235,7 +240,7 @@ describe('[Block]: block functions', () => { it('should test uncles hash validation', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) const blockRlp = hexToBytes(testDataPreLondon2.blocks[2].rlp as PrefixedHexString) - const block = Block.fromRLPSerializedBlock(blockRlp, { common, freeze: false }) + const block = createBlockFromRLPSerializedBlock(blockRlp, { common, freeze: false }) assert.equal(block.uncleHashIsValid(), true) ;(block.header as any).uncleHash = new Uint8Array(32) try { @@ -248,9 +253,9 @@ describe('[Block]: block functions', () => { it('should test data integrity', async () => { const unsignedTx = LegacyTransaction.fromTxData({}) - const txRoot = await Block.genTransactionsTrieRoot([unsignedTx]) + const txRoot = await genTransactionsTrieRoot([unsignedTx]) - let block = Block.fromBlockData({ + let block = createBlockFromBlockData({ transactions: [unsignedTx], header: { transactionsTrie: txRoot, @@ -272,7 +277,7 @@ describe('[Block]: block functions', () => { const zeroRoot = zeros(32) // Tx root - block = Block.fromBlockData({ + block = createBlockFromBlockData({ transactions: [unsignedTx], header: { transactionsTrie: zeroRoot, @@ -281,7 +286,7 @@ describe('[Block]: block functions', () => { await checkThrowsAsync(block.validateData(false, false), 'invalid transaction trie') // Withdrawals root - block = Block.fromBlockData( + block = createBlockFromBlockData( { header: { withdrawalsRoot: zeroRoot, @@ -293,7 +298,7 @@ describe('[Block]: block functions', () => { await checkThrowsAsync(block.validateData(false, false), 'invalid withdrawals trie') // Uncle root - block = Block.fromBlockData( + block = createBlockFromBlockData( { header: { uncleHash: zeroRoot, @@ -307,7 +312,7 @@ describe('[Block]: block functions', () => { const common = new Common({ chain: Chain.Mainnet, eips: [6800], hardfork: Hardfork.Cancun }) // Note: `executionWitness: undefined` will still initialize an execution witness in the block // So, only testing for `null` here - block = Block.fromBlockData({ executionWitness: null }, { common }) + block = createBlockFromBlockData({ executionWitness: null }, { common }) await checkThrowsAsync( block.validateData(false, false), 'Invalid block: ethereumjs stateless client needs executionWitness' @@ -315,9 +320,9 @@ describe('[Block]: block functions', () => { }) it('should test isGenesis (mainnet default)', () => { - const block = Block.fromBlockData({ header: { number: 1 } }) + const block = createBlockFromBlockData({ header: { number: 1 } }) assert.notEqual(block.isGenesis(), true) - const genesisBlock = Block.fromBlockData({ header: { number: 0 } }) + const genesisBlock = createBlockFromBlockData({ header: { number: 0 } }) assert.equal(genesisBlock.isGenesis(), true) }) @@ -325,7 +330,7 @@ describe('[Block]: block functions', () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) const rlp = hexToBytes(`0x${testDataGenesis.test.genesis_rlp_hex}`) const hash = hexToBytes(`0x${testDataGenesis.test.genesis_hash}`) - const block = Block.fromRLPSerializedBlock(rlp, { common }) + const block = createBlockFromRLPSerializedBlock(rlp, { common }) assert.ok(equalsBytes(block.hash(), hash), 'genesis hash match') }) @@ -333,7 +338,7 @@ describe('[Block]: block functions', () => { let common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) const rlp = hexToBytes(`0x${testDataGenesis.test.genesis_rlp_hex}`) const hash = hexToBytes(`0x${testDataGenesis.test.genesis_hash}`) - let block = Block.fromRLPSerializedBlock(rlp, { common }) + let block = createBlockFromRLPSerializedBlock(rlp, { common }) assert.ok(equalsBytes(block.hash(), hash), 'genesis hash match') common = new Common({ @@ -345,14 +350,14 @@ describe('[Block]: block functions', () => { }, }, }) - block = Block.fromRLPSerializedBlock(rlp, { common }) + block = createBlockFromRLPSerializedBlock(rlp, { common }) assert.deepEqual(block.hash(), new Uint8Array([1]), 'custom crypto applied on hash() method') }) it('should error on invalid params', () => { assert.throws( () => { - Block.fromRLPSerializedBlock('1' as any) + createBlockFromRLPSerializedBlock('1' as any) }, undefined, undefined, @@ -360,7 +365,7 @@ describe('[Block]: block functions', () => { ) assert.throws( () => { - Block.fromValuesArray([1, 2, 3, 4] as any) + createBlockFromValuesArray([1, 2, 3, 4] as any) }, undefined, undefined, @@ -370,19 +375,19 @@ describe('[Block]: block functions', () => { it('should return the same block data from raw()', () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const block = Block.fromRLPSerializedBlock( + const block = createBlockFromRLPSerializedBlock( toBytes(testDataPreLondon2.blocks[2].rlp as PrefixedHexString), { common, } ) - const blockFromRaw = Block.fromValuesArray(block.raw(), { common }) - assert.ok(equalsBytes(block.hash(), blockFromRaw.hash())) + const createBlockFromRaw = createBlockFromValuesArray(block.raw(), { common }) + assert.ok(equalsBytes(block.hash(), createBlockFromRaw.hash())) }) it('should test toJSON', () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const block = Block.fromRLPSerializedBlock( + const block = createBlockFromRLPSerializedBlock( toBytes(testDataPreLondon2.blocks[2].rlp as PrefixedHexString), { common, @@ -401,7 +406,7 @@ describe('[Block]: block functions', () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Dao }) assert.throws( function () { - Block.fromValuesArray(blockData as BlockBytes, { common }) + createBlockFromValuesArray(blockData as BlockBytes, { common }) }, /extraData should be 'dao-hard-fork/, undefined, @@ -412,13 +417,13 @@ describe('[Block]: block functions', () => { blockData[0][12] = hexToBytes('0x64616f2d686172642d666f726b') assert.doesNotThrow(function () { - Block.fromValuesArray(blockData as BlockBytes, { common }) + createBlockFromValuesArray(blockData as BlockBytes, { common }) }, 'should not throw on DAO HF block with correct extra data') }) it('should set canonical difficulty if I provide a calcDifficultyFromHeader header', () => { let common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - const genesis = Block.fromBlockData({}, { common }) + const genesis = createBlockFromBlockData({}, { common }) const nextBlockHeaderData = { number: genesis.header.number + BigInt(1), @@ -426,7 +431,7 @@ describe('[Block]: block functions', () => { } common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) - const blockWithoutDifficultyCalculation = Block.fromBlockData( + const blockWithoutDifficultyCalculation = createBlockFromBlockData( { header: nextBlockHeaderData, }, @@ -441,7 +446,7 @@ describe('[Block]: block functions', () => { ) // test if we set difficulty if we have a "difficulty header" in options; also verify this is equal to reported canonical difficulty. - const blockWithDifficultyCalculation = Block.fromBlockData( + const blockWithDifficultyCalculation = createBlockFromBlockData( { header: nextBlockHeaderData, }, @@ -467,7 +472,7 @@ describe('[Block]: block functions', () => { timestamp: genesis.header.timestamp + BigInt(10), } - const block_farAhead = Block.fromBlockData( + const block_farAhead = createBlockFromBlockData( { header: noParentHeaderData, }, @@ -485,7 +490,7 @@ describe('[Block]: block functions', () => { it('should be able to initialize shanghai blocks with correct hardfork defaults', () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Shanghai }) - const block = Block.fromBlockData({}, { common }) + const block = createBlockFromBlockData({}, { common }) assert.equal(block.common.hardfork(), Hardfork.Shanghai, 'hardfork should be set to shanghai') assert.deepEqual(block.withdrawals, [], 'withdrawals should be set to default empty array') }) diff --git a/packages/block/test/difficulty.spec.ts b/packages/block/test/difficulty.spec.ts index 57f7f8e0cc..945d69fc03 100644 --- a/packages/block/test/difficulty.spec.ts +++ b/packages/block/test/difficulty.spec.ts @@ -11,7 +11,9 @@ import * as difficultyEIP2384_random_to20M from '../../ethereum-tests/Difficulty import * as difficultyFrontier from '../../ethereum-tests/DifficultyTests/dfFrontier/difficultyFrontier.json' import * as difficultyGrayGlacier from '../../ethereum-tests/DifficultyTests/dfGrayGlacier/difficultyGrayGlacier.json' import * as difficultyHomestead from '../../ethereum-tests/DifficultyTests/dfHomestead/difficultyHomestead.json' -import { Block } from '../src/index.js' +import { createBlockFromBlockData } from '../src/constructors.js' + +import type { Block } from '../src/index.js' function runDifficultyTests(test: any, parentBlock: Block, block: Block, msg: string) { const dif = block.ethashCanonicalDifficulty(parentBlock) @@ -55,7 +57,7 @@ describe('[Header]: difficulty tests', () => { }) const blockOpts = { common } const uncleHash = test.parentUncles === '0x00' ? undefined : test.parentUncles - const parentBlock = Block.fromBlockData( + const parentBlock = createBlockFromBlockData( { header: { timestamp: test.parentTimestamp, @@ -66,7 +68,7 @@ describe('[Header]: difficulty tests', () => { blockOpts ) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { timestamp: test.currentTimestamp, @@ -90,7 +92,7 @@ describe('[Header]: difficulty tests', () => { const common = new Common({ chain }) const blockOpts = { common, setHardfork: true } const uncleHash = test.parentUncles === '0x00' ? undefined : test.parentUncles - const parentBlock = Block.fromBlockData( + const parentBlock = createBlockFromBlockData( { header: { timestamp: test.parentTimestamp, @@ -102,7 +104,7 @@ describe('[Header]: difficulty tests', () => { blockOpts ) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { timestamp: test.currentTimestamp, diff --git a/packages/block/test/eip1559block.spec.ts b/packages/block/test/eip1559block.spec.ts index 69407c7bf1..328ce8cc36 100644 --- a/packages/block/test/eip1559block.spec.ts +++ b/packages/block/test/eip1559block.spec.ts @@ -3,7 +3,7 @@ import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx' import { hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { Block } from '../src/block.js' +import { createBlockFromBlockData } from '../src/constructors.js' import { BlockHeader } from '../src/header.js' // Test data from Besu (retrieved via Discord) @@ -16,7 +16,7 @@ const common = new Common({ hardfork: Hardfork.London, }) -const genesis = Block.fromBlockData({}) +const genesis = createBlockFromBlockData({}) // Small hack to hack in the activation block number // (Otherwise there would be need for a custom chain only for testing purposes) @@ -105,7 +105,7 @@ describe('EIP1559 tests', () => { }) it('Header -> _genericFormValidation -> success case', async () => { - Block.fromBlockData( + createBlockFromBlockData( { header: { number: BigInt(1), @@ -146,7 +146,7 @@ describe('EIP1559 tests', () => { }) it('Header -> validate() -> success cases', async () => { - const block1 = Block.fromBlockData( + const block1 = createBlockFromBlockData( { header: { number: BigInt(1), @@ -161,7 +161,7 @@ describe('EIP1559 tests', () => { common, } ) - Block.fromBlockData( + createBlockFromBlockData( { header: { number: BigInt(2), @@ -223,7 +223,7 @@ describe('EIP1559 tests', () => { assert.ok(true, 'should not throw when elasticity is exactly matched') }) - const block1 = Block.fromBlockData( + const block1 = createBlockFromBlockData( { header: { number: BigInt(1), @@ -415,7 +415,7 @@ describe('EIP1559 tests', () => { }, { common } ).sign(hexToBytes(`0x${'46'.repeat(32)}`)) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { number: BigInt(1), diff --git a/packages/block/test/eip4788block.spec.ts b/packages/block/test/eip4788block.spec.ts index e74d5b7fc9..7f294c832b 100644 --- a/packages/block/test/eip4788block.spec.ts +++ b/packages/block/test/eip4788block.spec.ts @@ -2,8 +2,8 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { bytesToHex, zeros } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' +import { createBlockFromBlockData } from '../src/constructors.js' import { BlockHeader } from '../src/header.js' -import { Block } from '../src/index.js' describe('EIP4788 header tests', () => { it('should work', () => { @@ -55,7 +55,7 @@ describe('EIP4788 header tests', () => { ) }, 'correctly instantiates an EIP4788 block header') - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: BlockHeader.fromHeaderData({}, { common }), }, diff --git a/packages/block/test/eip4844block.spec.ts b/packages/block/test/eip4844block.spec.ts index dc2ff91ece..cb169c07c7 100644 --- a/packages/block/test/eip4844block.spec.ts +++ b/packages/block/test/eip4844block.spec.ts @@ -1,4 +1,4 @@ -import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { Chain, Common, Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common' import { BlobEIP4844Transaction } from '@ethereumjs/tx' import { blobsToCommitments, @@ -9,9 +9,9 @@ import { import { loadKZG } from 'kzg-wasm' import { assert, beforeAll, describe, it } from 'vitest' +import { createBlockFromBlockData } from '../src/constructors.js' import { BlockHeader } from '../src/header.js' import { fakeExponential, getNumBlobs } from '../src/helpers.js' -import { Block } from '../src/index.js' import gethGenesis from './testdata/4844-hardfork.json' @@ -24,7 +24,7 @@ describe('EIP4844 header tests', () => { beforeAll(async () => { const kzg = await loadKZG() - common = Common.fromGethGenesis(gethGenesis, { + common = createCommonFromGethGenesis(gethGenesis, { chain: 'customChain', hardfork: Hardfork.Cancun, customCrypto: { kzg }, @@ -87,7 +87,7 @@ describe('EIP4844 header tests', () => { ) }, 'correctly instantiates an EIP4844 block header') - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: BlockHeader.fromHeaderData({}, { common, skipConsensusFormatValidation: true }), }, @@ -102,7 +102,7 @@ describe('blob gas tests', () => { let blobGasPerBlob: bigint beforeAll(async () => { const kzg = await loadKZG() - common = Common.fromGethGenesis(gethGenesis, { + common = createCommonFromGethGenesis(gethGenesis, { chain: 'customChain', hardfork: Hardfork.Cancun, customCrypto: { kzg }, @@ -159,7 +159,7 @@ describe('transaction validation tests', () => { let blobGasPerBlob: bigint beforeAll(async () => { kzg = await loadKZG() - common = Common.fromGethGenesis(gethGenesis, { + common = createCommonFromGethGenesis(gethGenesis, { chain: 'customChain', hardfork: Hardfork.Cancun, customCrypto: { kzg }, @@ -213,7 +213,7 @@ describe('transaction validation tests', () => { }, { common, skipConsensusFormatValidation: true } ) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: blockHeader, transactions }, { common, skipConsensusFormatValidation: true } ) @@ -232,7 +232,7 @@ describe('transaction validation tests', () => { ) const blockJson = blockWithValidTx.toJSON() blockJson.header!.blobGasUsed = '0x0' - const blockWithInvalidHeader = Block.fromBlockData(blockJson, { common }) + const blockWithInvalidHeader = createBlockFromBlockData(blockJson, { common }) assert.throws( () => blockWithInvalidHeader.validateBlobTransactions(parentHeader), 'block blobGasUsed mismatch', diff --git a/packages/block/test/eip4895block.spec.ts b/packages/block/test/eip4895block.spec.ts index a0747dab48..2159b51bbc 100644 --- a/packages/block/test/eip4895block.spec.ts +++ b/packages/block/test/eip4895block.spec.ts @@ -10,8 +10,9 @@ import { } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { Block } from '../src/block.js' +import { createBlockFromBlockData, createBlockFromRLPSerializedBlock } from '../src/constructors.js' import { BlockHeader } from '../src/header.js' +import { genWithdrawalsTrieRoot } from '../src/helpers.js' import type { WithdrawalBytes, WithdrawalData } from '@ethereumjs/util' @@ -45,7 +46,7 @@ describe('EIP4895 tests', () => { assert.equal(withdrawals.length, 8, '8 withdrawals should have been found') const gethWitdrawalsRoot = (gethBlockBytesArray[0] as Uint8Array[])[16] as Uint8Array assert.deepEqual( - await Block.genWithdrawalsTrieRoot(withdrawals), + await genWithdrawalsTrieRoot(withdrawals), gethWitdrawalsRoot, 'withdrawalsRoot should be valid' ) @@ -91,7 +92,7 @@ describe('EIP4895 tests', () => { const earlyCommon = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) assert.throws( () => { - Block.fromBlockData( + createBlockFromBlockData( { withdrawals: [], }, @@ -105,7 +106,7 @@ describe('EIP4895 tests', () => { 'should throw when setting withdrawals with EIP4895 not being activated' ) assert.doesNotThrow(() => { - Block.fromBlockData( + createBlockFromBlockData( {}, { common, @@ -113,7 +114,7 @@ describe('EIP4895 tests', () => { ) }, 'should not throw when withdrawals is undefined with EIP4895 being activated') assert.doesNotThrow(() => { - Block.fromBlockData( + createBlockFromBlockData( { header: { withdrawalsRoot: zeros(32), @@ -125,7 +126,7 @@ describe('EIP4895 tests', () => { } ) }) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { withdrawalsRoot: zeros(32), @@ -146,7 +147,7 @@ describe('EIP4895 tests', () => { }, { common } ) - const validBlock = Block.fromBlockData( + const validBlock = createBlockFromBlockData( { header: validHeader, withdrawals: [], @@ -164,7 +165,7 @@ describe('EIP4895 tests', () => { amount: BigInt(1000), } - const validBlockWithWithdrawal = Block.fromBlockData( + const validBlockWithWithdrawal = createBlockFromBlockData( { header: { withdrawalsRoot: hexToBytes( @@ -189,7 +190,7 @@ describe('EIP4895 tests', () => { amount: BigInt(2000), } - const validBlockWithWithdrawal2 = Block.fromBlockData( + const validBlockWithWithdrawal2 = createBlockFromBlockData( { header: { withdrawalsRoot: hexToBytes( @@ -214,7 +215,7 @@ describe('EIP4895 tests', () => { }, 'hashed block with withdrawals') }) it('should throw if no withdrawal array is provided', () => { - const blockWithWithdrawals = Block.fromBlockData({}, { common }) + const blockWithWithdrawals = createBlockFromBlockData({}, { common }) const rlp = blockWithWithdrawals.serialize() const rlpDecoded = RLP.decode(rlp) as Uint8Array[] // remove withdrawals root @@ -224,7 +225,7 @@ describe('EIP4895 tests', () => { // throw check if withdrawals array is not provided in the rlp assert.throws( () => { - Block.fromRLPSerializedBlock(rlpWithoutWithdrawals, { common }) + createBlockFromRLPSerializedBlock(rlpWithoutWithdrawals, { common }) }, undefined, undefined, @@ -233,7 +234,7 @@ describe('EIP4895 tests', () => { }) it('should return early when withdrawals root equals KECCAK256_RLP', async () => { - const block = Block.fromBlockData({}, { common }) + const block = createBlockFromBlockData({}, { common }) // Set invalid withdrawalsRoot in cache block['cache'].withdrawalsTrieRoot = randomBytes(32) assert.ok( diff --git a/packages/block/test/eip7685block.spec.ts b/packages/block/test/eip7685block.spec.ts index 1d1962aacb..f51f129bfd 100644 --- a/packages/block/test/eip7685block.spec.ts +++ b/packages/block/test/eip7685block.spec.ts @@ -8,6 +8,12 @@ import { } from '@ethereumjs/util' import { assert, describe, expect, it } from 'vitest' +import { + createBlockFromBlockData, + createBlockFromRPC, + createBlockFromValuesArray, +} from '../src/constructors.js' +import { genRequestsTrieRoot } from '../src/helpers.js' import { Block, BlockHeader } from '../src/index.js' import type { CLRequest, CLRequestType } from '@ethereumjs/util' @@ -26,7 +32,7 @@ function getRandomDepositRequest(): CLRequest { function getRandomWithdrawalRequest(): CLRequest { const withdrawalRequestData = { sourceAddress: randomBytes(20), - validatorPublicKey: randomBytes(48), + validatorPubkey: randomBytes(48), amount: bytesToBigInt(randomBytes(8)), } return WithdrawalRequest.fromRequestData(withdrawalRequestData) as CLRequest @@ -39,7 +45,7 @@ const common = new Common({ }) describe('7685 tests', () => { it('should instantiate block with defaults', () => { - const block = Block.fromBlockData({}, { common }) + const block = createBlockFromBlockData({}, { common }) assert.deepEqual(block.header.requestsRoot, KECCAK256_RLP) const block2 = new Block(undefined, undefined, undefined, undefined, { common }) assert.deepEqual(block.header.requestsRoot, KECCAK256_RLP) @@ -47,8 +53,8 @@ describe('7685 tests', () => { }) it('should instantiate a block with requests', async () => { const request = getRandomDepositRequest() - const requestsRoot = await Block.genRequestsTrieRoot([request]) - const block = Block.fromBlockData( + const requestsRoot = await genRequestsTrieRoot([request]) + const block = createBlockFromBlockData( { requests: [request], header: { requestsRoot }, @@ -60,7 +66,7 @@ describe('7685 tests', () => { }) it('RequestsRootIsValid should return false when requestsRoot is invalid', async () => { const request = getRandomDepositRequest() - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { requests: [request], header: { requestsRoot: randomBytes(32) }, @@ -75,11 +81,11 @@ describe('7685 tests', () => { const request2 = getRandomDepositRequest() const request3 = getRandomWithdrawalRequest() const requests = [request1, request2, request3] - const requestsRoot = await Block.genRequestsTrieRoot(requests) + const requestsRoot = await genRequestsTrieRoot(requests) // Construct block with requests in correct order - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { requests, header: { requestsRoot }, @@ -91,7 +97,7 @@ describe('7685 tests', () => { // Throws when requests are not ordered correctly await expect(async () => - Block.fromBlockData( + createBlockFromBlockData( { requests: [request1, request3, request2], header: { requestsRoot }, @@ -104,7 +110,7 @@ describe('7685 tests', () => { describe('fromValuesArray tests', () => { it('should construct a block with empty requests root', () => { - const block = Block.fromValuesArray( + const block = createBlockFromValuesArray( [BlockHeader.fromHeaderData({}, { common }).raw(), [], [], [], []], { common, @@ -117,10 +123,10 @@ describe('fromValuesArray tests', () => { const request2 = getRandomWithdrawalRequest() const request3 = getRandomWithdrawalRequest() const requests = [request1, request2, request3] - const requestsRoot = await Block.genRequestsTrieRoot(requests) + const requestsRoot = await genRequestsTrieRoot(requests) const serializedRequests = [request1.serialize(), request2.serialize(), request3.serialize()] - const block = Block.fromValuesArray( + const block = createBlockFromValuesArray( [ BlockHeader.fromHeaderData({ requestsRoot }, { common }).raw(), [], @@ -143,10 +149,10 @@ describe('fromRPC tests', () => { const request2 = getRandomDepositRequest() const request3 = getRandomWithdrawalRequest() const requests = [request1, request2, request3] - const requestsRoot = await Block.genRequestsTrieRoot(requests) + const requestsRoot = await genRequestsTrieRoot(requests) const serializedRequests = [request1.serialize(), request2.serialize(), request3.serialize()] - const block = Block.fromValuesArray( + const block = createBlockFromValuesArray( [ BlockHeader.fromHeaderData({ requestsRoot }, { common }).raw(), [], @@ -160,7 +166,7 @@ describe('fromRPC tests', () => { ) const jsonBlock = block.toJSON() const rpcBlock: any = { ...jsonBlock.header, requests: jsonBlock.requests } - const blockFromJson = Block.fromRPC(rpcBlock, undefined, { common }) - assert.deepEqual(block.hash(), blockFromJson.hash()) + const createBlockFromJson = createBlockFromRPC(rpcBlock, undefined, { common }) + assert.deepEqual(block.hash(), createBlockFromJson.hash()) }) }) diff --git a/packages/block/test/from-beacon-payload.spec.ts b/packages/block/test/from-beacon-payload.spec.ts index 0faf8f31d0..519c649adc 100644 --- a/packages/block/test/from-beacon-payload.spec.ts +++ b/packages/block/test/from-beacon-payload.spec.ts @@ -1,9 +1,10 @@ -import { Common, Hardfork } from '@ethereumjs/common' +import { Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common' import { loadKZG } from 'kzg-wasm' import { assert, beforeAll, describe, it } from 'vitest' import * as shardingJson from '../../client/test/sim/configs/4844-devnet.json' -import { Block, BlockHeader } from '../src/index.js' +import { createBlockFromBeaconPayloadJson } from '../src/constructors.js' +import { BlockHeader } from '../src/index.js' import * as payloadKaustinen from './testdata/payload-kaustinen.json' import * as payload87335 from './testdata/payload-slot-87335.json' @@ -11,6 +12,7 @@ import * as payload87475 from './testdata/payload-slot-87475.json' import * as testnetVerkleKaustinen from './testdata/testnetVerkleKaustinen.json' import type { BeaconPayloadJson } from '../src/index.js' +import type { Common } from '@ethereumjs/common' import type { VerkleExecutionWitness } from '@ethereumjs/util' describe('[fromExecutionPayloadJson]: 4844 devnet 5', () => { @@ -21,7 +23,7 @@ describe('[fromExecutionPayloadJson]: 4844 devnet 5', () => { const commonJson = { ...shardingJson } commonJson.config = { ...commonJson.config, chainId: 4844001005 } const network = 'sharding' - common = Common.fromGethGenesis(commonJson, { chain: network, customCrypto: { kzg } }) + common = createCommonFromGethGenesis(commonJson, { chain: network, customCrypto: { kzg } }) // safely change chainId without modifying undelying json common.setHardfork(Hardfork.Cancun) @@ -30,7 +32,9 @@ describe('[fromExecutionPayloadJson]: 4844 devnet 5', () => { it('reconstruct cancun block with blob txs', async () => { for (const payload of [payload87335, payload87475]) { try { - const block = await Block.fromBeaconPayloadJson(payload as BeaconPayloadJson, { common }) + const block = await createBlockFromBeaconPayloadJson(payload as BeaconPayloadJson, { + common, + }) const parentHeader = BlockHeader.fromHeaderData( { excessBlobGas: BigInt(0), blobGasUsed: block.header.excessBlobGas! + BigInt(393216) }, { common } @@ -46,7 +50,7 @@ describe('[fromExecutionPayloadJson]: 4844 devnet 5', () => { it('should validate block hash', async () => { try { // construct a payload with differing block hash - await Block.fromBeaconPayloadJson( + await createBlockFromBeaconPayloadJson( { ...payload87335, block_hash: payload87475.block_hash, @@ -63,7 +67,7 @@ describe('[fromExecutionPayloadJson]: 4844 devnet 5', () => { it('should validate excess blob gas', async () => { try { // construct a payload with a different excess blob gas but matching hash - const block = await Block.fromBeaconPayloadJson( + const block = await createBlockFromBeaconPayloadJson( { ...payload87475, block_hash: '0x573714bdd0ca5e47bc32008751c4fc74237f8cb354fbc1475c1d0ece38236ea4', @@ -84,13 +88,13 @@ describe('[fromExecutionPayloadJson]: kaustinen', () => { const network = 'kaustinen' // safely change chainId without modifying undelying json - const common = Common.fromGethGenesis(testnetVerkleKaustinen, { + const common = createCommonFromGethGenesis(testnetVerkleKaustinen, { chain: network, eips: [6800], }) it('reconstruct kaustinen block', async () => { assert.ok(common.isActivatedEIP(6800), 'verkle eip should be activated') - const block = await Block.fromBeaconPayloadJson(payloadKaustinen as BeaconPayloadJson, { + const block = await createBlockFromBeaconPayloadJson(payloadKaustinen as BeaconPayloadJson, { common, }) // the witness object in payload has camel casing for now diff --git a/packages/block/test/from-rpc.spec.ts b/packages/block/test/from-rpc.spec.ts index 630f500518..23c255fbf2 100644 --- a/packages/block/test/from-rpc.spec.ts +++ b/packages/block/test/from-rpc.spec.ts @@ -2,9 +2,9 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { bytesToHex, equalsBytes, hexToBytes, randomBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { blockFromRpc } from '../src/from-rpc.js' +import { createBlockFromJsonRpcProvider } from '../src/constructors.js' +import { createBlockFromRpc } from '../src/from-rpc.js' import { blockHeaderFromRpc } from '../src/header-from-rpc.js' -import { Block } from '../src/index.js' import * as alchemy14151203 from './testdata/alchemy14151203.json' import * as infuraGoerliBlock10536893 from './testdata/infura-goerli-block-10536893.json' @@ -27,7 +27,7 @@ describe('[fromRPC]: block #2924874', () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) it('should create a block with transactions with valid signatures', () => { - const block = blockFromRpc(blockData as JsonRpcBlock, [], { common }) + const block = createBlockFromRpc(blockData as JsonRpcBlock, [], { common }) const allValid = block.transactions.every((tx) => tx.verifySignature()) assert.equal(allValid, true, 'all transaction signatures are valid') }) @@ -45,13 +45,13 @@ describe('[fromRPC]:', () => { const valueAsIntegerString = '1' const blockDataTransactionValueAsInteger = blockData blockDataTransactionValueAsInteger.transactions[0].value = valueAsIntegerString - const blockFromTransactionValueAsInteger = blockFromRpc( + const createBlockFromTransactionValueAsInteger = createBlockFromRpc( blockDataTransactionValueAsInteger as JsonRpcBlock, undefined, { common } ) assert.equal( - blockFromTransactionValueAsInteger.transactions[0].value.toString(), + createBlockFromTransactionValueAsInteger.transactions[0].value.toString(), valueAsIntegerString ) }) @@ -61,14 +61,14 @@ describe('[fromRPC]:', () => { const gasPriceAsIntegerString = '1' const blockDataTransactionGasPriceAsInteger = blockData blockDataTransactionGasPriceAsInteger.transactions[0].gasPrice = gasPriceAsIntegerString - const blockFromTransactionGasPriceAsInteger = blockFromRpc( + const createBlockFromTransactionGasPriceAsInteger = createBlockFromRpc( blockDataTransactionGasPriceAsInteger as JsonRpcBlock, undefined, { common } ) assert.equal( ( - blockFromTransactionGasPriceAsInteger.transactions[0] as LegacyTransaction + createBlockFromTransactionGasPriceAsInteger.transactions[0] as LegacyTransaction ).gasPrice.toString(), gasPriceAsIntegerString ) @@ -76,7 +76,7 @@ describe('[fromRPC]:', () => { it('should create a block given json data that includes a difficulty parameter of type integer string', () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) - const blockDifficultyAsInteger = blockFromRpc( + const blockDifficultyAsInteger = createBlockFromRpc( blockDataDifficultyAsInteger as JsonRpcBlock, undefined, { @@ -91,7 +91,7 @@ describe('[fromRPC]:', () => { it('should create a block from london hardfork', () => { const common = new Common({ chain: Chain.Goerli, hardfork: Hardfork.London }) - const block = blockFromRpc(testDataFromRpcGoerliLondon as JsonRpcBlock, [], { common }) + const block = createBlockFromRpc(testDataFromRpcGoerliLondon as JsonRpcBlock, [], { common }) assert.equal( `0x${block.header.baseFeePerGas?.toString(16)}`, testDataFromRpcGoerliLondon.baseFeePerGas @@ -101,13 +101,15 @@ describe('[fromRPC]:', () => { it('should create a block with uncles', () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const block = blockFromRpc(blockDataWithUncles as JsonRpcBlock, [uncleBlockData], { common }) + const block = createBlockFromRpc(blockDataWithUncles as JsonRpcBlock, [uncleBlockData], { + common, + }) assert.ok(block.uncleHashIsValid()) }) it('should create a block with EIP-4896 withdrawals', () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Shanghai }) - const block = blockFromRpc(blockDataWithWithdrawals as JsonRpcBlock, [], { common }) + const block = createBlockFromRpc(blockDataWithWithdrawals as JsonRpcBlock, [], { common }) assert.ok(block.withdrawalsTrieIsValid()) }) @@ -122,25 +124,28 @@ describe('[fromRPC]:', () => { describe('[fromRPC] - Alchemy/Infura API block responses', () => { it('should create pre merge block from Alchemy API response to eth_getBlockByHash', () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) - const block = blockFromRpc(alchemy14151203 as JsonRpcBlock, [], { common }) + const block = createBlockFromRpc(alchemy14151203 as JsonRpcBlock, [], { common }) assert.equal(bytesToHex(block.hash()), alchemy14151203.hash) }) it('should create pre and post merge blocks from Infura API responses to eth_getBlockByHash and eth_getBlockByNumber', () => { const common = new Common({ chain: Chain.Mainnet }) - let block = blockFromRpc(infura2000004woTxs as JsonRpcBlock, [], { common, setHardfork: true }) + let block = createBlockFromRpc(infura2000004woTxs as JsonRpcBlock, [], { + common, + setHardfork: true, + }) assert.equal( bytesToHex(block.hash()), infura2000004woTxs.hash, 'created premerge block w/o txns' ) - block = blockFromRpc(infura2000004wTxs as JsonRpcBlock, [], { common, setHardfork: true }) + block = createBlockFromRpc(infura2000004wTxs as JsonRpcBlock, [], { common, setHardfork: true }) assert.equal( bytesToHex(block.hash()), infura2000004wTxs.hash, 'created premerge block with txns' ) - block = blockFromRpc(infura15571241woTxs as JsonRpcBlock, [], { + block = createBlockFromRpc(infura15571241woTxs as JsonRpcBlock, [], { common, setHardfork: 58750000000000000000000n, }) @@ -150,7 +155,7 @@ describe('[fromRPC] - Alchemy/Infura API block responses', () => { 'created post merge block without txns' ) - block = blockFromRpc(infura15571241wTxs as JsonRpcBlock, [], { + block = createBlockFromRpc(infura15571241wTxs as JsonRpcBlock, [], { common, setHardfork: 58750000000000000000000n, }) @@ -203,14 +208,14 @@ describe('[fromJsonRpcProvider]', () => { } const blockHash = '0x1850b014065b23d804ecf71a8a4691d076ca87c2e6fb8fe81ee20a4d8e884c24' - const block = await Block.fromJsonRpcProvider(provider, blockHash, { common }) + const block = await createBlockFromJsonRpcProvider(provider, blockHash, { common }) assert.equal( bytesToHex(block.hash()), blockHash, 'assembled a block from blockdata from a provider' ) try { - await Block.fromJsonRpcProvider(provider, bytesToHex(randomBytes(32)), {}) + await createBlockFromJsonRpcProvider(provider, bytesToHex(randomBytes(32)), {}) assert.fail('should throw') } catch (err: any) { assert.ok( diff --git a/packages/block/test/header.spec.ts b/packages/block/test/header.spec.ts index 6f4f08bfa7..4d4cafc066 100644 --- a/packages/block/test/header.spec.ts +++ b/packages/block/test/header.spec.ts @@ -12,6 +12,7 @@ import { } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' +import { createBlockFromBlockData, createBlockFromRLPSerializedBlock } from '../src/constructors.js' import { BlockHeader } from '../src/header.js' import { Block } from '../src/index.js' @@ -197,7 +198,7 @@ describe('[Block]: Header functions', () => { it('should validate extraData', async () => { // PoW let common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - let genesis = Block.fromBlockData({}, { common }) + let genesis = createBlockFromBlockData({}, { common }) const number = 1 let parentHash = genesis.hash() @@ -241,7 +242,7 @@ describe('[Block]: Header functions', () => { // PoA common = new Common({ chain: Chain.Goerli, hardfork: Hardfork.Chainstart }) - genesis = Block.fromBlockData({ header: { extraData: new Uint8Array(97) } }, { common }) + genesis = createBlockFromBlockData({ header: { extraData: new Uint8Array(97) } }, { common }) parentHash = genesis.hash() gasLimit = genesis.header.gasLimit @@ -349,7 +350,7 @@ describe('[Block]: Header functions', () => { const blockchain = new Mockchain() const genesisRlp = toBytes(testDataPreLondon.genesisRLP) - const block = Block.fromRLPSerializedBlock(genesisRlp, { common }) + const block = createBlockFromRLPSerializedBlock(genesisRlp, { common }) await blockchain.putBlock(block) headerData.number = 1 @@ -429,7 +430,7 @@ describe('[Block]: Header functions', () => { const cliqueSigner = hexToBytes( '64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993' ) - const poaBlock = Block.fromRLPSerializedBlock(genesisRlp, { common, cliqueSigner }) + const poaBlock = createBlockFromRLPSerializedBlock(genesisRlp, { common, cliqueSigner }) await poaBlockchain.putBlock(poaBlock) header = BlockHeader.fromHeaderData(headerData, { common, cliqueSigner }) @@ -461,12 +462,12 @@ describe('[Block]: Header functions', () => { bcBlockGasLimitTestData[key as keyof typeof bcBlockGasLimitTestData] .genesisRLP as PrefixedHexString ) - const parentBlock = Block.fromRLPSerializedBlock(genesisRlp, { common }) + const parentBlock = createBlockFromRLPSerializedBlock(genesisRlp, { common }) const blockRlp = hexToBytes( bcBlockGasLimitTestData[key as keyof typeof bcBlockGasLimitTestData].blocks[0] .rlp as PrefixedHexString ) - const block = Block.fromRLPSerializedBlock(blockRlp, { common }) + const block = createBlockFromRLPSerializedBlock(blockRlp, { common }) assert.doesNotThrow(() => block.validateGasLimit(parentBlock)) } }) diff --git a/packages/block/test/mergeBlock.spec.ts b/packages/block/test/mergeBlock.spec.ts index 330e90624f..f9275d225a 100644 --- a/packages/block/test/mergeBlock.spec.ts +++ b/packages/block/test/mergeBlock.spec.ts @@ -10,6 +10,7 @@ import { import { assert, describe, it } from 'vitest' import { Block } from '../src/block.js' +import { createBlockFromBlockData } from '../src/constructors.js' import { BlockHeader } from '../src/header.js' const common = new Common({ @@ -119,7 +120,7 @@ describe('[Header]: Casper PoS / The Merge Functionality', () => { it('EIP-4399: prevRando should return mixHash value', () => { const mixHash = new Uint8Array(32).fill(3) - let block = Block.fromBlockData({ header: { mixHash } }, { common }) + let block = createBlockFromBlockData({ header: { mixHash } }, { common }) assert.ok( equalsBytes(block.header.prevRandao, mixHash), 'prevRandao should return mixHash value' @@ -127,7 +128,7 @@ describe('[Header]: Casper PoS / The Merge Functionality', () => { const commonLondon = common.copy() commonLondon.setHardfork(Hardfork.London) - block = Block.fromBlockData({ header: { mixHash } }, { common: commonLondon }) + block = createBlockFromBlockData({ header: { mixHash } }, { common: commonLondon }) try { block.header.prevRandao assert.fail('should have thrown') diff --git a/packages/block/test/util.ts b/packages/block/test/util.ts index ba7471da8f..1383be87a0 100644 --- a/packages/block/test/util.ts +++ b/packages/block/test/util.ts @@ -3,9 +3,9 @@ import { RLP } from '@ethereumjs/rlp' import { BIGINT_0, BIGINT_1, utf8ToBytes } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' -import { Block } from '../src/index.js' +import { createBlockFromBlockData } from '../src/constructors.js' -import type { BlockHeader } from '../src/index.js' +import type { Block, BlockHeader } from '../src/index.js' /** * This helper function creates a valid block (except the PoW) with the ability to add uncles. Returns a Block. @@ -37,7 +37,7 @@ function createBlock( ? parentBlock.header.calcNextBaseFee() : undefined - return Block.fromBlockData( + return createBlockFromBlockData( { header: { number, diff --git a/packages/blockchain/examples/gethGenesis.ts b/packages/blockchain/examples/gethGenesis.ts index 1052fd8970..97b7df1db4 100644 --- a/packages/blockchain/examples/gethGenesis.ts +++ b/packages/blockchain/examples/gethGenesis.ts @@ -1,13 +1,13 @@ -import { Blockchain } from '@ethereumjs/blockchain' -import { Common, parseGethGenesis } from '@ethereumjs/common' +import { createBlockchain } from '@ethereumjs/blockchain' +import { Common, createCommonFromGethGenesis, parseGethGenesis } from '@ethereumjs/common' import { bytesToHex, parseGethGenesisState } from '@ethereumjs/util' import gethGenesisJson from './genesisData/post-merge.json' const main = async () => { // Load geth genesis json file into lets say `gethGenesisJson` - const common = Common.fromGethGenesis(gethGenesisJson, { chain: 'customChain' }) + const common = createCommonFromGethGenesis(gethGenesisJson, { chain: 'customChain' }) const genesisState = parseGethGenesisState(gethGenesisJson) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ genesisState, common, }) diff --git a/packages/blockchain/examples/simple.ts b/packages/blockchain/examples/simple.ts index 7d1a0cd545..38c4024bee 100644 --- a/packages/blockchain/examples/simple.ts +++ b/packages/blockchain/examples/simple.ts @@ -1,19 +1,19 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { Block, createBlockFromBlockData } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' import { Common, Hardfork } from '@ethereumjs/common' import { bytesToHex } from '@ethereumjs/util' const main = async () => { const common = new Common({ chain: 'mainnet', hardfork: Hardfork.London }) // Use the safe static constructor which awaits the init method - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: false, // Skipping validation so we can make a simple chain without having to provide complete blocks validateConsensus: false, common, }) // We use minimal data to provide a sequence of blocks (increasing number, difficulty, and then setting parent hash to previous block) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { number: 1n, @@ -23,7 +23,7 @@ const main = async () => { }, { common, setHardfork: true } ) - const block2 = Block.fromBlockData( + const block2 = createBlockFromBlockData( { header: { number: 2n, diff --git a/packages/blockchain/package.json b/packages/blockchain/package.json index 180df21cc4..1ea6f94b08 100644 --- a/packages/blockchain/package.json +++ b/packages/blockchain/package.json @@ -16,7 +16,8 @@ }, "license": "MPL-2.0", "author": "mjbecze ", - "type": "commonjs", + "type": "module", + "sideEffects": false, "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "exports": { diff --git a/packages/blockchain/src/blockchain.ts b/packages/blockchain/src/blockchain.ts index 5c62cbde9c..ff6d8ac8b0 100644 --- a/packages/blockchain/src/blockchain.ts +++ b/packages/blockchain/src/blockchain.ts @@ -1,13 +1,5 @@ -import { Block, BlockHeader } from '@ethereumjs/block' -import { - Chain, - ChainGenesis, - Common, - ConsensusAlgorithm, - ConsensusType, - Hardfork, -} from '@ethereumjs/common' -import { genesisStateRoot as genMerkleGenesisStateRoot } from '@ethereumjs/trie' +import { Block, BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' +import { Chain, Common, ConsensusAlgorithm, ConsensusType, Hardfork } from '@ethereumjs/common' import { AsyncEventEmitter, BIGINT_0, @@ -40,41 +32,10 @@ import type { Consensus, OnBlock, } from './types.js' -import type { BlockData, HeaderData } from '@ethereumjs/block' +import type { HeaderData } from '@ethereumjs/block' import type { CliqueConfig } from '@ethereumjs/common' import type { BigIntLike, DB, DBObject, GenesisState } from '@ethereumjs/util' -/** - * Verkle or Merkle genesis root - * @param genesisState - * @param common - * @returns - */ -async function genGenesisStateRoot( - genesisState: GenesisState, - common: Common -): Promise { - const genCommon = common.copy() - genCommon.setHardforkBy({ - blockNumber: 0, - td: BigInt(genCommon.genesis().difficulty), - timestamp: genCommon.genesis().timestamp, - }) - if (genCommon.isActivatedEIP(6800)) { - throw Error(`Verkle tree state not yet supported`) - } else { - return genMerkleGenesisStateRoot(genesisState) - } -} - -/** - * Returns the genesis state root if chain is well known or an empty state's root otherwise - */ -async function getGenesisStateRoot(chainId: Chain, common: Common): Promise { - const chainGenesis = ChainGenesis[chainId] - return chainGenesis !== undefined ? chainGenesis.stateRoot : genGenesisStateRoot({}, common) -} - /** * This class stores and interacts with blocks. */ @@ -119,118 +80,17 @@ export class Blockchain implements BlockchainInterface { */ private _deletedBlocks: Block[] = [] - /** - * Safe creation of a new Blockchain object awaiting the initialization function, - * encouraged method to use when creating a blockchain object. - * - * @param opts Constructor options, see {@link BlockchainOptions} - */ - - public static async create(opts: BlockchainOptions = {}) { - const blockchain = new Blockchain(opts) - - await blockchain.consensus.setup({ blockchain }) - - let stateRoot = opts.genesisBlock?.header.stateRoot ?? opts.genesisStateRoot - if (stateRoot === undefined) { - if (blockchain._customGenesisState !== undefined) { - stateRoot = await genGenesisStateRoot(blockchain._customGenesisState, blockchain.common) - } else { - stateRoot = await getGenesisStateRoot( - Number(blockchain.common.chainId()) as Chain, - blockchain.common - ) - } - } - - const genesisBlock = opts.genesisBlock ?? blockchain.createGenesisBlock(stateRoot) - - let genesisHash = await blockchain.dbManager.numberToHash(BIGINT_0) - - const dbGenesisBlock = - genesisHash !== undefined ? await blockchain.dbManager.getBlock(genesisHash) : undefined - - // If the DB has a genesis block, then verify that the genesis block in the - // DB is indeed the Genesis block generated or assigned. - if (dbGenesisBlock !== undefined && !equalsBytes(genesisBlock.hash(), dbGenesisBlock.hash())) { - throw new Error( - 'The genesis block in the DB has a different hash than the provided genesis block.' - ) - } - - genesisHash = genesisBlock.hash() - - if (!dbGenesisBlock) { - // If there is no genesis block put the genesis block in the DB. - // For that TD, the BlockOrHeader, and the Lookups have to be saved. - const dbOps: DBOp[] = [] - dbOps.push(DBSetTD(genesisBlock.header.difficulty, BIGINT_0, genesisHash)) - DBSetBlockOrHeader(genesisBlock).map((op) => dbOps.push(op)) - DBSaveLookups(genesisHash, BIGINT_0).map((op) => dbOps.push(op)) - await blockchain.dbManager.batch(dbOps) - await blockchain.consensus.genesisInit(genesisBlock) - } - - // At this point, we can safely set the genesis: - // it is either the one we put in the DB, or it is equal to the one - // which we read from the DB. - blockchain._genesisBlock = genesisBlock - - // load verified iterator heads - const heads = await blockchain.dbManager.getHeads() - blockchain._heads = heads !== undefined ? heads : {} - - // load headerchain head - let hash = await blockchain.dbManager.getHeadHeader() - blockchain._headHeaderHash = hash !== undefined ? hash : genesisHash - - // load blockchain head - hash = await blockchain.dbManager.getHeadBlock() - blockchain._headBlockHash = hash !== undefined ? hash : genesisHash - - if (blockchain._hardforkByHeadBlockNumber) { - const latestHeader = await blockchain._getHeader(blockchain._headHeaderHash) - const td = await blockchain.getParentTD(latestHeader) - await blockchain.checkAndTransitionHardForkByNumber( - latestHeader.number, - td, - latestHeader.timestamp - ) - } - - return blockchain - } - - /** - * Creates a blockchain from a list of block objects, - * objects must be readable by {@link Block.fromBlockData} - * - * @param blockData List of block objects - * @param opts Constructor options, see {@link BlockchainOptions} - */ - public static async fromBlocksData(blocksData: BlockData[], opts: BlockchainOptions = {}) { - const blockchain = await Blockchain.create(opts) - for (const blockData of blocksData) { - const block = Block.fromBlockData(blockData, { - common: blockchain.common, - setHardfork: true, - }) - await blockchain.putBlock(block) - } - return blockchain - } - /** * Creates new Blockchain object. * * @deprecated The direct usage of this constructor is discouraged since * non-finalized async initialization might lead to side effects. Please - * use the async {@link Blockchain.create} constructor instead (same API). + * use the async {@link createBlockchain} constructor instead (same API). * * @param opts An object with the options that this constructor takes. See * {@link BlockchainOptions}. */ - protected constructor(opts: BlockchainOptions = {}) { + constructor(opts: BlockchainOptions = {}) { if (opts.common) { this.common = opts.common } else { @@ -1445,7 +1305,7 @@ export class Blockchain implements BlockchainInterface { header.extraData = concatBytes(new Uint8Array(32), new Uint8Array(65)) } } - return Block.fromBlockData( + return createBlockFromBlockData( { header, withdrawals: common.isActivatedEIP(4895) ? [] : undefined }, { common } ) diff --git a/packages/blockchain/src/consensus/clique.ts b/packages/blockchain/src/consensus/clique.ts index 2a24955411..9f59a7a95b 100644 --- a/packages/blockchain/src/consensus/clique.ts +++ b/packages/blockchain/src/consensus/clique.ts @@ -18,9 +18,8 @@ import type { Blockchain } from '../index.js' import type { Consensus, ConsensusOptions } from '../types.js' import type { Block, BlockHeader } from '@ethereumjs/block' import type { CliqueConfig } from '@ethereumjs/common' -const { debug: createDebugLogger } = debugDefault -const debug = createDebugLogger('blockchain:clique') +const debug = debugDefault('blockchain:clique') // Magic nonce number to vote on adding a new signer export const CLIQUE_NONCE_AUTH = hexToBytes('0xffffffffffffffff') diff --git a/packages/blockchain/src/consensus/ethash.ts b/packages/blockchain/src/consensus/ethash.ts index a5f118dcec..b02f4229c8 100644 --- a/packages/blockchain/src/consensus/ethash.ts +++ b/packages/blockchain/src/consensus/ethash.ts @@ -44,7 +44,7 @@ export class EthashConsensus implements Consensus { public async genesisInit(): Promise {} public async setup({ blockchain }: ConsensusOptions): Promise { this.blockchain = blockchain - this._ethash = new Ethash(this.blockchain.db as any) + this._ethash = new Ethash(this.blockchain!.db as any) } public async newBlock(): Promise {} } diff --git a/packages/blockchain/src/constructors.ts b/packages/blockchain/src/constructors.ts new file mode 100644 index 0000000000..ad310b9d8d --- /dev/null +++ b/packages/blockchain/src/constructors.ts @@ -0,0 +1,112 @@ +import { createBlockFromBlockData } from '@ethereumjs/block' +import { BIGINT_0, equalsBytes } from '@ethereumjs/util' + +import { + Blockchain, + DBSaveLookups, + DBSetBlockOrHeader, + DBSetTD, + genGenesisStateRoot, + getGenesisStateRoot, +} from './index.js' + +import type { BlockchainOptions, DBOp } from './index.js' +import type { BlockData } from '@ethereumjs/block' +import type { Chain } from '@ethereumjs/common' + +export async function createBlockchain(opts: BlockchainOptions = {}) { + const blockchain = new Blockchain(opts) + + await blockchain.consensus.setup({ blockchain }) + + let stateRoot = opts.genesisBlock?.header.stateRoot ?? opts.genesisStateRoot + if (stateRoot === undefined) { + if (blockchain['_customGenesisState'] !== undefined) { + stateRoot = await genGenesisStateRoot(blockchain['_customGenesisState'], blockchain.common) + } else { + stateRoot = await getGenesisStateRoot( + Number(blockchain.common.chainId()) as Chain, + blockchain.common + ) + } + } + + const genesisBlock = opts.genesisBlock ?? blockchain.createGenesisBlock(stateRoot) + + let genesisHash = await blockchain.dbManager.numberToHash(BIGINT_0) + + const dbGenesisBlock = + genesisHash !== undefined ? await blockchain.dbManager.getBlock(genesisHash) : undefined + + // If the DB has a genesis block, then verify that the genesis block in the + // DB is indeed the Genesis block generated or assigned. + if (dbGenesisBlock !== undefined && !equalsBytes(genesisBlock.hash(), dbGenesisBlock.hash())) { + throw new Error( + 'The genesis block in the DB has a different hash than the provided genesis block.' + ) + } + + genesisHash = genesisBlock.hash() + + if (!dbGenesisBlock) { + // If there is no genesis block put the genesis block in the DB. + // For that TD, the BlockOrHeader, and the Lookups have to be saved. + const dbOps: DBOp[] = [] + dbOps.push(DBSetTD(genesisBlock.header.difficulty, BIGINT_0, genesisHash)) + DBSetBlockOrHeader(genesisBlock).map((op) => dbOps.push(op)) + DBSaveLookups(genesisHash, BIGINT_0).map((op) => dbOps.push(op)) + await blockchain.dbManager.batch(dbOps) + await blockchain.consensus.genesisInit(genesisBlock) + } + + // At this point, we can safely set the genesis: + // it is either the one we put in the DB, or it is equal to the one + // which we read from the DB. + blockchain['_genesisBlock'] = genesisBlock + + // load verified iterator heads + const heads = await blockchain.dbManager.getHeads() + blockchain['_heads'] = heads !== undefined ? heads : {} + + // load headerchain head + let hash = await blockchain.dbManager.getHeadHeader() + blockchain['_headHeaderHash'] = hash !== undefined ? hash : genesisHash + + // load blockchain head + hash = await blockchain.dbManager.getHeadBlock() + blockchain['_headBlockHash'] = hash !== undefined ? hash : genesisHash + + if (blockchain['_hardforkByHeadBlockNumber']) { + const latestHeader = await blockchain['_getHeader'](blockchain['_headHeaderHash']) + const td = await blockchain.getParentTD(latestHeader) + await blockchain.checkAndTransitionHardForkByNumber( + latestHeader.number, + td, + latestHeader.timestamp + ) + } + + return blockchain +} + +/** + * Creates a blockchain from a list of block objects, + * objects must be readable by {@link Block.fromBlockData} + * + * @param blockData List of block objects + * @param opts Constructor options, see {@link BlockchainOptions} + */ +export async function createBlockchainFromBlocksData( + blocksData: BlockData[], + opts: BlockchainOptions = {} +) { + const blockchain = await createBlockchain(opts) + for (const blockData of blocksData) { + const block = createBlockFromBlockData(blockData, { + common: blockchain.common, + setHardfork: true, + }) + await blockchain.putBlock(block) + } + return blockchain +} diff --git a/packages/blockchain/src/db/manager.ts b/packages/blockchain/src/db/manager.ts index e7e21d63aa..c66310ea5b 100644 --- a/packages/blockchain/src/db/manager.ts +++ b/packages/blockchain/src/db/manager.ts @@ -1,4 +1,4 @@ -import { Block, BlockHeader, valuesArrayToHeaderData } from '@ethereumjs/block' +import { BlockHeader, createBlockFromValuesArray, valuesArrayToHeaderData } from '@ethereumjs/block' import { RLP } from '@ethereumjs/rlp' import { BIGINT_0, @@ -15,7 +15,7 @@ import { Cache } from './cache.js' import { DBOp, DBTarget } from './operation.js' import type { DatabaseKey } from './operation.js' -import type { BlockBodyBytes, BlockBytes, BlockOptions } from '@ethereumjs/block' +import type { Block, BlockBodyBytes, BlockBytes, BlockOptions } from '@ethereumjs/block' import type { Common } from '@ethereumjs/common' import type { BatchDBOp, DB, DBObject, DelBatch, PutBatch } from '@ethereumjs/util' @@ -148,7 +148,7 @@ export class DBManager { } else { opts.setHardfork = await this.getTotalDifficulty(header.parentHash, number - BIGINT_1) } - return Block.fromValuesArray(blockData, opts) + return createBlockFromValuesArray(blockData, opts) } /** diff --git a/packages/blockchain/src/helpers.ts b/packages/blockchain/src/helpers.ts new file mode 100644 index 0000000000..2d4d6c3046 --- /dev/null +++ b/packages/blockchain/src/helpers.ts @@ -0,0 +1,43 @@ +import { ChainGenesis } from '@ethereumjs/common' +import { genesisStateRoot as genMerkleGenesisStateRoot } from '@ethereumjs/trie' + +import type { Chain, Common } from '@ethereumjs/common' +import type { GenesisState } from '@ethereumjs/util' + +/** + * Safe creation of a new Blockchain object awaiting the initialization function, + * encouraged method to use when creating a blockchain object. + * + * @param opts Constructor options, see {@link BlockchainOptions} + */ + +/** + * Verkle or Merkle genesis root + * @param genesisState + * @param common + * @returns + */ +export async function genGenesisStateRoot( + genesisState: GenesisState, + common: Common +): Promise { + const genCommon = common.copy() + genCommon.setHardforkBy({ + blockNumber: 0, + td: BigInt(genCommon.genesis().difficulty), + timestamp: genCommon.genesis().timestamp, + }) + if (genCommon.isActivatedEIP(6800)) { + throw Error(`Verkle tree state not yet supported`) + } else { + return genMerkleGenesisStateRoot(genesisState) + } +} + +/** + * Returns the genesis state root if chain is well known or an empty state's root otherwise + */ +export async function getGenesisStateRoot(chainId: Chain, common: Common): Promise { + const chainGenesis = ChainGenesis[chainId] + return chainGenesis !== undefined ? chainGenesis.stateRoot : genGenesisStateRoot({}, common) +} diff --git a/packages/blockchain/src/index.ts b/packages/blockchain/src/index.ts index 71671df1e4..c554910e10 100644 --- a/packages/blockchain/src/index.ts +++ b/packages/blockchain/src/index.ts @@ -1,5 +1,6 @@ export { Blockchain } from './blockchain.js' export { CasperConsensus, CliqueConsensus, EthashConsensus } from './consensus/index.js' +export * from './constructors.js' export { DBOp, DBSaveLookups, @@ -7,4 +8,5 @@ export { DBSetHashToNumber, DBSetTD, } from './db/helpers.js' +export * from './helpers.js' export * from './types.js' diff --git a/packages/blockchain/src/types.ts b/packages/blockchain/src/types.ts index fd810759a1..7ae0b75539 100644 --- a/packages/blockchain/src/types.ts +++ b/packages/blockchain/src/types.ts @@ -1,4 +1,4 @@ -import type { Blockchain } from '.' +import type { Blockchain } from './index.js' import type { Block, BlockHeader } from '@ethereumjs/block' import type { Common, ConsensusAlgorithm } from '@ethereumjs/common' import type { AsyncEventEmitter, DB, DBObject, GenesisState } from '@ethereumjs/util' diff --git a/packages/blockchain/test/blockValidation.spec.ts b/packages/blockchain/test/blockValidation.spec.ts index e0dcb3d63c..2cdef214a8 100644 --- a/packages/blockchain/test/blockValidation.spec.ts +++ b/packages/blockchain/test/blockValidation.spec.ts @@ -1,18 +1,18 @@ -import { Block, BlockHeader } from '@ethereumjs/block' +import { BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { KECCAK256_RLP, bytesToHex, randomBytes } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak.js' import { assert, describe, expect, it } from 'vitest' -import { Blockchain } from '../src/index.js' +import { createBlockchain } from '../src/index.js' import { createBlock } from './util.js' describe('[Blockchain]: Block validation tests', () => { it('should throw if an uncle is included before', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - const blockchain = await Blockchain.create({ common, validateConsensus: false }) + const blockchain = await createBlockchain({ common, validateConsensus: false }) const genesis = blockchain.genesisBlock @@ -39,11 +39,11 @@ describe('[Blockchain]: Block validation tests', () => { it('should throw if the uncle parent block is not part of the canonical chain', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - const blockchain = await Blockchain.create({ common, validateConsensus: false }) + const blockchain = await createBlockchain({ common, validateConsensus: false }) const genesis = blockchain.genesisBlock - const emptyBlock = Block.fromBlockData({ header: { number: BigInt(1) } }, { common }) + const emptyBlock = createBlockFromBlockData({ header: { number: BigInt(1) } }, { common }) const uncleBlock = createBlock(emptyBlock, 'uncle', [], common) const block1 = createBlock(genesis, 'block1', [], common) @@ -66,7 +66,7 @@ describe('[Blockchain]: Block validation tests', () => { it('should throw if the uncle is too old', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - const blockchain = await Blockchain.create({ common, validateConsensus: false }) + const blockchain = await createBlockchain({ common, validateConsensus: false }) const genesis = blockchain.genesisBlock @@ -99,7 +99,7 @@ describe('[Blockchain]: Block validation tests', () => { it('should throw if uncle is too young', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - const blockchain = await Blockchain.create({ common, validateConsensus: false }) + const blockchain = await createBlockchain({ common, validateConsensus: false }) const genesis = blockchain.genesisBlock @@ -121,11 +121,11 @@ describe('[Blockchain]: Block validation tests', () => { it('should throw if the uncle header is invalid', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - const blockchain = await Blockchain.create({ common, validateConsensus: false }) + const blockchain = await createBlockchain({ common, validateConsensus: false }) const genesis = blockchain.genesisBlock - const uncleBlock = Block.fromBlockData( + const uncleBlock = createBlockFromBlockData( { header: { number: genesis.header.number + BigInt(1), @@ -155,7 +155,7 @@ describe('[Blockchain]: Block validation tests', () => { it('throws if uncle is a canonical block', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - const blockchain = await Blockchain.create({ common, validateConsensus: false }) + const blockchain = await createBlockchain({ common, validateConsensus: false }) const genesis = blockchain.genesisBlock @@ -178,7 +178,7 @@ describe('[Blockchain]: Block validation tests', () => { it('successfully validates uncles', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - const blockchain = await Blockchain.create({ common, validateConsensus: false }) + const blockchain = await createBlockchain({ common, validateConsensus: false }) const genesis = blockchain.genesisBlock @@ -204,7 +204,7 @@ describe('[Blockchain]: Block validation tests', () => { hardfork: Hardfork.London, }) - const blockchain = await Blockchain.create({ common, validateConsensus: false }) + const blockchain = await createBlockchain({ common, validateConsensus: false }) const genesis = blockchain.genesisBlock // Small hack to hack in the activation block number @@ -234,7 +234,7 @@ describe('[Blockchain]: Block validation tests', () => { } ) - const block = Block.fromBlockData({ header }, { common }) + const block = createBlockFromBlockData({ header }, { common }) await blockchain.putBlock(block) try { const header = BlockHeader.fromHeaderData( @@ -250,7 +250,7 @@ describe('[Blockchain]: Block validation tests', () => { common, } ) - const block2 = Block.fromBlockData({ header }, { common }) + const block2 = createBlockFromBlockData({ header }, { common }) await blockchain.putBlock(block2) } catch (e: any) { const expectedError = 'Invalid block: base fee not correct' @@ -298,7 +298,7 @@ describe('[Blockchain]: Block validation tests', () => { return BigInt(0) } - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateConsensus: false, validateBlocks: false, @@ -307,7 +307,7 @@ describe('[Blockchain]: Block validation tests', () => { common.setHardfork(Hardfork.Berlin) const mainnetForkBlock = common.hardforkBlock(Hardfork.London) - const rootBlock = Block.fromBlockData( + const rootBlock = createBlockFromBlockData( { header: { parentHash: blockchain.genesisBlock.hash(), @@ -345,7 +345,7 @@ describe('[Blockchain]: Block validation tests', () => { forkBlockHeaderData.uncleHash = bytesToHex(keccak256(RLP.encode([uncleHeader.raw()]))) - const forkBlock_ValidCommon = Block.fromBlockData( + const forkBlock_ValidCommon = createBlockFromBlockData( { header: forkBlockHeaderData, uncleHeaders: [uncleHeaderData], @@ -365,7 +365,7 @@ describe('[Blockchain]: Block validation tests', () => { assert.doesNotThrow( () => - Block.fromBlockData( + createBlockFromBlockData( { header: forkBlockHeaderData, uncleHeaders: [uncleHeaderData], @@ -387,11 +387,11 @@ describe('EIP 7685: requests field validation tests', () => { hardfork: Hardfork.Cancun, eips: [7685, 1559, 4895, 4844, 4788], }) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateConsensus: false, }) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { number: 1n, @@ -407,7 +407,7 @@ describe('EIP 7685: requests field validation tests', () => { await expect(async () => blockchain.putBlock(block)).rejects.toThrow('invalid requestsRoot') - const blockWithRequest = Block.fromBlockData( + const blockWithRequest = createBlockFromBlockData( { header: { number: 1n, @@ -417,7 +417,7 @@ describe('EIP 7685: requests field validation tests', () => { timestamp: blockchain.genesisBlock.header.timestamp + 1n, gasLimit: 5000, }, - requests: [{ type: 0x1, bytes: randomBytes(12), serialize: () => randomBytes(32) }], + requests: [{ type: 0x1, bytes: randomBytes(12), serialize: () => randomBytes(32) } as any], }, { common } ) diff --git a/packages/blockchain/test/clique.spec.ts b/packages/blockchain/test/clique.spec.ts index 24d630565a..832a8b2b7e 100644 --- a/packages/blockchain/test/clique.spec.ts +++ b/packages/blockchain/test/clique.spec.ts @@ -1,12 +1,21 @@ -import { Block } from '@ethereumjs/block' -import { Chain, Common, ConsensusAlgorithm, ConsensusType, Hardfork } from '@ethereumjs/common' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { + Chain, + Common, + ConsensusAlgorithm, + ConsensusType, + Hardfork, + createCustomCommon, +} from '@ethereumjs/common' import { Address, concatBytes, hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' import { CLIQUE_NONCE_AUTH, CLIQUE_NONCE_DROP } from '../src/consensus/clique.js' -import { Blockchain } from '../src/index.js' +import { createBlockchain } from '../src/index.js' import type { CliqueConsensus } from '../src/consensus/clique.js' +import type { Blockchain } from '../src/index.js' +import type { Block } from '@ethereumjs/block' import type { CliqueConfig } from '@ethereumjs/common' const COMMON = new Common({ chain: Chain.Goerli, hardfork: Hardfork.Chainstart }) @@ -76,13 +85,13 @@ const initWithSigners = async (signers: Signer[], common?: Common) => { ...signers.map((s) => s.address.toBytes()), new Uint8Array(65) ) - const genesisBlock = Block.fromBlockData( + const genesisBlock = createBlockFromBlockData( { header: { gasLimit: GAS_LIMIT, extraData } }, { common } ) blocks.push(genesisBlock) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: true, genesisBlock, @@ -140,7 +149,7 @@ function getBlock( // set signer const cliqueSigner = signer.privateKey - return Block.fromBlockData(blockData, { common, freeze: false, cliqueSigner }) + return createBlockFromBlockData(blockData, { common, freeze: false, cliqueSigner }) } const addNextBlockReorg = async ( @@ -182,7 +191,7 @@ const addNextBlock = async ( describe('Clique: Initialization', () => { it('should initialize a clique blockchain', async () => { const common = new Common({ chain: Chain.Goerli, hardfork: Hardfork.Chainstart }) - const blockchain = await Blockchain.create({ common }) + const blockchain = await createBlockchain({ common }) const head = await blockchain.getIteratorHead() assert.deepEqual(head.hash(), blockchain.genesisBlock.hash(), 'correct genesis hash') @@ -207,7 +216,7 @@ describe('Clique: Initialization', () => { unauthorizedSigner.toBytes(), new Uint8Array(65) ) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { number, extraData } }, { common: COMMON, cliqueSigner: A.privateKey } ) @@ -229,7 +238,7 @@ describe('Clique: Initialization', () => { const number = BigInt(2) const extraData = new Uint8Array(97) let difficulty = BigInt(5) - let block = Block.fromBlockData( + let block = createBlockFromBlockData( { header: { number, @@ -254,7 +263,7 @@ describe('Clique: Initialization', () => { difficulty = BigInt(1) const cliqueSigner = A.privateKey - block = Block.fromBlockData( + block = createBlockFromBlockData( { header: { number, @@ -606,7 +615,7 @@ describe('Clique: Initialization', () => { }) it('Clique Voting: Epoch transitions reset all votes to allow chain checkpointing', async () => { - const common = Common.custom( + const common = createCustomCommon( { consensus: { type: ConsensusType.ProofOfAuthority, @@ -662,7 +671,7 @@ describe('Clique: Initialization', () => { }) it('Clique Voting: Recent signatures should not reset on checkpoint blocks imported in a batch', async () => { - const common = Common.custom( + const common = createCustomCommon( { consensus: { type: ConsensusType.ProofOfAuthority, @@ -809,7 +818,7 @@ describe('clique: reorgs', () => { it( 'Two signers, voting to add one other signer, epoch transition, then reorg and revoke this addition', async (st) => { - const common = Common.custom( + const common = createCustomCommon( { consensus: { type: ConsensusType.ProofOfAuthority, diff --git a/packages/blockchain/test/customConsensus.spec.ts b/packages/blockchain/test/customConsensus.spec.ts index 27f1a760da..23dc717441 100644 --- a/packages/blockchain/test/customConsensus.spec.ts +++ b/packages/blockchain/test/customConsensus.spec.ts @@ -1,12 +1,12 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Common, Hardfork } from '@ethereumjs/common' import { bytesToHex } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { Blockchain, EthashConsensus } from '../src/index.js' +import { EthashConsensus, createBlockchain } from '../src/index.js' import type { Consensus } from '../src/index.js' -import type { BlockHeader } from '@ethereumjs/block' +import type { Block, BlockHeader } from '@ethereumjs/block' class fibonacciConsensus implements Consensus { algorithm: string @@ -46,7 +46,7 @@ describe('Optional consensus parameter in blockchain constructor', () => { const common = new Common({ chain: 'mainnet', hardfork: Hardfork.Chainstart }) const consensus = new fibonacciConsensus() try { - const blockchain = await Blockchain.create({ common, consensus }) + const blockchain = await createBlockchain({ common, consensus }) assert.equal( (blockchain.consensus as fibonacciConsensus).algorithm, 'fibonacciConsensus', @@ -62,8 +62,8 @@ describe('Custom consensus validation rules', () => { it('should validat custom consensus rules', async () => { const common = new Common({ chain: 'mainnet', hardfork: Hardfork.Chainstart }) const consensus = new fibonacciConsensus() - const blockchain = await Blockchain.create({ common, consensus }) - const block = Block.fromBlockData( + const blockchain = await createBlockchain({ common, consensus }) + const block = createBlockFromBlockData( { header: { number: 1n, @@ -88,7 +88,7 @@ describe('Custom consensus validation rules', () => { assert.fail('should have put block with valid difficulty and extraData') } - const blockWithBadDifficulty = Block.fromBlockData( + const blockWithBadDifficulty = createBlockFromBlockData( { header: { number: 2n, @@ -110,7 +110,7 @@ describe('Custom consensus validation rules', () => { ) } - const blockWithBadExtraData = Block.fromBlockData( + const blockWithBadExtraData = createBlockFromBlockData( { header: { number: 2n, @@ -140,7 +140,7 @@ describe('consensus transition checks', () => { it('should transition correctly', async () => { const common = new Common({ chain: 'mainnet', hardfork: Hardfork.Chainstart }) const consensus = new fibonacciConsensus() - const blockchain = await Blockchain.create({ common, consensus }) + const blockchain = await createBlockchain({ common, consensus }) try { await blockchain.checkAndTransitionHardForkByNumber(5n) diff --git a/packages/blockchain/test/index.spec.ts b/packages/blockchain/test/index.spec.ts index 4229df50bb..937ff6a187 100644 --- a/packages/blockchain/test/index.spec.ts +++ b/packages/blockchain/test/index.spec.ts @@ -1,20 +1,24 @@ -import { Block, BlockHeader } from '@ethereumjs/block' +import { + BlockHeader, + createBlockFromBlockData, + createBlockFromRLPSerializedBlock, +} from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { MapDB, bytesToHex, equalsBytes, hexToBytes, utf8ToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { Blockchain } from '../src/index.js' +import { Blockchain, createBlockchain, createBlockchainFromBlocksData } from '../src/index.js' import blocksData from './testdata/blocks_mainnet.json' import * as testDataPreLondon from './testdata/testdata_pre-london.json' import { createTestDB, generateBlockchain, generateBlocks, isConsecutive } from './util.js' -import type { BlockData, BlockOptions } from '@ethereumjs/block' +import type { Block, BlockData, BlockOptions } from '@ethereumjs/block' import type { PrefixedHexString } from '@ethereumjs/util' describe('blockchain test', () => { it('should not crash on getting head of a blockchain without a genesis', async () => { - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: false, }) @@ -23,7 +27,7 @@ describe('blockchain test', () => { it('should initialize correctly', async () => { const common = new Common({ chain: Chain.Mainnet }) - let blockchain = await Blockchain.create({ common }) + let blockchain = await createBlockchain({ common }) const iteratorHead = await blockchain.getIteratorHead() @@ -33,7 +37,7 @@ describe('blockchain test', () => { 'correct genesis hash (getIteratorHead())' ) - blockchain = await Blockchain.create({ common, hardforkByHeadBlockNumber: true }) + blockchain = await createBlockchain({ common, hardforkByHeadBlockNumber: true }) assert.equal( common.hardfork(), 'chainstart', @@ -45,7 +49,7 @@ describe('blockchain test', () => { // Taken from: https://github.com/eth-clients/holesky/blob/f1d14b9a80085c3f0cb9d729fea9172cde445588/README.md#hole%C5%A1ky-hole%C5%A1ovice-testnet const holeskyHash = '0xb5f7f912443c940f21fd611f12828d75b534364ed9e95ca4e307729a4661bde4' const common = new Common({ chain: Chain.Holesky }) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, }) const genesisHash = blockchain.genesisBlock.hash() @@ -53,9 +57,9 @@ describe('blockchain test', () => { assert.deepEqual(bytesToHex(genesisHash), holeskyHash, 'correct genesis hash for holesky') }) - it('should initialize correctly with Blockchain.fromBlocksData()', async () => { + it('should initialize correctly with createBlockchainFromBlocksData()', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - const blockchain = await Blockchain.fromBlocksData(blocksData as BlockData[], { + const blockchain = await createBlockchainFromBlocksData(blocksData as BlockData[], { validateBlocks: true, validateConsensus: false, common, @@ -67,11 +71,11 @@ describe('blockchain test', () => { it('should only initialize with supported consensus validation options', async () => { let common = new Common({ chain: Chain.Mainnet }) try { - await Blockchain.create({ common, validateConsensus: true }) - await Blockchain.create({ common, validateBlocks: true }) + await createBlockchain({ common, validateConsensus: true }) + await createBlockchain({ common, validateBlocks: true }) common = new Common({ chain: Chain.Goerli }) - await Blockchain.create({ common, validateConsensus: true }) - const chain = await Blockchain.create({ common, validateBlocks: true }) + await createBlockchain({ common, validateConsensus: true }) + const chain = await createBlockchain({ common, validateBlocks: true }) assert.ok(chain instanceof Blockchain, 'should not throw') } catch (error) { assert.fail('show not have thrown') @@ -80,8 +84,8 @@ describe('blockchain test', () => { it('should add a genesis block without errors', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - const genesisBlock = Block.fromBlockData({ header: { number: 0 } }, { common }) - const blockchain = await Blockchain.create({ + const genesisBlock = createBlockFromBlockData({ header: { number: 0 } }, { common }) + const blockchain = await createBlockchain({ common, validateBlocks: true, validateConsensus: false, @@ -95,9 +99,9 @@ describe('blockchain test', () => { }) it('should not validate a block incorrectly flagged as genesis', async () => { - const genesisBlock = Block.fromBlockData({ header: { number: BigInt(8) } }) + const genesisBlock = createBlockFromBlockData({ header: { number: BigInt(8) } }) try { - await Blockchain.create({ + await createBlockchain({ validateBlocks: true, validateConsensus: false, genesisBlock, @@ -108,7 +112,7 @@ describe('blockchain test', () => { }) it('should initialize with a genesis block', async () => { - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: false, }) @@ -121,10 +125,10 @@ describe('blockchain test', () => { const gasLimit = 8000000 const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const genesisBlock = Block.fromBlockData({ header: { gasLimit } }, { common }) + const genesisBlock = createBlockFromBlockData({ header: { gasLimit } }, { common }) blocks.push(genesisBlock) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: false, genesisBlock, @@ -142,7 +146,7 @@ describe('blockchain test', () => { gasLimit, }, } - const block = Block.fromBlockData(blockData, { + const block = createBlockFromBlockData(blockData, { calcDifficultyFromHeader: lastBlock.header, common, }) @@ -166,10 +170,10 @@ describe('blockchain test', () => { const gasLimit = 8000000 const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const genesisBlock = Block.fromBlockData({ header: { gasLimit } }, { common }) + const genesisBlock = createBlockFromBlockData({ header: { gasLimit } }, { common }) blocks.push(genesisBlock) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: false, genesisBlock, @@ -184,7 +188,7 @@ describe('blockchain test', () => { gasLimit, }, } - const block = Block.fromBlockData(blockData, { + const block = createBlockFromBlockData(blockData, { calcDifficultyFromHeader: genesisBlock.header, common, }) @@ -202,9 +206,9 @@ describe('blockchain test', () => { it('getBlock(): should get block by hash / not existing', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) const gasLimit = 8000000 - const genesisBlock = Block.fromBlockData({ header: { gasLimit } }, { common }) + const genesisBlock = createBlockFromBlockData({ header: { gasLimit } }, { common }) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateBlocks: true, validateConsensus: false, @@ -502,7 +506,7 @@ describe('blockchain test', () => { it('should put one block at a time', async () => { const blocks = generateBlocks(15) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: false, genesisBlock: blocks[0], @@ -514,7 +518,7 @@ describe('blockchain test', () => { it('should test nil bodies / throw', async () => { const blocks = generateBlocks(3) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: false, validateConsensus: false, genesisBlock: blocks[0], @@ -545,9 +549,9 @@ describe('blockchain test', () => { it('should put multiple blocks at once', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) const blocks: Block[] = [] - const genesisBlock = Block.fromBlockData({ header: { gasLimit: 8000000 } }, { common }) + const genesisBlock = createBlockFromBlockData({ header: { gasLimit: 8000000 } }, { common }) blocks.push(...generateBlocks(15, [genesisBlock])) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: false, genesisBlock, @@ -556,14 +560,14 @@ describe('blockchain test', () => { }) it('should validate', async () => { - const genesisBlock = Block.fromBlockData({ header: { gasLimit: 8000000 } }) - const blockchain = await Blockchain.create({ + const genesisBlock = createBlockFromBlockData({ header: { gasLimit: 8000000 } }) + const blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: false, genesisBlock, }) - const invalidBlock = Block.fromBlockData({ header: { number: 50 } }) + const invalidBlock = createBlockFromBlockData({ header: { number: 50 } }) try { await blockchain.putBlock(invalidBlock) assert.fail('should not validate an invalid block') @@ -575,15 +579,15 @@ describe('blockchain test', () => { it('should add block with body', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) const genesisRlp = hexToBytes(testDataPreLondon.genesisRLP as PrefixedHexString) - const genesisBlock = Block.fromRLPSerializedBlock(genesisRlp, { common }) - const blockchain = await Blockchain.create({ + const genesisBlock = createBlockFromRLPSerializedBlock(genesisRlp, { common }) + const blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: false, genesisBlock, }) const blockRlp = hexToBytes(testDataPreLondon.blocks[0].rlp as PrefixedHexString) - const block = Block.fromRLPSerializedBlock(blockRlp, { common }) + const block = createBlockFromRLPSerializedBlock(blockRlp, { common }) await blockchain.putBlock(block) }) @@ -592,7 +596,7 @@ describe('blockchain test', () => { if (typeof genesis === 'undefined') { return assert.fail('genesis not defined!') } - const blockchain = await Blockchain.create({ db, genesisBlock: genesis }) + const blockchain = await createBlockchain({ db, genesisBlock: genesis }) const number = await blockchain.dbManager.hashToNumber(genesis?.hash()) assert.equal(number, BigInt(0), 'should perform _hashToNumber correctly') @@ -610,8 +614,8 @@ describe('blockchain test', () => { const gasLimit = 8000000 const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const genesisBlock = Block.fromBlockData({ header: { gasLimit } }, { common }) - let blockchain = await Blockchain.create({ + const genesisBlock = createBlockFromBlockData({ header: { gasLimit } }, { common }) + let blockchain = await createBlockchain({ db, validateBlocks: true, validateConsensus: false, @@ -630,7 +634,7 @@ describe('blockchain test', () => { }) await blockchain.putHeader(header) - blockchain = await Blockchain.create({ + blockchain = await createBlockchain({ db, validateBlocks: true, validateConsensus: false, @@ -649,8 +653,8 @@ describe('blockchain test', () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) const opts: BlockOptions = { common } - const genesisBlock = Block.fromBlockData({ header: { gasLimit } }, opts) - const blockchain = await Blockchain.create({ + const genesisBlock = createBlockFromBlockData({ header: { gasLimit } }, opts) + const blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: false, genesisBlock, @@ -665,7 +669,7 @@ describe('blockchain test', () => { }, } opts.calcDifficultyFromHeader = genesisBlock.header - const block = Block.fromBlockData(blockData, opts) + const block = createBlockFromBlockData(blockData, opts) const headerData1 = { number: 1, @@ -708,7 +712,7 @@ describe('blockchain test', () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) const gasLimit = 8000000 - const genesisBlock = Block.fromBlockData({ header: { gasLimit } }, { common }) + const genesisBlock = createBlockFromBlockData({ header: { gasLimit } }, { common }) const blockData1 = { header: { @@ -726,14 +730,17 @@ describe('blockchain test', () => { const blocks = [ genesisBlock, - Block.fromBlockData(blockData1, { common, calcDifficultyFromHeader: genesisBlock.header }), - Block.fromBlockData(blockData2, { + createBlockFromBlockData(blockData1, { + common, + calcDifficultyFromHeader: genesisBlock.header, + }), + createBlockFromBlockData(blockData2, { common: new Common({ chain: Chain.Sepolia, hardfork: Hardfork.Chainstart }), calcDifficultyFromHeader: genesisBlock.header, }), ] - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateBlocks: true, validateConsensus: false, @@ -762,7 +769,7 @@ describe('initialization tests', () => { chain: Chain.Mainnet, hardfork: Hardfork.Chainstart, }) - const blockchain = await Blockchain.create({ common }) + const blockchain = await createBlockchain({ common }) const genesisHash = blockchain.genesisBlock.hash() assert.deepEqual( @@ -773,7 +780,7 @@ describe('initialization tests', () => { const db = blockchain.db - const newBlockchain = await Blockchain.create({ db, common }) + const newBlockchain = await createBlockchain({ db, common }) assert.deepEqual( (await newBlockchain.getIteratorHead()).hash(), @@ -784,7 +791,7 @@ describe('initialization tests', () => { it('should allow to put a custom genesis block', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - const genesisBlock = Block.fromBlockData( + const genesisBlock = createBlockFromBlockData( { header: { extraData: utf8ToBytes('custom extra data'), @@ -793,7 +800,7 @@ describe('initialization tests', () => { { common } ) const hash = genesisBlock.hash() - const blockchain = await Blockchain.create({ common, genesisBlock }) + const blockchain = await createBlockchain({ common, genesisBlock }) const db = blockchain.db assert.deepEqual( @@ -802,7 +809,7 @@ describe('initialization tests', () => { 'blockchain should put custom genesis block' ) - const newBlockchain = await Blockchain.create({ db, genesisBlock }) + const newBlockchain = await createBlockchain({ db, genesisBlock }) assert.deepEqual( (await newBlockchain.getIteratorHead()).hash(), hash, @@ -812,7 +819,7 @@ describe('initialization tests', () => { it('should not allow to change the genesis block in the database', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - const genesisBlock = Block.fromBlockData( + const genesisBlock = createBlockFromBlockData( { header: { extraData: utf8ToBytes('custom extra data'), @@ -821,10 +828,10 @@ describe('initialization tests', () => { { common } ) const hash = genesisBlock.hash() - const blockchain = await Blockchain.create({ common, genesisBlock }) + const blockchain = await createBlockchain({ common, genesisBlock }) const db = blockchain.db - const otherGenesisBlock = Block.fromBlockData( + const otherGenesisBlock = createBlockFromBlockData( { header: { extraData: utf8ToBytes('other extra data'), @@ -852,7 +859,7 @@ describe('initialization tests', () => { // trying to input a genesis block which differs from the one in db should throw on creation try { - await Blockchain.create({ genesisBlock: otherGenesisBlock, db }) + await createBlockchain({ genesisBlock: otherGenesisBlock, db }) assert.fail('creating blockchain with different genesis block than in db did not throw') } catch (e: any) { assert.equal( @@ -866,7 +873,7 @@ describe('initialization tests', () => { it('should correctly derive mainnet genesis block hash and stateRoot', async () => { const common = new Common({ chain: Chain.Mainnet }) - const blockchain = await Blockchain.create({ common }) + const blockchain = await createBlockchain({ common }) const mainnetGenesisBlockHash = hexToBytes( '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' ) diff --git a/packages/blockchain/test/iterator.spec.ts b/packages/blockchain/test/iterator.spec.ts index 55e13da868..2e1690eed1 100644 --- a/packages/blockchain/test/iterator.spec.ts +++ b/packages/blockchain/test/iterator.spec.ts @@ -1,7 +1,7 @@ import { bytesToHex, equalsBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { Blockchain } from '../src/index.js' +import { createBlockchain } from '../src/index.js' import { createTestDB, generateBlockchain, generateConsecutiveBlock } from './util.js' @@ -165,7 +165,7 @@ describe('blockchain test', () => { }) it('should not call iterator function in an empty blockchain', async () => { - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: false, }) @@ -178,7 +178,7 @@ describe('blockchain test', () => { it('should get heads', async () => { const [db, genesis] = await createTestDB() - const blockchain = await Blockchain.create({ db, genesisBlock: genesis }) + const blockchain = await createBlockchain({ db, genesisBlock: genesis }) const head = await blockchain.getIteratorHead() if (typeof genesis !== 'undefined') { diff --git a/packages/blockchain/test/pos.spec.ts b/packages/blockchain/test/pos.spec.ts index bad278fdad..4ad1c33d5c 100644 --- a/packages/blockchain/test/pos.spec.ts +++ b/packages/blockchain/test/pos.spec.ts @@ -1,12 +1,15 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { bytesToHex } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { Blockchain } from '../src/index.js' +import { createBlockchain } from '../src/index.js' import * as testnet from './testdata/testnet.json' +import type { Blockchain } from '../src/index.js' +import type { Block } from '@ethereumjs/block' + const buildChain = async (blockchain: Blockchain, common: Common, height: number) => { const blocks: Block[] = [] const londonBlockNumber = Number(common.hardforkBlock('london')!) @@ -19,7 +22,7 @@ const buildChain = async (blockchain: Blockchain, common: Common, height: number } else if (number > londonBlockNumber) { baseFeePerGas = blocks[number - 1].header.calcNextBaseFee() } - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { number, @@ -58,7 +61,7 @@ describe('Proof of Stake - inserting blocks into blockchain', () => { for (const s of scenarios) { it('should pass', async () => { - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: false, common: s.common, @@ -88,7 +91,7 @@ describe('Proof of Stake - inserting blocks into blockchain', () => { ) const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) - const powBlock = Block.fromBlockData( + const powBlock = createBlockFromBlockData( { header: { number: 16, diff --git a/packages/blockchain/test/reorg.spec.ts b/packages/blockchain/test/reorg.spec.ts index f3d0063191..5873765d12 100644 --- a/packages/blockchain/test/reorg.spec.ts +++ b/packages/blockchain/test/reorg.spec.ts @@ -1,19 +1,20 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { Address, equalsBytes, hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' import { CLIQUE_NONCE_AUTH } from '../src/consensus/clique.js' -import { Blockchain } from '../src/index.js' +import { createBlockchain } from '../src/index.js' import { generateConsecutiveBlock } from './util.js' import type { CliqueConsensus } from '../src/consensus/clique.js' +import type { Block } from '@ethereumjs/block' describe('reorg tests', () => { it('should correctly reorg the chain if the total difficulty is higher on a lower block number than the current head block', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.MuirGlacier }) - const genesis = Block.fromBlockData( + const genesis = createBlockFromBlockData( { header: { number: BigInt(0), @@ -66,11 +67,11 @@ describe('reorg tests', () => { it('should correctly reorg a poa chain and remove blocks from clique snapshots', async () => { const common = new Common({ chain: Chain.Goerli, hardfork: Hardfork.Chainstart }) - const genesisBlock = Block.fromBlockData( + const genesisBlock = createBlockFromBlockData( { header: { extraData: new Uint8Array(97) } }, { common } ) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: false, validateConsensus: false, common, @@ -87,7 +88,7 @@ describe('reorg tests', () => { const beneficiary1 = new Address(new Uint8Array(20).fill(1)) const beneficiary2 = new Address(new Uint8Array(20).fill(2)) - const block1_low = Block.fromBlockData( + const block1_low = createBlockFromBlockData( { header: { ...base, @@ -98,7 +99,7 @@ describe('reorg tests', () => { }, { common } ) - const block2_low = Block.fromBlockData( + const block2_low = createBlockFromBlockData( { header: { ...base, @@ -112,7 +113,7 @@ describe('reorg tests', () => { { common } ) - const block1_high = Block.fromBlockData( + const block1_high = createBlockFromBlockData( { header: { ...base, @@ -123,7 +124,7 @@ describe('reorg tests', () => { }, { common } ) - const block2_high = Block.fromBlockData( + const block2_high = createBlockFromBlockData( { header: { ...base, @@ -134,7 +135,7 @@ describe('reorg tests', () => { }, { common } ) - const block3_high = Block.fromBlockData( + const block3_high = createBlockFromBlockData( { header: { ...base, diff --git a/packages/blockchain/test/util.ts b/packages/blockchain/test/util.ts index cf21523340..2d519f85dd 100644 --- a/packages/blockchain/test/util.ts +++ b/packages/blockchain/test/util.ts @@ -1,4 +1,4 @@ -import { Block, BlockHeader } from '@ethereumjs/block' +import { Block, BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { @@ -11,7 +11,7 @@ import { } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak.js' -import { Blockchain } from '../src/index.js' +import { createBlockchain } from '../src/index.js' import type { DB } from '@ethereumjs/util' @@ -23,7 +23,7 @@ export const generateBlocks = (numberOfBlocks: number, existingBlocks?: Block[]) const opts = { common } if (blocks.length === 0) { - const genesis = Block.fromBlockData({ header: { gasLimit } }, opts) + const genesis = createBlockFromBlockData({ header: { gasLimit } }, opts) blocks.push(genesis) } @@ -37,7 +37,7 @@ export const generateBlocks = (numberOfBlocks: number, existingBlocks?: Block[]) timestamp: lastBlock.header.timestamp + BigInt(1), }, } - const block = Block.fromBlockData(blockData, { + const block = createBlockFromBlockData(blockData, { common, calcDifficultyFromHeader: lastBlock.header, }) @@ -51,7 +51,7 @@ export const generateBlockchain = async (numberOfBlocks: number, genesis?: Block const existingBlocks: Block[] = genesis ? [genesis] : [] const blocks = generateBlocks(numberOfBlocks, existingBlocks) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: false, genesisBlock: genesis ?? blocks[0], @@ -124,7 +124,7 @@ export const createTestDB = async (): Promise< [DB, Block] > => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - const genesis = Block.fromBlockData({ header: { number: 0 } }, { common }) + const genesis = createBlockFromBlockData({ header: { number: 0 } }, { common }) const db = new MapDB() await db.batch([ @@ -208,7 +208,7 @@ function createBlock( ? parentBlock.header.calcNextBaseFee() : undefined - return Block.fromBlockData( + return createBlockFromBlockData( { header: { number, diff --git a/packages/blockchain/test/utils.spec.ts b/packages/blockchain/test/utils.spec.ts index 503e50689d..6900c1fb86 100644 --- a/packages/blockchain/test/utils.spec.ts +++ b/packages/blockchain/test/utils.spec.ts @@ -1,17 +1,19 @@ -import { Common } from '@ethereumjs/common' +import { createCommonFromGethGenesis } from '@ethereumjs/common' import { genesisStateRoot } from '@ethereumjs/trie' import { bytesToHex, parseGethGenesisState } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { Blockchain } from '../src/blockchain.js' - // kiln genesis with deposit contract storage set +import { createBlockchain } from '../src/index.js' + import gethGenesisKilnJSON from './testdata/geth-genesis-kiln.json' +import type { Blockchain } from '../src/blockchain.js' + async function getBlockchain(gethGenesis: any): Promise { - const common = Common.fromGethGenesis(gethGenesis, { chain: 'kiln' }) + const common = createCommonFromGethGenesis(gethGenesis, { chain: 'kiln' }) const genesisState = parseGethGenesisState(gethGenesis) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ genesisState, common, }) diff --git a/packages/blockchain/tsconfig.json b/packages/blockchain/tsconfig.json index ad9065826e..109ec6fb26 100644 --- a/packages/blockchain/tsconfig.json +++ b/packages/blockchain/tsconfig.json @@ -2,7 +2,6 @@ "extends": "../../config/tsconfig.json", "include": ["src/**/*.ts", "src/**/*.json", "test/**/*.ts"], "compilerOptions": { - "types": ["node"], "typeRoots": ["node_modules/@types"] } } diff --git a/packages/client/archive/libp2p/index.ts b/packages/client/archive/libp2p/index.ts index b07928480f..f7c1b46164 100644 --- a/packages/client/archive/libp2p/index.ts +++ b/packages/client/archive/libp2p/index.ts @@ -75,7 +75,7 @@ export async function createClient(args: any) { `${datadir}/${common.chainName()}` ) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ db: new LevelDB(chainDB), common: config.chainCommon, hardforkByHeadBlockNumber: true, diff --git a/packages/client/bin/cli.ts b/packages/client/bin/cli.ts index fdb915f9e3..e3d552f829 100755 --- a/packages/client/bin/cli.ts +++ b/packages/client/bin/cli.ts @@ -1,8 +1,15 @@ #!/usr/bin/env node -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' -import { Chain, Common, ConsensusAlgorithm, Hardfork } from '@ethereumjs/common' +import { createBlockFromValuesArray } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' +import { + Chain, + Common, + ConsensusAlgorithm, + Hardfork, + createCommonFromGethGenesis, + getInitializedChains, +} from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { Address, @@ -55,7 +62,7 @@ import type { Logger } from '../src/logging.js' import type { FullEthereumService } from '../src/service/index.js' import type { ClientOpts } from '../src/types.js' import type { RPCArgs } from './startRpc.js' -import type { BlockBytes } from '@ethereumjs/block' +import type { Block, BlockBytes } from '@ethereumjs/block' import type { CustomCrypto } from '@ethereumjs/common' import type { GenesisState, PrefixedHexString } from '@ethereumjs/util' import type { AbstractLevel } from 'abstract-level' @@ -63,7 +70,7 @@ import type { Server as RPCServer } from 'jayson/promise/index.js' type Account = [address: Address, privateKey: Uint8Array] -const networks = Object.entries(Common.getInitializedChains().names) +const networks = Object.entries(getInitializedChains().names) let logger: Logger @@ -624,7 +631,7 @@ async function startClient( let blockchain if (genesisMeta.genesisState !== undefined || genesisMeta.genesisStateRoot !== undefined) { const validateConsensus = config.chainCommon.consensusAlgorithm() === ConsensusAlgorithm.Clique - blockchain = await Blockchain.create({ + blockchain = await createBlockchain({ db: new LevelDB(dbs.chainDB), ...genesisMeta, common: config.chainCommon, @@ -653,7 +660,7 @@ async function startClient( let buf = RLP.decode(blockRlp, true) while (buf.data?.length > 0 || buf.remainder?.length > 0) { try { - const block = Block.fromValuesArray(buf.data as BlockBytes, { + const block = createBlockFromValuesArray(buf.data as BlockBytes, { common: config.chainCommon, setHardfork: true, }) @@ -762,7 +769,10 @@ async function setupDevnet(prefundAddress: Address) { extraData, alloc: { [addr]: { balance: '0x10000000000000000000' } }, } - const common = Common.fromGethGenesis(chainData, { chain: 'devnet', hardfork: Hardfork.London }) + const common = createCommonFromGethGenesis(chainData, { + chain: 'devnet', + hardfork: Hardfork.London, + }) const customGenesisState = parseGethGenesisState(chainData) return { common, customGenesisState } } @@ -997,7 +1007,7 @@ 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, { + common = createCommonFromGethGenesis(genesisFile, { chain: chainName, mergeForkIdPostMerge: args.mergeForkIdPostMerge, }) diff --git a/packages/client/devnets/4844-interop/tools/txGenerator.ts b/packages/client/devnets/4844-interop/tools/txGenerator.ts index 7587ff8ea4..f6d6aea8a1 100644 --- a/packages/client/devnets/4844-interop/tools/txGenerator.ts +++ b/packages/client/devnets/4844-interop/tools/txGenerator.ts @@ -29,7 +29,7 @@ async function getNonce(client: Client, account: string) { async function run(data: any) { const kzg = await loadKZG() - const common = Common.fromGethGenesis(genesisJson, { + const common = createCommonFromGethGenesis(genesisJson, { chain: genesisJson.ChainName ?? 'devnet', hardfork: Hardfork.Cancun, customCrypto: { kzg }, diff --git a/packages/client/package.json b/packages/client/package.json index 112e177c25..648725f084 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -20,9 +20,9 @@ }, "license": "MPL-2.0", "author": "Vinay Pulim (v@pulim.com)", + "type": "module", "main": "dist/esm/bin/cli.js", "types": "dist/esm/src/index.d.ts", - "type": "module", "bin": { "ethereumjs": "dist/esm/bin/cli.js" }, diff --git a/packages/client/src/blockchain/chain.ts b/packages/client/src/blockchain/chain.ts index abee427d64..a2a8dbe50a 100644 --- a/packages/client/src/blockchain/chain.ts +++ b/packages/client/src/blockchain/chain.ts @@ -1,5 +1,5 @@ -import { Block, BlockHeader } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { BlockHeader, createBlockFromValuesArray } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' import { ConsensusAlgorithm, Hardfork } from '@ethereumjs/common' import { BIGINT_0, BIGINT_1, equalsBytes } from '@ethereumjs/util' @@ -7,6 +7,8 @@ import { LevelDB } from '../execution/level.js' import { Event } from '../types.js' import type { Config } from '../config.js' +import type { Block } from '@ethereumjs/block' +import type { Blockchain } from '@ethereumjs/blockchain' import type { DB, DBObject, GenesisState } from '@ethereumjs/util' import type { AbstractLevel } from 'abstract-level' @@ -162,7 +164,7 @@ export class Chain { options.blockchain = options.blockchain ?? - (await Blockchain.create({ + (await createBlockchain({ db: new LevelDB(options.chainDB), common: options.config.chainCommon, hardforkByHeadBlockNumber: true, @@ -449,7 +451,7 @@ export class Chain { await this.blockchain.consensus.setup({ blockchain: this.blockchain }) } - const block = Block.fromValuesArray(b.raw(), { + const block = createBlockFromValuesArray(b.raw(), { common: this.config.chainCommon, setHardfork: td, }) diff --git a/packages/client/src/net/protocol/ethprotocol.ts b/packages/client/src/net/protocol/ethprotocol.ts index 4b50731e4e..681091c691 100644 --- a/packages/client/src/net/protocol/ethprotocol.ts +++ b/packages/client/src/net/protocol/ethprotocol.ts @@ -1,4 +1,9 @@ -import { Block, BlockHeader, getDifficulty, valuesArrayToHeaderData } from '@ethereumjs/block' +import { + BlockHeader, + createBlockFromValuesArray, + getDifficulty, + valuesArrayToHeaderData, +} from '@ethereumjs/block' import { Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { @@ -27,7 +32,7 @@ import { Protocol } from './protocol.js' import type { Chain } from '../../blockchain/index.js' import type { TxReceiptWithType } from '../../execution/receipt.js' import type { Message, ProtocolOptions } from './protocol.js' -import type { BlockBodyBytes, BlockBytes, BlockHeaderBytes } from '@ethereumjs/block' +import type { Block, BlockBodyBytes, BlockBytes, BlockHeaderBytes } from '@ethereumjs/block' import type { Log } from '@ethereumjs/evm' import type { TypedTransaction } from '@ethereumjs/tx' import type { BigIntLike, PrefixedHexString } from '@ethereumjs/util' @@ -201,7 +206,7 @@ export class EthProtocol extends Protocol { code: 0x07, encode: ([block, td]: [Block, bigint]) => [block.raw(), bigIntToUnpaddedBytes(td)], decode: ([block, td]: [BlockBytes, Uint8Array]) => [ - Block.fromValuesArray(block, { + createBlockFromValuesArray(block, { common: this.config.chainCommon, setHardfork: true, }), diff --git a/packages/client/src/rpc/error-code.ts b/packages/client/src/rpc/error-code.ts index b9f3327288..9698444c91 100644 --- a/packages/client/src/rpc/error-code.ts +++ b/packages/client/src/rpc/error-code.ts @@ -8,6 +8,7 @@ export const INTERNAL_ERROR = -32603 export const TOO_LARGE_REQUEST = -38004 export const UNSUPPORTED_FORK = -38005 export const UNKNOWN_PAYLOAD = -32001 +export const INVALID_HEX_STRING = -32000 export const validEngineCodes = [ PARSE_ERROR, diff --git a/packages/client/src/rpc/helpers.ts b/packages/client/src/rpc/helpers.ts index 91f135eaba..99528f452a 100644 --- a/packages/client/src/rpc/helpers.ts +++ b/packages/client/src/rpc/helpers.ts @@ -23,7 +23,7 @@ export function callWithStackTrace(handler: Function, debug: boolean) { message: error.message, } if (debug === true) { - e['trace'] = error.stack ?? 'Stack trace is not available' + e['trace'] = error.stack } throw e @@ -58,6 +58,7 @@ export const jsonRpcTx = (tx: TypedTransaction, block?: Block, txIndex?: number) s: txJSON.s!, maxFeePerBlobGas: txJSON.maxFeePerBlobGas, blobVersionedHashes: txJSON.blobVersionedHashes, + yParity: txJSON.yParity, } } diff --git a/packages/client/src/rpc/modules/debug.ts b/packages/client/src/rpc/modules/debug.ts index b6b1823bb1..abfd4eaffa 100644 --- a/packages/client/src/rpc/modules/debug.ts +++ b/packages/client/src/rpc/modules/debug.ts @@ -1,4 +1,5 @@ import { Address, TypeOutput, bigIntToHex, bytesToHex, hexToBytes, toType } from '@ethereumjs/util' +import { type VM, encodeReceipt } from '@ethereumjs/vm' import { INTERNAL_ERROR, INVALID_PARAMS } from '../error-code.js' import { callWithStackTrace, getBlockByOption } from '../helpers.js' @@ -10,7 +11,6 @@ import type { FullEthereumService } from '../../service/index.js' import type { RpcTx } from '../types.js' import type { Block } from '@ethereumjs/block' import type { PrefixedHexString } from '@ethereumjs/util' -import type { VM } from '@ethereumjs/vm' export interface tracerOpts { disableStack?: boolean @@ -111,6 +111,26 @@ export class Debug { [validators.unsignedInteger], ] ) + this.getRawBlock = middleware( + callWithStackTrace(this.getRawBlock.bind(this), this._rpcDebug), + 1, + [[validators.blockOption]] + ) + this.getRawHeader = middleware( + callWithStackTrace(this.getRawHeader.bind(this), this._rpcDebug), + 1, + [[validators.blockOption]] + ) + this.getRawReceipts = middleware( + callWithStackTrace(this.getRawReceipts.bind(this), this._rpcDebug), + 1, + [[validators.blockOption]] + ) + this.getRawTransaction = middleware( + callWithStackTrace(this.getRawTransaction.bind(this), this._rpcDebug), + 1, + [[validators.hex]] + ) } /** @@ -340,4 +360,54 @@ export class Debug { limit ) } + /** + * Returns an RLP-encoded block + * @param blockOpt Block number or tag + */ + async getRawBlock(params: [string]) { + const [blockOpt] = params + const block = await getBlockByOption(blockOpt, this.chain) + return bytesToHex(block.serialize()) + } + /** + * Returns an RLP-encoded block header + * @param blockOpt Block number or tag + * @returns + */ + async getRawHeader(params: [string]) { + const [blockOpt] = params + const block = await getBlockByOption(blockOpt, this.chain) + return bytesToHex(block.header.serialize()) + } + /** + * Returns an array of EIP-2718 binary-encoded receipts + * @param blockOpt Block number or tag + */ + async getRawReceipts(params: [string]) { + const [blockOpt] = params + if (!this.service.execution.receiptsManager) throw new Error('missing receiptsManager') + const block = await getBlockByOption(blockOpt, this.chain) + const receipts = await this.service.execution.receiptsManager.getReceipts( + block.hash(), + true, + true + ) + return receipts.map((r) => bytesToHex(encodeReceipt(r, r.txType))) + } + /** + * Returns the bytes of the transaction. + * @param blockOpt Block number or tag + */ + async getRawTransaction(params: [string]) { + const [txHash] = params + if (!this.service.execution.receiptsManager) throw new Error('missing receiptsManager') + const result = await this.service.execution.receiptsManager.getReceiptByTxHash( + hexToBytes(txHash) + ) + if (!result) return null + const [_receipt, blockHash, txIndex] = result + const block = await this.chain.getBlock(blockHash) + const tx = block.transactions[txIndex] + return bytesToHex(tx.serialize()) + } } diff --git a/packages/client/src/rpc/modules/engine/util/newPayload.ts b/packages/client/src/rpc/modules/engine/util/newPayload.ts index 6e15109515..f566fb2361 100644 --- a/packages/client/src/rpc/modules/engine/util/newPayload.ts +++ b/packages/client/src/rpc/modules/engine/util/newPayload.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromExecutionPayload } from '@ethereumjs/block' import { Hardfork } from '@ethereumjs/common' import { BlobEIP4844Transaction } from '@ethereumjs/tx' import { equalsBytes, hexToBytes } from '@ethereumjs/util' @@ -10,7 +10,7 @@ import { validHash } from './generic.js' import type { Chain } from '../../../../blockchain/index.js' import type { ChainCache, PayloadStatusV1 } from '../types.js' -import type { ExecutionPayload } from '@ethereumjs/block' +import type { Block, ExecutionPayload } from '@ethereumjs/block' import type { PrefixedHexString } from '@ethereumjs/util' /** @@ -33,7 +33,7 @@ export const assembleBlock = async ( common.setHardforkBy({ blockNumber, td: ttd !== null ? ttd : undefined, timestamp }) try { - const block = await Block.fromExecutionPayload(payload, { common }) + const block = await createBlockFromExecutionPayload(payload, { common }) // TODO: validateData is also called in applyBlock while runBlock, may be it can be optimized // by removing/skipping block data validation from there await block.validateData() diff --git a/packages/client/src/rpc/modules/eth.ts b/packages/client/src/rpc/modules/eth.ts index f6c251b68a..0a6bb135a6 100644 --- a/packages/client/src/rpc/modules/eth.ts +++ b/packages/client/src/rpc/modules/eth.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Hardfork } from '@ethereumjs/common' import { BlobEIP4844Transaction, Capability, TransactionFactory } from '@ethereumjs/tx' import { @@ -16,10 +16,9 @@ import { intToHex, setLengthLeft, toType, - utf8ToBytes, } from '@ethereumjs/util' -import { INTERNAL_ERROR, INVALID_PARAMS, PARSE_ERROR } from '../error-code.js' +import { INTERNAL_ERROR, INVALID_HEX_STRING, INVALID_PARAMS, PARSE_ERROR } from '../error-code.js' import { callWithStackTrace, getBlockByOption, jsonRpcTx } from '../helpers.js' import { middleware, validators } from '../validation.js' @@ -29,7 +28,7 @@ import type { EthereumClient } from '../../index.js' import type { EthProtocol } from '../../net/protocol/index.js' import type { FullEthereumService, Service } from '../../service/index.js' import type { RpcTx } from '../types.js' -import type { JsonRpcBlock } from '@ethereumjs/block' +import type { Block, JsonRpcBlock } from '@ethereumjs/block' import type { Log } from '@ethereumjs/evm' import type { Proof } from '@ethereumjs/statemanager' import type { @@ -79,6 +78,7 @@ type JsonRpcReceipt = { status?: string // QUANTITY, either 1 (success) or 0 (failure) blobGasUsed?: string // QUANTITY, blob gas consumed by transaction (if blob transaction) blobGasPrice?: string // QUAntity, blob gas price for block including this transaction (if blob transaction) + type: string // QUANTITY, transaction type } type JsonRpcLog = { removed: boolean // TAG - true when the log was removed, due to a chain reorganization. false if it's a valid log. @@ -130,7 +130,7 @@ const jsonRpcBlock = async ( difficulty: header.difficulty!, totalDifficulty: bigIntToHex(td), extraData: header.extraData!, - size: intToHex(utf8ToBytes(JSON.stringify(json)).byteLength), + size: intToHex(block.serialize().length), gasLimit: header.gasLimit!, gasUsed: header.gasUsed!, timestamp: header.timestamp!, @@ -206,6 +206,7 @@ const jsonRpcReceipt = async ( : undefined, blobGasUsed: blobGasUsed !== undefined ? bigIntToHex(blobGasUsed) : undefined, blobGasPrice: blobGasPrice !== undefined ? bigIntToHex(blobGasPrice) : undefined, + type: intToHex(tx.type), }) const calculateRewards = async ( @@ -371,6 +372,12 @@ export class Eth { [[validators.hex, validators.blockHash], [validators.hex]] ) + this.getTransactionByBlockNumberAndIndex = middleware( + callWithStackTrace(this.getTransactionByBlockNumberAndIndex.bind(this), this._rpcDebug), + 2, + [[validators.hex, validators.blockOption], [validators.hex]] + ) + this.getTransactionByHash = middleware( callWithStackTrace(this.getTransactionByHash.bind(this), this._rpcDebug), 1, @@ -383,6 +390,11 @@ export class Eth { [[validators.address], [validators.blockOption]] ) + this.getBlockReceipts = middleware( + callWithStackTrace(this.getBlockReceipts.bind(this), this._rpcDebug), + 1, + [[validators.blockOption]] + ) this.getTransactionReceipt = middleware( callWithStackTrace(this.getTransactionReceipt.bind(this), this._rpcDebug), 1, @@ -492,7 +504,9 @@ export class Eth { const vm = await this._vm.shallowCopy() await vm.stateManager.setStateRoot(block.header.stateRoot) - const { from, to, gas: gasLimit, gasPrice, value, data } = transaction + const { from, to, gas: gasLimit, gasPrice, value } = transaction + + const data = transaction.data ?? transaction.input const runCallOpts = { caller: from !== undefined ? Address.fromString(from) : undefined, @@ -501,6 +515,7 @@ export class Eth { gasPrice: toType(gasPrice, TypeOutput.BigInt), value: toType(value, TypeOutput.BigInt), data: data !== undefined ? hexToBytes(data) : undefined, + block, } const { execResult } = await vm.evm.runCall(runCallOpts) return bytesToHex(execResult.returnValue) @@ -562,7 +577,7 @@ export class Eth { gasLimit: transaction.gas, } - const blockToRunOn = Block.fromBlockData( + const blockToRunOn = createBlockFromBlockData( { header: { parentHash: block.hash(), @@ -653,10 +668,7 @@ export class Eth { const block = await this._chain.getBlock(hexToBytes(blockHash)) return await jsonRpcBlock(block, this._chain, includeTransactions) } catch (error) { - throw { - code: INVALID_PARAMS, - message: 'Unknown block', - } + return null } } @@ -668,8 +680,19 @@ export class Eth { */ async getBlockByNumber(params: [string, boolean]) { const [blockOpt, includeTransactions] = params - const block = await getBlockByOption(blockOpt, this._chain) - return jsonRpcBlock(block, this._chain, includeTransactions) + if (blockOpt === 'pending') { + throw { + code: INVALID_PARAMS, + message: `"pending" is not yet supported`, + } + } + try { + const block = await getBlockByOption(blockOpt, this._chain) + const response = await jsonRpcBlock(block, this._chain, includeTransactions) + return response + } catch { + return null + } } /** @@ -720,7 +743,18 @@ export class Eth { */ async getStorageAt(params: [string, PrefixedHexString, string]) { const [addressHex, keyHex, blockOpt] = params - + if (!/^[0-9a-fA-F]+$/.test(keyHex.slice(2))) { + throw { + code: INVALID_HEX_STRING, + message: `unable to decode storage key: hex string invalid`, + } + } + if (keyHex.length > 66) { + throw { + code: INVALID_HEX_STRING, + message: `unable to decode storage key: hex string too long, want at most 32 bytes`, + } + } if (blockOpt === 'pending') { throw { code: INVALID_PARAMS, @@ -773,6 +807,31 @@ export class Eth { } } + /** + * Returns information about a transaction given a block hash and a transaction's index position. + * @param params An array of two parameter: + * 1. a block number + * 2. an integer of the transaction index position encoded as a hexadecimal. + */ + async getTransactionByBlockNumberAndIndex(params: [PrefixedHexString, string]) { + try { + const [blockNumber, txIndexHex] = params + const txIndex = parseInt(txIndexHex, 16) + const block = await getBlockByOption(blockNumber, this._chain) + if (block.transactions.length <= txIndex) { + return null + } + + const tx = block.transactions[txIndex] + return jsonRpcTx(tx, block, txIndex) + } catch (error: any) { + throw { + code: INVALID_PARAMS, + message: error.message.toString(), + } + } + } + /** * Returns the transaction by hash when available within `--txLookupLimit` * @param params An array of one parameter: @@ -855,6 +914,64 @@ export class Eth { return block.uncleHeaders.length } + async getBlockReceipts(params: [string]) { + const [blockOpt] = params + let block: Block + try { + if (blockOpt.length === 66) { + block = await this._chain.getBlock(hexToBytes(blockOpt)) + } else { + block = await getBlockByOption(blockOpt, this._chain) + } + } catch { + return null + } + const blockHash = block.hash() + if (!this.receiptsManager) throw new Error('missing receiptsManager') + const result = await this.receiptsManager.getReceipts(blockHash, true, true) + if (result.length === 0) return [] + const parentBlock = await this._chain.getBlock(block.header.parentHash) + const vmCopy = await this._vm!.shallowCopy() + vmCopy.common.setHardfork(block.common.hardfork()) + // Run tx through copied vm to get tx gasUsed and createdAddress + const runBlockResult = await vmCopy.runBlock({ + block, + root: parentBlock.header.stateRoot, + skipBlockValidation: true, + }) + + const receipts = await Promise.all( + result.map(async (r, i) => { + const tx = block.transactions[i] + const { totalGasSpent, createdAddress } = runBlockResult.results[i] + const { blobGasPrice, blobGasUsed } = runBlockResult.receipts[i] as EIP4844BlobTxReceipt + const effectiveGasPrice = + tx.supports(Capability.EIP1559FeeMarket) === true + ? (tx as FeeMarketEIP1559Transaction).maxPriorityFeePerGas < + (tx as FeeMarketEIP1559Transaction).maxFeePerGas - block.header.baseFeePerGas! + ? (tx as FeeMarketEIP1559Transaction).maxPriorityFeePerGas + : (tx as FeeMarketEIP1559Transaction).maxFeePerGas - + block.header.baseFeePerGas! + + block.header.baseFeePerGas! + : (tx as LegacyTransaction).gasPrice + + return jsonRpcReceipt( + r, + totalGasSpent, + effectiveGasPrice, + block, + tx, + i, + i, + createdAddress, + blobGasUsed, + blobGasPrice + ) + }) + ) + return receipts + } + /** * Returns the receipt of a transaction by transaction hash. * *Note* That the receipt is not available for pending transactions. @@ -989,7 +1106,7 @@ export class Eth { } }) let addressBytes: Uint8Array[] | undefined - if (address !== undefined) { + if (address !== undefined && address !== null) { if (Array.isArray(address)) { addressBytes = address.map((a) => hexToBytes(a)) } else { @@ -1123,6 +1240,9 @@ export class Eth { const address = Address.fromString(addressHex) const slots = slotsHex.map((slotHex) => setLengthLeft(hexToBytes(slotHex), 32)) const proof = await vm.stateManager.getProof!(address, slots) + for (const p of proof.storageProof) { + p.key = bigIntToHex(BigInt(p.key)) + } return proof } diff --git a/packages/client/src/rpc/types.ts b/packages/client/src/rpc/types.ts index 71f0f6a8f2..21eeb14c31 100644 --- a/packages/client/src/rpc/types.ts +++ b/packages/client/src/rpc/types.ts @@ -7,6 +7,7 @@ export interface RpcTx { gasPrice?: PrefixedHexString value?: PrefixedHexString data?: PrefixedHexString + input?: PrefixedHexString // This is the "official" name of the property the client uses for "data" in the RPC spec maxPriorityFeePerGas?: PrefixedHexString maxFeePerGas?: PrefixedHexString type?: PrefixedHexString diff --git a/packages/client/src/service/skeleton.ts b/packages/client/src/service/skeleton.ts index c7b02a588e..e31815d85c 100644 --- a/packages/client/src/service/skeleton.ts +++ b/packages/client/src/service/skeleton.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromRLPSerializedBlock } from '@ethereumjs/block' import { RLP } from '@ethereumjs/rlp' import { BIGINT_0, @@ -21,7 +21,7 @@ import { DBKey, MetaDBManager } from '../util/metaDBManager.js' import type { SnapFetcherDoneFlags } from '../sync/fetcher/types.js' import type { MetaDBManagerOptions } from '../util/metaDBManager.js' -import type { BlockHeader } from '@ethereumjs/block' +import type { Block, BlockHeader } from '@ethereumjs/block' import type { Hardfork } from '@ethereumjs/common' const INVALID_PARAMS = -32602 @@ -1385,7 +1385,7 @@ export class Skeleton extends MetaDBManager { const common = this.config.chainCommon.copy() common.setHardfork(hardfork) - const block = Block.fromRLPSerializedBlock(blockRLP, { + const block = createBlockFromRLPSerializedBlock(blockRLP, { common, }) return block diff --git a/packages/client/src/sync/fetcher/accountfetcher.ts b/packages/client/src/sync/fetcher/accountfetcher.ts index 64bc633ca0..7a5e888443 100644 --- a/packages/client/src/sync/fetcher/accountfetcher.ts +++ b/packages/client/src/sync/fetcher/accountfetcher.ts @@ -34,7 +34,6 @@ import type { FetcherOptions } from './fetcher.js' import type { StorageRequest } from './storagefetcher.js' import type { Job, SnapFetcherDoneFlags } from './types.js' import type { Debugger } from 'debug' -const { debug: createDebugLogger } = debugDefault type AccountDataResponse = AccountData[] & { completed?: boolean } @@ -99,7 +98,7 @@ export class AccountFetcher extends Fetcher this.stateManager = options.stateManager ?? new DefaultStateManager() this.accountTrie = this.stateManager['_getAccountTrie']() - this.debug = createDebugLogger('client:AccountFetcher') + this.debug = debugDefault('client:AccountFetcher') this.storageFetcher = new StorageFetcher({ config: this.config, diff --git a/packages/client/src/sync/fetcher/blockfetcher.ts b/packages/client/src/sync/fetcher/blockfetcher.ts index 1ee65cfc06..7568dcd259 100644 --- a/packages/client/src/sync/fetcher/blockfetcher.ts +++ b/packages/client/src/sync/fetcher/blockfetcher.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromValuesArray } from '@ethereumjs/block' import { KECCAK256_RLP, KECCAK256_RLP_ARRAY, equalsBytes } from '@ethereumjs/util' import { Event } from '../../types.js' @@ -8,7 +8,7 @@ import { BlockFetcherBase } from './blockfetcherbase.js' import type { Peer } from '../../net/peer/index.js' import type { BlockFetcherOptions, JobTask } from './blockfetcherbase.js' import type { Job } from './types.js' -import type { BlockBytes } from '@ethereumjs/block' +import type { Block, BlockBytes } from '@ethereumjs/block' /** * Implements an eth/66 based block fetcher @@ -91,7 +91,7 @@ export class BlockFetcher extends BlockFetcherBase { values.push(withdrawalsData) } // Supply the common from the corresponding block header already set on correct fork - const block = Block.fromValuesArray(values, { common: headers[i].common }) + const block = createBlockFromValuesArray(values, { common: headers[i].common }) // Only validate the data integrity // Upon putting blocks into blockchain (for BlockFetcher), `validateData` is called again // In ReverseBlockFetcher we do not need to validate the entire block, since CL diff --git a/packages/client/src/sync/fetcher/bytecodefetcher.ts b/packages/client/src/sync/fetcher/bytecodefetcher.ts index 816ae7aa1c..e0fdb957bc 100644 --- a/packages/client/src/sync/fetcher/bytecodefetcher.ts +++ b/packages/client/src/sync/fetcher/bytecodefetcher.ts @@ -6,7 +6,7 @@ import { concatBytes, equalsBytes, } from '@ethereumjs/util' -import debugDefault from 'debug' +import debug from 'debug' import { keccak256 } from 'ethereum-cryptography/keccak' import { Fetcher } from './fetcher.js' @@ -17,7 +17,6 @@ import type { FetcherOptions } from './fetcher.js' import type { Job, SnapFetcherDoneFlags } from './types.js' import type { BatchDBOp, DB } from '@ethereumjs/util' import type { Debugger } from 'debug' -const { debug: createDebugLogger } = debugDefault type ByteCodeDataResponse = Uint8Array[] & { completed?: boolean } @@ -62,7 +61,7 @@ export class ByteCodeFetcher extends Fetcher this.keccakFunction = this.config.chainCommon.customCrypto.keccak256 ?? keccak256 - this.debug = createDebugLogger('client:ByteCodeFetcher') + this.debug = debug('client:ByteCodeFetcher') if (this.hashes.length > 0) { const fullJob = { task: { hashes: this.hashes } } as Job this.debug( diff --git a/packages/client/src/sync/fetcher/fetcher.ts b/packages/client/src/sync/fetcher/fetcher.ts index c2750bf917..6bc1e92d90 100644 --- a/packages/client/src/sync/fetcher/fetcher.ts +++ b/packages/client/src/sync/fetcher/fetcher.ts @@ -1,4 +1,4 @@ -import debugDefault from 'debug' +import debug from 'debug' import { Readable, Writable } from 'stream' import { Heap } from '../../ext/qheap.js' @@ -12,8 +12,6 @@ import type { JobTask as BlockFetcherJobTask } from './blockfetcherbase.js' import type { Job } from './types.js' import type { Debugger } from 'debug' -const { debug: createDebugLogger } = debugDefault - export interface FetcherOptions { /* Common chain config*/ config: Config @@ -80,7 +78,7 @@ export abstract class Fetcher extends Readable super({ ...options, objectMode: true }) this.config = options.config - this.debug = createDebugLogger('client:fetcher') + this.debug = debug('client:fetcher') this.pool = options.pool this.timeout = options.timeout ?? 8000 diff --git a/packages/client/src/sync/fetcher/storagefetcher.ts b/packages/client/src/sync/fetcher/storagefetcher.ts index 6ffae1fed4..6602785af8 100644 --- a/packages/client/src/sync/fetcher/storagefetcher.ts +++ b/packages/client/src/sync/fetcher/storagefetcher.ts @@ -24,7 +24,6 @@ import type { StorageData } from '../../net/protocol/snapprotocol.js' import type { FetcherOptions } from './fetcher.js' import type { Job, SnapFetcherDoneFlags } from './types.js' import type { Debugger } from 'debug' -const { debug: createDebugLogger } = debugDefault const TOTAL_RANGE_END = BIGINT_2 ** BIGINT_256 - BIGINT_1 @@ -95,7 +94,7 @@ export class StorageFetcher extends Fetcher() - this.debug = createDebugLogger('client:StorageFetcher') + this.debug = debugDefault('client:StorageFetcher') if (this.storageRequests.length > 0) { const fullJob = { task: { storageRequests: this.storageRequests }, diff --git a/packages/client/src/sync/fetcher/trienodefetcher.ts b/packages/client/src/sync/fetcher/trienodefetcher.ts index 300e8dbb89..54b8ab96ad 100644 --- a/packages/client/src/sync/fetcher/trienodefetcher.ts +++ b/packages/client/src/sync/fetcher/trienodefetcher.ts @@ -15,7 +15,7 @@ import { KECCAK256_RLP, unprefixedHexToBytes, } from '@ethereumjs/util' -import debugPkg from 'debug' +import debug from 'debug' import { keccak256 } from 'ethereum-cryptography/keccak.js' import { bytesToHex, equalsBytes, hexToBytes } from 'ethereum-cryptography/utils' import { OrderedMap } from 'js-sdsl' @@ -28,7 +28,6 @@ import type { FetcherOptions } from './fetcher.js' import type { Job, SnapFetcherDoneFlags } from './types.js' import type { BatchDBOp, DB } from '@ethereumjs/util' import type { Debugger } from 'debug' -const { debug: createDebugLogger } = debugPkg type TrieNodesResponse = Uint8Array[] & { completed?: boolean } @@ -111,7 +110,7 @@ export class TrieNodeFetcher extends Fetcher this.codeDB = this.stateManager['_getCodeDB']() this.nodeCount = 0 - this.debug = createDebugLogger('client:TrieNodeFetcher') + this.debug = debug('client:TrieNodeFetcher') this.keccakFunction = this.config.chainCommon.customCrypto.keccak256 ?? keccak256 diff --git a/packages/client/src/util/debug.ts b/packages/client/src/util/debug.ts index 0084216e7d..3c01e39507 100644 --- a/packages/client/src/util/debug.ts +++ b/packages/client/src/util/debug.ts @@ -37,7 +37,7 @@ const main = async () => { const common = new Common({ chain: '${execution.config.execCommon.chainName()}', hardfork: '${ execution.hardfork }' }) - const block = Block.fromRLPSerializedBlock(hexToBytes('${bytesToHex( + const block = createBlockFromRLPSerializedBlock(hexToBytes('${bytesToHex( block.serialize() )}'), { common }) @@ -51,7 +51,7 @@ const main = async () => { const chainDB = new Level('${execution.config.getDataDirectory(DataDirectory.Chain)}') - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ db: chainDB, common, validateBlocks: true, diff --git a/packages/client/test/blockchain/chain.spec.ts b/packages/client/test/blockchain/chain.spec.ts index da8d0df2d1..981573a9ab 100644 --- a/packages/client/test/blockchain/chain.spec.ts +++ b/packages/client/test/blockchain/chain.spec.ts @@ -1,5 +1,5 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' import { KeyEncoding, ValueEncoding, bytesToHex, equalsBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' @@ -49,7 +49,7 @@ describe('[Chain]', () => { }) it('should detect unopened chain', async () => { - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: false, validateConsensus: false, }) @@ -59,7 +59,7 @@ describe('[Chain]', () => { difficulty: BigInt(0xabcdffff), parentHash: chain.genesis.hash(), } - const block = Block.fromBlockData({ header: headerData } as BlockData, { + const block = createBlockFromBlockData({ header: headerData } as BlockData, { common: config.chainCommon, }) @@ -122,7 +122,7 @@ describe('[Chain]', () => { it('should add block to chain', async () => { // TODO: add test cases with activated block validation - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: false, validateConsensus: false, }) @@ -133,7 +133,7 @@ describe('[Chain]', () => { difficulty: BigInt(0xabcdffff), parentHash: chain.genesis.hash(), } - const block = Block.fromBlockData({ header: headerData } as BlockData, { + const block = createBlockFromBlockData({ header: headerData } as BlockData, { common: config.chainCommon, }) await chain.putBlocks([block]) diff --git a/packages/client/test/execution/vmexecution.spec.ts b/packages/client/test/execution/vmexecution.spec.ts index 69dc36af2f..72f7095631 100644 --- a/packages/client/test/execution/vmexecution.spec.ts +++ b/packages/client/test/execution/vmexecution.spec.ts @@ -1,5 +1,5 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockFromExecutionPayload } from '@ethereumjs/block' +import { createBlockchain, createBlockchainFromBlocksData } from '@ethereumjs/blockchain' import { Chain as ChainEnum, Common, Hardfork } from '@ethereumjs/common' import { bytesToHex } from '@ethereumjs/util' import { VM } from '@ethereumjs/vm' @@ -15,6 +15,7 @@ import testnet from '../testdata/common/testnet.json' import shanghaiJSON from '../testdata/geth-genesis/withdrawals.json' import type { BlockData, ExecutionPayload } from '@ethereumjs/block' +import type { Blockchain } from '@ethereumjs/blockchain' import type { ChainConfig } from '@ethereumjs/common' const shanghaiPayload = { @@ -104,7 +105,7 @@ describe('[VMExecution]', () => { } it('Block execution / Hardforks PoW (mainnet)', async () => { - let blockchain = await Blockchain.create({ + let blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: false, }) @@ -114,7 +115,7 @@ describe('[VMExecution]', () => { let newHead = await exec.vm.blockchain.getIteratorHead!() assert.deepEqual(newHead.hash(), oldHead.hash(), 'should not modify blockchain on empty run') - blockchain = await Blockchain.fromBlocksData(blocksDataMainnet as BlockData[], { + blockchain = await createBlockchainFromBlocksData(blocksDataMainnet as BlockData[], { validateBlocks: true, validateConsensus: false, }) @@ -130,13 +131,13 @@ describe('[VMExecution]', () => { }) it('Test block execution using executeBlocks function', async () => { - let blockchain = await Blockchain.create({ + let blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: false, }) let exec = await testSetup(blockchain) - blockchain = await Blockchain.fromBlocksData(blocksDataMainnet as BlockData[], { + blockchain = await createBlockchainFromBlocksData(blocksDataMainnet as BlockData[], { validateBlocks: true, validateConsensus: false, }) @@ -152,7 +153,7 @@ describe('[VMExecution]', () => { }) it('Should fail opening if vmPromise already assigned', async () => { - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: false, }) @@ -174,7 +175,7 @@ describe('[VMExecution]', () => { it('Block execution / Hardforks PoA (goerli)', async () => { const common = new Common({ chain: ChainEnum.Goerli, hardfork: Hardfork.Chainstart }) - let blockchain = await Blockchain.create({ + let blockchain = await createBlockchain({ validateBlocks: true, validateConsensus: false, common, @@ -185,7 +186,7 @@ describe('[VMExecution]', () => { let newHead = await exec.vm.blockchain.getIteratorHead!() assert.deepEqual(newHead.hash(), oldHead.hash(), 'should not modify blockchain on empty run') - blockchain = await Blockchain.fromBlocksData(blocksDataGoerli as BlockData[], { + blockchain = await createBlockchainFromBlocksData(blocksDataGoerli as BlockData[], { validateBlocks: true, validateConsensus: false, common, @@ -201,7 +202,7 @@ describe('[VMExecution]', () => { engine: true, }) - const block = await Block.fromExecutionPayload(shanghaiPayload as ExecutionPayload) + const block = await createBlockFromExecutionPayload(shanghaiPayload as ExecutionPayload) const oldHead = await blockchain.getIteratorHead() const parentStateRoot = oldHead.header.stateRoot diff --git a/packages/client/test/integration/beaconsync.spec.ts b/packages/client/test/integration/beaconsync.spec.ts index 8f54feef22..c19043c5d7 100644 --- a/packages/client/test/integration/beaconsync.spec.ts +++ b/packages/client/test/integration/beaconsync.spec.ts @@ -1,5 +1,5 @@ import { BlockHeader } from '@ethereumjs/block' -import { Common } from '@ethereumjs/common' +import { createCommonFromGethGenesis } from '@ethereumjs/common' import { assert, describe, it, vi } from 'vitest' import { Event } from '../../src/types.js' @@ -7,7 +7,7 @@ import genesisJSON from '../testdata/geth-genesis/post-merge.json' import { destroy, setup, wait } from './util.js' -const common = Common.fromGethGenesis(genesisJSON, { chain: 'post-merge' }) +const common = createCommonFromGethGenesis(genesisJSON, { chain: 'post-merge' }) common.setHardforkBy({ blockNumber: BigInt(0), td: BigInt(0) }) describe('should sync blocks', async () => { diff --git a/packages/client/test/integration/fullethereumservice.spec.ts b/packages/client/test/integration/fullethereumservice.spec.ts index eb56f0c8c8..8a8f0b1612 100644 --- a/packages/client/test/integration/fullethereumservice.spec.ts +++ b/packages/client/test/integration/fullethereumservice.spec.ts @@ -1,5 +1,5 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' import { Hardfork } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx' @@ -26,7 +26,7 @@ DefaultStateManager.prototype.shallowCopy = function () { } async function setup(): Promise<[MockServer, FullEthereumService]> { const server = new MockServer({ config }) as any - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common: config.chainCommon, validateBlocks: false, validateConsensus: false, @@ -81,7 +81,7 @@ describe( }) peer.eth!.send('NewBlockHashes', [[hash, BigInt(2)]]) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { number: 1, diff --git a/packages/client/test/integration/merge.spec.ts b/packages/client/test/integration/merge.spec.ts index 40660c61ed..5e07abd0f0 100644 --- a/packages/client/test/integration/merge.spec.ts +++ b/packages/client/test/integration/merge.spec.ts @@ -1,11 +1,11 @@ import { BlockHeader } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockchain } from '@ethereumjs/blockchain' import { Chain as ChainCommon, - Common, ConsensusAlgorithm, ConsensusType, Hardfork, + createCustomCommon, } from '@ethereumjs/common' import { Address, hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' @@ -19,8 +19,9 @@ import { MockServer } from './mocks/mockserver.js' import { destroy, setup } from './util.js' import type { CliqueConsensus } from '@ethereumjs/blockchain' +import type { Common } from '@ethereumjs/common' -const commonPoA = Common.custom( +const commonPoA = createCustomCommon( { consensus: { type: ConsensusType.ProofOfAuthority, @@ -43,7 +44,7 @@ const commonPoA = Common.custom( }, { baseChain: ChainCommon.Goerli, hardfork: Hardfork.London } ) -const commonPoW = Common.custom( +const commonPoW = createCustomCommon( { genesis: { gasLimit: 16777216, @@ -73,7 +74,7 @@ const accounts: [Address, Uint8Array][] = [ async function minerSetup(common: Common): Promise<[MockServer, FullEthereumService]> { const config = new Config({ common, accountCache: 10000, storageCache: 1000 }) const server = new MockServer({ config }) as any - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateBlocks: false, validateConsensus: false, diff --git a/packages/client/test/integration/miner.spec.ts b/packages/client/test/integration/miner.spec.ts index aad5813f5b..64d7dd98d7 100644 --- a/packages/client/test/integration/miner.spec.ts +++ b/packages/client/test/integration/miner.spec.ts @@ -1,10 +1,11 @@ -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockchain } from '@ethereumjs/blockchain' import { Chain as ChainCommon, Common, ConsensusAlgorithm, ConsensusType, Hardfork, + createCustomCommon, } from '@ethereumjs/common' import { Address, hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' @@ -27,7 +28,7 @@ const hardforks = new Common({ chain: ChainCommon.Goerli }) ? { ...h, block: 0, timestamp: undefined } : { ...h, timestamp: undefined } ) -const common = Common.custom( +const common = createCustomCommon( { hardforks, consensus: { @@ -51,7 +52,7 @@ async function minerSetup(): Promise<[MockServer, FullEthereumService]> { const config = new Config({ common, accountCache: 10000, storageCache: 1000 }) const server = new MockServer({ config }) as any - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateBlocks: false, validateConsensus: false, diff --git a/packages/client/test/integration/mocks/mockchain.ts b/packages/client/test/integration/mocks/mockchain.ts index da4b91ffd6..d0c888d4d1 100644 --- a/packages/client/test/integration/mocks/mockchain.ts +++ b/packages/client/test/integration/mocks/mockchain.ts @@ -1,9 +1,10 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Hardfork } from '@ethereumjs/common' import { Chain } from '../../../src/blockchain/index.js' import type { ChainOptions } from '../../../src/blockchain/index.js' +import type { Block } from '@ethereumjs/block' interface MockChainOptions extends ChainOptions { height?: number @@ -29,7 +30,7 @@ export class MockChain extends Chain { const common = this.config.chainCommon const blocks: Block[] = [] for (let number = 0; number < this.height; number++) { - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { number: number + 1, diff --git a/packages/client/test/integration/peerpool.spec.ts b/packages/client/test/integration/peerpool.spec.ts index e4dedf1ea7..583e9f425b 100644 --- a/packages/client/test/integration/peerpool.spec.ts +++ b/packages/client/test/integration/peerpool.spec.ts @@ -1,4 +1,4 @@ -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockchain } from '@ethereumjs/blockchain' import { assert, describe, it } from 'vitest' import { Config } from '../../src/config.js' @@ -65,7 +65,7 @@ describe('should ban peer', async () => { describe('should handle peer messages', async () => { const config = new Config({ accountCache: 10000, storageCache: 1000 }) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: false, validateConsensus: false, }) diff --git a/packages/client/test/integration/pow.spec.ts b/packages/client/test/integration/pow.spec.ts index 8a40da24e6..51f0b3d4ba 100644 --- a/packages/client/test/integration/pow.spec.ts +++ b/packages/client/test/integration/pow.spec.ts @@ -1,4 +1,4 @@ -import { Common, Hardfork } from '@ethereumjs/common' +import { Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common' import { Address, hexToBytes, parseGethGenesisState } from '@ethereumjs/util' import { rmSync } from 'fs' import { assert, describe, it } from 'vitest' @@ -49,7 +49,10 @@ async function setupPowDevnet(prefundAddress: Address, cleanStart: boolean) { extraData, alloc: { [addr]: { balance: '0x10000000000000000000' } }, } - const common = Common.fromGethGenesis(chainData, { chain: 'devnet', hardfork: Hardfork.London }) + const common = createCommonFromGethGenesis(chainData, { + chain: 'devnet', + hardfork: Hardfork.London, + }) const customGenesisState = parseGethGenesisState(chainData) const config = new Config({ diff --git a/packages/client/test/integration/util.ts b/packages/client/test/integration/util.ts index 336742815d..16e9cf0e78 100644 --- a/packages/client/test/integration/util.ts +++ b/packages/client/test/integration/util.ts @@ -1,4 +1,4 @@ -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockchain } from '@ethereumjs/blockchain' import { MemoryLevel } from 'memory-level' import { Config } from '../../src/config.js' @@ -39,7 +39,7 @@ export async function setup( }) const server = new MockServer({ config, location }) as any - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: false, validateConsensus: false, common, diff --git a/packages/client/test/miner/miner.spec.ts b/packages/client/test/miner/miner.spec.ts index 2dc2322705..5611ee520f 100644 --- a/packages/client/test/miner/miner.spec.ts +++ b/packages/client/test/miner/miner.spec.ts @@ -1,5 +1,11 @@ -import { Block, BlockHeader } from '@ethereumjs/block' -import { Common, Chain as CommonChain, Hardfork } from '@ethereumjs/common' +import { BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' +import { + Common, + Chain as CommonChain, + Hardfork, + createCommonFromGethGenesis, + createCustomCommon, +} from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' import { FeeMarketEIP1559Transaction, LegacyTransaction } from '@ethereumjs/tx' import { Address, equalsBytes, hexToBytes } from '@ethereumjs/util' @@ -15,6 +21,7 @@ import { FullEthereumService } from '../../src/service/index.js' import { wait } from '../integration/util.js' import type { FullSynchronizer } from '../../src/sync/index.js' +import type { Block } from '@ethereumjs/block' import type { CliqueConsensus } from '@ethereumjs/blockchain' import type { VM } from '@ethereumjs/vm' @@ -52,7 +59,7 @@ class FakeChain { } get blocks() { return { - latest: Block.fromBlockData(), + latest: createBlockFromBlockData(), height: BigInt(0), } } @@ -72,7 +79,7 @@ class FakeChain { }, validateHeader: () => {}, getIteratorHead: () => { - return Block.fromBlockData({ header: { number: 1 } }) + return createBlockFromBlockData({ header: { number: 1 } }) }, getTotalDifficulty: () => { return 1n @@ -129,7 +136,7 @@ const chainData = { extraData, alloc: { [addr]: { balance: '0x10000000000000000000' } }, } -const customCommon = Common.fromGethGenesis(chainData, { +const customCommon = createCommonFromGethGenesis(chainData, { chain: 'devnet', hardfork: Hardfork.Berlin, }) @@ -406,7 +413,7 @@ describe('assembleBlocks() -> with saveReceipts', async () => { describe('assembleBlocks() -> should not include tx under the baseFee', async () => { const customChainParams = { hardforks: [{ name: 'london', block: 0 }] } - const common = Common.custom(customChainParams, { + const common = createCustomCommon(customChainParams, { baseChain: CommonChain.Goerli, hardfork: Hardfork.London, }) @@ -418,7 +425,7 @@ describe('assembleBlocks() -> should not include tx under the baseFee', async () common, }) const chain = new FakeChain() as any - const block = Block.fromBlockData({}, { common }) + const block = createBlockFromBlockData({}, { common }) Object.defineProperty(chain, 'headers', { get() { return { latest: block.header, height: block.header.number } @@ -467,7 +474,7 @@ describe('assembleBlocks() -> should not include tx under the baseFee', async () describe("assembleBlocks() -> should stop assembling a block after it's full", async () => { const chain = new FakeChain() as any const gasLimit = 100000 - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { gasLimit } }, { common: customCommon, setHardfork: true } ) @@ -587,7 +594,7 @@ describe.skip('should handle mining over the london hardfork block', async () => { name: 'london', block: 3 }, ], } - const common = Common.custom(customChainParams, { baseChain: CommonChain.Goerli }) + const common = createCustomCommon(customChainParams, { baseChain: CommonChain.Goerli }) common.setHardforkBy({ blockNumber: 0 }) const config = new Config({ accountCache: 10000, @@ -695,7 +702,7 @@ describe.skip('should handle mining ethash PoW', async () => { extraData, alloc: { [addr]: { balance: '0x10000000000000000000' } }, } - const common = Common.fromGethGenesis(chainData, { + const common = createCommonFromGethGenesis(chainData, { chain: 'devnet', hardfork: Hardfork.London, }) diff --git a/packages/client/test/miner/pendingBlock.spec.ts b/packages/client/test/miner/pendingBlock.spec.ts index 3ee8765ec5..f280c8f653 100644 --- a/packages/client/test/miner/pendingBlock.spec.ts +++ b/packages/client/test/miner/pendingBlock.spec.ts @@ -1,5 +1,10 @@ import { Block, BlockHeader } from '@ethereumjs/block' -import { Common, Chain as CommonChain, Hardfork } from '@ethereumjs/common' +import { + Common, + Chain as CommonChain, + Hardfork, + createCommonFromGethGenesis, +} from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' import { BlobEIP4844Transaction, @@ -356,7 +361,7 @@ describe('[PendingBlock]', async () => { it('construct blob bundles', async () => { const kzg = await loadKZG() - const common = Common.fromGethGenesis(gethGenesis, { + const common = createCommonFromGethGenesis(gethGenesis, { chain: 'customChain', hardfork: Hardfork.Cancun, customCrypto: { @@ -437,7 +442,7 @@ describe('[PendingBlock]', async () => { const gethGenesis = await import('../../../block/test/testdata/4844-hardfork.json') const kzg = await loadKZG() - const common = Common.fromGethGenesis(gethGenesis, { + const common = createCommonFromGethGenesis(gethGenesis, { chain: 'customChain', hardfork: Hardfork.Cancun, customCrypto: { kzg }, diff --git a/packages/client/test/net/protocol/ethprotocol.spec.ts b/packages/client/test/net/protocol/ethprotocol.spec.ts index d429be2811..5801a62a05 100644 --- a/packages/client/test/net/protocol/ethprotocol.spec.ts +++ b/packages/client/test/net/protocol/ethprotocol.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Common, Chain as CommonChain, Hardfork } from '@ethereumjs/common' import { FeeMarketEIP1559Transaction, TransactionFactory, TransactionType } from '@ethereumjs/tx' import { Address, bigIntToBytes, bytesToBigInt, hexToBytes, randomBytes } from '@ethereumjs/util' @@ -81,7 +81,7 @@ describe('[EthProtocol]', () => { const chain = await Chain.create({ config }) const p = new EthProtocol({ config, chain }) const td = BigInt(100) - const block = Block.fromBlockData({}, { common: config.chainCommon }) + const block = createBlockFromBlockData({}, { common: config.chainCommon }) const res = p.decode(p.messages.filter((message) => message.name === 'NewBlock')[0], [ block.raw(), bigIntToBytes(td), @@ -98,7 +98,7 @@ describe('[EthProtocol]', () => { const config = new Config({ accountCache: 10000, storageCache: 1000 }) const chain = await Chain.create({ config }) const p = new EthProtocol({ config, chain }) - const block = Block.fromBlockData({}) + const block = createBlockFromBlockData({}) const res = p.decode(p.messages.filter((message) => message.name === 'GetReceipts')[0], [ bigIntToBytes(1n), [block.hash()], diff --git a/packages/client/test/rpc/debug/getRawBlock.spec.ts b/packages/client/test/rpc/debug/getRawBlock.spec.ts new file mode 100644 index 0000000000..b4d32790e1 --- /dev/null +++ b/packages/client/test/rpc/debug/getRawBlock.spec.ts @@ -0,0 +1,146 @@ +import { BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' +import { createCustomCommon } from '@ethereumjs/common' +import { BlobEIP4844Transaction, LegacyTransaction } from '@ethereumjs/tx' +import { Address, bytesToHex, hexToBytes } from '@ethereumjs/util' +import { loadKZG } from 'kzg-wasm' +import { assert, describe, it } from 'vitest' + +import { INVALID_PARAMS } from '../../../src/rpc/error-code.js' +import { createClient, createManager, dummy, getRpcClient, startRPC } from '../helpers.js' + +const kzg = await loadKZG() + +const common = createCustomCommon({ chainId: 1 }, { customCrypto: { kzg } }) + +common.setHardfork('cancun') +const mockedTx1 = LegacyTransaction.fromTxData({}).sign(dummy.privKey) +const mockedTx2 = LegacyTransaction.fromTxData({ nonce: 1 }).sign(dummy.privKey) +const mockedBlobTx3 = BlobEIP4844Transaction.fromTxData( + { nonce: 2, blobsData: ['0x1234'], to: Address.zero() }, + { common } +).sign(dummy.privKey) +const blockHash = hexToBytes('0xdcf93da321b27bca12087d6526d2c10540a4c8dc29db1b36610c3004e0e5d2d5') +const transactions = [mockedTx1] +const transactions2 = [mockedTx2] + +const block = { + hash: () => blockHash, + header: { + number: BigInt(1), + hash: () => blockHash, + serialize: () => BlockHeader.fromHeaderData({ number: 1 }).serialize(), + }, + toJSON: () => ({ + ...createBlockFromBlockData({ header: { number: 1 } }).toJSON(), + transactions: transactions2, + }), + serialize: () => + createBlockFromBlockData({ header: { number: 1 }, transactions: transactions2 }).serialize(), + transactions: transactions2, + uncleHeaders: [], +} + +const genesisBlockHash = hexToBytes( + '0xdcf93da321b27bca12087d6526d2c10540a4c8dc29db1b36610c3004e0e5d2d5' +) +const genesisBlock = { + hash: () => genesisBlockHash, + header: { + number: BigInt(0), + }, + toJSON: () => ({ ...createBlockFromBlockData({ header: { number: 0 } }).toJSON(), transactions }), + serialize: () => createBlockFromBlockData({ header: { number: 0 }, transactions }).serialize(), + transactions, + uncleHeaders: [], +} +function createChain(headBlock = block) { + const block = headBlock + return { + blocks: { latest: block }, + getBlock: () => genesisBlock, + getCanonicalHeadBlock: () => block, + getCanonicalHeadHeader: () => block.header, + getTd: () => BigInt(0), + } +} + +const method = 'debug_getRawBlock' + +describe(method, async () => { + it('call with valid arguments', async () => { + const manager = createManager(await createClient({ chain: createChain() })) + const rpc = getRpcClient(startRPC(manager.getMethods())) + + const res = await rpc.request(method, ['0x0']) + assert.equal(res.result, bytesToHex(genesisBlock.serialize()), 'should return a valid block') + }) + + it('call with earliest param', async () => { + const manager = createManager(await createClient({ chain: createChain() })) + const rpc = getRpcClient(startRPC(manager.getMethods())) + + const res = await rpc.request(method, ['earliest']) + assert.equal( + res.result, + bytesToHex(genesisBlock.serialize()), + 'should return the genesis block as earliest' + ) + }) + + it('call with latest param', async () => { + const manager = createManager(await createClient({ chain: createChain() })) + const rpc = getRpcClient(startRPC(manager.getMethods())) + + const res = await rpc.request(method, ['latest']) + assert.equal(res.result, bytesToHex(block.serialize()), 'should return block 1 RLP') + }) + + it('call with unimplemented pending param', async () => { + const manager = createManager(await createClient({ chain: createChain() })) + const rpc = getRpcClient(startRPC(manager.getMethods())) + const res = await rpc.request(method, ['pending']) + assert.equal(res.error.code, INVALID_PARAMS) + assert.ok(res.error.message.includes('"pending" is not yet supported')) + }) + + it('call with non-string block number', async () => { + const manager = createManager(await createClient({ chain: createChain() })) + const rpc = getRpcClient(startRPC(manager.getMethods())) + const res = await rpc.request(method, [10]) + assert.equal(res.error.code, INVALID_PARAMS) + assert.ok(res.error.message.includes('invalid argument 0: argument must be a string')) + }) + + it('call with invalid block number', async () => { + const manager = createManager(await createClient({ chain: createChain() })) + const rpc = getRpcClient(startRPC(manager.getMethods())) + const res = await rpc.request(method, ['WRONG BLOCK NUMBER']) + assert.equal(res.error.code, INVALID_PARAMS) + assert.ok( + res.error.message.includes( + 'invalid argument 0: block option must be a valid 0x-prefixed block hash or hex integer, or "latest", "earliest" or "pending"' + ) + ) + }) +}) +describe('call with block with blob txs', () => { + it('retrieves a block with a blob tx in it', async () => { + const genesisBlock = createBlockFromBlockData({ header: { number: 0 } }) + const block1 = createBlockFromBlockData( + { + header: { number: 1, parentHash: genesisBlock.header.hash() }, + transactions: [mockedBlobTx3], + }, + { common } + ) + const manager = createManager(await createClient({ chain: createChain(block1 as any) })) + const rpc = getRpcClient(startRPC(manager.getMethods())) + const res = await rpc.request(method, ['latest']) + + assert.equal( + res.result, + bytesToHex(block1.serialize()), + 'block body contains a transaction with the blobVersionedHashes field' + ) + }) +}) diff --git a/packages/client/test/rpc/debug/getRawHeader.spec.ts b/packages/client/test/rpc/debug/getRawHeader.spec.ts new file mode 100644 index 0000000000..409423edad --- /dev/null +++ b/packages/client/test/rpc/debug/getRawHeader.spec.ts @@ -0,0 +1,151 @@ +import { BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' +import { createCustomCommon } from '@ethereumjs/common' +import { BlobEIP4844Transaction, LegacyTransaction } from '@ethereumjs/tx' +import { Address, bytesToHex, hexToBytes } from '@ethereumjs/util' +import { loadKZG } from 'kzg-wasm' +import { assert, describe, it } from 'vitest' + +import { INVALID_PARAMS } from '../../../src/rpc/error-code.js' +import { createClient, createManager, dummy, getRpcClient, startRPC } from '../helpers.js' + +const kzg = await loadKZG() + +const common = createCustomCommon({ chainId: 1 }, { customCrypto: { kzg } }) + +common.setHardfork('cancun') +const mockedTx1 = LegacyTransaction.fromTxData({}).sign(dummy.privKey) +const mockedTx2 = LegacyTransaction.fromTxData({ nonce: 1 }).sign(dummy.privKey) +const mockedBlobTx3 = BlobEIP4844Transaction.fromTxData( + { nonce: 2, blobsData: ['0x1234'], to: Address.zero() }, + { common } +).sign(dummy.privKey) +const blockHash = hexToBytes('0xdcf93da321b27bca12087d6526d2c10540a4c8dc29db1b36610c3004e0e5d2d5') +const transactions = [mockedTx1] +const transactions2 = [mockedTx2] + +const block = { + hash: () => blockHash, + header: { + number: BigInt(1), + hash: () => blockHash, + serialize: () => BlockHeader.fromHeaderData({ number: 1 }).serialize(), + }, + toJSON: () => ({ + ...createBlockFromBlockData({ header: { number: 1 } }).toJSON(), + transactions: transactions2, + }), + serialize: () => + createBlockFromBlockData({ header: { number: 1 }, transactions: transactions2 }).serialize(), + transactions: transactions2, + uncleHeaders: [], +} + +const genesisBlockHash = hexToBytes( + '0xdcf93da321b27bca12087d6526d2c10540a4c8dc29db1b36610c3004e0e5d2d5' +) +const genesisBlock = { + hash: () => genesisBlockHash, + header: { + number: BigInt(0), + serialize: () => BlockHeader.fromHeaderData({ number: 0 }).serialize(), + }, + toJSON: () => ({ ...createBlockFromBlockData({ header: { number: 0 } }).toJSON(), transactions }), + serialize: () => createBlockFromBlockData({ header: { number: 0 }, transactions }).serialize(), + transactions, + uncleHeaders: [], +} +function createChain(headBlock = block) { + const block = headBlock + return { + blocks: { latest: block }, + getBlock: () => genesisBlock, + getCanonicalHeadBlock: () => block, + getCanonicalHeadHeader: () => block.header, + getTd: () => BigInt(0), + } +} + +const method = 'debug_getRawHeader' + +describe(method, async () => { + it('call with valid arguments', async () => { + const manager = createManager(await createClient({ chain: createChain() })) + const rpc = getRpcClient(startRPC(manager.getMethods())) + + const res = await rpc.request(method, ['0x0']) + assert.equal( + res.result, + bytesToHex(genesisBlock.header.serialize()), + 'should return a valid block' + ) + }) + + it('call with earliest param', async () => { + const manager = createManager(await createClient({ chain: createChain() })) + const rpc = getRpcClient(startRPC(manager.getMethods())) + + const res = await rpc.request(method, ['earliest']) + assert.equal( + res.result, + bytesToHex(genesisBlock.header.serialize()), + 'should return the genesis block as earliest' + ) + }) + + it('call with latest param', async () => { + const manager = createManager(await createClient({ chain: createChain() })) + const rpc = getRpcClient(startRPC(manager.getMethods())) + + const res = await rpc.request(method, ['latest']) + assert.equal(res.result, bytesToHex(block.header.serialize()), 'should return block 1 RLP') + }) + + it('call with unimplemented pending param', async () => { + const manager = createManager(await createClient({ chain: createChain() })) + const rpc = getRpcClient(startRPC(manager.getMethods())) + const res = await rpc.request(method, ['pending']) + assert.equal(res.error.code, INVALID_PARAMS) + assert.ok(res.error.message.includes('"pending" is not yet supported')) + }) + + it('call with non-string block number', async () => { + const manager = createManager(await createClient({ chain: createChain() })) + const rpc = getRpcClient(startRPC(manager.getMethods())) + const res = await rpc.request(method, [10]) + assert.equal(res.error.code, INVALID_PARAMS) + assert.ok(res.error.message.includes('invalid argument 0: argument must be a string')) + }) + + it('call with invalid block number', async () => { + const manager = createManager(await createClient({ chain: createChain() })) + const rpc = getRpcClient(startRPC(manager.getMethods())) + const res = await rpc.request(method, ['WRONG BLOCK NUMBER']) + assert.equal(res.error.code, INVALID_PARAMS) + assert.ok( + res.error.message.includes( + 'invalid argument 0: block option must be a valid 0x-prefixed block hash or hex integer, or "latest", "earliest" or "pending"' + ) + ) + }) +}) +describe('call with block with blob txs', () => { + it('retrieves a block with a blob tx in it', async () => { + const genesisBlock = createBlockFromBlockData({ header: { number: 0 } }) + const block1 = createBlockFromBlockData( + { + header: { number: 1, parentHash: genesisBlock.header.hash() }, + transactions: [mockedBlobTx3], + }, + { common } + ) + const manager = createManager(await createClient({ chain: createChain(block1 as any) })) + const rpc = getRpcClient(startRPC(manager.getMethods())) + const res = await rpc.request(method, ['latest']) + + assert.equal( + res.result, + bytesToHex(block1.header.serialize()), + 'block body contains a transaction with the blobVersionedHashes field' + ) + }) +}) diff --git a/packages/client/test/rpc/debug/getRawReceipts.spec.ts b/packages/client/test/rpc/debug/getRawReceipts.spec.ts new file mode 100644 index 0000000000..87159a06cc --- /dev/null +++ b/packages/client/test/rpc/debug/getRawReceipts.spec.ts @@ -0,0 +1,167 @@ +import { Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common' +import { + BlobEIP4844Transaction, + FeeMarketEIP1559Transaction, + LegacyTransaction, +} from '@ethereumjs/tx' +import { + bigIntToHex, + blobsToCommitments, + bytesToHex, + commitmentsToVersionedHashes, + getBlobs, + hexToBytes, + randomBytes, +} from '@ethereumjs/util' +import { encodeReceipt } from '@ethereumjs/vm' +import { loadKZG } from 'kzg-wasm' +import { assert, describe, it } from 'vitest' + +import pow from '../../testdata/geth-genesis/pow.json' +import { + dummy, + getRpcClient, + gethGenesisStartLondon, + runBlockWithTxs, + setupChain, +} from '../helpers.js' + +import type { TxReceipt } from '@ethereumjs/vm' + +const method = 'eth_getTransactionReceipt' +const method2 = 'debug_getRawReceipts' + +describe(method, () => { + it('call with legacy tx', async () => { + const { chain, common, execution, server } = await setupChain(pow, 'pow') + const rpc = getRpcClient(server) + // construct tx + const tx = LegacyTransaction.fromTxData( + { + gasLimit: 2000000, + gasPrice: 100, + to: '0x0000000000000000000000000000000000000000', + }, + { common } + ).sign(dummy.privKey) + const block = await runBlockWithTxs(chain, execution, [tx]) + const res0 = await rpc.request(method, [bytesToHex(tx.hash())]) + const rec: TxReceipt = { + cumulativeBlockGasUsed: BigInt(res0.result.cumulativeGasUsed), + logs: [], + stateRoot: hexToBytes(res0.result.root), + bitvector: hexToBytes(res0.result.logsBloom), + } + const receipt = bytesToHex(encodeReceipt(rec, 0)) + const res2 = await rpc.request(method2, [bigIntToHex(block.header.number)]) + assert.deepEqual(res2.result, [receipt]) + }) + + it('call with 1559 tx', async () => { + const { chain, common, execution, server } = await setupChain( + gethGenesisStartLondon(pow), + 'powLondon' + ) + const rpc = getRpcClient(server) + // construct tx + const tx = FeeMarketEIP1559Transaction.fromTxData( + { + gasLimit: 2000000, + maxFeePerGas: 975000000, + maxPriorityFeePerGas: 10, + to: '0x1230000000000000000000000000000000000321', + }, + { common } + ).sign(dummy.privKey) + + const block = await runBlockWithTxs(chain, execution, [tx]) + + // get the tx + const res0 = await rpc.request(method, [bytesToHex(tx.hash())]) + const rec: TxReceipt = { + cumulativeBlockGasUsed: BigInt(res0.result.cumulativeGasUsed), + logs: [], + bitvector: hexToBytes(res0.result.logsBloom), + status: 1, + } + const receipt = bytesToHex(encodeReceipt(rec, 2)) + const res1 = await rpc.request(method2, [bigIntToHex(block.header.number)]) + assert.equal(res1.result, receipt, 'transaction result is 1 since succeeded') + }) + + it('call with unknown block hash', async () => { + const { server } = await setupChain(pow, 'pow') + const rpc = getRpcClient(server) + // get a random tx hash + const res = await rpc.request(method, [ + '0x89ea5b54111befb936851660a72b686a21bc2fc4889a9a308196ff99d08925a0', + ]) + assert.equal(res.result, null, 'should return null') + }) + + it('get blobGasUsed/blobGasPrice in blob tx receipt', async () => { + const isBrowser = new Function('try {return this===window;}catch(e){ return false;}') + if (isBrowser() === true) { + assert.ok(true) + } else { + const gethGenesis = await import('../../../../block/test/testdata/4844-hardfork.json') + + const kzg = await loadKZG() + + const common = createCommonFromGethGenesis(gethGenesis, { + chain: 'customChain', + hardfork: Hardfork.Cancun, + customCrypto: { + kzg, + }, + }) + const { chain, execution, server } = await setupChain(gethGenesis, 'customChain', { + customCrypto: { kzg }, + }) + common.setHardfork(Hardfork.Cancun) + const rpc = getRpcClient(server) + + const blobs = getBlobs('hello world') + const commitments = blobsToCommitments(kzg, blobs) + const blobVersionedHashes = commitmentsToVersionedHashes(commitments) + const proofs = blobs.map((blob, ctx) => kzg.computeBlobKzgProof(blob, commitments[ctx])) + const tx = BlobEIP4844Transaction.fromTxData( + { + blobVersionedHashes, + blobs, + kzgCommitments: commitments, + kzgProofs: proofs, + maxFeePerBlobGas: 1000000n, + gasLimit: 0xffffn, + maxFeePerGas: 10000000n, + maxPriorityFeePerGas: 1000000n, + to: randomBytes(20), + nonce: 0n, + }, + { common } + ).sign(dummy.privKey) + + const block = await runBlockWithTxs(chain, execution, [tx], true) + + const res = await rpc.request(method, [bytesToHex(tx.hash())]) + + assert.equal(res.result.blobGasUsed, '0x20000', 'receipt has correct blob gas usage') + assert.equal(res.result.blobGasPrice, '0x1', 'receipt has correct blob gas price') + + const rec: TxReceipt = { + cumulativeBlockGasUsed: BigInt(res.result.cumulativeGasUsed), + logs: [], + bitvector: hexToBytes(res.result.logsBloom), + blobGasPrice: BigInt(res.result.blobGasPrice), + blobGasUsed: BigInt(res.result.blobGasUsed), + status: 1, + } + + const receipt = bytesToHex(encodeReceipt(rec, tx.type)) + + const res2 = await rpc.request(method2, [bigIntToHex(block.header.number)]) + + assert.equal(res2.result, receipt) + } + }) +}) diff --git a/packages/client/test/rpc/debug/getRawTransaction.spec.ts b/packages/client/test/rpc/debug/getRawTransaction.spec.ts new file mode 100644 index 0000000000..8811fec759 --- /dev/null +++ b/packages/client/test/rpc/debug/getRawTransaction.spec.ts @@ -0,0 +1,71 @@ +import { FeeMarketEIP1559Transaction, LegacyTransaction } from '@ethereumjs/tx' +import { bytesToHex } from '@ethereumjs/util' +import { assert, describe, it } from 'vitest' + +import pow from '../../testdata/geth-genesis/pow.json' +import { + dummy, + getRpcClient, + gethGenesisStartLondon, + runBlockWithTxs, + setupChain, +} from '../helpers.js' + +const method = 'debug_getRawTransaction' + +describe(method, () => { + it('call with legacy tx', async () => { + const { chain, common, execution, server } = await setupChain(pow, 'pow', { txLookupLimit: 1 }) + const rpc = getRpcClient(server) + // construct tx + const tx = LegacyTransaction.fromTxData( + { gasLimit: 2000000, gasPrice: 100, to: '0x0000000000000000000000000000000000000000' }, + { common } + ).sign(dummy.privKey) + + await runBlockWithTxs(chain, execution, [tx]) + + // get the tx + const res1 = await rpc.request(method, [bytesToHex(tx.hash())]) + assert.equal(res1.result, bytesToHex(tx.serialize()), 'should return the correct tx') + + // run a block to ensure tx hash index is cleaned up when txLookupLimit=1 + await runBlockWithTxs(chain, execution, []) + const res2 = await rpc.request(method, [bytesToHex(tx.hash())]) + assert.equal(res2.result, null, 'should return null when past txLookupLimit') + }) + + it('call with 1559 tx', async () => { + const { chain, common, execution, server } = await setupChain( + gethGenesisStartLondon(pow), + 'powLondon' + ) + const rpc = getRpcClient(server) + // construct tx + const tx = FeeMarketEIP1559Transaction.fromTxData( + { + gasLimit: 2000000, + maxFeePerGas: 975000000, + maxPriorityFeePerGas: 10, + to: '0x0000000000000000000000000000000000000000', + }, + { common } + ).sign(dummy.privKey) + + await runBlockWithTxs(chain, execution, [tx]) + + // get the tx + const res1 = await rpc.request(method, [bytesToHex(tx.hash())]) + assert.equal(res1.result, bytesToHex(tx.serialize()), 'should return the correct tx type') + }) + + it('call with unknown tx hash', async () => { + const { server } = await setupChain(pow, 'pow') + const rpc = getRpcClient(server) + // get a random tx hash + const res = await rpc.request(method, [ + '0x89ea5b54111befb936851660a72b686a21bc2fc4889a9a308196ff99d08925a0', + ]) + assert.equal(res.result, null, 'should return null') + }) +}) diff --git a/packages/client/test/rpc/debug/traceCall.spec.ts b/packages/client/test/rpc/debug/traceCall.spec.ts index 9b63357e65..06ca9d8b13 100644 --- a/packages/client/test/rpc/debug/traceCall.spec.ts +++ b/packages/client/test/rpc/debug/traceCall.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { TransactionFactory } from '@ethereumjs/tx' import { bytesToHex } from '@ethereumjs/util' import { assert, describe, expect, expectTypeOf, it } from 'vitest' @@ -65,7 +65,7 @@ describe('trace a call', async () => { tx.getSenderAddress = () => { return dummy.addr } - const block = Block.fromBlockData({}, { common }) + const block = createBlockFromBlockData({}, { common }) block.transactions[0] = tx await runBlockWithTxs(chain, execution, [tx], true) diff --git a/packages/client/test/rpc/debug/traceTransaction.spec.ts b/packages/client/test/rpc/debug/traceTransaction.spec.ts index ec07a48289..135c43e584 100644 --- a/packages/client/test/rpc/debug/traceTransaction.spec.ts +++ b/packages/client/test/rpc/debug/traceTransaction.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { TransactionFactory } from '@ethereumjs/tx' import { bytesToHex } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' @@ -64,7 +64,7 @@ describe(method, () => { tx.getSenderAddress = () => { return dummy.addr } - const block = Block.fromBlockData({}, { common }) + const block = createBlockFromBlockData({}, { common }) block.transactions[0] = tx await runBlockWithTxs(chain, execution, [tx], true) @@ -93,7 +93,7 @@ describe(method, () => { tx.getSenderAddress = () => { return dummy.addr } - const block = Block.fromBlockData({}, { common }) + const block = createBlockFromBlockData({}, { common }) block.transactions[0] = tx await runBlockWithTxs(chain, execution, [tx], true) @@ -122,7 +122,7 @@ describe(method, () => { tx.getSenderAddress = () => { return dummy.addr } - const block = Block.fromBlockData({}, { common }) + const block = createBlockFromBlockData({}, { common }) block.transactions[0] = tx await runBlockWithTxs(chain, execution, [tx], true) @@ -155,7 +155,7 @@ describe(method, () => { tx.getSenderAddress = () => { return dummy.addr } - const block = Block.fromBlockData({}, { common }) + const block = createBlockFromBlockData({}, { common }) block.transactions[0] = tx await runBlockWithTxs(chain, execution, [tx], true) diff --git a/packages/client/test/rpc/engine/CLConnectionManager.spec.ts b/packages/client/test/rpc/engine/CLConnectionManager.spec.ts index 1ac49ba6cb..49e0d67ff2 100644 --- a/packages/client/test/rpc/engine/CLConnectionManager.spec.ts +++ b/packages/client/test/rpc/engine/CLConnectionManager.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Common, parseGethGenesis } from '@ethereumjs/common' import { assert, describe, expect, it, vi } from 'vitest' @@ -122,7 +122,7 @@ describe('updates stats when a new block is processed', () => { const manager = new CLConnectionManager({ config }) manager.lastForkchoiceUpdate(update) manager.lastNewPayload(payload) - const block = Block.fromBlockData({ + const block = createBlockFromBlockData({ header: { parentHash: payload.payload.blockHash, number: payload.payload.blockNumber, diff --git a/packages/client/test/rpc/engine/forkchoiceUpdatedV1.spec.ts b/packages/client/test/rpc/engine/forkchoiceUpdatedV1.spec.ts index 7c270c3be4..3b3a14f777 100644 --- a/packages/client/test/rpc/engine/forkchoiceUpdatedV1.spec.ts +++ b/packages/client/test/rpc/engine/forkchoiceUpdatedV1.spec.ts @@ -1,4 +1,4 @@ -import { Block, BlockHeader } from '@ethereumjs/block' +import { BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { bytesToHex, randomBytes, zeros } from '@ethereumjs/util' import { assert, describe, it, vi } from 'vitest' @@ -9,7 +9,7 @@ import blocks from '../../testdata/blocks/beacon.json' import genesisJSON from '../../testdata/geth-genesis/post-merge.json' import { baseSetup, batchBlocks, getRpcClient, setupChain } from '../helpers.js' -import type { BlockData } from '@ethereumjs/block' +import type { Block, BlockData } from '@ethereumjs/block' const method = 'engine_forkchoiceUpdatedV1' @@ -31,7 +31,7 @@ const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Paris }) function createBlock(parentBlock: Block) { const prevRandao = randomBytes(32) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { parentHash: parentBlock.hash(), @@ -166,7 +166,7 @@ describe(method, () => { engine: true, }) const rpc = getRpcClient(server) - const newBlock = Block.fromBlockData( + const newBlock = createBlockFromBlockData( { header: { number: blocks[0].blockNumber, diff --git a/packages/client/test/rpc/engine/getPayloadBodiesByHashV1.spec.ts b/packages/client/test/rpc/engine/getPayloadBodiesByHashV1.spec.ts index f6ecd44977..ff1644de20 100644 --- a/packages/client/test/rpc/engine/getPayloadBodiesByHashV1.spec.ts +++ b/packages/client/test/rpc/engine/getPayloadBodiesByHashV1.spec.ts @@ -1,4 +1,4 @@ -import { Block, BlockHeader } from '@ethereumjs/block' +import { BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' import { Hardfork } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' import { TransactionFactory } from '@ethereumjs/tx' @@ -66,7 +66,7 @@ describe(method, () => { }, { common } ).sign(pkey) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { transactions: [tx], header: BlockHeader.fromHeaderData( @@ -76,7 +76,7 @@ describe(method, () => { }, { common, skipConsensusFormatValidation: true } ) - const block2 = Block.fromBlockData( + const block2 = createBlockFromBlockData( { transactions: [tx2], header: BlockHeader.fromHeaderData( @@ -152,7 +152,7 @@ describe(method, () => { }, { common } ).sign(pkey) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { transactions: [tx], header: BlockHeader.fromHeaderData( @@ -162,7 +162,7 @@ describe(method, () => { }, { common, skipConsensusFormatValidation: true } ) - const block2 = Block.fromBlockData( + const block2 = createBlockFromBlockData( { transactions: [tx2], header: BlockHeader.fromHeaderData( diff --git a/packages/client/test/rpc/engine/getPayloadBodiesByRangeV1.spec.ts b/packages/client/test/rpc/engine/getPayloadBodiesByRangeV1.spec.ts index 1ddfb52afd..466b891493 100644 --- a/packages/client/test/rpc/engine/getPayloadBodiesByRangeV1.spec.ts +++ b/packages/client/test/rpc/engine/getPayloadBodiesByRangeV1.spec.ts @@ -1,4 +1,4 @@ -import { Block, BlockHeader } from '@ethereumjs/block' +import { BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' import { Hardfork } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' import { TransactionFactory } from '@ethereumjs/tx' @@ -68,7 +68,7 @@ describe(method, () => { }, { common } ).sign(pkey) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { transactions: [tx], header: BlockHeader.fromHeaderData( @@ -78,7 +78,7 @@ describe(method, () => { }, { common, skipConsensusFormatValidation: true } ) - const block2 = Block.fromBlockData( + const block2 = createBlockFromBlockData( { transactions: [tx2], header: BlockHeader.fromHeaderData( @@ -150,7 +150,7 @@ describe(method, () => { }, { common } ).sign(pkey) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { transactions: [tx], header: BlockHeader.fromHeaderData( @@ -160,7 +160,7 @@ describe(method, () => { }, { common, skipConsensusFormatValidation: true } ) - const block2 = Block.fromBlockData( + const block2 = createBlockFromBlockData( { transactions: [tx2], header: BlockHeader.fromHeaderData( diff --git a/packages/client/test/rpc/engine/kaustinen6.spec.ts b/packages/client/test/rpc/engine/kaustinen6.spec.ts index 99f58a5380..4ad0f3b2b2 100644 --- a/packages/client/test/rpc/engine/kaustinen6.spec.ts +++ b/packages/client/test/rpc/engine/kaustinen6.spec.ts @@ -1,4 +1,9 @@ -import { Block, BlockHeader, executionPayloadFromBeaconPayload } from '@ethereumjs/block' +import { + BlockHeader, + createBlockFromExecutionPayload, + createBlockFromRLPSerializedBlock, + executionPayloadFromBeaconPayload, +} from '@ethereumjs/block' import { hexToBytes } from '@ethereumjs/util' import { readFileSync } from 'fs' import * as td from 'testdouble' @@ -56,7 +61,7 @@ async function runBlock( const parentPayload = isBeaconData === true ? executionPayloadFromBeaconPayload(parent as any) : parent - const parentBlock = await Block.fromExecutionPayload(parentPayload, { common }) + const parentBlock = await createBlockFromExecutionPayload(parentPayload, { common }) blockCache.remoteBlocks.set(parentPayload.blockHash.slice(2), parentBlock) blockCache.executedBlocks.set(parentPayload.blockHash.slice(2), parentBlock) @@ -208,21 +213,21 @@ async function loadGethVectors(vectorsDirPath: string, opts: { common: Common }) }, } const block0RlpHex = readFileSync(`${vectorsDirPath}/block0.rlp.hex`, 'utf8').trim() - const block0 = Block.fromRLPSerializedBlock(hexToBytes(`0x${block0RlpHex}`), { + const block0 = createBlockFromRLPSerializedBlock(hexToBytes(`0x${block0RlpHex}`), { ...opts, executionWitness: executionWitness0, }) const _block0Payload = block0.toExecutionPayload() const block1RlpHex = readFileSync(`${vectorsDirPath}/block1.rlp.hex`, 'utf8').trim() - const block1 = Block.fromRLPSerializedBlock(hexToBytes(`0x${block1RlpHex}`), { + const block1 = createBlockFromRLPSerializedBlock(hexToBytes(`0x${block1RlpHex}`), { ...opts, executionWitness: executionWitness1, }) const block1Payload = block1.toExecutionPayload() const block2RlpHex = readFileSync(`${vectorsDirPath}/block2.rlp.hex`, 'utf8').trim() - const block2 = Block.fromRLPSerializedBlock(hexToBytes(`0x${block2RlpHex}`), { + const block2 = createBlockFromRLPSerializedBlock(hexToBytes(`0x${block2RlpHex}`), { ...opts, executionWitness: executionWitness2, }) diff --git a/packages/client/test/rpc/engine/preimages.spec.ts b/packages/client/test/rpc/engine/preimages.spec.ts index 4329fab9e1..87a499866b 100644 --- a/packages/client/test/rpc/engine/preimages.spec.ts +++ b/packages/client/test/rpc/engine/preimages.spec.ts @@ -1,4 +1,9 @@ -import { Block, BlockHeader } from '@ethereumjs/block' +import { + BlockHeader, + createBlockFromBlockData, + genTransactionsTrieRoot, + genWithdrawalsTrieRoot, +} from '@ethereumjs/block' import { TransactionFactory } from '@ethereumjs/tx' import { Withdrawal, @@ -45,7 +50,7 @@ async function genBlockWithdrawals(blockNumber: number) { } }) const withdrawalsRoot = bytesToHex( - await Block.genWithdrawalsTrieRoot(withdrawals.map(Withdrawal.fromWithdrawalData)) + await genWithdrawalsTrieRoot(withdrawals.map(Withdrawal.fromWithdrawalData)) ) return { withdrawals, withdrawalsRoot } @@ -77,7 +82,7 @@ async function runBlock( throw validationError } } - const transactionsTrie = bytesToHex(await Block.genTransactionsTrieRoot(txs)) + const transactionsTrie = bytesToHex(await genTransactionsTrieRoot(txs)) const { withdrawals, withdrawalsRoot } = await genBlockWithdrawals(Number(blockNumber)) @@ -92,7 +97,7 @@ async function runBlock( coinbase, } const blockData = { header: headerData, transactions: txs, withdrawals } - const executeBlock = Block.fromBlockData(blockData, { common }) + const executeBlock = createBlockFromBlockData(blockData, { common }) const executePayload = blockToExecutionPayload(executeBlock, BigInt(0)).executionPayload const res = await rpc.request('engine_newPayloadV2', [executePayload]) assert.equal(res.result.status, 'VALID', 'valid status should be received') diff --git a/packages/client/test/rpc/engine/withdrawals.spec.ts b/packages/client/test/rpc/engine/withdrawals.spec.ts index 4809d53ee1..c2616a4437 100644 --- a/packages/client/test/rpc/engine/withdrawals.spec.ts +++ b/packages/client/test/rpc/engine/withdrawals.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { genWithdrawalsTrieRoot } from '@ethereumjs/block' import { Trie } from '@ethereumjs/trie' import { Withdrawal, bigIntToHex, bytesToHex, intToHex } from '@ethereumjs/util' import { assert, it } from 'vitest' @@ -105,7 +105,7 @@ for (const { name, withdrawals, withdrawalsRoot, gethBlockRlp } of testCases) { it(name, async () => { // check withdrawals root computation const computedWithdrawalsRoot = bytesToHex( - await Block.genWithdrawalsTrieRoot(withdrawals.map(Withdrawal.fromWithdrawalData), new Trie()) + await genWithdrawalsTrieRoot(withdrawals.map(Withdrawal.fromWithdrawalData), new Trie()) ) assert.equal( withdrawalsRoot, diff --git a/packages/client/test/rpc/eth/call.spec.ts b/packages/client/test/rpc/eth/call.spec.ts index f1fa842b76..4015e8a89f 100644 --- a/packages/client/test/rpc/eth/call.spec.ts +++ b/packages/client/test/rpc/eth/call.spec.ts @@ -1,5 +1,5 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { LegacyTransaction } from '@ethereumjs/tx' import { Address, bigIntToHex, bytesToHex } from '@ethereumjs/util' @@ -9,6 +9,7 @@ import { INVALID_PARAMS } from '../../../src/rpc/error-code.js' import { createClient, createManager, getRpcClient, startRPC } from '../helpers.js' import type { FullEthereumService } from '../../../src/service/index.js' +import type { Block } from '@ethereumjs/block' import type { PrefixedHexString } from '@ethereumjs/util' const method = 'eth_call' @@ -16,7 +17,7 @@ const method = 'eth_call' describe(method, () => { it('call with valid arguments', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateBlocks: false, validateConsensus: false, @@ -54,7 +55,7 @@ describe(method, () => { return address } const parent = await blockchain.getCanonicalHeadHeader() - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { parentHash: parent.hash(), @@ -122,7 +123,7 @@ describe(method, () => { }) it('call with unsupported block argument', async () => { - const blockchain = await Blockchain.create() + const blockchain = await createBlockchain() const client = await createClient({ blockchain, includeVM: true }) const manager = createManager(client) @@ -148,7 +149,7 @@ describe(method, () => { }) it('call with invalid hex params', async () => { - const blockchain = await Blockchain.create() + const blockchain = await createBlockchain() const client = await createClient({ blockchain, includeVM: true }) const manager = createManager(client) diff --git a/packages/client/test/rpc/eth/estimateGas.spec.ts b/packages/client/test/rpc/eth/estimateGas.spec.ts index 73c951f95e..f93461d301 100644 --- a/packages/client/test/rpc/eth/estimateGas.spec.ts +++ b/packages/client/test/rpc/eth/estimateGas.spec.ts @@ -1,15 +1,16 @@ -import { Block, BlockHeader } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' -import { Common } from '@ethereumjs/common' +import { BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' +import { createCommonFromGethGenesis } from '@ethereumjs/common' import { getGenesis } from '@ethereumjs/genesis' import { LegacyTransaction } from '@ethereumjs/tx' import { Address, bigIntToHex } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { INVALID_PARAMS } from '../../../src/rpc/error-code' +import { INVALID_PARAMS } from '../../../src/rpc/error-code.js' import { createClient, createManager, getRpcClient, startRPC } from '../helpers.js' import type { FullEthereumService } from '../../../src/service/index.js' +import type { Block } from '@ethereumjs/block' import type { PrefixedHexString } from '@ethereumjs/util' const method = 'eth_estimateGas' @@ -20,8 +21,11 @@ describe( it('call with valid arguments', async () => { // Use custom genesis so we can test EIP1559 txs more easily const genesisJson = await import('../../testdata/geth-genesis/rpctestnet.json') - const common = Common.fromGethGenesis(genesisJson, { chain: 'testnet', hardfork: 'berlin' }) - const blockchain = await Blockchain.create({ + const common = createCommonFromGethGenesis(genesisJson, { + chain: 'testnet', + hardfork: 'berlin', + }) + const blockchain = await createBlockchain({ common, validateBlocks: false, validateConsensus: false, @@ -60,7 +64,7 @@ describe( return address } const parent = await blockchain.getCanonicalHeadHeader() - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { parentHash: parent.hash(), @@ -125,7 +129,7 @@ describe( service.execution.vm.common.setHardfork('london') service.chain.config.chainCommon.setHardfork('london') const headBlock = await service.chain.getCanonicalHeadBlock() - const londonBlock = Block.fromBlockData( + const londonBlock = createBlockFromBlockData( { header: BlockHeader.fromHeaderData( { @@ -185,7 +189,7 @@ describe( }) it('call with unsupported block argument', async () => { - const blockchain = await Blockchain.create() + const blockchain = await createBlockchain() const client = await createClient({ blockchain, includeVM: true }) const manager = createManager(client) diff --git a/packages/client/test/rpc/eth/getBalance.spec.ts b/packages/client/test/rpc/eth/getBalance.spec.ts index ac3ee5199c..57b4175155 100644 --- a/packages/client/test/rpc/eth/getBalance.spec.ts +++ b/packages/client/test/rpc/eth/getBalance.spec.ts @@ -1,5 +1,5 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { getGenesis } from '@ethereumjs/genesis' import { LegacyTransaction } from '@ethereumjs/tx' @@ -18,7 +18,7 @@ describe( () => { it('ensure balance deducts after a tx', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) - const blockchain = await Blockchain.create({ common }) + const blockchain = await createBlockchain({ common }) const client = await createClient({ blockchain, commonChain: common, includeVM: true }) const manager = createManager(client) @@ -51,7 +51,7 @@ describe( tx.getSenderAddress = () => { return address } - const block = Block.fromBlockData({}, { common }) + const block = createBlockFromBlockData({}, { common }) block.transactions[0] = tx const result = await vm.runBlock({ block, generate: true, skipBlockValidation: true }) @@ -93,7 +93,7 @@ describe( }) it('call with unsupported block argument', async () => { - const blockchain = await Blockchain.create() + const blockchain = await createBlockchain() const client = await createClient({ blockchain, includeVM: true }) const manager = createManager(client) diff --git a/packages/client/test/rpc/eth/getBlockByNumber.spec.ts b/packages/client/test/rpc/eth/getBlockByNumber.spec.ts index a78bfb488e..7916783d26 100644 --- a/packages/client/test/rpc/eth/getBlockByNumber.spec.ts +++ b/packages/client/test/rpc/eth/getBlockByNumber.spec.ts @@ -1,5 +1,5 @@ -import { Block } from '@ethereumjs/block' -import { Common } from '@ethereumjs/common' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { createCustomCommon } from '@ethereumjs/common' import { BlobEIP4844Transaction, LegacyTransaction } from '@ethereumjs/tx' import { Address, hexToBytes } from '@ethereumjs/util' import { loadKZG } from 'kzg-wasm' @@ -10,7 +10,7 @@ import { createClient, createManager, dummy, getRpcClient, startRPC } from '../h const kzg = await loadKZG() -const common = Common.custom({ chainId: 1 }, { customCrypto: { kzg } }) +const common = createCustomCommon({ chainId: 1 }, { customCrypto: { kzg } }) common.setHardfork('cancun') const mockedTx1 = LegacyTransaction.fromTxData({}).sign(dummy.privKey) @@ -25,12 +25,14 @@ const transactions2 = [mockedTx2] const block = { hash: () => blockHash, + serialize: () => + createBlockFromBlockData({ header: { number: 1 }, transactions: transactions2 }).serialize(), header: { number: BigInt(1), hash: () => blockHash, }, toJSON: () => ({ - ...Block.fromBlockData({ header: { number: 1 } }).toJSON(), + ...createBlockFromBlockData({ header: { number: 1 } }).toJSON(), transactions: transactions2, }), transactions: transactions2, @@ -43,10 +45,14 @@ function createChain(headBlock = block) { ) const genesisBlock = { hash: () => genesisBlockHash, + serialize: () => createBlockFromBlockData({ header: { number: 0 }, transactions }).serialize(), header: { number: BigInt(0), }, - toJSON: () => ({ ...Block.fromBlockData({ header: { number: 0 } }).toJSON(), transactions }), + toJSON: () => ({ + ...createBlockFromBlockData({ header: { number: 0 } }).toJSON(), + transactions, + }), transactions, uncleHeaders: [], } @@ -160,8 +166,8 @@ describe(method, async () => { describe('call with block with blob txs', () => { it('retrieves a block with a blob tx in it', async () => { - const genesisBlock = Block.fromBlockData({ header: { number: 0 } }) - const block1 = Block.fromBlockData( + const genesisBlock = createBlockFromBlockData({ header: { number: 0 } }) + const block1 = createBlockFromBlockData( { header: { number: 1, parentHash: genesisBlock.header.hash() }, transactions: [mockedBlobTx3], diff --git a/packages/client/test/rpc/eth/getBlockReceipts.spec.ts b/packages/client/test/rpc/eth/getBlockReceipts.spec.ts new file mode 100644 index 0000000000..62ce82605c --- /dev/null +++ b/packages/client/test/rpc/eth/getBlockReceipts.spec.ts @@ -0,0 +1,159 @@ +import { Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common' +import { + BlobEIP4844Transaction, + FeeMarketEIP1559Transaction, + LegacyTransaction, +} from '@ethereumjs/tx' +import { + bigIntToHex, + blobsToCommitments, + bytesToHex, + commitmentsToVersionedHashes, + getBlobs, + randomBytes, +} from '@ethereumjs/util' +import { loadKZG } from 'kzg-wasm' +import { assert, describe, it } from 'vitest' + +import pow from '../../testdata/geth-genesis/pow.json' +import { + dummy, + getRpcClient, + gethGenesisStartLondon, + runBlockWithTxs, + setupChain, +} from '../helpers.js' + +const method = 'eth_getTransactionReceipt' +const method2 = 'eth_getBlockReceipts' + +describe(method, () => { + it('call with legacy tx', async () => { + const { chain, common, execution, server } = await setupChain(pow, 'pow') + const rpc = getRpcClient(server) + // construct tx + const tx = LegacyTransaction.fromTxData( + { + gasLimit: 2000000, + gasPrice: 100, + to: '0x0000000000000000000000000000000000000000', + }, + { common } + ).sign(dummy.privKey) + const tx2 = LegacyTransaction.fromTxData( + { + gasLimit: 2000000, + gasPrice: 100, + to: '0x0000000000000000000000000000000000000000', + nonce: 1, + }, + { common } + ).sign(dummy.privKey) + const block = await runBlockWithTxs(chain, execution, [tx, tx2]) + const res0 = await rpc.request(method, [bytesToHex(tx.hash())]) + const res1 = await rpc.request(method, [bytesToHex(tx2.hash())]) + const res2 = await rpc.request(method2, [bigIntToHex(block.header.number)]) + assert.deepEqual(res2.result, [res0.result, res1.result]) + }) + + it('call with 1559 tx', async () => { + const { chain, common, execution, server } = await setupChain( + gethGenesisStartLondon(pow), + 'powLondon' + ) + const rpc = getRpcClient(server) + // construct tx + const tx = FeeMarketEIP1559Transaction.fromTxData( + { + gasLimit: 2000000, + maxFeePerGas: 975000000, + maxPriorityFeePerGas: 10, + to: '0x1230000000000000000000000000000000000321', + }, + { common } + ).sign(dummy.privKey) + const tx1 = FeeMarketEIP1559Transaction.fromTxData( + { + gasLimit: 2000000, + maxFeePerGas: 975000000, + maxPriorityFeePerGas: 10, + to: '0x1230000000000000000000000000000000000321', + nonce: 1, + }, + { common } + ).sign(dummy.privKey) + + const block = await runBlockWithTxs(chain, execution, [tx, tx1]) + + // get the tx + const res0 = await rpc.request(method, [bytesToHex(tx.hash())]) + const res1 = await rpc.request(method, [bytesToHex(tx1.hash())]) + const res2 = await rpc.request(method2, [bigIntToHex(block.header.number)]) + assert.deepEqual(res2.result, [res0.result, res1.result], 'returns array of tx receipts') + }) + + it('call with unknown block hash', async () => { + const { server } = await setupChain(pow, 'pow') + const rpc = getRpcClient(server) + // get a random tx hash + const res = await rpc.request(method, [ + '0x89ea5b54111befb936851660a72b686a21bc2fc4889a9a308196ff99d08925a0', + ]) + assert.equal(res.result, null, 'should return null') + }) + + it('get blobGasUsed/blobGasPrice in blob tx receipt', async () => { + const isBrowser = new Function('try {return this===window;}catch(e){ return false;}') + if (isBrowser() === true) { + assert.ok(true) + } else { + const gethGenesis = await import('../../../../block/test/testdata/4844-hardfork.json') + + const kzg = await loadKZG() + + const common = createCommonFromGethGenesis(gethGenesis, { + chain: 'customChain', + hardfork: Hardfork.Cancun, + customCrypto: { + kzg, + }, + }) + const { chain, execution, server } = await setupChain(gethGenesis, 'customChain', { + customCrypto: { kzg }, + }) + common.setHardfork(Hardfork.Cancun) + const rpc = getRpcClient(server) + + const blobs = getBlobs('hello world') + const commitments = blobsToCommitments(kzg, blobs) + const blobVersionedHashes = commitmentsToVersionedHashes(commitments) + const proofs = blobs.map((blob, ctx) => kzg.computeBlobKzgProof(blob, commitments[ctx])) + const tx = BlobEIP4844Transaction.fromTxData( + { + blobVersionedHashes, + blobs, + kzgCommitments: commitments, + kzgProofs: proofs, + maxFeePerBlobGas: 1000000n, + gasLimit: 0xffffn, + maxFeePerGas: 10000000n, + maxPriorityFeePerGas: 1000000n, + to: randomBytes(20), + nonce: 0n, + }, + { common } + ).sign(dummy.privKey) + + const block = await runBlockWithTxs(chain, execution, [tx], true) + + const res = await rpc.request(method, [bytesToHex(tx.hash())]) + + assert.equal(res.result.blobGasUsed, '0x20000', 'receipt has correct blob gas usage') + assert.equal(res.result.blobGasPrice, '0x1', 'receipt has correct blob gas price') + + const res2 = await rpc.request(method2, [bigIntToHex(block.header.number)]) + + assert.deepEqual(res2.result, [res.result], 'transaction result is 1 since succeeded') + } + }) +}) diff --git a/packages/client/test/rpc/eth/getBlockTransactionCountByNumber.spec.ts b/packages/client/test/rpc/eth/getBlockTransactionCountByNumber.spec.ts index 552bd8c3ca..74c974f541 100644 --- a/packages/client/test/rpc/eth/getBlockTransactionCountByNumber.spec.ts +++ b/packages/client/test/rpc/eth/getBlockTransactionCountByNumber.spec.ts @@ -1,5 +1,5 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { getGenesis } from '@ethereumjs/genesis' import { LegacyTransaction } from '@ethereumjs/tx' @@ -10,6 +10,7 @@ import { INVALID_PARAMS } from '../../../src/rpc/error-code.js' import { createClient, createManager, getRpcClient, startRPC } from '../helpers.js' import type { FullEthereumService } from '../../../src/service/index.js' +import type { Block } from '@ethereumjs/block' const method = 'eth_getBlockTransactionCountByNumber' @@ -17,7 +18,7 @@ const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart describe(method, () => { it('call with valid arguments', async () => { - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateBlocks: false, validateConsensus: false, @@ -41,7 +42,7 @@ describe(method, () => { return address } const parent = await blockchain.getCanonicalHeadHeader() - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { parentHash: parent.hash(), @@ -64,7 +65,7 @@ describe(method, () => { }) it('call with valid arguments (multiple transactions)', async () => { - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateBlocks: false, validateConsensus: false, @@ -103,7 +104,7 @@ describe(method, () => { } const parent = await blockchain.getCanonicalHeadHeader() - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { parentHash: parent.hash(), @@ -129,7 +130,7 @@ describe(method, () => { }) it('call with unsupported block argument', async () => { - const blockchain = await Blockchain.create() + const blockchain = await createBlockchain() const client = await createClient({ blockchain, includeVM: true }) const manager = createManager(client) diff --git a/packages/client/test/rpc/eth/getCode.spec.ts b/packages/client/test/rpc/eth/getCode.spec.ts index 741cf024b1..0e1419bb38 100644 --- a/packages/client/test/rpc/eth/getCode.spec.ts +++ b/packages/client/test/rpc/eth/getCode.spec.ts @@ -1,5 +1,5 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { getGenesis } from '@ethereumjs/genesis' import { LegacyTransaction } from '@ethereumjs/tx' @@ -10,6 +10,7 @@ import { INVALID_PARAMS } from '../../../src/rpc/error-code.js' import { createClient, createManager, getRpcClient, startRPC } from '../helpers.js' import type { FullEthereumService } from '../../../src/service/index.js' +import type { Block } from '@ethereumjs/block' const method = 'eth_getCode' @@ -17,7 +18,7 @@ const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart describe(method, () => { it('call with valid arguments', async () => { - const blockchain = await Blockchain.create({ common }) + const blockchain = await createBlockchain({ common }) const client = await createClient({ blockchain, commonChain: common, includeVM: true }) const manager = createManager(client) @@ -37,7 +38,7 @@ describe(method, () => { }) it('ensure returns correct code', async () => { - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateBlocks: false, validateConsensus: false, @@ -67,7 +68,7 @@ describe(method, () => { return address } const parent = await blockchain.getCanonicalHeadHeader() - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { parentHash: parent.hash(), @@ -98,7 +99,7 @@ describe(method, () => { }) it('call with unsupported block argument', async () => { - const blockchain = await Blockchain.create() + const blockchain = await createBlockchain() const client = await createClient({ blockchain, includeVM: true }) const manager = createManager(client) diff --git a/packages/client/test/rpc/eth/getProof.spec.ts b/packages/client/test/rpc/eth/getProof.spec.ts index 677fb7a54a..0c298ba2b7 100644 --- a/packages/client/test/rpc/eth/getProof.spec.ts +++ b/packages/client/test/rpc/eth/getProof.spec.ts @@ -1,5 +1,5 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' import { Common } from '@ethereumjs/common' import { LegacyTransaction } from '@ethereumjs/tx' import { Address, bigIntToHex } from '@ethereumjs/util' @@ -8,6 +8,7 @@ import { assert, describe, it } from 'vitest' import { createClient, createManager, getRpcClient, startRPC } from '../helpers.js' import type { FullEthereumService } from '../../../src/service/index.js' +import type { Block } from '@ethereumjs/block' import type { PrefixedHexString } from '@ethereumjs/util' const method = 'eth_getProof' @@ -24,7 +25,7 @@ const expectedProof = { ], storageProof: [ { - key: '0x0000000000000000000000000000000000000000000000000000000000000000', + key: '0x0', value: '0x04d2', proof: [ '0xf8518080a036bb5f2fd6f99b186600638644e2f0396989955e201672f7e81e8c8f466ed5b9a010859880cfb38603690e8c4dfcc5595c203de6b901a503f944ef21a6120926a680808080808080808080808080', @@ -90,7 +91,7 @@ const common = new Common({ chain: 'testnet2', customChains: [testnetData] }) describe(method, async () => { it('call with valid arguments', async () => { - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateBlocks: false, validateConsensus: false, @@ -131,7 +132,7 @@ describe(method, async () => { return address } const parent = await blockchain.getCanonicalHeadHeader() - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { parentHash: parent.hash(), @@ -163,7 +164,7 @@ describe(method, async () => { storeTx.getSenderAddress = () => { return address } - const block2 = Block.fromBlockData( + const block2 = createBlockFromBlockData( { header: { parentHash: ranBlock!.hash(), diff --git a/packages/client/test/rpc/eth/getStorageAt.spec.ts b/packages/client/test/rpc/eth/getStorageAt.spec.ts index 42bfd59282..0dada977d2 100644 --- a/packages/client/test/rpc/eth/getStorageAt.spec.ts +++ b/packages/client/test/rpc/eth/getStorageAt.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { LegacyTransaction } from '@ethereumjs/tx' import { Address } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' @@ -7,6 +7,8 @@ import { INVALID_PARAMS } from '../../../src/rpc/error-code.js' import pow from '../../testdata/geth-genesis/pow.json' import { getRpcClient, setupChain } from '../helpers.js' +import type { Block } from '@ethereumjs/block' + const method = 'eth_getStorageAt' describe(method, async () => { @@ -30,7 +32,7 @@ describe(method, async () => { const signedTx = tx.sign(tx.getHashedMessageToSign()) const parent = await chain.blockchain.getCanonicalHeadHeader() - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { parentHash: parent.hash(), diff --git a/packages/client/test/rpc/eth/getTransactionCount.spec.ts b/packages/client/test/rpc/eth/getTransactionCount.spec.ts index 5c67430b00..71b8eb29f8 100644 --- a/packages/client/test/rpc/eth/getTransactionCount.spec.ts +++ b/packages/client/test/rpc/eth/getTransactionCount.spec.ts @@ -1,5 +1,5 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { getGenesis } from '@ethereumjs/genesis' import { LegacyTransaction, TransactionFactory } from '@ethereumjs/tx' @@ -9,6 +9,7 @@ import { assert, describe, it } from 'vitest' import { createClient, createManager, getRpcClient, startRPC } from '../helpers.js' import type { FullEthereumService } from '../../../src/service/index.js' +import type { Block } from '@ethereumjs/block' const method = 'eth_getTransactionCount' @@ -16,7 +17,7 @@ const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart describe(method, () => { it('call with valid arguments', async () => { - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateBlocks: false, validateConsensus: false, @@ -47,7 +48,7 @@ describe(method, () => { return address } const parent = await blockchain.getCanonicalHeadHeader() - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { parentHash: parent.hash(), @@ -74,7 +75,7 @@ describe(method, () => { }, 40000) it('call with pending block argument', async () => { - const blockchain = await Blockchain.create() + const blockchain = await createBlockchain() const client = await createClient({ blockchain, includeVM: true }) const manager = createManager(client) diff --git a/packages/client/test/rpc/eth/getTransactionReceipt.spec.ts b/packages/client/test/rpc/eth/getTransactionReceipt.spec.ts index ba0868facc..8ce2c8cf2e 100644 --- a/packages/client/test/rpc/eth/getTransactionReceipt.spec.ts +++ b/packages/client/test/rpc/eth/getTransactionReceipt.spec.ts @@ -1,4 +1,4 @@ -import { Common, Hardfork } from '@ethereumjs/common' +import { Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common' import { BlobEIP4844Transaction, FeeMarketEIP1559Transaction, @@ -91,7 +91,7 @@ describe(method, () => { const kzg = await loadKZG() - const common = Common.fromGethGenesis(gethGenesis, { + const common = createCommonFromGethGenesis(gethGenesis, { chain: 'customChain', hardfork: Hardfork.Cancun, customCrypto: { diff --git a/packages/client/test/rpc/eth/sendRawTransaction.spec.ts b/packages/client/test/rpc/eth/sendRawTransaction.spec.ts index 74a8ba3740..3fd39270a5 100644 --- a/packages/client/test/rpc/eth/sendRawTransaction.spec.ts +++ b/packages/client/test/rpc/eth/sendRawTransaction.spec.ts @@ -1,5 +1,5 @@ import { BlockHeader } from '@ethereumjs/block' -import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { Chain, Common, Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' import { BlobEIP4844Transaction, @@ -221,7 +221,7 @@ describe(method, () => { const kzg = await loadKZG() - const common = Common.fromGethGenesis(gethGenesis, { + const common = createCommonFromGethGenesis(gethGenesis, { chain: 'customChain', hardfork: Hardfork.Cancun, customCrypto: { kzg }, diff --git a/packages/client/test/rpc/helpers.ts b/packages/client/test/rpc/helpers.ts index 847187faf8..b3b6304b3c 100644 --- a/packages/client/test/rpc/helpers.ts +++ b/packages/client/test/rpc/helpers.ts @@ -1,5 +1,5 @@ import { BlockHeader } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockchain } from '@ethereumjs/blockchain' import { Chain as ChainEnum, Common, Hardfork, parseGethGenesis } from '@ethereumjs/common' import { getGenesis } from '@ethereumjs/genesis' import { @@ -28,6 +28,7 @@ import { mockBlockchain } from './mockBlockchain.js' import type { EthereumClient } from '../../src/client.js' import type { FullEthereumService } from '../../src/service/index.js' +import type { Blockchain } from '@ethereumjs/blockchain' import type { TypedTransaction } from '@ethereumjs/tx' import type { GenesisState } from '@ethereumjs/util' import type { IncomingMessage } from 'connect' @@ -235,7 +236,7 @@ export async function setupChain(genesisFile: any, chainName = 'dev', clientOpts // use genesisStateRoot for blockchain init as well as to start of the stateless // client. else the stateroot could have been generated out of box const genesisMeta = common.gteHardfork(Hardfork.Osaka) ? { genesisStateRoot } : { genesisState } - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateBlocks: false, validateConsensus: false, @@ -302,6 +303,7 @@ export async function runBlockWithTxs( // put block into chain and run execution await chain.putBlocks([block], fromEngine) await execution.run() + return block } /** diff --git a/packages/client/test/rpc/mockBlockchain.ts b/packages/client/test/rpc/mockBlockchain.ts index fd7efff58e..79d12643b2 100644 --- a/packages/client/test/rpc/mockBlockchain.ts +++ b/packages/client/test/rpc/mockBlockchain.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { LegacyTransaction } from '@ethereumjs/tx' import { equalsBytes, toBytes } from '@ethereumjs/util' @@ -13,12 +13,13 @@ export function mockBlockchain(options: any = {}) { ] const block = { hash: () => toBytes(blockHash), + serialize: () => createBlockFromBlockData({ header: { number }, transactions }).serialize(), header: { number: BigInt(number), hash: () => toBytes(blockHash), }, toJSON: () => ({ - ...Block.fromBlockData({ header: { number } }).toJSON(), + ...createBlockFromBlockData({ header: { number } }).toJSON(), hash: options.hash ?? blockHash, transactions: transactions.map((t: LegacyTransaction) => t.toJSON()), }), @@ -34,7 +35,7 @@ export function mockBlockchain(options: any = {}) { return block }, getCanonicalHeadHeader: () => { - return Block.fromBlockData().header + return createBlockFromBlockData().header }, getIteratorHead: () => { return block diff --git a/packages/client/test/rpc/rpc.spec.ts b/packages/client/test/rpc/rpc.spec.ts index 103f40b654..08e9263986 100644 --- a/packages/client/test/rpc/rpc.spec.ts +++ b/packages/client/test/rpc/rpc.spec.ts @@ -172,7 +172,7 @@ describe('JSON-RPC call', () => { }) describe('callWithStackTrace', () => { it('call with stack trace and gets a stack trace in the error', async () => { - const method = 'eth_getBlockByNumber' + const method = 'eth_getBlockTransactionCountByNumber' const mockBlockNumber = BigInt(123) const mockChain = { headers: { latest: { number: mockBlockNumber } }, @@ -192,7 +192,7 @@ describe('callWithStackTrace', () => { }) it('call with stack trace and gets an error without a stack trace', async () => { - const method = 'eth_getBlockByNumber' + const method = 'eth_getStorageAt' const mockBlockNumber = BigInt(123) const mockChain = { headers: { latest: { number: mockBlockNumber } }, @@ -207,7 +207,7 @@ describe('callWithStackTrace', () => { port: (server.address()! as AddressInfo).port, }) - const res = await rpc.request(method, ['0x678', false]) + const res = await rpc.request(method, ['0xavc', false]) assert.ok(res.error.trace === undefined) }) diff --git a/packages/client/test/rpc/txpool/content.spec.ts b/packages/client/test/rpc/txpool/content.spec.ts index d83844d921..4c7910a367 100644 --- a/packages/client/test/rpc/txpool/content.spec.ts +++ b/packages/client/test/rpc/txpool/content.spec.ts @@ -1,5 +1,5 @@ -import { Block, BlockHeader } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { getGenesis } from '@ethereumjs/genesis' import { TransactionFactory } from '@ethereumjs/tx' @@ -9,13 +9,14 @@ import { assert, describe, it } from 'vitest' import { createClient, createManager, getRpcClient, startRPC } from '../helpers.js' import type { FullEthereumService } from '../../../src/service/index.js' +import type { Block } from '@ethereumjs/block' const method = 'txpool_content' describe(method, () => { it('call with valid arguments', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateBlocks: false, validateConsensus: false, @@ -30,7 +31,7 @@ describe(method, () => { await vm.stateManager.generateCanonicalGenesis(getGenesis(1)) const gasLimit = 2000000 const parent = await blockchain.getCanonicalHeadHeader() - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { parentHash: parent.hash(), @@ -49,7 +50,7 @@ describe(method, () => { service.execution.vm.common.setHardfork('london') service.chain.config.chainCommon.setHardfork('london') const headBlock = await service.chain.getCanonicalHeadBlock() - const londonBlock = Block.fromBlockData( + const londonBlock = createBlockFromBlockData( { header: BlockHeader.fromHeaderData( { diff --git a/packages/client/test/service/fullethereumservice.spec.ts b/packages/client/test/service/fullethereumservice.spec.ts index 316eed8e7b..50041146ee 100644 --- a/packages/client/test/service/fullethereumservice.spec.ts +++ b/packages/client/test/service/fullethereumservice.spec.ts @@ -1,4 +1,4 @@ -import { Common, Hardfork } from '@ethereumjs/common' +import { Common, Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common' import { TransactionFactory, TransactionType } from '@ethereumjs/tx' import { equalsBytes, hexToBytes, randomBytes } from '@ethereumjs/util' import { assert, describe, expect, it, vi } from 'vitest' @@ -390,7 +390,7 @@ describe.skip('should handle structuring NewPooledTransactionHashes with eth/68 }) describe('should start on beacon sync when past merge', async () => { - const common = Common.fromGethGenesis(genesisJSON, { chain: 'post-merge' }) + const common = createCommonFromGethGenesis(genesisJSON, { chain: 'post-merge' }) common.setHardforkBy({ blockNumber: BigInt(0), td: BigInt(0) }) const config = new Config({ accountCache: 10000, storageCache: 1000, common }) const chain = await Chain.create({ config }) diff --git a/packages/client/test/sim/4844-blobpost.spec.ts b/packages/client/test/sim/4844-blobpost.spec.ts index ee87b96ebb..237098b5aa 100644 --- a/packages/client/test/sim/4844-blobpost.spec.ts +++ b/packages/client/test/sim/4844-blobpost.spec.ts @@ -1,4 +1,4 @@ -import { Common } from '@ethereumjs/common' +import { createCommonFromGethGenesis } from '@ethereumjs/common' import { bytesToHex, hexToBytes, privateToAddress } from '@ethereumjs/util' import { Client } from 'jayson/promise' import { randomBytes } from 'node:crypto' @@ -39,7 +39,7 @@ const shardingJson = require(`./configs/${network}.json`) // safely change chainId without modifying undelying json const commonJson = { ...shardingJson } commonJson.config = { ...commonJson.config, chainId } -const common = Common.fromGethGenesis(commonJson, { chain: network }) +const common = createCommonFromGethGenesis(commonJson, { chain: network }) export async function runTx(data: PrefixedHexString, to?: PrefixedHexString, value?: bigint) { return runTxHelper({ client, common, sender, pkey }, data, to, value) diff --git a/packages/client/test/sim/4844-devnet.spec.ts b/packages/client/test/sim/4844-devnet.spec.ts index f6805e8925..4623c99c20 100644 --- a/packages/client/test/sim/4844-devnet.spec.ts +++ b/packages/client/test/sim/4844-devnet.spec.ts @@ -1,4 +1,4 @@ -import { Common } from '@ethereumjs/common' +import { createCommonFromGethGenesis } from '@ethereumjs/common' import { TransactionFactory } from '@ethereumjs/tx' import { bytesToHex, hexToBytes, privateToAddress } from '@ethereumjs/util' import { Client } from 'jayson/promise' @@ -24,7 +24,7 @@ const client = Client.http({ port: 8545 }) const network = '4844-devnet' const shardingJson = require(`./configs/${network}.json`) -const common = Common.fromGethGenesis(shardingJson, { chain: network }) +const common = createCommonFromGethGenesis(shardingJson, { chain: network }) export async function runTx(data: PrefixedHexString, to?: PrefixedHexString, value?: bigint) { return runTxHelper({ client, common, sender, pkey }, data, to, value) diff --git a/packages/client/test/sim/beaconsync.spec.ts b/packages/client/test/sim/beaconsync.spec.ts index 6fd668af4f..cbc46eb1e4 100644 --- a/packages/client/test/sim/beaconsync.spec.ts +++ b/packages/client/test/sim/beaconsync.spec.ts @@ -1,4 +1,4 @@ -import { Common } from '@ethereumjs/common' +import { createCommonFromGethGenesis } from '@ethereumjs/common' import { bytesToHex, hexToBytes, parseGethGenesisState, privateToAddress } from '@ethereumjs/util' import debug from 'debug' import { Client } from 'jayson/promise' @@ -26,7 +26,7 @@ const client = Client.http({ port: 8545 }) const network = 'mainnet' const networkJson = require(`./configs/${network}.json`) -const common = Common.fromGethGenesis(networkJson, { chain: network }) +const common = createCommonFromGethGenesis(networkJson, { chain: network }) const customGenesisState = parseGethGenesisState(networkJson) const pkey = hexToBytes('0xae557af4ceefda559c924516cabf029bedc36b68109bf8d6183fe96e04121f4e') diff --git a/packages/client/test/sim/eof.spec.ts b/packages/client/test/sim/eof.spec.ts index 0071de6ebc..33401e9e2b 100644 --- a/packages/client/test/sim/eof.spec.ts +++ b/packages/client/test/sim/eof.spec.ts @@ -1,4 +1,4 @@ -import { Common } from '@ethereumjs/common' +import { createCommonFromGethGenesis } from '@ethereumjs/common' import { bytesToHex, hexToBytes, privateToAddress } from '@ethereumjs/util' import { Client } from 'jayson/promise' import { assert, describe, it } from 'vitest' @@ -19,7 +19,7 @@ const client = Client.http({ port: 8545 }) const network = 'eof' const eofJson = require(`./configs/${network}.json`) -const common = Common.fromGethGenesis(eofJson, { chain: network }) +const common = createCommonFromGethGenesis(eofJson, { chain: network }) export async function runTx(data: PrefixedHexString | '', to?: PrefixedHexString, value?: bigint) { return runTxHelper({ client, common, sender, pkey }, data, to, value) diff --git a/packages/client/test/sim/mainnet.spec.ts b/packages/client/test/sim/mainnet.spec.ts index b3ae45903a..459a5c0614 100644 --- a/packages/client/test/sim/mainnet.spec.ts +++ b/packages/client/test/sim/mainnet.spec.ts @@ -1,4 +1,4 @@ -import { Common } from '@ethereumjs/common' +import { createCommonFromGethGenesis } from '@ethereumjs/common' import { bytesToHex, hexToBytes, privateToAddress } from '@ethereumjs/util' import { Client } from 'jayson/promise' import { assert, describe, it } from 'vitest' @@ -20,7 +20,7 @@ const client = Client.http({ port: 8545 }) const network = 'mainnet' const eofJson = require(`./configs/${network}.json`) -const common = Common.fromGethGenesis(eofJson, { chain: network }) +const common = createCommonFromGethGenesis(eofJson, { chain: network }) export async function runTx(data: PrefixedHexString | '', to?: PrefixedHexString, value?: bigint) { return runTxHelper({ client, common, sender, pkey }, data, to, value) diff --git a/packages/client/test/sim/simutils.ts b/packages/client/test/sim/simutils.ts index 52b031aaf4..a65e4e3de4 100644 --- a/packages/client/test/sim/simutils.ts +++ b/packages/client/test/sim/simutils.ts @@ -1,5 +1,5 @@ import { executionPayloadFromBeaconPayload } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockchain } from '@ethereumjs/blockchain' import { BlobEIP4844Transaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx' import { Address, @@ -445,7 +445,7 @@ export async function createInlineClient( `${datadir}/${common.chainName()}/metaDB` ) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ db: new LevelDB(chainDB), genesisState: customGenesisState, common: config.chainCommon, diff --git a/packages/client/test/sim/snapsync.spec.ts b/packages/client/test/sim/snapsync.spec.ts index 6b37878716..81ab062c2f 100644 --- a/packages/client/test/sim/snapsync.spec.ts +++ b/packages/client/test/sim/snapsync.spec.ts @@ -1,4 +1,4 @@ -import { Common } from '@ethereumjs/common' +import { createCommonFromGethGenesis } from '@ethereumjs/common' import { Address, bytesToHex, @@ -32,7 +32,7 @@ const client = Client.http({ port: 8545 }) const network = 'mainnet' const networkJson = require(`./configs/${network}.json`) -const common = Common.fromGethGenesis(networkJson, { chain: network }) +const common = createCommonFromGethGenesis(networkJson, { chain: network }) const customGenesisState = parseGethGenesisState(networkJson) const pkey = hexToBytes('0xae557af4ceefda559c924516cabf029bedc36b68109bf8d6183fe96e04121f4e') diff --git a/packages/client/test/sync/beaconsync.spec.ts b/packages/client/test/sync/beaconsync.spec.ts index ce45b94ffb..81173704eb 100644 --- a/packages/client/test/sync/beaconsync.spec.ts +++ b/packages/client/test/sync/beaconsync.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { MemoryLevel } from 'memory-level' import * as td from 'testdouble' import { assert, describe, it, vi } from 'vitest' @@ -218,7 +218,7 @@ describe('[BeaconSynchronizer]', async () => { const chain = await Chain.create({ config }) const skeleton = new Skeleton({ chain, config, metaDB: new MemoryLevel() }) const sync = new BeaconSynchronizer({ config, pool, chain, execution, skeleton }) - const head = Block.fromBlockData({ header: { number: BigInt(15) } }) + const head = createBlockFromBlockData({ header: { number: BigInt(15) } }) await skeleton['putBlock'](head) ;(skeleton as any).status.progress.subchains = [ { @@ -227,12 +227,14 @@ describe('[BeaconSynchronizer]', async () => { }, ] await sync.open() - const block = Block.fromBlockData({ header: { number: BigInt(16), parentHash: head.hash() } }) + const block = createBlockFromBlockData({ + header: { number: BigInt(16), parentHash: head.hash() }, + }) assert.ok(await sync.extendChain(block), 'should extend chain successfully') assert.ok(await sync.setHead(block), 'should set head successfully') assert.equal(skeleton.bounds().head, BigInt(16), 'head should be updated') - const gapBlock = Block.fromBlockData({ header: { number: BigInt(18) } }) + const gapBlock = createBlockFromBlockData({ header: { number: BigInt(18) } }) assert.notOk(await sync.extendChain(gapBlock), 'should not extend chain with gapped block') assert.ok( await sync.setHead(gapBlock), diff --git a/packages/client/test/sync/fetcher/reverseblockfetcher.spec.ts b/packages/client/test/sync/fetcher/reverseblockfetcher.spec.ts index 170c1ff16e..ca63d3c87a 100644 --- a/packages/client/test/sync/fetcher/reverseblockfetcher.spec.ts +++ b/packages/client/test/sync/fetcher/reverseblockfetcher.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { MemoryLevel } from 'memory-level' import { assert, describe, it, vi } from 'vitest' @@ -195,29 +195,29 @@ describe('[ReverseBlockFetcher]', async () => { count: BigInt(5), timeout: 5, }) - const block47 = Block.fromBlockData( + const block47 = createBlockFromBlockData( { header: { number: BigInt(47), difficulty: BigInt(1) } }, { setHardfork: true } ) - const block48 = Block.fromBlockData( + const block48 = createBlockFromBlockData( { header: { number: BigInt(48), parentHash: block47.hash(), difficulty: BigInt(1) }, }, { setHardfork: true } ) - const block49 = Block.fromBlockData( + const block49 = createBlockFromBlockData( { header: { number: BigInt(49), parentHash: block48.hash(), difficulty: BigInt(1) }, }, { setHardfork: true } ) - const block4 = Block.fromBlockData( + const block4 = createBlockFromBlockData( { header: { number: BigInt(4), difficulty: BigInt(1) }, }, { setHardfork: true } ) - const block5 = Block.fromBlockData( + const block5 = createBlockFromBlockData( { header: { number: BigInt(5), difficulty: BigInt(1), parentHash: block4.hash() }, }, diff --git a/packages/client/test/sync/fullsync.spec.ts b/packages/client/test/sync/fullsync.spec.ts index 6e5d4c1ee8..5dd4c104cf 100644 --- a/packages/client/test/sync/fullsync.spec.ts +++ b/packages/client/test/sync/fullsync.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import * as td from 'testdouble' import { assert, describe, it, vi } from 'vitest' @@ -235,10 +235,10 @@ describe('[FullSynchronizer]', async () => { ] ;(sync as any).pool = { peers } - const chainTip = Block.fromBlockData({ + const chainTip = createBlockFromBlockData({ header: {}, }) - const newBlock = Block.fromBlockData({ + const newBlock = createBlockFromBlockData({ header: { parentHash: chainTip.hash(), }, @@ -277,10 +277,10 @@ describe('[FullSynchronizer]', async () => { execution, }) - const chainTip = Block.fromBlockData({ + const chainTip = createBlockFromBlockData({ header: {}, }) - const newBlock = Block.fromBlockData({ + const newBlock = createBlockFromBlockData({ header: { parentHash: chainTip.hash(), }, diff --git a/packages/client/test/sync/skeleton.spec.ts b/packages/client/test/sync/skeleton.spec.ts index 6103458021..8220a98316 100644 --- a/packages/client/test/sync/skeleton.spec.ts +++ b/packages/client/test/sync/skeleton.spec.ts @@ -1,5 +1,5 @@ -import { Block, BlockHeader } from '@ethereumjs/block' -import { Common } from '@ethereumjs/common' +import { BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' +import { Common, createCommonFromGethGenesis } from '@ethereumjs/common' import { equalsBytes, utf8ToBytes } from '@ethereumjs/util' import { MemoryLevel } from 'memory-level' import { assert, describe, it, vi } from 'vitest' @@ -11,26 +11,28 @@ import { Skeleton, errReorgDenied, errSyncMerged } from '../../src/sync/index.js import { short } from '../../src/util/index.js' import { wait } from '../integration/util.js' import genesisJSON from '../testdata/geth-genesis/post-merge.json' + +import type { Block } from '@ethereumjs/block' type Subchain = { head: bigint tail: bigint } const common = new Common({ chain: 1 }) -const block49 = Block.fromBlockData({ header: { number: 49 } }, { common }) -const block49B = Block.fromBlockData( +const block49 = createBlockFromBlockData({ header: { number: 49 } }, { common }) +const block49B = createBlockFromBlockData( { header: { number: 49, extraData: utf8ToBytes('B') } }, { common } ) -const block50 = Block.fromBlockData( +const block50 = createBlockFromBlockData( { header: { number: 50, parentHash: block49.hash() } }, { common } ) -const block50B = Block.fromBlockData( +const block50B = createBlockFromBlockData( { header: { number: 50, parentHash: block49.hash(), gasLimit: 999 } }, { common } ) -const block51 = Block.fromBlockData( +const block51 = createBlockFromBlockData( { header: { number: 51, parentHash: block50.hash() } }, { common } ) @@ -414,7 +416,7 @@ describe('[Skeleton] / setHead', async () => { extraData: '0x00000000000000000', difficulty: '0x1', } - const common = Common.fromGethGenesis(genesis, { chain: 'merge-not-set' }) + const common = createCommonFromGethGenesis(genesis, { chain: 'merge-not-set' }) const config = new Config({ common }) const chain = await Chain.create({ config }) ;(chain.blockchain as any)._validateBlocks = false @@ -433,15 +435,15 @@ describe('[Skeleton] / setHead', async () => { await chain.open() const genesis = await chain.getBlock(BigInt(0)) - const block1 = Block.fromBlockData( + const block1 = createBlockFromBlockData( { header: { number: 1, parentHash: genesis.hash(), difficulty: 100 } }, { common, setHardfork: true } ) - const block2 = Block.fromBlockData( + const block2 = createBlockFromBlockData( { header: { number: 2, parentHash: block1.hash(), difficulty: 100 } }, { common, setHardfork: true } ) - const block3 = Block.fromBlockData( + const block3 = createBlockFromBlockData( { header: { number: 3, difficulty: 100 } }, { common, setHardfork: true } ) @@ -545,23 +547,23 @@ describe('[Skeleton] / setHead', async () => { await chain.open() const genesis = await chain.getBlock(BigInt(0)) - const block1 = Block.fromBlockData( + const block1 = createBlockFromBlockData( { header: { number: 1, parentHash: genesis.hash(), difficulty: 100 } }, { common, setHardfork: true } ) - const block2 = Block.fromBlockData( + const block2 = createBlockFromBlockData( { header: { number: 2, parentHash: block1.hash(), difficulty: 100 } }, { common, setHardfork: true } ) - const block3 = Block.fromBlockData( + const block3 = createBlockFromBlockData( { header: { number: 3, parentHash: block2.hash(), difficulty: 100 } }, { common, setHardfork: true } ) - const block4 = Block.fromBlockData( + const block4 = createBlockFromBlockData( { header: { number: 4, parentHash: block3.hash(), difficulty: 100 } }, { common, setHardfork: true } ) - const block5 = Block.fromBlockData( + const block5 = createBlockFromBlockData( { header: { number: 5, parentHash: block4.hash(), difficulty: 100 } }, { common, setHardfork: true } ) @@ -624,23 +626,23 @@ describe('[Skeleton] / setHead', async () => { const genesis = await chain.getBlock(BigInt(0)) - const block1 = Block.fromBlockData( + const block1 = createBlockFromBlockData( { header: { number: 1, parentHash: genesis.hash(), difficulty: 100 } }, { common, setHardfork: true } ) - const block2 = Block.fromBlockData( + const block2 = createBlockFromBlockData( { header: { number: 2, parentHash: block1.hash(), difficulty: 100 } }, { common, setHardfork: true } ) - const block3 = Block.fromBlockData( + const block3 = createBlockFromBlockData( { header: { number: 3, parentHash: block2.hash(), difficulty: 100 } }, { common, setHardfork: true } ) - const block4 = Block.fromBlockData( + const block4 = createBlockFromBlockData( { header: { number: 4, parentHash: block3.hash(), difficulty: 100 } }, { common, setHardfork: true } ) - const block5 = Block.fromBlockData( + const block5 = createBlockFromBlockData( { header: { number: 5, parentHash: block4.hash(), difficulty: 100 } }, { common, setHardfork: true } ) @@ -693,15 +695,15 @@ describe('[Skeleton] / setHead', async () => { // restore linkedStatus skeleton['status'].linked = prevLinked - const block41 = Block.fromBlockData( + const block41 = createBlockFromBlockData( { header: { number: 4, parentHash: block3.hash(), difficulty: 101 } }, { common, setHardfork: true } ) - const block51 = Block.fromBlockData( + const block51 = createBlockFromBlockData( { header: { number: 5, parentHash: block41.hash(), difficulty: 100 } }, { common, setHardfork: true } ) - const block61 = Block.fromBlockData( + const block61 = createBlockFromBlockData( { header: { number: 6, parentHash: block51.hash(), difficulty: 100 } }, { common, setHardfork: true } ) @@ -724,15 +726,15 @@ describe('[Skeleton] / setHead', async () => { assert.equal(skeleton['status'].linked, true, 'should be linked') assert.equal(chain.blocks.height, BigInt(6), 'all blocks should be in chain') - const block71 = Block.fromBlockData( + const block71 = createBlockFromBlockData( { header: { number: 7, parentHash: block61.hash(), difficulty: 100 } }, { common, setHardfork: true } ) - const block81 = Block.fromBlockData( + const block81 = createBlockFromBlockData( { header: { number: 8, parentHash: block71.hash(), difficulty: 100 } }, { common, setHardfork: true } ) - const block91 = Block.fromBlockData( + const block91 = createBlockFromBlockData( { header: { number: 9, parentHash: block81.hash(), difficulty: 100 } }, { common, setHardfork: true } ) @@ -775,11 +777,11 @@ describe('[Skeleton] / setHead', async () => { ) // do a very common reorg that happens in a network: reorged head block - const block92 = Block.fromBlockData( + const block92 = createBlockFromBlockData( { header: { number: 9, parentHash: block81.hash(), difficulty: 101 } }, { common, setHardfork: true } ) - const block102 = Block.fromBlockData( + const block102 = createBlockFromBlockData( { header: { number: 10, parentHash: block92.hash(), difficulty: 100 } }, { common, setHardfork: true } ) @@ -822,7 +824,7 @@ describe('[Skeleton] / setHead', async () => { extraData: '0x00000000000000000', difficulty: '0x1', } - const common = Common.fromGethGenesis(genesis, { chain: 'post-merge' }) + const common = createCommonFromGethGenesis(genesis, { chain: 'post-merge' }) common.setHardforkBy({ blockNumber: BigInt(0), td: BigInt(0) }) const config = new Config({ common, @@ -834,31 +836,31 @@ describe('[Skeleton] / setHead', async () => { await chain.open() const genesisBlock = await chain.getBlock(BigInt(0)) - const block1 = Block.fromBlockData( + const block1 = createBlockFromBlockData( { header: { number: 1, parentHash: genesisBlock.hash(), difficulty: 100 } }, { common } ) - const block2 = Block.fromBlockData( + const block2 = createBlockFromBlockData( { header: { number: 2, parentHash: block1.hash(), difficulty: 100 } }, { common } ) - const block3PoW = Block.fromBlockData( + const block3PoW = createBlockFromBlockData( { header: { number: 3, parentHash: block2.hash(), difficulty: 100 } }, { common } ) - const block3PoS = Block.fromBlockData( + const block3PoS = createBlockFromBlockData( { header: { number: 3, parentHash: block2.hash(), difficulty: 0 } }, { common, setHardfork: BigInt(200) } ) - const block4InvalidPoS = Block.fromBlockData( + const block4InvalidPoS = createBlockFromBlockData( { header: { number: 4, parentHash: block3PoW.hash(), difficulty: 0 } }, { common, setHardfork: BigInt(200) } ) - const block4PoS = Block.fromBlockData( + const block4PoS = createBlockFromBlockData( { header: { number: 4, parentHash: block3PoS.hash(), difficulty: 0 } }, { common, setHardfork: BigInt(200) } ) - const block5 = Block.fromBlockData( + const block5 = createBlockFromBlockData( { header: { number: 5, parentHash: block4PoS.hash(), difficulty: 0 } }, { common, setHardfork: BigInt(200) } ) @@ -926,7 +928,7 @@ describe('[Skeleton] / setHead', async () => { extraData: '0x00000000000000000', difficulty: '0x1', } - const common = Common.fromGethGenesis(genesis, { chain: 'post-merge' }) + const common = createCommonFromGethGenesis(genesis, { chain: 'post-merge' }) common.setHardforkBy({ blockNumber: BigInt(0), td: BigInt(0) }) const config = new Config({ common, @@ -939,19 +941,19 @@ describe('[Skeleton] / setHead', async () => { await chain.open() const genesisBlock = await chain.getBlock(BigInt(0)) - const block1 = Block.fromBlockData( + const block1 = createBlockFromBlockData( { header: { number: 1, parentHash: genesisBlock.hash(), difficulty: 100 } }, { common } ) - const block2 = Block.fromBlockData( + const block2 = createBlockFromBlockData( { header: { number: 2, parentHash: block1.hash(), difficulty: 100 } }, { common } ) - const block3PoW = Block.fromBlockData( + const block3PoW = createBlockFromBlockData( { header: { number: 3, parentHash: block2.hash(), difficulty: 100 } }, { common } ) - const block4InvalidPoS = Block.fromBlockData( + const block4InvalidPoS = createBlockFromBlockData( { header: { number: 4, parentHash: block3PoW.hash(), difficulty: 0 } }, { common, setHardfork: 200 } ) @@ -986,7 +988,7 @@ describe('[Skeleton] / setHead', async () => { }, difficulty: '0x1', } - const common = Common.fromGethGenesis(genesis, { chain: 'post-merge' }) + const common = createCommonFromGethGenesis(genesis, { chain: 'post-merge' }) common.setHardforkBy({ blockNumber: BigInt(0), td: BigInt(0) }) const config = new Config({ common, @@ -1014,19 +1016,19 @@ describe('[Skeleton] / setHead', async () => { await chain.open() const genesisBlock = await chain.getBlock(BigInt(0)) - const block1 = Block.fromBlockData( + const block1 = createBlockFromBlockData( { header: { number: 1, parentHash: genesisBlock.hash(), difficulty: 100 } }, { common } ) - const block2 = Block.fromBlockData( + const block2 = createBlockFromBlockData( { header: { number: 2, parentHash: block1.hash(), difficulty: 100 } }, { common } ) - const block2PoS = Block.fromBlockData( + const block2PoS = createBlockFromBlockData( { header: { number: 2, parentHash: block1.hash(), difficulty: 0 } }, { common } ) - const block3 = Block.fromBlockData( + const block3 = createBlockFromBlockData( { header: { number: 3, parentHash: block2.hash(), difficulty: 0 } }, { common } ) diff --git a/packages/client/test/sync/txpool.spec.ts b/packages/client/test/sync/txpool.spec.ts index 17c14f97e5..f2c31222cf 100644 --- a/packages/client/test/sync/txpool.spec.ts +++ b/packages/client/test/sync/txpool.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' import { AccessListEIP2930Transaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx' @@ -766,12 +766,12 @@ describe('[TxPool]', async () => { assert.equal(pool.pool.size, 1, 'pool size 1') // Craft block with tx not in pool - let block = Block.fromBlockData({ transactions: [txA02] }, { common }) + let block = createBlockFromBlockData({ transactions: [txA02] }, { common }) pool.removeNewBlockTxs([block]) assert.equal(pool.pool.size, 1, 'pool size 1') // Craft block with tx in pool - block = Block.fromBlockData({ transactions: [txA01] }, { common }) + block = createBlockFromBlockData({ transactions: [txA01] }, { common }) pool.removeNewBlockTxs([block]) assert.equal(pool.pool.size, 0, 'pool should be empty') @@ -789,20 +789,20 @@ describe('[TxPool]', async () => { assert.equal(poolContent.length, 2, 'two txs') // Craft block with tx not in pool - block = Block.fromBlockData({ transactions: [txA02] }, { common }) + block = createBlockFromBlockData({ transactions: [txA02] }, { common }) pool.removeNewBlockTxs([block]) assert.equal(pool.pool.size, 1, 'pool size 1') poolContent = pool.pool.get(address)! assert.equal(poolContent.length, 2, 'two txs') // Craft block with tx in pool - block = Block.fromBlockData({ transactions: [txB01] }, { common }) + block = createBlockFromBlockData({ transactions: [txB01] }, { common }) pool.removeNewBlockTxs([block]) poolContent = pool.pool.get(address)! assert.equal(poolContent.length, 1, 'only one tx') // Craft block with tx in pool - block = Block.fromBlockData({ transactions: [txB02] }, { common }) + block = createBlockFromBlockData({ transactions: [txB02] }, { common }) pool.removeNewBlockTxs([block]) assert.equal(pool.pool.size, 0, 'pool size 0') diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json index 3c51a81481..ce0d04ef29 100644 --- a/packages/client/tsconfig.json +++ b/packages/client/tsconfig.json @@ -3,6 +3,6 @@ "include": ["bin", "src", "test", "package.json"], "compilerOptions": { "typeRoots": ["node_modules/@types", "src/@types"], - "moduleResolution": "Node16" + "allowSyntheticDefaultImports": true } } diff --git a/packages/client/tsconfig.prod.esm.json b/packages/client/tsconfig.prod.esm.json index a855a8ff28..baf85d72af 100644 --- a/packages/client/tsconfig.prod.esm.json +++ b/packages/client/tsconfig.prod.esm.json @@ -4,17 +4,18 @@ "compilerOptions": { "outDir": "dist/esm", "typeRoots": ["node_modules/@types", "src/@types"], - "composite": true + "composite": true, + "allowSyntheticDefaultImports": true }, "references": [ - { "path": "../block/tsconfig.prod.cjs.json" }, - { "path": "../blockchain/tsconfig.prod.cjs.json" }, - { "path": "../common/tsconfig.prod.cjs.json" }, - { "path": "../devp2p/tsconfig.prod.cjs.json" }, - { "path": "../rlp/tsconfig.prod.cjs.json" }, - { "path": "../trie/tsconfig.prod.cjs.json" }, - { "path": "../tx/tsconfig.prod.cjs.json" }, - { "path": "../util/tsconfig.prod.cjs.json" }, - { "path": "../vm/tsconfig.prod.cjs.json" } + { "path": "../block/tsconfig.prod.esm.json" }, + { "path": "../blockchain/tsconfig.prod.esm.json" }, + { "path": "../common/tsconfig.prod.esm.json" }, + { "path": "../devp2p/tsconfig.prod.esm.json" }, + { "path": "../rlp/tsconfig.prod.esm.json" }, + { "path": "../trie/tsconfig.prod.esm.json" }, + { "path": "../tx/tsconfig.prod.esm.json" }, + { "path": "../util/tsconfig.prod.esm.json" }, + { "path": "../vm/tsconfig.prod.esm.json" } ] } diff --git a/packages/common/examples/common.ts b/packages/common/examples/common.ts index cc7dba92e4..b7dcacab5c 100644 --- a/packages/common/examples/common.ts +++ b/packages/common/examples/common.ts @@ -1,4 +1,4 @@ -import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { Chain, Common, createCustomCommon, Hardfork } from '@ethereumjs/common' // With enums: const commonWithEnums = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) @@ -23,5 +23,5 @@ c = new Common({ chain: Chain.Mainnet, eips: [4844] }) console.log(`EIP 4844 is active -- ${c.isActivatedEIP(4844)}`) // Instantiate common with custom chainID -const commonWithCustomChainId = Common.custom({ chainId: 1234 }) +const commonWithCustomChainId = createCustomCommon({ chainId: 1234 }) console.log(`The current chain ID is ${commonWithCustomChainId.chainId}`) diff --git a/packages/common/examples/customCrypto.ts b/packages/common/examples/customCrypto.ts index 1f7949f71b..60668a71d2 100644 --- a/packages/common/examples/customCrypto.ts +++ b/packages/common/examples/customCrypto.ts @@ -1,13 +1,13 @@ import { keccak256, waitReady } from '@polkadot/wasm-crypto' import { Chain, Common } from '@ethereumjs/common' -import { Block } from '@ethereumjs/block' +import { Block, createBlockFromBlockData } from '@ethereumjs/block' const main = async () => { // @polkadot/wasm-crypto specific initialization await waitReady() const common = new Common({ chain: Chain.Mainnet, customCrypto: { keccak256 } }) - const block = Block.fromBlockData({}, { common }) + const block = createBlockFromBlockData({}, { common }) // Method invocations within EthereumJS library instantiations where the common // instance above is passed will now use the custom keccak256 implementation diff --git a/packages/common/examples/fromGeth.ts b/packages/common/examples/fromGeth.ts index 439bf2f8fc..2164390404 100644 --- a/packages/common/examples/fromGeth.ts +++ b/packages/common/examples/fromGeth.ts @@ -1,11 +1,11 @@ -import { Common } from '@ethereumjs/common' +import { Common, createCommonFromGethGenesis } from '@ethereumjs/common' import { hexToBytes } from '@ethereumjs/util' import genesisJson from './genesisData/post-merge.json' const genesisHash = hexToBytes('0x3b8fb240d288781d4aac94d3fd16809ee413bc99294a085798a589dae51ddd4a') // Load geth genesis json file into lets say `genesisJson` and optional `chain` and `genesisHash` -const common = Common.fromGethGenesis(genesisJson, { chain: 'customChain', genesisHash }) +const common = createCommonFromGethGenesis(genesisJson, { chain: 'customChain', genesisHash }) // If you don't have `genesisHash` while initiating common, you can later configure common (for e.g. // after calculating it via `blockchain`) common.setForkHashes(genesisHash) diff --git a/packages/common/package.json b/packages/common/package.json index 422276764e..7e817c1f8d 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -26,7 +26,8 @@ "email": "Holger.Drewes@gmail.com" } ], - "type": "commonjs", + "type": "module", + "sideEffects": false, "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "exports": { @@ -56,7 +57,8 @@ "tsc": "../../config/cli/ts-compile.sh" }, "dependencies": { - "@ethereumjs/util": "^9.0.3" + "@ethereumjs/util": "^9.0.3", + "ethereum-cryptography": "^2.2.1" }, "devDependencies": { "@polkadot/util": "^12.6.2", diff --git a/packages/common/src/common.ts b/packages/common/src/common.ts index 1ee56627c7..ddba7ecb36 100644 --- a/packages/common/src/common.ts +++ b/packages/common/src/common.ts @@ -9,29 +9,25 @@ import { } from '@ethereumjs/util' import { EventEmitter } from 'events' -import { chains as CHAIN_SPECS } from './chains.js' import { crc32 } from './crc.js' import { EIPs } from './eips.js' -import { Chain, CustomChain, Hardfork } from './enums.js' +import { Hardfork } from './enums.js' import { hardforks as HARDFORK_SPECS } from './hardforks.js' -import { parseGethGenesis } from './utils.js' -import type { ConsensusAlgorithm, ConsensusType } from './enums.js' +import { _getChainParams } from './index.js' + +import type { Chain, ConsensusAlgorithm, ConsensusType } from './enums.js' import type { BootstrapNodeConfig, CasperConfig, ChainConfig, - ChainName, - ChainsConfig, CliqueConfig, CommonOpts, - CustomCommonOpts, CustomCrypto, EIPConfig, EIPOrHFConfig, EthashConfig, GenesisBlockConfig, - GethConfigOpts, HardforkByOpts, HardforkConfig, HardforkTransitionConfig, @@ -68,171 +64,6 @@ export class Common { public events: EventEmitter - /** - * Creates a {@link Common} object for a custom chain, based on a standard one. - * - * It uses all the {@link Chain} parameters from the {@link baseChain} option except the ones overridden - * in a provided {@link chainParamsOrName} dictionary. Some usage example: - * - * ```javascript - * Common.custom({chainId: 123}) - * ``` - * - * There are also selected supported custom chains which can be initialized by using one of the - * {@link CustomChains} for {@link chainParamsOrName}, e.g.: - * - * ```javascript - * Common.custom(CustomChains.MaticMumbai) - * ``` - * - * Note that these supported custom chains only provide some base parameters (usually the chain and - * network ID and a name) and can only be used for selected use cases (e.g. sending a tx with - * the `@ethereumjs/tx` library to a Layer-2 chain). - * - * @param chainParamsOrName Custom parameter dict (`name` will default to `custom-chain`) or string with name of a supported custom chain - * @param opts Custom chain options to set the {@link CustomCommonOpts.baseChain}, selected {@link CustomCommonOpts.hardfork} and others - */ - static custom( - chainParamsOrName: Partial | CustomChain, - opts: CustomCommonOpts = {} - ): Common { - const baseChain = opts.baseChain ?? 'mainnet' - const standardChainParams = { ...Common._getChainParams(baseChain) } - standardChainParams['name'] = 'custom-chain' - - if (typeof chainParamsOrName !== 'string') { - return new Common({ - chain: { - ...standardChainParams, - ...chainParamsOrName, - }, - ...opts, - }) - } else { - if (chainParamsOrName === CustomChain.PolygonMainnet) { - return Common.custom( - { - name: CustomChain.PolygonMainnet, - chainId: 137, - networkId: 137, - }, - opts - ) - } - if (chainParamsOrName === CustomChain.PolygonMumbai) { - return Common.custom( - { - name: CustomChain.PolygonMumbai, - chainId: 80001, - networkId: 80001, - }, - opts - ) - } - if (chainParamsOrName === CustomChain.ArbitrumOne) { - return Common.custom( - { - name: CustomChain.ArbitrumOne, - chainId: 42161, - networkId: 42161, - }, - opts - ) - } - if (chainParamsOrName === CustomChain.xDaiChain) { - return Common.custom( - { - name: CustomChain.xDaiChain, - chainId: 100, - networkId: 100, - }, - opts - ) - } - - if (chainParamsOrName === CustomChain.OptimisticKovan) { - return Common.custom( - { - name: CustomChain.OptimisticKovan, - chainId: 69, - networkId: 69, - }, - opts - ) - } - - if (chainParamsOrName === CustomChain.OptimisticEthereum) { - return Common.custom( - { - name: CustomChain.OptimisticEthereum, - chainId: 10, - networkId: 10, - }, - // Optimism has not implemented the London hardfork yet (targeting Q1.22) - { hardfork: Hardfork.Berlin, ...opts } - ) - } - throw new Error(`Custom chain ${chainParamsOrName} not supported`) - } - } - - /** - * Static method to load and set common from a geth genesis json - * @param genesisJson json of geth configuration - * @param { chain, eips, genesisHash, hardfork, mergeForkIdPostMerge } to further configure the common instance - * @returns Common - */ - static fromGethGenesis( - genesisJson: any, - { chain, eips, genesisHash, hardfork, mergeForkIdPostMerge, customCrypto }: GethConfigOpts - ): Common { - const genesisParams = parseGethGenesis(genesisJson, chain, mergeForkIdPostMerge) - const common = new Common({ - chain: genesisParams.name ?? 'custom', - customChains: [genesisParams], - eips, - hardfork: hardfork ?? genesisParams.hardfork, - customCrypto, - }) - if (genesisHash !== undefined) { - common.setForkHashes(genesisHash) - } - return common - } - - /** - * Static method to determine if a {@link chainId} is supported as a standard chain - * @param chainId bigint id (`1`) of a standard chain - * @returns boolean - */ - static isSupportedChainId(chainId: bigint): boolean { - const initializedChains = this.getInitializedChains() - return Boolean((initializedChains['names'] as ChainName)[chainId.toString()]) - } - - protected static _getChainParams( - chain: string | number | Chain | bigint, - customChains?: ChainConfig[] - ): ChainConfig { - const initializedChains = this.getInitializedChains(customChains) - if (typeof chain === 'number' || typeof chain === 'bigint') { - chain = chain.toString() - - if ((initializedChains['names'] as ChainName)[chain]) { - const name: string = (initializedChains['names'] as ChainName)[chain] - return initializedChains[name] as ChainConfig - } - - throw new Error(`Chain with ID ${chain} not supported`) - } - - if (initializedChains[chain] !== undefined) { - return initializedChains[chain] as ChainConfig - } - - throw new Error(`Chain with name ${chain} not supported`) - } - constructor(opts: CommonOpts) { this.events = new EventEmitter() @@ -268,7 +99,7 @@ export class Common { */ setChain(chain: string | number | Chain | bigint | object): ChainConfig { if (typeof chain === 'number' || typeof chain === 'bigint' || typeof chain === 'string') { - this._chainParams = Common._getChainParams(chain, this._customChains) + this._chainParams = _getChainParams(chain, this._customChains) } else if (typeof chain === 'object') { if (this._customChains.length > 0) { throw new Error( @@ -540,6 +371,15 @@ export class Common { } else { this._mergeWithParamsCache(hfChanges[1]) } + if ( + hfChanges[1].vm || + hfChanges[1].gasConfig || + hfChanges[1].gasPrices || + hfChanges[1].pow || + hfChanges[1].sharding + ) { + this._mergeWithParamsCache(hfChanges[1]) + } if (hfChanges[0] === hardfork) break } // Iterate through all additionally activated EIPs @@ -583,7 +423,7 @@ export class Common { (this._paramsCache as any)[topic] !== undefined && (this._paramsCache as any)[topic][name] !== undefined ) { - value = (this._paramsCache as any)[topic][name].v + value = (this._paramsCache as any)[topic][name] } return BigInt(value ?? 0) } @@ -611,7 +451,7 @@ export class Common { (hfChanges[1] as any)[topic] !== undefined && (hfChanges[1] as any)[topic][name] !== undefined ) { - value = (hfChanges[1] as any)[topic][name].v + value = (hfChanges[1] as any)[topic][name] } } if (hfChanges[0] === hardfork) break @@ -638,7 +478,7 @@ export class Common { if (eipParams[topic][name] === undefined) { return undefined } - const value = eipParams[topic][name].v + const value = eipParams[topic][name] return BigInt(value) } @@ -1104,21 +944,4 @@ export class Common { copy.events = new EventEmitter() return copy } - - static getInitializedChains(customChains?: ChainConfig[]): ChainsConfig { - const names: ChainName = {} - for (const [name, id] of Object.entries(Chain)) { - names[id] = name.toLowerCase() - } - const chains = { ...CHAIN_SPECS } as ChainsConfig - if (customChains) { - for (const chain of customChains) { - const { name } = chain - names[chain.chainId.toString()] = name - chains[name] = chain - } - } - chains.names = names - return chains - } } diff --git a/packages/common/src/constructors.ts b/packages/common/src/constructors.ts new file mode 100644 index 0000000000..4fbb87208d --- /dev/null +++ b/packages/common/src/constructors.ts @@ -0,0 +1,135 @@ +import { Common, CustomChain, Hardfork, _getChainParams, parseGethGenesis } from './index.js' + +import type { ChainConfig, CustomCommonOpts, GethConfigOpts } from './index.js' + +/** + * Creates a {@link Common} object for a custom chain, based on a standard one. + * + * It uses all the {@link Chain} parameters from the {@link baseChain} option except the ones overridden + * in a provided {@link chainParamsOrName} dictionary. Some usage example: + * + * ```javascript + * createCustomCommon({chainId: 123}) + * ``` + * + * There are also selected supported custom chains which can be initialized by using one of the + * {@link CustomChains} for {@link chainParamsOrName}, e.g.: + * + * ```javascript + * createCustomCommon(CustomChains.MaticMumbai) + * ``` + * + * Note that these supported custom chains only provide some base parameters (usually the chain and + * network ID and a name) and can only be used for selected use cases (e.g. sending a tx with + * the `@ethereumjs/tx` library to a Layer-2 chain). + * + * @param chainParamsOrName Custom parameter dict (`name` will default to `custom-chain`) or string with name of a supported custom chain + * @param opts Custom chain options to set the {@link CustomCommonOpts.baseChain}, selected {@link CustomCommonOpts.hardfork} and others + */ +export function createCustomCommon( + chainParamsOrName: Partial | CustomChain, + opts: CustomCommonOpts = {} +): Common { + const baseChain = opts.baseChain ?? 'mainnet' + const standardChainParams = { ..._getChainParams(baseChain) } + standardChainParams['name'] = 'custom-chain' + + if (typeof chainParamsOrName !== 'string') { + return new Common({ + chain: { + ...standardChainParams, + ...chainParamsOrName, + }, + ...opts, + }) + } else { + if (chainParamsOrName === CustomChain.PolygonMainnet) { + return createCustomCommon( + { + name: CustomChain.PolygonMainnet, + chainId: 137, + networkId: 137, + }, + opts + ) + } + if (chainParamsOrName === CustomChain.PolygonMumbai) { + return createCustomCommon( + { + name: CustomChain.PolygonMumbai, + chainId: 80001, + networkId: 80001, + }, + opts + ) + } + if (chainParamsOrName === CustomChain.ArbitrumOne) { + return createCustomCommon( + { + name: CustomChain.ArbitrumOne, + chainId: 42161, + networkId: 42161, + }, + opts + ) + } + if (chainParamsOrName === CustomChain.xDaiChain) { + return createCustomCommon( + { + name: CustomChain.xDaiChain, + chainId: 100, + networkId: 100, + }, + opts + ) + } + + if (chainParamsOrName === CustomChain.OptimisticKovan) { + return createCustomCommon( + { + name: CustomChain.OptimisticKovan, + chainId: 69, + networkId: 69, + }, + opts + ) + } + + if (chainParamsOrName === CustomChain.OptimisticEthereum) { + return createCustomCommon( + { + name: CustomChain.OptimisticEthereum, + chainId: 10, + networkId: 10, + }, + // Optimism has not implemented the London hardfork yet (targeting Q1.22) + { hardfork: Hardfork.Berlin, ...opts } + ) + } + throw new Error(`Custom chain ${chainParamsOrName} not supported`) + } +} + +/** + * Static method to load and set common from a geth genesis json + * @param genesisJson json of geth configuration + * @param { chain, eips, genesisHash, hardfork, mergeForkIdPostMerge } to further configure the common instance + * @returns Common + */ +export function createCommonFromGethGenesis( + genesisJson: any, + { chain, eips, genesisHash, hardfork, mergeForkIdPostMerge, customCrypto }: GethConfigOpts +): Common { + const genesisParams = parseGethGenesis(genesisJson, chain, mergeForkIdPostMerge) + const common = new Common({ + chain: genesisParams.name ?? 'custom', + customChains: [genesisParams], + eips, + hardfork: hardfork ?? genesisParams.hardfork, + customCrypto, + }) + if (genesisHash !== undefined) { + common.setForkHashes(genesisHash) + } + return common +} diff --git a/packages/common/src/eips.ts b/packages/common/src/eips.ts index 4f78f9c83d..b6016ba84b 100644 --- a/packages/common/src/eips.ts +++ b/packages/common/src/eips.ts @@ -21,14 +21,8 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.Chainstart, requiredEIPs: [], gasPrices: { - tstore: { - v: 100, - d: 'Base fee of the TSTORE opcode', - }, - tload: { - v: 100, - d: 'Base fee of the TLOAD opcode', - }, + tstore: 100, // Base fee of the TSTORE opcode + tload: 100, // Base fee of the TLOAD opcode }, }, 1559: { @@ -38,18 +32,9 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.Berlin, requiredEIPs: [2930], gasConfig: { - baseFeeMaxChangeDenominator: { - v: 8, - d: 'Maximum base fee change denominator', - }, - elasticityMultiplier: { - v: 2, - d: 'Maximum block gas target elasticity', - }, - initialBaseFee: { - v: 1000000000, - d: 'Initial base fee on first EIP1559 block', - }, + baseFeeMaxChangeDenominator: 8, // Maximum base fee change denominator + elasticityMultiplier: 2, // Maximum block gas target elasticity + initialBaseFee: 1000000000, // Initial base fee on first EIP1559 block }, }, 2565: { @@ -59,10 +44,7 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.Byzantium, requiredEIPs: [], gasPrices: { - modexpGquaddivisor: { - v: 3, - d: 'Gquaddivisor from modexp precompile for gas calculation', - }, + modexpGquaddivisor: 3, // Gquaddivisor from modexp precompile for gas calculation }, }, 2537: { @@ -73,38 +55,14 @@ export const EIPs: EIPsDict = { requiredEIPs: [], gasConfig: {}, gasPrices: { - Bls12381G1AddGas: { - v: 500, - d: 'Gas cost of a single BLS12-381 G1 addition precompile-call', - }, - Bls12381G1MulGas: { - v: 12000, - d: 'Gas cost of a single BLS12-381 G1 multiplication precompile-call', - }, - Bls12381G2AddGas: { - v: 800, - d: 'Gas cost of a single BLS12-381 G2 addition precompile-call', - }, - Bls12381G2MulGas: { - v: 45000, - d: 'Gas cost of a single BLS12-381 G2 multiplication precompile-call', - }, - Bls12381PairingBaseGas: { - v: 65000, - d: 'Base gas cost of BLS12-381 pairing check', - }, - Bls12381PairingPerPairGas: { - v: 43000, - d: 'Per-pair gas cost of BLS12-381 pairing check', - }, - Bls12381MapG1Gas: { - v: 5500, - d: 'Gas cost of BLS12-381 map field element to G1', - }, - Bls12381MapG2Gas: { - v: 75000, - d: 'Gas cost of BLS12-381 map field element to G2', - }, + Bls12381G1AddGas: 500, // Gas cost of a single BLS12-381 G1 addition precompile-call + Bls12381G1MulGas: 12000, // Gas cost of a single BLS12-381 G1 multiplication precompile-call + Bls12381G2AddGas: 800, // Gas cost of a single BLS12-381 G2 addition precompile-call + Bls12381G2MulGas: 45000, // Gas cost of a single BLS12-381 G2 multiplication precompile-call + Bls12381PairingBaseGas: 65000, // Base gas cost of BLS12-381 pairing check + Bls12381PairingPerPairGas: 43000, // Per-pair gas cost of BLS12-381 pairing check + Bls12381MapG1Gas: 5500, // Gas cost of BLS12-381 map field element to G1 + Bls12381MapG2Gas: 75000, // Gas cost of BLS12-381 map field element to G2 }, vm: {}, pow: {}, @@ -123,78 +81,24 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.Chainstart, requiredEIPs: [], gasPrices: { - coldsload: { - v: 2100, - d: 'Gas cost of the first read of storage from a given location (per transaction)', - }, - coldaccountaccess: { - v: 2600, - d: 'Gas cost of the first read of a given address (per transaction)', - }, - warmstorageread: { - v: 100, - d: "Gas cost of reading storage locations which have already loaded 'cold'", - }, - sstoreCleanGasEIP2200: { - v: 2900, - d: 'Once per SSTORE operation from clean non-zero to something else', - }, - sstoreNoopGasEIP2200: { - v: 100, - d: "Once per SSTORE operation if the value doesn't change", - }, - sstoreDirtyGasEIP2200: { - v: 100, - d: 'Once per SSTORE operation if a dirty value is changed', - }, - sstoreInitRefundEIP2200: { - v: 19900, - d: 'Once per SSTORE operation for resetting to the original zero value', - }, - sstoreCleanRefundEIP2200: { - v: 4900, - d: 'Once per SSTORE operation for resetting to the original non-zero value', - }, - call: { - v: 0, - d: 'Base fee of the CALL opcode', - }, - callcode: { - v: 0, - d: 'Base fee of the CALLCODE opcode', - }, - delegatecall: { - v: 0, - d: 'Base fee of the DELEGATECALL opcode', - }, - staticcall: { - v: 0, - d: 'Base fee of the STATICCALL opcode', - }, - balance: { - v: 0, - d: 'Base fee of the BALANCE opcode', - }, - extcodesize: { - v: 0, - d: 'Base fee of the EXTCODESIZE opcode', - }, - extcodecopy: { - v: 0, - d: 'Base fee of the EXTCODECOPY opcode', - }, - extcodehash: { - v: 0, - d: 'Base fee of the EXTCODEHASH opcode', - }, - sload: { - v: 0, - d: 'Base fee of the SLOAD opcode', - }, - sstore: { - v: 0, - d: 'Base fee of the SSTORE opcode', - }, + coldsload: 2100, // Gas cost of the first read of storage from a given location (per transaction) + coldaccountaccess: 2600, // Gas cost of the first read of a given address (per transaction) + warmstorageread: 100, // Gas cost of reading storage locations which have already loaded 'cold' + sstoreCleanGasEIP2200: 2900, // Once per SSTORE operation from clean non-zero to something else + sstoreNoopGasEIP2200: 100, // Once per SSTORE operation if the value doesn't change + sstoreDirtyGasEIP2200: 100, // Once per SSTORE operation if a dirty value is changed + sstoreInitRefundEIP2200: 19900, // Once per SSTORE operation for resetting to the original zero value + sstoreCleanRefundEIP2200: 4900, // Once per SSTORE operation for resetting to the original non-zero value + call: 0, // Base fee of the CALL opcode + callcode: 0, // Base fee of the CALLCODE opcode + delegatecall: 0, // Base fee of the DELEGATECALL opcode + staticcall: 0, // Base fee of the STATICCALL opcode + balance: 0, // Base fee of the BALANCE opcode + extcodesize: 0, // Base fee of the EXTCODESIZE opcode + extcodecopy: 0, // Base fee of the EXTCODECOPY opcode + extcodehash: 0, // Base fee of the EXTCODEHASH opcode + sload: 0, // Base fee of the SLOAD opcode + sstore: 0, // Base fee of the SSTORE opcode }, }, 2930: { @@ -204,14 +108,8 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.Istanbul, requiredEIPs: [2718, 2929], gasPrices: { - accessListStorageKeyCost: { - v: 1900, - d: 'Gas cost per storage key in an Access List transaction', - }, - accessListAddressCost: { - v: 2400, - d: 'Gas cost per storage key in an Access List transaction', - }, + accessListStorageKeyCost: 1900, // Gas cost per storage key in an Access List transaction + accessListAddressCost: 2400, // Gas cost per storage key in an Access List transaction }, }, 2935: { @@ -221,14 +119,8 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.Chainstart, requiredEIPs: [], vm: { - historyStorageAddress: { - v: BigInt('0x0aae40965e6800cd9b1f4b05ff21581047e3f91e'), - d: 'The address where the historical blockhashes are stored', - }, - historyServeWindow: { - v: BigInt(8192), - d: 'The amount of blocks to be served by the historical blockhash contract', - }, + historyStorageAddress: BigInt('0x0aae40965e6800cd9b1f4b05ff21581047e3f91e'), // The address where the historical blockhashes are stored + historyServeWindow: BigInt(8192), // The amount of blocks to be served by the historical blockhash contract }, }, 3074: { @@ -238,18 +130,9 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.London, requiredEIPs: [], gasPrices: { - auth: { - v: 3100, - d: 'Gas cost of the AUTH opcode', - }, - authcall: { - v: 0, - d: 'Gas cost of the AUTHCALL opcode', - }, - authcallValueTransfer: { - v: 6700, - d: 'Paid for CALL when the value transfer is non-zero', - }, + auth: 3100, // Gas cost of the AUTH opcode + authcall: 0, // Gas cost of the AUTHCALL opcode + authcallValueTransfer: 6700, // Paid for CALL when the value transfer is non-zero }, }, 3198: { @@ -259,10 +142,7 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.London, requiredEIPs: [], gasPrices: { - basefee: { - v: 2, - d: 'Gas cost of the BASEFEE opcode', - }, + basefee: 2, // Gas cost of the BASEFEE opcode }, }, 3529: { @@ -272,20 +152,11 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.Berlin, requiredEIPs: [2929], gasConfig: { - maxRefundQuotient: { - v: 5, - d: 'Maximum refund quotient; max tx refund is min(tx.gasUsed/maxRefundQuotient, tx.gasRefund)', - }, + maxRefundQuotient: 5, // Maximum refund quotient; max tx refund is min(tx.gasUsed/maxRefundQuotient, tx.gasRefund) }, gasPrices: { - selfdestructRefund: { - v: 0, - d: 'Refunded following a selfdestruct operation', - }, - sstoreClearRefundEIP2200: { - v: 4800, - d: 'Once per SSTORE operation for clearing an originally existing storage slot', - }, + selfdestructRefund: 0, // Refunded following a selfdestruct operation + sstoreClearRefundEIP2200: 4800, // Once per SSTORE operation for clearing an originally existing storage slot }, }, 3540: { @@ -309,10 +180,7 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.MuirGlacier, requiredEIPs: [], pow: { - difficultyBombDelay: { - v: 9500000, - d: 'the amount of blocks to delay the difficulty bomb with', - }, + difficultyBombDelay: 9500000, // the amount of blocks to delay the difficulty bomb with }, }, 3607: { @@ -354,10 +222,7 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.Chainstart, requiredEIPs: [], gasPrices: { - push0: { - v: 2, - d: 'Base fee of the PUSH0 opcode', - }, + push0: 2, // Base fee of the PUSH0 opcode }, }, 3860: { @@ -367,16 +232,10 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.SpuriousDragon, requiredEIPs: [], gasPrices: { - initCodeWordCost: { - v: 2, - d: 'Gas to pay for each word (32 bytes) of initcode when creating a contract', - }, + initCodeWordCost: 2, // Gas to pay for each word (32 bytes) of initcode when creating a contract }, vm: { - maxInitCodeSize: { - v: 49152, - d: 'Maximum length of initialization code when creating a contract', - }, + maxInitCodeSize: 49152, // Maximum length of initialization code when creating a contract }, }, 4345: { @@ -386,10 +245,7 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.London, requiredEIPs: [], pow: { - difficultyBombDelay: { - v: 10700000, - d: 'the amount of blocks to delay the difficulty bomb with', - }, + difficultyBombDelay: 10700000, // the amount of blocks to delay the difficulty bomb with }, }, 4399: { @@ -399,10 +255,7 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.London, requiredEIPs: [], gasPrices: { - prevrandao: { - v: 2, - d: 'Base fee of the PREVRANDAO opcode (previously DIFFICULTY)', - }, + prevrandao: 2, // Base fee of the PREVRANDAO opcode (previously DIFFICULTY) }, }, 4788: { @@ -413,10 +266,7 @@ export const EIPs: EIPsDict = { requiredEIPs: [], gasPrices: {}, vm: { - historicalRootsLength: { - v: 8191, - d: 'The modulo parameter of the beaconroot ring buffer in the beaconroot statefull precompile', - }, + historicalRootsLength: 8191, // The modulo parameter of the beaconroot ring buffer in the beaconroot statefull precompile }, }, 4844: { @@ -426,50 +276,20 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.Paris, requiredEIPs: [1559, 2718, 2930, 4895], gasConfig: { - blobGasPerBlob: { - v: 131072, - d: 'The base fee for blob gas per blob', - }, - targetBlobGasPerBlock: { - v: 393216, - d: 'The target blob gas consumed per block', - }, - maxblobGasPerBlock: { - v: 786432, - d: 'The max blob gas allowable per block', - }, - blobGasPriceUpdateFraction: { - v: 3338477, - d: 'The denominator used in the exponential when calculating a blob gas price', - }, + blobGasPerBlob: 131072, // The base fee for blob gas per blob + targetBlobGasPerBlock: 393216, // The target blob gas consumed per block + maxblobGasPerBlock: 786432, // The max blob gas allowable per block + blobGasPriceUpdateFraction: 3338477, // The denominator used in the exponential when calculating a blob gas price }, gasPrices: { - simpleGasPerBlob: { - v: 12000, - d: 'The basic gas fee for each blob', - }, - minBlobGasPrice: { - v: 1, - d: 'The minimum fee per blob gas', - }, - kzgPointEvaluationGasPrecompilePrice: { - v: 50000, - d: 'The fee associated with the point evaluation precompile', - }, - blobhash: { - v: 3, - d: 'Base fee of the BLOBHASH opcode', - }, + simpleGasPerBlob: 12000, // The basic gas fee for each blob + minBlobGasPrice: 1, // The minimum fee per blob gas + kzgPointEvaluationGasPrecompilePrice: 50000, // The fee associated with the point evaluation precompile + blobhash: 3, // Base fee of the BLOBHASH opcode }, sharding: { - blobCommitmentVersionKzg: { - v: 1, - d: 'The number indicated a versioned hash is a KZG commitment', - }, - fieldElementsPerBlob: { - v: 4096, - d: 'The number of field elements allowed per blob', - }, + blobCommitmentVersionKzg: 1, // The number indicated a versioned hash is a KZG commitment + fieldElementsPerBlob: 4096, // The number of field elements allowed per blob }, }, 4895: { @@ -486,10 +306,7 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.GrayGlacier, requiredEIPs: [], pow: { - difficultyBombDelay: { - v: 11400000, - d: 'the amount of blocks to delay the difficulty bomb with', - }, + difficultyBombDelay: 11400000, // the amount of blocks to delay the difficulty bomb with }, }, 5656: { @@ -499,10 +316,7 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.Shanghai, requiredEIPs: [], gasPrices: { - mcopy: { - v: 3, - d: 'Base fee of the MCOPY opcode', - }, + mcopy: 3, // Base fee of the MCOPY opcode }, }, 6110: { @@ -526,22 +340,13 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.London, requiredEIPs: [], gasPrices: { - create: { - v: 1000, - d: 'Base fee of the CREATE opcode', - }, - coldsload: { - v: 0, - d: 'Gas cost of the first read of storage from a given location (per transaction)', - }, + create: 1000, // Base fee of the CREATE opcode + coldsload: 0, // Gas cost of the first read of storage from a given location (per transaction) }, vm: { // kaustinen 6 current uses this address, however this will be updated to correct address // in next iteration - historyStorageAddress: { - v: BigInt('0xfffffffffffffffffffffffffffffffffffffffe'), - d: 'The address where the historical blockhashes are stored', - }, + historyStorageAddress: BigInt('0xfffffffffffffffffffffffffffffffffffffffe'), // The address where the historical blockhashes are stored }, }, 7002: { @@ -551,54 +356,18 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.Paris, requiredEIPs: [7685], vm: { - withdrawalRequestType: { - v: BigInt(0x01), - d: 'The withdrawal request type for EIP-7685', - }, - excessWithdrawalsRequestStorageSlot: { - v: BigInt(0), - d: 'The storage slot of the excess withdrawals', - }, - withdrawalsRequestCountStorage: { - v: BigInt(1), - d: 'The storage slot of the withdrawal request count', - }, - withdrawalsRequestQueueHeadStorageSlot: { - v: BigInt(2), - d: 'The storage slot of the withdrawal request head of the queue', - }, - withdrawalsRequestTailHeadStorageSlot: { - v: BigInt(3), - d: 'The storage slot of the withdrawal request tail of the queue', - }, - withdrawalsRequestQueueStorageOffset: { - v: BigInt(4), - d: 'The storage slot of the withdrawal request queue offset', - }, - maxWithdrawalRequestsPerBlock: { - v: BigInt(16), - d: 'The max withdrawal requests per block', - }, - targetWithdrawalRequestsPerBlock: { - v: BigInt(2), - d: 'The target withdrawal requests per block', - }, - minWithdrawalRequestFee: { - v: BigInt(1), - d: 'The minimum withdrawal request fee (in wei)', - }, - withdrawalRequestFeeUpdateFraction: { - v: BigInt(17), - d: 'The withdrawal request fee update fraction (used in the fake exponential)', - }, - systemAddress: { - v: BigInt('0xfffffffffffffffffffffffffffffffffffffffe'), - d: 'The system address to perform operations on the withdrawal requests predeploy address', - }, - withdrawalRequestPredeployAddress: { - v: BigInt('0x00A3ca265EBcb825B45F985A16CEFB49958cE017'), - d: 'Address of the validator excess address', - }, + withdrawalRequestType: BigInt(0x01), // The withdrawal request type for EIP-7685 + excessWithdrawalsRequestStorageSlot: BigInt(0), // The storage slot of the excess withdrawals + withdrawalsRequestCountStorage: BigInt(1), // The storage slot of the withdrawal request count + withdrawalsRequestQueueHeadStorageSlot: BigInt(2), // The storage slot of the withdrawal request head of the queue + withdrawalsRequestTailHeadStorageSlot: BigInt(3), // The storage slot of the withdrawal request tail of the queue + withdrawalsRequestQueueStorageOffset: BigInt(4), // The storage slot of the withdrawal request queue offset + maxWithdrawalRequestsPerBlock: BigInt(16), // The max withdrawal requests per block + targetWithdrawalRequestsPerBlock: BigInt(2), // The target withdrawal requests per block + minWithdrawalRequestFee: BigInt(1), // The minimum withdrawal request fee (in wei) + withdrawalRequestFeeUpdateFraction: BigInt(17), // The withdrawal request fee update fraction (used in the fake exponential) + systemAddress: BigInt('0xfffffffffffffffffffffffffffffffffffffffe'), // The system address to perform operations on the withdrawal requests predeploy address + withdrawalRequestPredeployAddress: BigInt('0x00A3ca265EBcb825B45F985A16CEFB49958cE017'), // Address of the validator excess address }, }, 7251: { @@ -608,18 +377,9 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.Paris, requiredEIPs: [7685], vm: { - consolidationRequestType: { - v: BigInt(0x02), - d: 'The withdrawal request type for EIP-7685', - }, - systemAddress: { - v: BigInt('0xfffffffffffffffffffffffffffffffffffffffe'), - d: 'The system address to perform operations on the consolidation requests predeploy address', - }, - consolidationRequestPredeployAddress: { - v: BigInt('0x00b42dbF2194e931E80326D950320f7d9Dbeac02'), - d: 'Address of the consolidations contract', - }, + consolidationRequestType: BigInt(0x02), // The withdrawal request type for EIP-7685 + systemAddress: BigInt('0xfffffffffffffffffffffffffffffffffffffffe'), // The system address to perform operations on the consolidation requests predeploy address + consolidationRequestPredeployAddress: BigInt('0x00b42dbF2194e931E80326D950320f7d9Dbeac02'), // Address of the consolidations contract }, }, 7516: { @@ -629,10 +389,7 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.Paris, requiredEIPs: [4844], gasPrices: { - blobbasefee: { - v: 2, - d: 'Gas cost of the BLOBBASEFEE opcode', - }, + blobbasefee: 2, // Gas cost of the BLOBBASEFEE opcode }, }, 7685: { @@ -652,10 +409,7 @@ export const EIPs: EIPsDict = { minimumHardfork: Hardfork.Cancun, requiredEIPs: [2718, 2929, 2930], gasPrices: { - perAuthBaseCost: { - v: 2500, - d: 'Gas cost of each authority item', - }, + perAuthBaseCost: 2500, // Gas cost of each authority item }, }, 7709: { diff --git a/packages/common/src/hardforks.ts b/packages/common/src/hardforks.ts index 0a3c927c78..43d509f122 100644 --- a/packages/common/src/hardforks.ts +++ b/packages/common/src/hardforks.ts @@ -13,432 +13,117 @@ export const hardforks: HardforksDict = { url: '', status: Status.Final, gasConfig: { - minGasLimit: { - v: 5000, - d: 'Minimum the gas limit may ever be', - }, - gasLimitBoundDivisor: { - v: 1024, - d: 'The bound divisor of the gas limit, used in update calculations', - }, - maxRefundQuotient: { - v: 2, - d: 'Maximum refund quotient; max tx refund is min(tx.gasUsed/maxRefundQuotient, tx.gasRefund)', - }, + minGasLimit: 5000, // Minimum the gas limit may ever be + gasLimitBoundDivisor: 1024, // The bound divisor of the gas limit, used in update calculations + maxRefundQuotient: 2, // Maximum refund quotient; max tx refund is min(tx.gasUsed/maxRefundQuotient, tx.gasRefund) }, gasPrices: { - base: { - v: 2, - d: 'Gas base cost, used e.g. for ChainID opcode (Istanbul)', - }, - exp: { - v: 10, - d: 'Base fee of the EXP opcode', - }, - expByte: { - v: 10, - d: 'Times ceil(log256(exponent)) for the EXP instruction', - }, - keccak256: { - v: 30, - d: 'Base fee of the SHA3 opcode', - }, - keccak256Word: { - v: 6, - d: "Once per word of the SHA3 operation's data", - }, - sload: { - v: 50, - d: 'Base fee of the SLOAD opcode', - }, - sstoreSet: { - v: 20000, - d: 'Once per SSTORE operation if the zeroness changes from zero', - }, - sstoreReset: { - v: 5000, - d: 'Once per SSTORE operation if the zeroness does not change from zero', - }, - sstoreRefund: { - v: 15000, - d: 'Once per SSTORE operation if the zeroness changes to zero', - }, - jumpdest: { - v: 1, - d: 'Base fee of the JUMPDEST opcode', - }, - log: { - v: 375, - d: 'Base fee of the LOG opcode', - }, - logData: { - v: 8, - d: "Per byte in a LOG* operation's data", - }, - logTopic: { - v: 375, - d: 'Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas', - }, - create: { - v: 32000, - d: 'Base fee of the CREATE opcode', - }, - call: { - v: 40, - d: 'Base fee of the CALL opcode', - }, - callStipend: { - v: 2300, - d: 'Free gas given at beginning of call', - }, - callValueTransfer: { - v: 9000, - d: 'Paid for CALL when the value transfor is non-zero', - }, - callNewAccount: { - v: 25000, - d: "Paid for CALL when the destination address didn't exist prior", - }, - selfdestructRefund: { - v: 24000, - d: 'Refunded following a selfdestruct operation', - }, - memory: { - v: 3, - d: 'Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL', - }, - quadCoeffDiv: { - v: 512, - d: 'Divisor for the quadratic particle of the memory cost equation', - }, - createData: { - v: 200, - d: '', - }, - tx: { - v: 21000, - d: 'Per transaction. NOTE: Not payable on data of calls between transactions', - }, - txCreation: { - v: 32000, - d: 'The cost of creating a contract via tx', - }, - txDataZero: { - v: 4, - d: 'Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions', - }, - txDataNonZero: { - v: 68, - d: 'Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions', - }, - copy: { - v: 3, - d: 'Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added', - }, - ecRecover: { - v: 3000, - d: '', - }, - sha256: { - v: 60, - d: '', - }, - sha256Word: { - v: 12, - d: '', - }, - ripemd160: { - v: 600, - d: '', - }, - ripemd160Word: { - v: 120, - d: '', - }, - identity: { - v: 15, - d: '', - }, - identityWord: { - v: 3, - d: '', - }, - stop: { - v: 0, - d: 'Base fee of the STOP opcode', - }, - add: { - v: 3, - d: 'Base fee of the ADD opcode', - }, - mul: { - v: 5, - d: 'Base fee of the MUL opcode', - }, - sub: { - v: 3, - d: 'Base fee of the SUB opcode', - }, - div: { - v: 5, - d: 'Base fee of the DIV opcode', - }, - sdiv: { - v: 5, - d: 'Base fee of the SDIV opcode', - }, - mod: { - v: 5, - d: 'Base fee of the MOD opcode', - }, - smod: { - v: 5, - d: 'Base fee of the SMOD opcode', - }, - addmod: { - v: 8, - d: 'Base fee of the ADDMOD opcode', - }, - mulmod: { - v: 8, - d: 'Base fee of the MULMOD opcode', - }, - signextend: { - v: 5, - d: 'Base fee of the SIGNEXTEND opcode', - }, - lt: { - v: 3, - d: 'Base fee of the LT opcode', - }, - gt: { - v: 3, - d: 'Base fee of the GT opcode', - }, - slt: { - v: 3, - d: 'Base fee of the SLT opcode', - }, - sgt: { - v: 3, - d: 'Base fee of the SGT opcode', - }, - eq: { - v: 3, - d: 'Base fee of the EQ opcode', - }, - iszero: { - v: 3, - d: 'Base fee of the ISZERO opcode', - }, - and: { - v: 3, - d: 'Base fee of the AND opcode', - }, - or: { - v: 3, - d: 'Base fee of the OR opcode', - }, - xor: { - v: 3, - d: 'Base fee of the XOR opcode', - }, - not: { - v: 3, - d: 'Base fee of the NOT opcode', - }, - byte: { - v: 3, - d: 'Base fee of the BYTE opcode', - }, - address: { - v: 2, - d: 'Base fee of the ADDRESS opcode', - }, - balance: { - v: 20, - d: 'Base fee of the BALANCE opcode', - }, - origin: { - v: 2, - d: 'Base fee of the ORIGIN opcode', - }, - caller: { - v: 2, - d: 'Base fee of the CALLER opcode', - }, - callvalue: { - v: 2, - d: 'Base fee of the CALLVALUE opcode', - }, - calldataload: { - v: 3, - d: 'Base fee of the CALLDATALOAD opcode', - }, - calldatasize: { - v: 2, - d: 'Base fee of the CALLDATASIZE opcode', - }, - calldatacopy: { - v: 3, - d: 'Base fee of the CALLDATACOPY opcode', - }, - codesize: { - v: 2, - d: 'Base fee of the CODESIZE opcode', - }, - codecopy: { - v: 3, - d: 'Base fee of the CODECOPY opcode', - }, - gasprice: { - v: 2, - d: 'Base fee of the GASPRICE opcode', - }, - extcodesize: { - v: 20, - d: 'Base fee of the EXTCODESIZE opcode', - }, - extcodecopy: { - v: 20, - d: 'Base fee of the EXTCODECOPY opcode', - }, - blockhash: { - v: 20, - d: 'Base fee of the BLOCKHASH opcode', - }, - coinbase: { - v: 2, - d: 'Base fee of the COINBASE opcode', - }, - timestamp: { - v: 2, - d: 'Base fee of the TIMESTAMP opcode', - }, - number: { - v: 2, - d: 'Base fee of the NUMBER opcode', - }, - difficulty: { - v: 2, - d: 'Base fee of the DIFFICULTY opcode', - }, - gaslimit: { - v: 2, - d: 'Base fee of the GASLIMIT opcode', - }, - pop: { - v: 2, - d: 'Base fee of the POP opcode', - }, - mload: { - v: 3, - d: 'Base fee of the MLOAD opcode', - }, - mstore: { - v: 3, - d: 'Base fee of the MSTORE opcode', - }, - mstore8: { - v: 3, - d: 'Base fee of the MSTORE8 opcode', - }, - sstore: { - v: 0, - d: 'Base fee of the SSTORE opcode', - }, - jump: { - v: 8, - d: 'Base fee of the JUMP opcode', - }, - jumpi: { - v: 10, - d: 'Base fee of the JUMPI opcode', - }, - pc: { - v: 2, - d: 'Base fee of the PC opcode', - }, - msize: { - v: 2, - d: 'Base fee of the MSIZE opcode', - }, - gas: { - v: 2, - d: 'Base fee of the GAS opcode', - }, - push: { - v: 3, - d: 'Base fee of the PUSH opcode', - }, - dup: { - v: 3, - d: 'Base fee of the DUP opcode', - }, - swap: { - v: 3, - d: 'Base fee of the SWAP opcode', - }, - callcode: { - v: 40, - d: 'Base fee of the CALLCODE opcode', - }, - return: { - v: 0, - d: 'Base fee of the RETURN opcode', - }, - invalid: { - v: 0, - d: 'Base fee of the INVALID opcode', - }, - selfdestruct: { - v: 0, - d: 'Base fee of the SELFDESTRUCT opcode', - }, + base: 2, // Gas base cost, used e.g. for ChainID opcode (Istanbul) + exp: 10, // Base fee of the EXP opcode + expByte: 10, // Times ceil(log256(exponent)) for the EXP instruction + keccak256: 30, // Base fee of the SHA3 opcode + keccak256Word: 6, // Once per word of the SHA3 operation's data + sload: 50, // Base fee of the SLOAD opcode + sstoreSet: 20000, // Once per SSTORE operation if the zeroness changes from zero + sstoreReset: 5000, // Once per SSTORE operation if the zeroness does not change from zero + sstoreRefund: 15000, // Once per SSTORE operation if the zeroness changes to zero + jumpdest: 1, // Base fee of the JUMPDEST opcode + log: 375, // Base fee of the LOG opcode + logData: 8, // Per byte in a LOG* operation's data + logTopic: 375, // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas + create: 32000, // Base fee of the CREATE opcode + call: 40, // Base fee of the CALL opcode + callStipend: 2300, // Free gas given at beginning of call + callValueTransfer: 9000, // Paid for CALL when the value transfor is non-zero + callNewAccount: 25000, // Paid for CALL when the destination address didn't exist prior + selfdestructRefund: 24000, // Refunded following a selfdestruct operation + memory: 3, // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL + quadCoeffDiv: 512, // Divisor for the quadratic particle of the memory cost equation + createData: 200, // + tx: 21000, // Per transaction. NOTE: Not payable on data of calls between transactions + txCreation: 32000, // The cost of creating a contract via tx + txDataZero: 4, // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions + txDataNonZero: 68, // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions + copy: 3, // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added + ecRecover: 3000, + sha256: 60, + sha256Word: 12, + ripemd160: 600, + ripemd160Word: 120, + identity: 15, + identityWord: 3, + stop: 0, // Base fee of the STOP opcode + add: 3, // Base fee of the ADD opcode + mul: 5, // Base fee of the MUL opcode + sub: 3, // Base fee of the SUB opcode + div: 5, // Base fee of the DIV opcode + sdiv: 5, // Base fee of the SDIV opcode + mod: 5, // Base fee of the MOD opcode + smod: 5, // Base fee of the SMOD opcode + addmod: 8, // Base fee of the ADDMOD opcode + mulmod: 8, // Base fee of the MULMOD opcode + signextend: 5, // Base fee of the SIGNEXTEND opcode + lt: 3, // Base fee of the LT opcode + gt: 3, // Base fee of the GT opcode + slt: 3, // Base fee of the SLT opcode + sgt: 3, // Base fee of the SGT opcode + eq: 3, // Base fee of the EQ opcode + iszero: 3, // Base fee of the ISZERO opcode + and: 3, // Base fee of the AND opcode + or: 3, // Base fee of the OR opcode + xor: 3, // Base fee of the XOR opcode + not: 3, // Base fee of the NOT opcode + byte: 3, // Base fee of the BYTE opcode + address: 2, // Base fee of the ADDRESS opcode + balance: 20, // Base fee of the BALANCE opcode + origin: 2, // Base fee of the ORIGIN opcode + caller: 2, // Base fee of the CALLER opcode + callvalue: 2, // Base fee of the CALLVALUE opcode + calldataload: 3, // Base fee of the CALLDATALOAD opcode + calldatasize: 2, // Base fee of the CALLDATASIZE opcode + calldatacopy: 3, // Base fee of the CALLDATACOPY opcode + codesize: 2, // Base fee of the CODESIZE opcode + codecopy: 3, // Base fee of the CODECOPY opcode + gasprice: 2, // Base fee of the GASPRICE opcode + extcodesize: 20, // Base fee of the EXTCODESIZE opcode + extcodecopy: 20, // Base fee of the EXTCODECOPY opcode + blockhash: 20, // Base fee of the BLOCKHASH opcode + coinbase: 2, // Base fee of the COINBASE opcode + timestamp: 2, // Base fee of the TIMESTAMP opcode + number: 2, // Base fee of the NUMBER opcode + difficulty: 2, // Base fee of the DIFFICULTY opcode + gaslimit: 2, // Base fee of the GASLIMIT opcode + pop: 2, // Base fee of the POP opcode + mload: 3, // Base fee of the MLOAD opcode + mstore: 3, // Base fee of the MSTORE opcode + mstore8: 3, // Base fee of the MSTORE8 opcode + sstore: 0, // Base fee of the SSTORE opcode + jump: 8, // Base fee of the JUMP opcode + jumpi: 10, // Base fee of the JUMPI opcode + pc: 2, // Base fee of the PC opcode + msize: 2, // Base fee of the MSIZE opcode + gas: 2, // Base fee of the GAS opcode + push: 3, // Base fee of the PUSH opcode + dup: 3, // Base fee of the DUP opcode + swap: 3, // Base fee of the SWAP opcode + callcode: 40, // Base fee of the CALLCODE opcode + return: 0, // Base fee of the RETURN opcode + invalid: 0, // Base fee of the INVALID opcode + selfdestruct: 0, // Base fee of the SELFDESTRUCT opcode }, vm: { - stackLimit: { - v: 1024, - d: 'Maximum size of VM stack allowed', - }, - callCreateDepth: { - v: 1024, - d: 'Maximum depth of call/create stack', - }, - maxExtraDataSize: { - v: 32, - d: 'Maximum size extra data may be after Genesis', - }, + stackLimit: 1024, // Maximum size of VM stack allowed + callCreateDepth: 1024, // Maximum depth of call/create stack + maxExtraDataSize: 32, // Maximum size extra data may be after Genesis }, pow: { - minimumDifficulty: { - v: 131072, - d: 'The minimum that the difficulty may ever be', - }, - difficultyBoundDivisor: { - v: 2048, - d: 'The bound divisor of the difficulty, used in the update calculations', - }, - durationLimit: { - v: 13, - d: 'The decision boundary on the blocktime duration used to determine whether difficulty should go up or not', - }, - epochDuration: { - v: 30000, - d: 'Duration between proof-of-work epochs', - }, - timebombPeriod: { - v: 100000, - d: 'Exponential difficulty timebomb period', - }, - minerReward: { - v: BigInt('5000000000000000000'), - d: 'the amount a miner get rewarded for mining a block', - }, - difficultyBombDelay: { - v: 0, - d: 'the amount of blocks to delay the difficulty bomb with', - }, + minimumDifficulty: 131072, // The minimum that the difficulty may ever be + difficultyBoundDivisor: 2048, // The bound divisor of the difficulty, used in the update calculations + durationLimit: 13, // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not + epochDuration: 30000, // Duration between proof-of-work epochs + timebombPeriod: 100000, // Exponential difficulty timebomb period + minerReward: BigInt('5000000000000000000'), // the amount a miner get rewarded for mining a block + difficultyBombDelay: 0, // the amount of blocks to delay the difficulty bomb with }, }, homestead: { @@ -447,10 +132,7 @@ export const hardforks: HardforksDict = { url: 'https://eips.ethereum.org/EIPS/eip-606', status: Status.Final, gasPrices: { - delegatecall: { - v: 40, - d: 'Base fee of the DELEGATECALL opcode', - }, + delegatecall: 40, // Base fee of the DELEGATECALL opcode }, }, dao: { @@ -465,38 +147,14 @@ export const hardforks: HardforksDict = { url: 'https://eips.ethereum.org/EIPS/eip-608', status: Status.Final, gasPrices: { - sload: { - v: 200, - d: 'Once per SLOAD operation', - }, - call: { - v: 700, - d: 'Once per CALL operation & message call transaction', - }, - extcodesize: { - v: 700, - d: 'Base fee of the EXTCODESIZE opcode', - }, - extcodecopy: { - v: 700, - d: 'Base fee of the EXTCODECOPY opcode', - }, - balance: { - v: 400, - d: 'Base fee of the BALANCE opcode', - }, - delegatecall: { - v: 700, - d: 'Base fee of the DELEGATECALL opcode', - }, - callcode: { - v: 700, - d: 'Base fee of the CALLCODE opcode', - }, - selfdestruct: { - v: 5000, - d: 'Base fee of the SELFDESTRUCT opcode', - }, + sload: 200, // Once per SLOAD operation + call: 700, // Once per CALL operation & message call transaction + extcodesize: 700, // Base fee of the EXTCODESIZE opcode + extcodecopy: 700, // Base fee of the EXTCODECOPY opcode + balance: 400, // Base fee of the BALANCE opcode + delegatecall: 700, // Base fee of the DELEGATECALL opcode + callcode: 700, // Base fee of the CALLCODE opcode + selfdestruct: 5000, // Base fee of the SELFDESTRUCT opcode }, }, spuriousDragon: { @@ -506,16 +164,10 @@ export const hardforks: HardforksDict = { url: 'https://eips.ethereum.org/EIPS/eip-607', status: Status.Final, gasPrices: { - expByte: { - v: 50, - d: 'Times ceil(log256(exponent)) for the EXP instruction', - }, + expByte: 50, // Times ceil(log256(exponent)) for the EXP instruction }, vm: { - maxCodeSize: { - v: 24576, - d: 'Maximum length of contract code', - }, + maxCodeSize: 24576, // Maximum length of contract code }, }, byzantium: { @@ -524,52 +176,19 @@ export const hardforks: HardforksDict = { url: 'https://eips.ethereum.org/EIPS/eip-609', status: Status.Final, gasPrices: { - modexpGquaddivisor: { - v: 20, - d: 'Gquaddivisor from modexp precompile for gas calculation', - }, - ecAdd: { - v: 500, - d: 'Gas costs for curve addition precompile', - }, - ecMul: { - v: 40000, - d: 'Gas costs for curve multiplication precompile', - }, - ecPairing: { - v: 100000, - d: 'Base gas costs for curve pairing precompile', - }, - ecPairingWord: { - v: 80000, - d: 'Gas costs regarding curve pairing precompile input length', - }, - revert: { - v: 0, - d: 'Base fee of the REVERT opcode', - }, - staticcall: { - v: 700, - d: 'Base fee of the STATICCALL opcode', - }, - returndatasize: { - v: 2, - d: 'Base fee of the RETURNDATASIZE opcode', - }, - returndatacopy: { - v: 3, - d: 'Base fee of the RETURNDATACOPY opcode', - }, + modexpGquaddivisor: 20, // Gquaddivisor from modexp precompile for gas calculation + ecAdd: 500, // Gas costs for curve addition precompile + ecMul: 40000, // Gas costs for curve multiplication precompile + ecPairing: 100000, // Base gas costs for curve pairing precompile + ecPairingWord: 80000, // Gas costs regarding curve pairing precompile input length + revert: 0, // Base fee of the REVERT opcode + staticcall: 700, // Base fee of the STATICCALL opcode + returndatasize: 2, // Base fee of the RETURNDATASIZE opcode + returndatacopy: 3, // Base fee of the RETURNDATACOPY opcode }, pow: { - minerReward: { - v: BigInt('3000000000000000000'), - d: 'the amount a miner get rewarded for mining a block', - }, - difficultyBombDelay: { - v: 3000000, - d: 'the amount of blocks to delay the difficulty bomb with', - }, + minerReward: BigInt('3000000000000000000'), // the amount a miner get rewarded for mining a block + difficultyBombDelay: 3000000, // the amount of blocks to delay the difficulty bomb with }, }, constantinople: { @@ -578,64 +197,22 @@ export const hardforks: HardforksDict = { url: 'https://eips.ethereum.org/EIPS/eip-1013', status: Status.Final, gasPrices: { - netSstoreNoopGas: { - v: 200, - d: "Once per SSTORE operation if the value doesn't change", - }, - netSstoreInitGas: { - v: 20000, - d: 'Once per SSTORE operation from clean zero', - }, - netSstoreCleanGas: { - v: 5000, - d: 'Once per SSTORE operation from clean non-zero', - }, - netSstoreDirtyGas: { - v: 200, - d: 'Once per SSTORE operation from dirty', - }, - netSstoreClearRefund: { - v: 15000, - d: 'Once per SSTORE operation for clearing an originally existing storage slot', - }, - netSstoreResetRefund: { - v: 4800, - d: 'Once per SSTORE operation for resetting to the original non-zero value', - }, - netSstoreResetClearRefund: { - v: 19800, - d: 'Once per SSTORE operation for resetting to the original zero value', - }, - shl: { - v: 3, - d: 'Base fee of the SHL opcode', - }, - shr: { - v: 3, - d: 'Base fee of the SHR opcode', - }, - sar: { - v: 3, - d: 'Base fee of the SAR opcode', - }, - extcodehash: { - v: 400, - d: 'Base fee of the EXTCODEHASH opcode', - }, - create2: { - v: 32000, - d: 'Base fee of the CREATE2 opcode', - }, + netSstoreNoopGas: 200, // Once per SSTORE operation if the value doesn't change + netSstoreInitGas: 20000, // Once per SSTORE operation from clean zero + netSstoreCleanGas: 5000, // Once per SSTORE operation from clean non-zero + netSstoreDirtyGas: 200, // Once per SSTORE operation from dirty + netSstoreClearRefund: 15000, // Once per SSTORE operation for clearing an originally existing storage slot + netSstoreResetRefund: 4800, // Once per SSTORE operation for resetting to the original non-zero value + netSstoreResetClearRefund: 19800, // Once per SSTORE operation for resetting to the original zero value + shl: 3, // Base fee of the SHL opcode + shr: 3, // Base fee of the SHR opcode + sar: 3, // Base fee of the SAR opcode + extcodehash: 400, // Base fee of the EXTCODEHASH opcode + create2: 32000, // Base fee of the CREATE2 opcode }, pow: { - minerReward: { - v: BigInt('2000000000000000000'), - d: 'The amount a miner gets rewarded for mining a block', - }, - difficultyBombDelay: { - v: 5000000, - d: 'the amount of blocks to delay the difficulty bomb with', - }, + minerReward: BigInt('2000000000000000000'), // The amount a miner gets rewarded for mining a block + difficultyBombDelay: 5000000, // the amount of blocks to delay the difficulty bomb with }, }, petersburg: { @@ -645,34 +222,13 @@ export const hardforks: HardforksDict = { url: 'https://eips.ethereum.org/EIPS/eip-1716', status: Status.Final, gasPrices: { - netSstoreNoopGas: { - v: null, - d: 'Removed along EIP-1283', - }, - netSstoreInitGas: { - v: null, - d: 'Removed along EIP-1283', - }, - netSstoreCleanGas: { - v: null, - d: 'Removed along EIP-1283', - }, - netSstoreDirtyGas: { - v: null, - d: 'Removed along EIP-1283', - }, - netSstoreClearRefund: { - v: null, - d: 'Removed along EIP-1283', - }, - netSstoreResetRefund: { - v: null, - d: 'Removed along EIP-1283', - }, - netSstoreResetClearRefund: { - v: null, - d: 'Removed along EIP-1283', - }, + netSstoreNoopGas: null, // Removed along EIP-1283 + netSstoreInitGas: null, // Removed along EIP-1283 + netSstoreCleanGas: null, // Removed along EIP-1283 + netSstoreDirtyGas: null, // Removed along EIP-1283 + netSstoreClearRefund: null, // Removed along EIP-1283 + netSstoreResetRefund: null, // Removed along EIP-1283 + netSstoreResetClearRefund: null, // Removed along EIP-1283 }, }, istanbul: { @@ -682,82 +238,25 @@ export const hardforks: HardforksDict = { status: Status.Final, gasConfig: {}, gasPrices: { - blake2Round: { - v: 1, - d: 'Gas cost per round for the Blake2 F precompile', - }, - ecAdd: { - v: 150, - d: 'Gas costs for curve addition precompile', - }, - ecMul: { - v: 6000, - d: 'Gas costs for curve multiplication precompile', - }, - ecPairing: { - v: 45000, - d: 'Base gas costs for curve pairing precompile', - }, - ecPairingWord: { - v: 34000, - d: 'Gas costs regarding curve pairing precompile input length', - }, - txDataNonZero: { - v: 16, - d: 'Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions', - }, - sstoreSentryGasEIP2200: { - v: 2300, - d: 'Minimum gas required to be present for an SSTORE call, not consumed', - }, - sstoreNoopGasEIP2200: { - v: 800, - d: "Once per SSTORE operation if the value doesn't change", - }, - sstoreDirtyGasEIP2200: { - v: 800, - d: 'Once per SSTORE operation if a dirty value is changed', - }, - sstoreInitGasEIP2200: { - v: 20000, - d: 'Once per SSTORE operation from clean zero to non-zero', - }, - sstoreInitRefundEIP2200: { - v: 19200, - d: 'Once per SSTORE operation for resetting to the original zero value', - }, - sstoreCleanGasEIP2200: { - v: 5000, - d: 'Once per SSTORE operation from clean non-zero to something else', - }, - sstoreCleanRefundEIP2200: { - v: 4200, - d: 'Once per SSTORE operation for resetting to the original non-zero value', - }, - sstoreClearRefundEIP2200: { - v: 15000, - d: 'Once per SSTORE operation for clearing an originally existing storage slot', - }, - balance: { - v: 700, - d: 'Base fee of the BALANCE opcode', - }, - extcodehash: { - v: 700, - d: 'Base fee of the EXTCODEHASH opcode', - }, - chainid: { - v: 2, - d: 'Base fee of the CHAINID opcode', - }, - selfbalance: { - v: 5, - d: 'Base fee of the SELFBALANCE opcode', - }, - sload: { - v: 800, - d: 'Base fee of the SLOAD opcode', - }, + blake2Round: 1, // Gas cost per round for the Blake2 F precompile + ecAdd: 150, // Gas costs for curve addition precompile + ecMul: 6000, // Gas costs for curve multiplication precompile + ecPairing: 45000, // Base gas costs for curve pairing precompile + ecPairingWord: 34000, // Gas costs regarding curve pairing precompile input length + txDataNonZero: 16, // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions + sstoreSentryGasEIP2200: 2300, // Minimum gas required to be present for an SSTORE call, not consumed + sstoreNoopGasEIP2200: 800, // Once per SSTORE operation if the value doesn't change + sstoreDirtyGasEIP2200: 800, // Once per SSTORE operation if a dirty value is changed + sstoreInitGasEIP2200: 20000, // Once per SSTORE operation from clean zero to non-zero + sstoreInitRefundEIP2200: 19200, // Once per SSTORE operation for resetting to the original zero value + sstoreCleanGasEIP2200: 5000, // Once per SSTORE operation from clean non-zero to something else + sstoreCleanRefundEIP2200: 4200, // Once per SSTORE operation for resetting to the original non-zero value + sstoreClearRefundEIP2200: 15000, // Once per SSTORE operation for clearing an originally existing storage slot + balance: 700, // Base fee of the BALANCE opcode + extcodehash: 700, // Base fee of the EXTCODEHASH opcode + chainid: 2, // Base fee of the CHAINID opcode + selfbalance: 5, // Base fee of the SELFBALANCE opcode + sload: 800, // Base fee of the SLOAD opcode }, }, muirGlacier: { @@ -766,10 +265,7 @@ export const hardforks: HardforksDict = { url: 'https://eips.ethereum.org/EIPS/eip-2384', status: Status.Final, pow: { - difficultyBombDelay: { - v: 9000000, - d: 'the amount of blocks to delay the difficulty bomb with', - }, + difficultyBombDelay: 9000000, // the amount of blocks to delay the difficulty bomb with }, }, berlin: { @@ -841,7 +337,7 @@ export const hardforks: HardforksDict = { 'Next feature hardfork after cancun, internally used for pectra testing/implementation (incomplete/experimental)', url: 'https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/prague.md', status: Status.Draft, - eips: [2537, 2935, 3074, 6110, 7002, 7251, 7685], + eips: [2537, 2935, 6110, 7002, 7251, 7685, 7702], }, osaka: { name: 'osaka', diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index f58785550a..db416328bc 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -1,4 +1,5 @@ export * from './common.js' +export * from './constructors.js' export * from './enums.js' export * from './interfaces.js' export * from './types.js' diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index 49c07608cd..6507efbadd 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -166,29 +166,24 @@ export interface HardforkByOpts { td?: BigIntLike | string } -type ParamDict = { - v: number | bigint | null - d: string -} - export type EIPOrHFConfig = { comment: string url: string status: string gasConfig?: { - [key: string]: ParamDict + [key: string]: number | bigint | null } gasPrices?: { - [key: string]: ParamDict + [key: string]: number | bigint | null } pow?: { - [key: string]: ParamDict + [key: string]: number | bigint | null } sharding?: { - [key: string]: ParamDict + [key: string]: number | bigint | null } vm?: { - [key: string]: ParamDict + [key: string]: number | bigint | null } } diff --git a/packages/common/src/utils.ts b/packages/common/src/utils.ts index c48af557e9..91b6db3931 100644 --- a/packages/common/src/utils.ts +++ b/packages/common/src/utils.ts @@ -1,7 +1,9 @@ import { intToHex, isHexString, stripHexPrefix } from '@ethereumjs/util' -import { Hardfork } from './enums.js' +import { chains as CHAIN_SPECS } from './chains.js' +import { Chain, Hardfork } from './enums.js' +import type { ChainConfig, ChainName, ChainsConfig } from './index.js' import type { PrefixedHexString } from '@ethereumjs/util' type ConfigHardfork = @@ -236,3 +238,53 @@ export function parseGethGenesis(json: any, name?: string, mergeForkIdPostMerge? throw new Error(`Error parsing parameters file: ${e.message}`) } } + +export function getInitializedChains(customChains?: ChainConfig[]): ChainsConfig { + const names: ChainName = {} + for (const [name, id] of Object.entries(Chain)) { + names[id] = name.toLowerCase() + } + const chains = { ...CHAIN_SPECS } as ChainsConfig + if (customChains) { + for (const chain of customChains) { + const { name } = chain + names[chain.chainId.toString()] = name + chains[name] = chain + } + } + chains.names = names + return chains +} + +/** + * Determine if a {@link chainId} is supported as a standard chain + * @param chainId bigint id (`1`) of a standard chain + * @returns boolean + */ +export function isSupportedChainId(chainId: bigint): boolean { + const initializedChains = getInitializedChains() + return Boolean((initializedChains['names'] as ChainName)[chainId.toString()]) +} + +export function _getChainParams( + chain: string | number | Chain | bigint, + customChains?: ChainConfig[] +): ChainConfig { + const initializedChains = getInitializedChains(customChains) + if (typeof chain === 'number' || typeof chain === 'bigint') { + chain = chain.toString() + + if ((initializedChains['names'] as ChainName)[chain]) { + const name: string = (initializedChains['names'] as ChainName)[chain] + return initializedChains[name] as ChainConfig + } + + throw new Error(`Chain with ID ${chain} not supported`) + } + + if (initializedChains[chain] !== undefined) { + return initializedChains[chain] as ChainConfig + } + + throw new Error(`Chain with name ${chain} not supported`) +} diff --git a/packages/common/test/chains.spec.ts b/packages/common/test/chains.spec.ts index f17f0935ff..3a2da8caea 100644 --- a/packages/common/test/chains.spec.ts +++ b/packages/common/test/chains.spec.ts @@ -1,6 +1,13 @@ import { assert, describe, it } from 'vitest' -import { Chain, Common, ConsensusAlgorithm, ConsensusType, Hardfork } from '../src/index.js' +import { + Chain, + Common, + ConsensusAlgorithm, + ConsensusType, + Hardfork, + isSupportedChainId, +} from '../src/index.js' describe('[Common/Chains]: Initialization / Chain params', () => { it('Should initialize with chain provided', () => { @@ -126,11 +133,11 @@ describe('[Common/Chains]: Initialization / Chain params', () => { describe('[Common]: isSupportedChainId static method', () => { it('Should return true for supported chainId', () => { - assert.equal(Common.isSupportedChainId(BigInt(1)), true, 'returns true') + assert.equal(isSupportedChainId(BigInt(1)), true, 'returns true') }) it('Should return false for unsupported chainId', () => { - assert.equal(Common.isSupportedChainId(BigInt(0)), false, 'returns false') + assert.equal(isSupportedChainId(BigInt(0)), false, 'returns false') }) }) diff --git a/packages/common/test/customChains.spec.ts b/packages/common/test/customChains.spec.ts index a29f8b9b02..55b04a35c0 100644 --- a/packages/common/test/customChains.spec.ts +++ b/packages/common/test/customChains.spec.ts @@ -1,7 +1,15 @@ +import { BIGINT_0 } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' import { Status } from '../src/hardforks.js' -import { Chain, Common, ConsensusType, CustomChain, Hardfork } from '../src/index.js' +import { + Chain, + Common, + ConsensusType, + CustomChain, + Hardfork, + createCustomCommon, +} from '../src/index.js' import * as testnet from './data/testnet.json' import * as testnet2 from './data/testnet2.json' @@ -36,7 +44,9 @@ describe('[Common]: Custom chains', () => { const mainnetCommon = new Common({ chain: Chain.Mainnet }) const customChainParams = { name: 'custom', chainId: 123, networkId: 678 } - const customChainCommon = Common.custom(customChainParams, { hardfork: Hardfork.Byzantium }) + const customChainCommon = createCustomCommon(customChainParams, { + hardfork: Hardfork.Byzantium, + }) // From custom chain params assert.equal(customChainCommon.chainName(), customChainParams.name) @@ -53,18 +63,18 @@ describe('[Common]: Custom chains', () => { }) it('custom() -> behavior', () => { - let common = Common.custom({ chainId: 123 }) + let common = createCustomCommon({ chainId: 123 }) assert.deepEqual(common.networkId(), BigInt(1), 'should default to mainnet base chain') assert.equal(common.chainName(), 'custom-chain', 'should set default custom chain name') - common = Common.custom(CustomChain.PolygonMumbai) + common = createCustomCommon(CustomChain.PolygonMumbai) assert.deepEqual( common.networkId(), BigInt(80001), 'supported chain -> should initialize with correct chain ID' ) for (const customChain of Object.values(CustomChain)) { - common = Common.custom(customChain) + common = createCustomCommon(customChain) assert.equal( common.chainName(), customChain, @@ -72,14 +82,14 @@ describe('[Common]: Custom chains', () => { ) } - common = Common.custom(CustomChain.PolygonMumbai) + common = createCustomCommon(CustomChain.PolygonMumbai) assert.equal( common.hardfork(), common.DEFAULT_HARDFORK, 'uses default hardfork when no options are present' ) - common = Common.custom(CustomChain.OptimisticEthereum, { hardfork: Hardfork.Byzantium }) + common = createCustomCommon(CustomChain.OptimisticEthereum, { hardfork: Hardfork.Byzantium }) assert.equal( common.hardfork(), Hardfork.Byzantium, @@ -88,7 +98,7 @@ describe('[Common]: Custom chains', () => { try { //@ts-ignore TypeScript complains, nevertheless do the test for JS behavior - Common.custom('this-chain-is-not-supported') + createCustomCommon('this-chain-is-not-supported') assert.fail('test should fail') } catch (e: any) { assert.ok( @@ -152,7 +162,9 @@ describe('[Common]: Custom chains', () => { networkId: 678, depositContractAddress: '0x4242424242424242424242424242424242424242', } - const customChainCommon = Common.custom(customChainParams, { hardfork: Hardfork.Byzantium }) + const customChainCommon = createCustomCommon(customChainParams, { + hardfork: Hardfork.Byzantium, + }) assert.equal( customChainCommon['_chainParams'].depositContractAddress, @@ -168,7 +180,7 @@ describe('[Common]: Custom chains', () => { }) it('customHardforks parameter: initialization and transition tests', () => { - const c = Common.custom({ + const c = createCustomCommon({ customHardforks: { testEIP2935Hardfork: { name: 'testEIP2935Hardfork', @@ -218,6 +230,42 @@ describe('[Common]: Custom chains', () => { assert.equal(c.hardfork(), 'testEIP2935Hardfork') assert.ok(c.isActivatedEIP(2935)) }) + + it('customHardforks: override params', () => { + const c = createCustomCommon({ + customHardforks: { + stop10Gas: { + name: 'stop10Gas', + comment: 'Hardfork which changes the gas of STOP from 0 to 10', + url: '', + status: Status.Final, + eips: [2935], + vm: { + stop: BigInt(10), + }, + }, + }, + hardforks: [ + { + name: 'chainstart', + block: 0, + }, + { + name: 'stop10Gas', + block: null, + timestamp: 1000, + }, + ], + }) + c.setHardfork(Hardfork.Chainstart) + assert.equal(c.param('vm', 'stop'), BIGINT_0) + c.setHardforkBy({ + blockNumber: 1, + timestamp: 1000, + }) + assert.equal(c.hardfork(), 'stop10Gas') + assert.equal(c.param('vm', 'stop'), BigInt(10)) + }) }) describe('custom chain setup with hardforks with undefined/null block numbers', () => { @@ -233,7 +281,7 @@ describe('custom chain setup with hardforks with undefined/null block numbers', ] assert.throws( - () => Common.custom({ hardforks: undefinedHardforks as HardforkTransitionConfig[] }), + () => createCustomCommon({ hardforks: undefinedHardforks as HardforkTransitionConfig[] }), undefined, undefined, 'throws when a hardfork with an undefined block number is passed' @@ -248,7 +296,7 @@ describe('custom chain setup with hardforks with undefined/null block numbers', { name: 'tangerineWhistle', block: 10 }, ] - const common = Common.custom({ hardforks: nullHardforks }) + const common = createCustomCommon({ hardforks: nullHardforks }) common.setHardforkBy({ blockNumber: 10n }) assert.equal('tangerineWhistle', common.hardfork(), 'set correct hardfork') common.setHardforkBy({ blockNumber: 3n }) diff --git a/packages/common/test/customCrypto.spec.ts b/packages/common/test/customCrypto.spec.ts index 9a2ef5255b..0783026c84 100644 --- a/packages/common/test/customCrypto.spec.ts +++ b/packages/common/test/customCrypto.spec.ts @@ -1,7 +1,7 @@ import { concatBytes, randomBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { Chain, Common } from '../src/index.js' +import { Chain, Common, createCustomCommon } from '../src/index.js' import type { ECDSASignature } from '@ethereumjs/util' @@ -42,7 +42,7 @@ describe('[Common]: Custom Crypto', () => { assert.deepEqual(c.copy().customCrypto.keccak256!(value), new Uint8Array([2, 1]), msg) const customChainParams = { name: 'custom', chainId: 123, networkId: 678 } - c = Common.custom(customChainParams, { customCrypto }) + c = createCustomCommon(customChainParams, { customCrypto }) msg = 'Should initialize with custom keccak256 function and use properly (custom() constructor)' assert.deepEqual(c.customCrypto.keccak256!(value), new Uint8Array([2, 1]), msg) }) diff --git a/packages/common/test/hardforks.spec.ts b/packages/common/test/hardforks.spec.ts index 18cbee59ad..9375d3a2b2 100644 --- a/packages/common/test/hardforks.spec.ts +++ b/packages/common/test/hardforks.spec.ts @@ -1,7 +1,15 @@ import { hexToBytes, zeros } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { Chain, Common, ConsensusAlgorithm, ConsensusType, Hardfork } from '../src/index.js' +import { + Chain, + Common, + ConsensusAlgorithm, + ConsensusType, + Hardfork, + createCommonFromGethGenesis, + createCustomCommon, +} from '../src/index.js' import * as gethGenesisKilnJSON from './data/geth-genesis/geth-genesis-kiln.json' @@ -77,7 +85,7 @@ describe('[Common]: Hardfork logic', () => { }, ] - const c = Common.custom({ hardforks }, { baseChain: Chain.Sepolia }) + const c = createCustomCommon({ hardforks }, { baseChain: Chain.Sepolia }) const f = () => { c.getHardforkBy({ blockNumber: 0n }) } @@ -314,7 +322,7 @@ describe('[Common]: Hardfork logic', () => { mergeForkIdPostMerge: true, } const genesisHash = zeros(32) - const zeroCommon = Common.fromGethGenesis(defaultConfig, gethConfig) + const zeroCommon = createCommonFromGethGenesis(defaultConfig, gethConfig) const zeroCommonShanghaiFork = zeroCommon.forkHash(Hardfork.Shanghai, genesisHash) const zeroCommonCancunFork = zeroCommon.forkHash(Hardfork.Shanghai, genesisHash) @@ -414,7 +422,7 @@ describe('[Common]: Hardfork logic', () => { ) // For kiln MergeForkIdTransition happens BEFORE Merge - c = Common.fromGethGenesis(gethGenesisKilnJSON, { + c = createCommonFromGethGenesis(gethGenesisKilnJSON, { chain: 'kiln', mergeForkIdPostMerge: false, }) diff --git a/packages/common/test/mergePOS.spec.ts b/packages/common/test/mergePOS.spec.ts index cb0adcd696..f5dbe3a826 100644 --- a/packages/common/test/mergePOS.spec.ts +++ b/packages/common/test/mergePOS.spec.ts @@ -1,6 +1,6 @@ import { assert, describe, it } from 'vitest' -import { Chain, Common, Hardfork } from '../src/index.js' +import { Chain, Common, Hardfork, createCommonFromGethGenesis } from '../src/index.js' import * as postMergeJSON from './data/geth-genesis/post-merge.json' import * as testnetMerge from './data/merge/testnetMerge.json' @@ -175,7 +175,7 @@ describe('[Common]: Merge/POS specific logic', () => { }) it('should get the correct merge hardfork at genesis', async () => { - const c = Common.fromGethGenesis(postMergeJSON, { chain: 'post-merge' }) + const c = createCommonFromGethGenesis(postMergeJSON, { chain: 'post-merge' }) const msg = 'should get HF correctly' assert.equal(c.getHardforkBy({ blockNumber: 0n }), Hardfork.London, msg) assert.equal(c.getHardforkBy({ blockNumber: 0n, td: 0n }), Hardfork.Paris, msg) diff --git a/packages/common/test/timestamp.spec.ts b/packages/common/test/timestamp.spec.ts index 67e1ad6c86..7bd4008208 100644 --- a/packages/common/test/timestamp.spec.ts +++ b/packages/common/test/timestamp.spec.ts @@ -1,13 +1,19 @@ import { hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { Chain, Common, Hardfork } from '../src/index.js' +import { + Chain, + Common, + Hardfork, + createCommonFromGethGenesis, + createCustomCommon, +} from '../src/index.js' import * as timestampJson from './data/shanghai-time.json' describe('[Common]: Timestamp Hardfork logic', () => { it('shanghai-time', () => { - const c = Common.fromGethGenesis(timestampJson, { + const c = createCommonFromGethGenesis(timestampJson, { chain: 'withdrawals', }) assert.equal( @@ -32,7 +38,7 @@ describe('[Common]: Timestamp Hardfork logic', () => { cancunTime: timestampJson.config.shanghaiTime, }) const modifiedJson = Object.assign({}, timestampJson, { config }) - const c = Common.fromGethGenesis(modifiedJson, { + const c = createCommonFromGethGenesis(modifiedJson, { chain: 'modified', }) assert.equal( @@ -52,7 +58,7 @@ describe('[Common]: Timestamp Hardfork logic', () => { cancunTime: timestampJson.config.shanghaiTime + 1000, }) const modifiedJson = Object.assign({}, timestampJson, { config }) - const c = Common.fromGethGenesis(modifiedJson, { + const c = createCommonFromGethGenesis(modifiedJson, { chain: 'modified', }) assert.equal( @@ -93,7 +99,7 @@ describe('[Common]: Timestamp Hardfork logic', () => { }, ]) - const c = Common.custom({ hardforks }, { baseChain: Chain.Mainnet }) + const c = createCustomCommon({ hardforks }, { baseChain: Chain.Mainnet }) const mainnetGenesisHash = hexToBytes( '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' ) @@ -134,7 +140,7 @@ describe('[Common]: Timestamp Hardfork logic', () => { }, ]) - const c = Common.custom({ hardforks }, { baseChain: Chain.Mainnet }) + const c = createCustomCommon({ hardforks }, { baseChain: Chain.Mainnet }) const mainnetGenesisHash = hexToBytes( '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' ) diff --git a/packages/common/test/utils.spec.ts b/packages/common/test/utils.spec.ts index f9d6e960f7..c478553469 100644 --- a/packages/common/test/utils.spec.ts +++ b/packages/common/test/utils.spec.ts @@ -1,9 +1,9 @@ import { hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { Common } from '../src/common.js' +import { createCommonFromGethGenesis } from '../src/constructors.js' import { Hardfork } from '../src/enums.js' -import { parseGethGenesis } from '../src/utils.js' +import { getInitializedChains, parseGethGenesis } from '../src/utils.js' import * as gethGenesisKilnJSON from './data/geth-genesis/geth-genesis-kiln.json' import * as invalidSpuriousDragonJSON from './data/geth-genesis/invalid-spurious-dragon.json' @@ -57,7 +57,7 @@ describe('[Utils/Parse]', () => { }) it('should successfully parse kiln genesis and set forkhash', async () => { - const common = Common.fromGethGenesis(gethGenesisKilnJSON, { + const common = createCommonFromGethGenesis(gethGenesisKilnJSON, { chain: 'customChain', genesisHash: hexToBytes('0x51c7fe41be669f69c45c33a56982cbde405313342d9e2b00d7c91a7b284dd4f8'), mergeForkIdPostMerge: false, @@ -91,7 +91,7 @@ describe('[Utils/Parse]', () => { // 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(gethGenesisKilnJSON.config, { shanghaiTime: Math.floor(Date.now() / 1000) }) - const common1 = Common.fromGethGenesis(gethGenesisKilnJSON, { + const common1 = createCommonFromGethGenesis(gethGenesisKilnJSON, { chain: 'customChain', }) // merge hardfork is now scheduled just after shanghai even if mergeForkIdTransition is not confirmed @@ -120,7 +120,7 @@ describe('[Utils/Parse]', () => { }) it('should successfully parse genesis with hardfork scheduled post merge', async () => { - const common = Common.fromGethGenesis(postMergeHardforkJSON, { + const common = createCommonFromGethGenesis(postMergeHardforkJSON, { chain: 'customChain', }) assert.deepEqual( @@ -175,16 +175,16 @@ describe('[Utils/Parse]', () => { }) it('should successfully assign mainnet deposit contract address when none provided', async () => { - const common = Common.fromGethGenesis(postMergeHardforkJSON, { + const common = createCommonFromGethGenesis(postMergeHardforkJSON, { chain: 'customChain', }) const depositContractAddress = common['_chainParams'].depositContractAddress ?? - Common.getInitializedChains().mainnet.depositContractAddress + getInitializedChains().mainnet.depositContractAddress assert.equal( depositContractAddress, - Common.getInitializedChains().mainnet.depositContractAddress, + getInitializedChains().mainnet.depositContractAddress, 'should assign mainnet deposit contract' ) }) @@ -196,12 +196,12 @@ describe('[Utils/Parse]', () => { depositContractAddress: '0x4242424242424242424242424242424242424242', }) - const common = Common.fromGethGenesis(customJson, { + const common = createCommonFromGethGenesis(customJson, { chain: 'customChain', }) const depositContractAddress = common['_chainParams'].depositContractAddress ?? - Common.getInitializedChains().mainnet.depositContractAddress + getInitializedChains().mainnet.depositContractAddress assert.equal( depositContractAddress, diff --git a/packages/devp2p/examples/peer-communication-les.ts b/packages/devp2p/examples/peer-communication-les.ts index eb7886d9f9..f85d1ad84e 100644 --- a/packages/devp2p/examples/peer-communication-les.ts +++ b/packages/devp2p/examples/peer-communication-les.ts @@ -1,5 +1,5 @@ import { bytesToInt, intToBytes, randomBytes, bytesToHex, hexToBytes } from '@ethereumjs/util' -import { Block, BlockHeader } from '@ethereumjs/block' +import { Block, BlockHeader, createBlockFromValuesArray } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import chalk from 'chalk' import ms from 'ms' @@ -131,7 +131,7 @@ rlpx.events.on('peer:added', (peer) => { const header2 = requests.bodies.shift() const txs = payload[2][0][0] const uncleHeaders = payload[2][0][1] - const block = Block.fromValuesArray([header2.raw(), txs, uncleHeaders], { common }) + const block = createBlockFromValuesArray([header2.raw(), txs, uncleHeaders], { common }) const isValid = await isValidBlock(block) let isValidPayload = false if (isValid) { diff --git a/packages/devp2p/examples/peer-communication.ts b/packages/devp2p/examples/peer-communication.ts index 8835df7551..df602d8c72 100644 --- a/packages/devp2p/examples/peer-communication.ts +++ b/packages/devp2p/examples/peer-communication.ts @@ -6,7 +6,7 @@ import { equalsBytes, hexToBytes, } from '@ethereumjs/util' -import { Block, BlockHeader } from '@ethereumjs/block' +import { Block, BlockHeader, createBlockFromValuesArray } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { TransactionFactory, TypedTransaction } from '@ethereumjs/tx' @@ -237,7 +237,7 @@ rlpx.events.on('peer:added', (peer) => { const header = requests.bodies.shift() const txs = payload[1][0][0] const uncleHeaders = payload[1][0][1] - const block = Block.fromValuesArray([header.raw(), txs, uncleHeaders], { common }) + const block = createBlockFromValuesArray([header.raw(), txs, uncleHeaders], { common }) const isValid = await isValidBlock(block) if (isValid) { isValidPayload = true @@ -256,7 +256,7 @@ rlpx.events.on('peer:added', (peer) => { case devp2p.ETH.MESSAGE_CODES.NEW_BLOCK: { if (!forkVerified) break - const newBlock = Block.fromValuesArray(payload[0], { common }) + const newBlock = createBlockFromValuesArray(payload[0], { common }) const isValidNewBlock = await isValidBlock(newBlock) if (isValidNewBlock) onNewBlock(newBlock, peer) diff --git a/packages/devp2p/package.json b/packages/devp2p/package.json index d14c3ccb8f..a20a1e5591 100644 --- a/packages/devp2p/package.json +++ b/packages/devp2p/package.json @@ -29,8 +29,9 @@ "Martin Becze ", "Holger Drewes " ], - "main": "dist/cjs/index.js", "type": "module", + "sideEffects": false, + "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "exports": { ".": { diff --git a/packages/devp2p/src/dns/dns.ts b/packages/devp2p/src/dns/dns.ts index 9af414d118..d2d056994e 100644 --- a/packages/devp2p/src/dns/dns.ts +++ b/packages/devp2p/src/dns/dns.ts @@ -5,9 +5,8 @@ import { ENR } from './enr.js' import type { DNSOptions, PeerInfo } from '../types.js' import type { Common } from '@ethereumjs/common' -const { debug: createDebugLogger } = debugDefault -const debug = createDebugLogger('devp2p:dns:dns') +const debug = debugDefault('devp2p:dns:dns') type SearchContext = { domain: string diff --git a/packages/devp2p/src/dpt/ban-list.ts b/packages/devp2p/src/dpt/ban-list.ts index 678e5a161f..639a700e96 100644 --- a/packages/devp2p/src/dpt/ban-list.ts +++ b/packages/devp2p/src/dpt/ban-list.ts @@ -6,10 +6,9 @@ import { formatLogId } from '../util.js' import { KBucket } from './kbucket.js' import type { PeerInfo } from '../types.js' -const { debug: createDebugLogger } = debugDefault -const debug = createDebugLogger('devp2p:dpt:ban-list') -const verbose = createDebugLogger('verbose').enabled +const debug = debugDefault('devp2p:dpt:ban-list') +const verbose = debugDefault('verbose').enabled export class BanList { private _lru: LRUCache diff --git a/packages/devp2p/src/dpt/message.ts b/packages/devp2p/src/dpt/message.ts index 55bfaf60dd..f0d09ccfcd 100644 --- a/packages/devp2p/src/dpt/message.ts +++ b/packages/devp2p/src/dpt/message.ts @@ -8,9 +8,8 @@ import { assertEq, ipToBytes, ipToString, isV4Format, isV6Format, unstrictDecode import type { PeerInfo } from '../types.js' import type { Common } from '@ethereumjs/common' -const { debug: createDebugLogger } = debugDefault -const debug = createDebugLogger('devp2p:dpt:server') +const debug = debugDefault('devp2p:dpt:server') function getTimestamp() { return (Date.now() / 1000) | 0 diff --git a/packages/devp2p/src/dpt/server.ts b/packages/devp2p/src/dpt/server.ts index 7c0fd5147d..dfb982e3f0 100644 --- a/packages/devp2p/src/dpt/server.ts +++ b/packages/devp2p/src/dpt/server.ts @@ -13,10 +13,9 @@ import type { DPT } from './dpt.js' import type { Common } from '@ethereumjs/common' import type { Debugger } from 'debug' import type { Socket as DgramSocket, RemoteInfo } from 'dgram' -const { debug: createDebugLogger } = debugDefault const DEBUG_BASE_NAME = 'dpt:server' -const verbose = createDebugLogger('verbose').enabled +const verbose = debugDefault('verbose').enabled const VERSION = 0x04 diff --git a/packages/devp2p/src/protocol/protocol.ts b/packages/devp2p/src/protocol/protocol.ts index bc8dcc28a9..01ccd3e22e 100644 --- a/packages/devp2p/src/protocol/protocol.ts +++ b/packages/devp2p/src/protocol/protocol.ts @@ -7,7 +7,6 @@ import { devp2pDebug } from '../util.js' import type { Peer } from '../rlpx/peer.js' import type { SendMethod } from '../types.js' import type { Debugger } from 'debug' -const { debug: createDebugLogger } = debugDefault type MessageCodes = { [key: number | string]: number | string } @@ -50,7 +49,7 @@ export abstract class Protocol { : undefined this._debug = devp2pDebug.extend(protocol) - this._verbose = createDebugLogger('verbose').enabled + this._verbose = debugDefault('verbose').enabled this.initMsgDebuggers(protocol) } diff --git a/packages/devp2p/src/rlpx/ecies.ts b/packages/devp2p/src/rlpx/ecies.ts index 21535fded5..d13fa10886 100644 --- a/packages/devp2p/src/rlpx/ecies.ts +++ b/packages/devp2p/src/rlpx/ecies.ts @@ -12,10 +12,9 @@ import { assertEq, genPrivateKey, id2pk, pk2id, unstrictDecode, xor, zfill } fro import { MAC } from './mac.js' import type { Common } from '@ethereumjs/common' -const { debug: createDebugLogger } = debugDefault type Decipher = crypto.Decipher -const debug = createDebugLogger('devp2p:rlpx:peer') +const debug = debugDefault('devp2p:rlpx:peer') function ecdhX(publicKey: Uint8Array, privateKey: Uint8Array) { // return (publicKey * privateKey).x diff --git a/packages/devp2p/src/rlpx/peer.ts b/packages/devp2p/src/rlpx/peer.ts index 88ff611c5d..e980c89688 100644 --- a/packages/devp2p/src/rlpx/peer.ts +++ b/packages/devp2p/src/rlpx/peer.ts @@ -23,10 +23,9 @@ import type { Capabilities, PeerOptions } from '../types.js' import type { Common } from '@ethereumjs/common' import type { Debugger } from 'debug' import type { Socket } from 'net' -const { debug: createDebugLogger } = debugDefault const DEBUG_BASE_NAME = 'rlpx:peer' -const verbose = createDebugLogger('verbose').enabled +const verbose = debugDefault('verbose').enabled const BASE_PROTOCOL_VERSION = 5 const BASE_PROTOCOL_LENGTH = 16 diff --git a/packages/devp2p/src/rlpx/rlpx.ts b/packages/devp2p/src/rlpx/rlpx.ts index 7790d1c9e0..45232dcf4c 100644 --- a/packages/devp2p/src/rlpx/rlpx.ts +++ b/packages/devp2p/src/rlpx/rlpx.ts @@ -22,12 +22,11 @@ import type { DPT } from '../dpt/index.js' import type { Capabilities, PeerInfo, RLPxOptions } from '../types.js' import type { Common } from '@ethereumjs/common' import type { Debugger } from 'debug' -const { debug: createDebugLogger } = debugDefault // note: relative path only valid in .js file in dist const DEBUG_BASE_NAME = 'rlpx' -const verbose = createDebugLogger('verbose').enabled +const verbose = debugDefault('verbose').enabled export class RLPx { public events: EventEmitter diff --git a/packages/devp2p/src/types.ts b/packages/devp2p/src/types.ts index 9c1ba4dd84..a7f12d382c 100644 --- a/packages/devp2p/src/types.ts +++ b/packages/devp2p/src/types.ts @@ -1,5 +1,5 @@ -import type { DPT } from './dpt' -import type { Protocol } from './protocol/protocol' +import type { DPT } from './dpt/index.js' +import type { Protocol } from './protocol/protocol.js' import type { Common } from '@ethereumjs/common' import type { Socket } from 'net' diff --git a/packages/devp2p/src/util.ts b/packages/devp2p/src/util.ts index 5263471383..746cece35e 100644 --- a/packages/devp2p/src/util.ts +++ b/packages/devp2p/src/util.ts @@ -1,14 +1,13 @@ import { RLP } from '@ethereumjs/rlp' import { bytesToHex, bytesToUnprefixedHex, concatBytes, equalsBytes } from '@ethereumjs/util' -import debugDefault from 'debug' +import debug from 'debug' import { publicKeyConvert } from 'ethereum-cryptography/secp256k1-compat.js' import { secp256k1 } from 'ethereum-cryptography/secp256k1.js' import type { ETH } from './protocol/eth.js' import type { LES } from './protocol/les.js' -const { debug: createDebugLogger } = debugDefault -export const devp2pDebug = createDebugLogger('devp2p') +export const devp2pDebug = debug('devp2p') export function genPrivateKey(): Uint8Array { const privateKey = secp256k1.utils.randomPrivateKey() diff --git a/packages/ethash/examples/miner.ts b/packages/ethash/examples/miner.ts index 629fc0f08a..ab783d6d60 100644 --- a/packages/ethash/examples/miner.ts +++ b/packages/ethash/examples/miner.ts @@ -1,8 +1,8 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Ethash } from '@ethereumjs/ethash' import { DBObject, MapDB, bytesToHex } from '@ethereumjs/util' -const block = Block.fromBlockData( +const block = createBlockFromBlockData( { header: { difficulty: BigInt(100), diff --git a/packages/ethash/examples/powBlock.ts b/packages/ethash/examples/powBlock.ts index 1df0414cb7..fcd54323c0 100644 --- a/packages/ethash/examples/powBlock.ts +++ b/packages/ethash/examples/powBlock.ts @@ -1,5 +1,5 @@ import { Ethash } from '@ethereumjs/ethash' -import { Block } from '@ethereumjs/block' +import { createBlockFromRLPSerializedBlock } from '@ethereumjs/block' import { DBObject, hexToBytes, MapDB } from '@ethereumjs/util' const cacheDB = new MapDB() @@ -8,7 +8,7 @@ const ethash = new Ethash(cacheDB) const validblockRlp = '0xf90667f905fba0a8d5b7a4793baaede98b5236954f634a0051842df6a252f6a80492fd888678bda01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0f93c8db1e931daa2e22e39b5d2da6fb4074e3d544094857608536155e3521bc1a0bb7495628f9160ddbcf6354380ee32c300d594e833caec3a428041a66e7bade1a0c7778a7376099ee2e5c455791c1885b5c361b95713fddcbe32d97fd01334d296b90100000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000400000000000000000000000000000000000000000000000000000008302000001832fefd882560b84559c17b9b9040001020304050607080910111213141516171819202122232410000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000a09c7b47112a3afb385c12924bf6280d273c106eea7caeaf5131d8776f61056c148876ae05d46b58d1fff866f864800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d8785012a05f200801ba01d2c92cfaeb04e53acdff2b5d42005ff6aacdb0105e64eb8c30c273f445d2782a01e7d50ffce57840360c57d94977b8cdebde614da23e8d1e77dc07928763cfe21c0' -const validBlock = Block.fromRLPSerializedBlock(hexToBytes(validblockRlp), { +const validBlock = createBlockFromRLPSerializedBlock(hexToBytes(validblockRlp), { setHardfork: true, skipConsensusFormatValidation: true, }) diff --git a/packages/ethash/package.json b/packages/ethash/package.json index c3eb89c7fc..df473dcc59 100644 --- a/packages/ethash/package.json +++ b/packages/ethash/package.json @@ -18,6 +18,7 @@ "license": "MPL-2.0", "author": "mjbecze ", "type": "module", + "sideEffects": false, "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "exports": { diff --git a/packages/ethash/src/index.ts b/packages/ethash/src/index.ts index dd8d3c8f5d..31b8157c8d 100644 --- a/packages/ethash/src/index.ts +++ b/packages/ethash/src/index.ts @@ -1,4 +1,4 @@ -import { Block, BlockHeader } from '@ethereumjs/block' +import { Block, BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' import { RLP } from '@ethereumjs/rlp' import { BIGINT_0, @@ -96,7 +96,7 @@ export class Miner { const data = this.block.toJSON() data.header!.mixHash = solution.mixHash data.header!.nonce = solution.nonce - return Block.fromBlockData(data, { common: this.block.common }) + return createBlockFromBlockData(data, { common: this.block.common }) } else { const data = this.blockHeader.toJSON() data.mixHash = solution.mixHash diff --git a/packages/ethash/test/block.spec.ts b/packages/ethash/test/block.spec.ts index a8bafc4ca7..86edb0bd4f 100644 --- a/packages/ethash/test/block.spec.ts +++ b/packages/ethash/test/block.spec.ts @@ -1,4 +1,8 @@ -import { Block } from '@ethereumjs/block' +import { + createBlockFromBlockData, + createBlockFromRLPSerializedBlock, + createBlockFromValuesArray, +} from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { MapDB, hexToBytes, toBytes } from '@ethereumjs/util' @@ -18,12 +22,12 @@ describe('Verify POW for valid and invalid blocks', () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const genesis = Block.fromBlockData({}, { common }) + const genesis = createBlockFromBlockData({}, { common }) const genesisResult = await e.verifyPOW(genesis) assert.ok(genesisResult, 'genesis block should be valid') const validRlp = hexToBytes(`0x${validBlockRlp}`) - const validBlock = Block.fromRLPSerializedBlock(validRlp, { common }) + const validBlock = createBlockFromRLPSerializedBlock(validRlp, { common }) const validBlockResult = await e.verifyPOW(validBlock) assert.ok(validBlockResult, 'should be valid') @@ -31,13 +35,13 @@ describe('Verify POW for valid and invalid blocks', () => { // Put correct amount of extraData in block extraData field so block can be deserialized const values = RLP.decode(Uint8Array.from(invalidRlp)) as BlockBytes values[0][12] = new Uint8Array(32) - const invalidBlock = Block.fromValuesArray(values, { common }) + const invalidBlock = createBlockFromValuesArray(values, { common }) const invalidBlockResult = await e.verifyPOW(invalidBlock) assert.ok(!invalidBlockResult, 'should be invalid') const testData = require('./block_tests_data.json') const blockRlp = toBytes(testData.blocks[0].rlp) - const block = Block.fromRLPSerializedBlock(blockRlp, { common }) + const block = createBlockFromRLPSerializedBlock(blockRlp, { common }) const uncleBlockResult = await e.verifyPOW(block) assert.ok(uncleBlockResult, 'should be valid') }) diff --git a/packages/ethash/test/miner.spec.ts b/packages/ethash/test/miner.spec.ts index 2f9ead6116..493245da5b 100644 --- a/packages/ethash/test/miner.spec.ts +++ b/packages/ethash/test/miner.spec.ts @@ -1,11 +1,11 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { MapDB } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' import { Ethash } from '../src/index.js' -import type { BlockHeader } from '@ethereumjs/block' +import type { Block, BlockHeader } from '@ethereumjs/block' import type { DBObject } from '@ethereumjs/util' const cacheDb = new MapDB() @@ -15,7 +15,7 @@ describe('Miner', () => { it('Check if miner works as expected', async () => { const e = new Ethash(cacheDb) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { difficulty: BigInt(100), @@ -37,7 +37,7 @@ describe('Miner', () => { const solution = await miner.iterate(-1) - const validBlock = Block.fromBlockData( + const validBlock = createBlockFromBlockData( { header: { difficulty: block.header.difficulty, @@ -57,7 +57,7 @@ describe('Miner', () => { it('Check if it is possible to mine Blocks and BlockHeaders', async () => { const e = new Ethash(cacheDb as any) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { difficulty: BigInt(100), @@ -70,7 +70,7 @@ describe('Miner', () => { const solution = await miner.mine(-1) assert.ok( - e.verifyPOW(Block.fromBlockData({ header: solution.toJSON() }, { common })), + e.verifyPOW(createBlockFromBlockData({ header: solution.toJSON() }, { common })), 'successfully mined block' ) @@ -83,7 +83,7 @@ describe('Miner', () => { it('Check if it is possible to stop the miner', async () => { const e = new Ethash(cacheDb as any) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { difficulty: BigInt(10000000000000), @@ -118,7 +118,7 @@ describe('Miner', () => { it('Should keep common when mining blocks or headers', async () => { const e = new Ethash(cacheDb as any) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { difficulty: BigInt(100), diff --git a/packages/evm/.eslintignore b/packages/evm/.eslintignore index 3c018eed27..e28502fb87 100644 --- a/packages/evm/.eslintignore +++ b/packages/evm/.eslintignore @@ -1 +1 @@ -vite.config.ts \ No newline at end of file +vite.config.bundler.ts \ No newline at end of file diff --git a/packages/evm/examples/runCode.ts b/packages/evm/examples/runCode.ts index 6cdc1da404..d442043e37 100644 --- a/packages/evm/examples/runCode.ts +++ b/packages/evm/examples/runCode.ts @@ -1,11 +1,11 @@ -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockchain } from '@ethereumjs/blockchain' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { EVM } from '@ethereumjs/evm' import { bytesToHex, hexToBytes } from '@ethereumjs/util' const main = async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) - const blockchain = await Blockchain.create() + const blockchain = await createBlockchain() const evm = await EVM.create({ common, diff --git a/packages/evm/examples/withBlockchain.ts b/packages/evm/examples/withBlockchain.ts index de4acf19a6..f7ba32049e 100644 --- a/packages/evm/examples/withBlockchain.ts +++ b/packages/evm/examples/withBlockchain.ts @@ -1,4 +1,4 @@ -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockchain } from '@ethereumjs/blockchain' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { EVM } from '@ethereumjs/evm' import { DefaultStateManager } from '@ethereumjs/statemanager' @@ -7,7 +7,7 @@ import { bytesToHex } from '@ethereumjs/util' const main = async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Shanghai }) const stateManager = new DefaultStateManager() - const blockchain = await Blockchain.create() + const blockchain = await createBlockchain() const evm = await EVM.create({ common, diff --git a/packages/evm/package.json b/packages/evm/package.json index d080ceccf2..bdd1015585 100644 --- a/packages/evm/package.json +++ b/packages/evm/package.json @@ -21,6 +21,8 @@ "contributors": [ "Alex Beregszaszi " ], + "type": "module", + "sideEffects": false, "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "exports": { @@ -51,7 +53,7 @@ "test:browser": "npx vitest run --config=./vitest.config.browser.mts", "test:node": "npx vitest run", "tsc": "../../config/cli/ts-compile.sh", - "visualize:bundle": "npx vite build" + "visualize:bundle": "npx vite build --config=./vite.config.bundler.ts --emptyOutDir=false --outDir ." }, "dependencies": { "@ethereumjs/common": "^4.3.0", diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index 2a04fe429f..9cb4b6611c 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -1,5 +1,5 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' -import { DefaultStateManager } from '@ethereumjs/statemanager' +import { SimpleStateManager } from '@ethereumjs/statemanager' import { Account, Address, @@ -52,11 +52,10 @@ import type { bn128, } from './types.js' import type { EVMStateManagerInterface } from '@ethereumjs/common' -const { debug: createDebugLogger } = debugDefault -const debug = createDebugLogger('evm:evm') -const debugGas = createDebugLogger('evm:gas') -const debugPrecompiles = createDebugLogger('evm:precompiles') +const debug = debugDefault('evm:evm') +const debugGas = debugDefault('evm:gas') +const debugPrecompiles = debugDefault('evm:precompiles') let initializedRustBN: bn128 | undefined = undefined @@ -169,7 +168,7 @@ export class EVM implements EVMInterface { } if (opts.stateManager === undefined) { - opts.stateManager = new DefaultStateManager() + opts.stateManager = new SimpleStateManager() } return new EVM(opts, bn128) diff --git a/packages/evm/src/index.ts b/packages/evm/src/index.ts index c441fefe97..3c2303d301 100644 --- a/packages/evm/src/index.ts +++ b/packages/evm/src/index.ts @@ -21,6 +21,7 @@ import type { Log, bn128, } from './types.js' +export * from './logger.js' export type { bn128, diff --git a/packages/evm/src/interpreter.ts b/packages/evm/src/interpreter.ts index bba5c9ce90..ec46ecb3e1 100644 --- a/packages/evm/src/interpreter.ts +++ b/packages/evm/src/interpreter.ts @@ -25,9 +25,8 @@ import type { AsyncOpHandler, Opcode, OpcodeMapEntry } from './opcodes/index.js' import type { Block, Blockchain, EVMProfilerOpts, EVMResult, Log } from './types.js' import type { AccessWitnessInterface, Common, EVMStateManagerInterface } from '@ethereumjs/common' import type { Address, PrefixedHexString } from '@ethereumjs/util' -const { debug: createDebugLogger } = debugDefault -const debugGas = createDebugLogger('evm:gas') +const debugGas = debugDefault('evm:gas') export interface InterpreterOpts { pc?: number @@ -435,7 +434,7 @@ export class Interpreter { } if (!(name in this.opDebuggers)) { - this.opDebuggers[name] = createDebugLogger(`evm:ops:${name}`) + this.opDebuggers[name] = debugDefault(`evm:ops:${name}`) } this.opDebuggers[name](JSON.stringify(opTrace)) } diff --git a/packages/evm/src/journal.ts b/packages/evm/src/journal.ts index 1a84e51ac5..60dad80bd3 100644 --- a/packages/evm/src/journal.ts +++ b/packages/evm/src/journal.ts @@ -13,7 +13,6 @@ import { hexToBytes } from 'ethereum-cryptography/utils' import type { Common, EVMStateManagerInterface } from '@ethereumjs/common' import type { Account, PrefixedHexString } from '@ethereumjs/util' import type { Debugger } from 'debug' -const { debug: createDebugLogger } = debugDefault type AddressString = string type SlotString = string @@ -54,7 +53,7 @@ export class Journal { this.DEBUG = typeof window === 'undefined' ? process?.env?.DEBUG?.includes('ethjs') ?? false : false - this._debug = createDebugLogger('statemanager:statemanager') + this._debug = debugDefault('statemanager:statemanager') // TODO maybe call into this.clearJournal this.cleanJournal() diff --git a/packages/evm/src/precompiles/bls12_381/noble.ts b/packages/evm/src/precompiles/bls12_381/noble.ts index 577c7c33fa..1d2ad640be 100644 --- a/packages/evm/src/precompiles/bls12_381/noble.ts +++ b/packages/evm/src/precompiles/bls12_381/noble.ts @@ -21,7 +21,7 @@ import { } from './constants.js' import type { EVMBLSInterface } from '../../types.js' -import type { AffinePoint, ProjPointType } from '@noble/curves/abstract/weierstrass.js' +import type { AffinePoint, ProjPointType } from '@noble/curves/abstract/weierstrass' // Copied from @noble/curves/bls12-381 (only local declaration) type Fp2 = { diff --git a/packages/evm/src/precompiles/bls12_381/util.ts b/packages/evm/src/precompiles/bls12_381/util.ts index 446de8788e..ca09ef1271 100644 --- a/packages/evm/src/precompiles/bls12_381/util.ts +++ b/packages/evm/src/precompiles/bls12_381/util.ts @@ -2,7 +2,7 @@ import { equalsBytes, short } from '@ethereumjs/util' import { BLS_GAS_DISCOUNT_PAIRS } from './constants.js' -import type { PrecompileInput } from '../types' +import type { PrecompileInput } from '../types.js' const ZERO_BYTES_16 = new Uint8Array(16) diff --git a/packages/evm/src/types.ts b/packages/evm/src/types.ts index f423302e33..fa420aa964 100644 --- a/packages/evm/src/types.ts +++ b/packages/evm/src/types.ts @@ -279,7 +279,13 @@ export interface EVMOpts { bls?: EVMBLSInterface /* - * The StateManager which is used to update the trie + * The EVM comes with a basic dependency-minimized `SimpleStateManager` implementation + * which serves most code execution use cases and which is included in the + * `@ethereumjs/statemanager` package. + * + * The `@ethereumjs/statemanager` package also provides a variety of state manager + * implementations for different needs (MPT-tree backed, RPC, experimental verkle) + * which can be used by this option as a replacement. */ stateManager?: EVMStateManagerInterface diff --git a/packages/evm/test/blobVersionedHashes.spec.ts b/packages/evm/test/blobVersionedHashes.spec.ts index f4e7af49f6..4608c571a6 100644 --- a/packages/evm/test/blobVersionedHashes.spec.ts +++ b/packages/evm/test/blobVersionedHashes.spec.ts @@ -1,4 +1,4 @@ -import { Common, Hardfork } from '@ethereumjs/common' +import { Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common' import { Account, Address, bytesToHex, hexToBytes, unpadBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' @@ -11,7 +11,7 @@ describe('BLOBHASH / access blobVersionedHashes in calldata', () => { it('should work', async () => { // setup the evm const genesisJSON = await import('../../client/test/testdata/geth-genesis/eip4844.json') - const common = Common.fromGethGenesis(genesisJSON, { + const common = createCommonFromGethGenesis(genesisJSON, { chain: 'custom', hardfork: Hardfork.Cancun, }) @@ -41,7 +41,7 @@ describe(`BLOBHASH: access blobVersionedHashes within contract calls`, () => { it('should work', async () => { // setup the evm const genesisJSON = await import('../../client/test/testdata/geth-genesis/eip4844.json') - const common = Common.fromGethGenesis(genesisJSON, { + const common = createCommonFromGethGenesis(genesisJSON, { chain: 'custom', hardfork: Hardfork.Cancun, }) @@ -91,7 +91,7 @@ describe(`BLOBHASH: access blobVersionedHashes in a CREATE/CREATE2 frame`, () => it('should work', async () => { // setup the evm const genesisJSON = await import('../../client/test/testdata/geth-genesis/eip4844.json') - const common = Common.fromGethGenesis(genesisJSON, { + const common = createCommonFromGethGenesis(genesisJSON, { chain: 'custom', hardfork: Hardfork.Cancun, }) diff --git a/packages/evm/test/eips/eip-3860.spec.ts b/packages/evm/test/eips/eip-3860.spec.ts index 84560c38fc..e46a3bd6d0 100644 --- a/packages/evm/test/eips/eip-3860.spec.ts +++ b/packages/evm/test/eips/eip-3860.spec.ts @@ -1,5 +1,4 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' -import { DefaultStateManager } from '@ethereumjs/statemanager' import { Address, concatBytes, equalsBytes, hexToBytes, privateToAddress } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' @@ -17,7 +16,6 @@ describe('EIP 3860 tests', () => { }) const evm = await EVM.create({ common, - stateManager: new DefaultStateManager(), }) const buffer = new Uint8Array(1000000).fill(0x60) @@ -58,11 +56,9 @@ describe('EIP 3860 tests', () => { const caller = Address.fromString('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b') const evm = await EVM.create({ common: commonWith3860, - stateManager: new DefaultStateManager(), }) const evmWithout3860 = await EVM.create({ common: commonWithout3860, - stateManager: new DefaultStateManager(), }) const contractFactory = Address.fromString('0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b') const contractAccount = await evm.stateManager.getAccount(contractFactory) @@ -104,11 +100,9 @@ describe('EIP 3860 tests', () => { const caller = Address.fromString('0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b') const evm = await EVM.create({ common: commonWith3860, - stateManager: new DefaultStateManager(), }) const evmWithout3860 = await EVM.create({ common: commonWithout3860, - stateManager: new DefaultStateManager(), }) const contractFactory = Address.fromString('0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b') const contractAccount = await evm.stateManager.getAccount(contractFactory) @@ -143,8 +137,6 @@ describe('EIP 3860 tests', () => { }) const evm = await EVM.create({ common, - stateManager: new DefaultStateManager(), - allowUnlimitedInitCodeSize: true, }) @@ -179,14 +171,11 @@ describe('EIP 3860 tests', () => { for (const code of ['F0', 'F5']) { const evm = await EVM.create({ common: commonWith3860, - stateManager: new DefaultStateManager(), allowUnlimitedInitCodeSize: true, }) const evmDisabled = await EVM.create({ common: commonWith3860, - stateManager: new DefaultStateManager(), - allowUnlimitedInitCodeSize: false, }) const contractFactory = Address.fromString('0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b') diff --git a/packages/evm/test/precompiles/0a-pointevaluation.spec.ts b/packages/evm/test/precompiles/0a-pointevaluation.spec.ts index 6027dfb59e..f9ab7c4825 100644 --- a/packages/evm/test/precompiles/0a-pointevaluation.spec.ts +++ b/packages/evm/test/precompiles/0a-pointevaluation.spec.ts @@ -1,4 +1,4 @@ -import { Common, Hardfork } from '@ethereumjs/common' +import { Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common' import { bytesToBigInt, computeVersionedHash, @@ -23,7 +23,7 @@ describe('Precompiles: point evaluation', () => { const kzg = await loadKZG() - const common = Common.fromGethGenesis(genesisJSON, { + const common = createCommonFromGethGenesis(genesisJSON, { chain: 'custom', hardfork: Hardfork.Cancun, customCrypto: { kzg }, diff --git a/packages/evm/test/runCall.spec.ts b/packages/evm/test/runCall.spec.ts index 01ea90578e..ebdc7513b3 100644 --- a/packages/evm/test/runCall.spec.ts +++ b/packages/evm/test/runCall.spec.ts @@ -1,4 +1,4 @@ -import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { Chain, Common, Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common' import { Account, Address, @@ -541,7 +541,7 @@ describe('RunCall tests', () => { }) it('runCall() => use BLOBHASH opcode from EIP 4844', async () => { // setup the evm - const common = Common.fromGethGenesis(genesisJSON, { + const common = createCommonFromGethGenesis(genesisJSON, { chain: 'custom', hardfork: Hardfork.Cancun, }) @@ -578,7 +578,7 @@ describe('RunCall tests', () => { it('runCall() => use BLOBBASEFEE opcode from EIP 7516', async () => { // setup the evm - const common = Common.fromGethGenesis(genesisJSON, { + const common = createCommonFromGethGenesis(genesisJSON, { chain: 'custom', hardfork: Hardfork.Cancun, }) diff --git a/packages/evm/vite.config.ts b/packages/evm/vite.config.bundler.ts similarity index 100% rename from packages/evm/vite.config.ts rename to packages/evm/vite.config.bundler.ts diff --git a/packages/genesis/package.json b/packages/genesis/package.json index 85bbda42ed..804470346b 100644 --- a/packages/genesis/package.json +++ b/packages/genesis/package.json @@ -16,9 +16,10 @@ }, "license": "MIT", "author": "g11tech ", + "type": "module", + "sideEffects": false, "main": "dist/cjs/index.js", "module": "dist/esm/index.js", - "type": "commonjs", "exports": { ".": { "import": "./dist/esm/index.js", diff --git a/packages/rlp/package.json b/packages/rlp/package.json index d3951d43fd..adc19455c9 100644 --- a/packages/rlp/package.json +++ b/packages/rlp/package.json @@ -25,6 +25,7 @@ "Paul Miller " ], "type": "module", + "sideEffects": false, "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "exports": { diff --git a/packages/statemanager/README.md b/packages/statemanager/README.md index 6b74a9dbd6..85e194be10 100644 --- a/packages/statemanager/README.md +++ b/packages/statemanager/README.md @@ -17,17 +17,20 @@ To obtain the latest version, simply require the project using `npm`: npm install @ethereumjs/statemanager ``` -Note: this library was part of the [@ethereumjs/vm](../vm/) package up till VM `v5`. - ## Usage ### Introduction The `StateManager` provides high-level access and manipulation methods to and for the Ethereum state, thinking in terms of accounts or contract code rather then the storage operations of the underlying data structure (e.g. a [Trie](../trie/)). -The library includes a TypeScript interface `StateManager` to ensure a unified interface (e.g. when passed to the VM), a concrete Trie-based `DefaultStateManager` implementation, as well as an `RPCStateManager` implementation that sources state and history data from an external JSON-RPC provider. +This library includes several different implementations that all implement the `StateManager` interface which is accepted by the `vm` library. These include: + +- [`SimpleStateManager`](./src/simpleStateManager.ts) -a minimally functional (and dependency minimized) version of the state manager suitable for most basic EVM bytecode operations +- [`DefaultStateManager`](./src//stateManager.ts) - a Merkle-Patricia Trie-based `DefaultStateManager` implementation that is used by the `@ethereumjs/client` and `@ethereumjs/vm` +- [`RPCStateManager`](./src/rpcStateManager.ts) - a light-weight implementation that sources state and history data from an external JSON-RPC provider +- [`StatelessVerkleStateManager`](./src/statelessVerkleStateManager.ts) - an experimental implementation of a "stateless" state manager that uses Verkle proofs to provide necessary state access for processing verkle-trie based blocks -It also includes a checkpoint/revert/commit mechanism to either persist or revert state changes and provides a sophisticated caching mechanism under the hood to reduce the need for direct state accesses. +It also includes a checkpoint/revert/commit mechanism to either persist or revert state changes and provides a sophisticated caching mechanism under the hood to reduce the need reading state accesses from disk. ### `DefaultStateManager` @@ -69,6 +72,31 @@ Caches now "survive" a flush operation and especially long-lived usage scenarios Have a loot at the extended `CacheOptions` on how to use and leverage the new cache system. +### `SimpleStateManager` + +The `SimpleStateManager` is a dependency-minimized simple state manager implementation. While this state manager implementation lacks the implementations of some non-core functionality as well as proof related logic (e.g. `setStateRoot()`) it is suitable for a lot use cases where things like sophisticated caching or state root handling is not needed. + +This state manager can be instantiated and used as follows: + +```ts +// ./examples/simple.ts + +import { SimpleStateManager } from '../src/index.js' +import { Account, Address, randomBytes } from '@ethereumjs/util' + +const main = async () => { + const sm = new SimpleStateManager() + const address = Address.fromPrivateKey(randomBytes(32)) + const account = new Account(0n, 0xfffffn) + await sm.putAccount(address, account) + console.log(await sm.getAccount(address)) +} + +main() +``` + +### `DefaultStateManager` -> Proofs + #### Instantiating from a Proof The `DefaultStateManager` has a static constructor `fromProof` that accepts one or more [EIP-1186](https://eips.ethereum.org/EIPS/eip-1186) [proofs](./src/stateManager.ts) and will instantiate a `DefaultStateManager` with a partial trie containing the state provided by the proof(s). Be aware that this constructor accepts the `StateManagerOpts` dictionary as a third parameter (i.e. `stateManager.fromProof(proof, safe, opts)`). Therefore, if you need to use a customized trie (e.g. one that does not use key hashing) or specify caching options, you can pass them in here. If you do instantiate a trie and pass it into the `fromProof` constructor, you also need to instantiate the trie using the corresponding `fromProof` constructor to ensure the state root matches when the proof data is added to the trie. See [this test](./test/stateManager.spec.ts#L287-L288) for more details. @@ -174,7 +202,7 @@ const main = async () => { const blockchain = new RPCBlockChain(provider) const blockTag = 1n const state = new RPCStateManager({ provider, blockTag }) - const evm = new EVM({ blockchain, stateManager: state }) // note that evm is ready to run BLOCKHASH opcodes (over RPC) + const evm = await EVM.create({ blockchain, stateManager: state }) // note that evm is ready to run BLOCKHASH opcodes (over RPC) } catch (e) { console.log(e.message) // fetch would fail because provider url is not real. please replace provider with a valid rpc url string. } diff --git a/packages/statemanager/examples/simple.ts b/packages/statemanager/examples/simple.ts new file mode 100644 index 0000000000..b7618b4127 --- /dev/null +++ b/packages/statemanager/examples/simple.ts @@ -0,0 +1,12 @@ +import { SimpleStateManager } from '../src/index.js' +import { Account, Address, randomBytes } from '@ethereumjs/util' + +const main = async () => { + const sm = new SimpleStateManager() + const address = Address.fromPrivateKey(randomBytes(32)) + const account = new Account(0n, 0xfffffn) + await sm.putAccount(address, account) + console.log(await sm.getAccount(address)) +} + +main() diff --git a/packages/statemanager/package.json b/packages/statemanager/package.json index 821879a00e..051d8e8239 100644 --- a/packages/statemanager/package.json +++ b/packages/statemanager/package.json @@ -19,7 +19,8 @@ "contributors": [ "g11tech " ], - "type": "commonjs", + "type": "module", + "sideEffects": false, "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "exports": { diff --git a/packages/statemanager/src/accessWitness.ts b/packages/statemanager/src/accessWitness.ts index 5a66756d63..db20508298 100644 --- a/packages/statemanager/src/accessWitness.ts +++ b/packages/statemanager/src/accessWitness.ts @@ -19,8 +19,7 @@ import debugDefault from 'debug' import type { AccessEventFlags, AccessWitnessInterface } from '@ethereumjs/common' import type { Address, PrefixedHexString, VerkleCrypto } from '@ethereumjs/util' -const { debug: createDebugLogger } = debugDefault -const debug = createDebugLogger('statemanager:verkle:aw') +const debug = debugDefault('statemanager:verkle:aw') /** * Tree key constants. diff --git a/packages/statemanager/src/cache/account.ts b/packages/statemanager/src/cache/account.ts index 0619ee12fe..c78ca1b41f 100644 --- a/packages/statemanager/src/cache/account.ts +++ b/packages/statemanager/src/cache/account.ts @@ -8,7 +8,6 @@ import { CacheType } from './types.js' import type { CacheOpts } from './types.js' import type { Account, Address } from '@ethereumjs/util' -const { debug: createDebugLogger } = debugDefault /** * account: undefined @@ -45,7 +44,7 @@ export class AccountCache extends Cache { } this._diffCache.push(new Map()) - this._debug = createDebugLogger('statemanager:cache:account') + this._debug = debugDefault('statemanager:cache:account') } _saveCachePreState(cacheKeyHex: string) { diff --git a/packages/statemanager/src/cache/cache.ts b/packages/statemanager/src/cache/cache.ts index 878c2172d7..3be3a71215 100644 --- a/packages/statemanager/src/cache/cache.ts +++ b/packages/statemanager/src/cache/cache.ts @@ -1,7 +1,6 @@ import debugDefault from 'debug' import type { Debugger } from 'debug' -const { debug: createDebugLogger } = debugDefault export class Cache { _debug: Debugger @@ -32,6 +31,6 @@ export class Cache { this.DEBUG = typeof window === 'undefined' ? process?.env?.DEBUG?.includes('ethjs') ?? false : false - this._debug = createDebugLogger('statemanager:cache') + this._debug = debugDefault('statemanager:cache') } } diff --git a/packages/statemanager/src/cache/code.ts b/packages/statemanager/src/cache/code.ts index f28f607ce0..5579da224d 100644 --- a/packages/statemanager/src/cache/code.ts +++ b/packages/statemanager/src/cache/code.ts @@ -9,8 +9,6 @@ import { CacheType } from './types.js' import type { CacheOpts } from './types.js' import type { Address } from '@ethereumjs/util' -const { debug: createDebugLogger } = debugDefault - /** * Represents a cached code element. */ @@ -45,7 +43,7 @@ export class CodeCache extends Cache { } this._diffCache.push(new Map()) - this._debug = createDebugLogger('statemanager:cache:code') + this._debug = debugDefault('statemanager:cache:code') } /** diff --git a/packages/statemanager/src/cache/originalStorageCache.ts b/packages/statemanager/src/cache/originalStorageCache.ts index 50c6169e91..dd837be4e3 100644 --- a/packages/statemanager/src/cache/originalStorageCache.ts +++ b/packages/statemanager/src/cache/originalStorageCache.ts @@ -4,6 +4,15 @@ import type { Address } from '@ethereumjs/util' type getContractStorage = (address: Address, key: Uint8Array) => Promise +/** + * Helper class to cache original storage values (so values already being present in + * the pre-state of a call), mainly for correct gas cost calculation in EVM/VM. + * + * TODO: Usage of this class is very implicit through the injected `getContractStorage()` + * method bound to the calling state manager. It should be examined if there are alternative + * designs being more transparent and direct along the next breaking release round. + * + */ export class OriginalStorageCache { private map: Map> private getContractStorage: getContractStorage diff --git a/packages/statemanager/src/cache/storage.ts b/packages/statemanager/src/cache/storage.ts index 52ae969bc7..eb8d52bdd9 100644 --- a/packages/statemanager/src/cache/storage.ts +++ b/packages/statemanager/src/cache/storage.ts @@ -8,7 +8,6 @@ import { CacheType } from './types.js' import type { CacheOpts } from './types.js' import type { Address } from '@ethereumjs/util' -const { debug: createDebugLogger } = debugDefault /** * key -> storage mapping @@ -47,7 +46,7 @@ export class StorageCache extends Cache { this._diffCache.push(new Map()) if (this.DEBUG) { - this._debug = createDebugLogger('statemanager:cache:storage') + this._debug = debugDefault('statemanager:cache:storage') } } diff --git a/packages/statemanager/src/index.ts b/packages/statemanager/src/index.ts index 099da76970..d6d3d9c56a 100644 --- a/packages/statemanager/src/index.ts +++ b/packages/statemanager/src/index.ts @@ -1,5 +1,6 @@ export * from './accessWitness.js' export * from './cache/index.js' export * from './rpcStateManager.js' +export * from './simpleStateManager.js' export * from './statelessVerkleStateManager.js' export * from './stateManager.js' diff --git a/packages/statemanager/src/rpcStateManager.ts b/packages/statemanager/src/rpcStateManager.ts index 500b92a471..2935088ced 100644 --- a/packages/statemanager/src/rpcStateManager.ts +++ b/packages/statemanager/src/rpcStateManager.ts @@ -25,7 +25,6 @@ import type { } from '@ethereumjs/common' import type { Address, PrefixedHexString } from '@ethereumjs/util' import type { Debugger } from 'debug' -const { debug: createDebugLogger } = debugDefault export interface RPCStateManagerOpts { provider: string @@ -57,7 +56,7 @@ export class RPCStateManager implements EVMStateManagerInterface { this.DEBUG = typeof window === 'undefined' ? process?.env?.DEBUG?.includes('ethjs') ?? false : false - this._debug = createDebugLogger('statemanager:rpcStateManager') + this._debug = debugDefault('statemanager:rpcStateManager') if (typeof opts.provider === 'string' && opts.provider.startsWith('http')) { this._provider = opts.provider } else { diff --git a/packages/statemanager/src/simpleStateManager.ts b/packages/statemanager/src/simpleStateManager.ts new file mode 100644 index 0000000000..f9976f3f71 --- /dev/null +++ b/packages/statemanager/src/simpleStateManager.ts @@ -0,0 +1,198 @@ +import { Account, bytesToHex } from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak.js' + +import { OriginalStorageCache } from './cache/originalStorageCache.js' + +import type { + AccountFields, + Common, + EVMStateManagerInterface, + Proof, + StorageDump, + StorageRange, +} from '@ethereumjs/common' +import type { Address, PrefixedHexString } from '@ethereumjs/util' + +/** + * Options for constructing a {@link SimpleStateManager}. + */ +export interface SimpleStateManagerOpts { + /** + * The common to use + */ + common?: Common +} + +/** + * Simple and dependency-free state manager for basic state access use cases + * where a merkle-patricia or verkle tree backed state manager is too heavy-weight. + * + * This state manager comes with the basic state access logic for + * accounts, storage and code (put* and get* methods) as well as a simple + * implementation of checkpointing but lacks methods implementations of + * state root related logic as well as some other non-core functions. + * + * Functionality provided is sufficient to be used for simple EVM use + * cases and the state manager is used as default there. + * + * For a more full fledged and MPT-backed state manager implementation + * have a look at the `@ethereumjs/statemanager` package. + */ +export class SimpleStateManager implements EVMStateManagerInterface { + public accountStack: Map[] = [] + public codeStack: Map[] = [] + public storageStack: Map[] = [] + + originalStorageCache: { + get(address: Address, key: Uint8Array): Promise + clear(): void + } + + public readonly common?: Common + + constructor(opts: SimpleStateManagerOpts = {}) { + this.checkpointSync() + this.originalStorageCache = new OriginalStorageCache(this.getContractStorage.bind(this)) + this.common = opts.common + } + + protected topAccountStack() { + return this.accountStack[this.accountStack.length - 1] + } + protected topCodeStack() { + return this.codeStack[this.codeStack.length - 1] + } + protected topStorageStack() { + return this.storageStack[this.storageStack.length - 1] + } + + // Synchronous version of checkpoint() to allow to call from constructor + protected checkpointSync() { + const newTopA = new Map(this.topAccountStack()) + for (const [address, account] of newTopA) { + const accountCopy = + account !== undefined + ? Object.assign(Object.create(Object.getPrototypeOf(account)), account) + : undefined + newTopA.set(address, accountCopy) + } + this.accountStack.push(newTopA) + this.codeStack.push(new Map(this.topCodeStack())) + this.storageStack.push(new Map(this.topStorageStack())) + } + + async getAccount(address: Address): Promise { + return this.topAccountStack().get(address.toString()) + } + + async putAccount(address: Address, account?: Account | undefined): Promise { + this.topAccountStack().set(address.toString(), account) + } + + async deleteAccount(address: Address): Promise { + this.topAccountStack().set(address.toString(), undefined) + } + + async modifyAccountFields(address: Address, accountFields: AccountFields): Promise { + let account = await this.getAccount(address) + if (!account) { + account = new Account() + } + account.nonce = accountFields.nonce ?? account.nonce + account.balance = accountFields.balance ?? account.balance + account.storageRoot = accountFields.storageRoot ?? account.storageRoot + account.codeHash = accountFields.codeHash ?? account.codeHash + await this.putAccount(address, account) + } + + async getContractCode(address: Address): Promise { + return this.topCodeStack().get(address.toString()) ?? new Uint8Array(0) + } + + async putContractCode(address: Address, value: Uint8Array): Promise { + this.topCodeStack().set(address.toString(), value) + if ((await this.getAccount(address)) === undefined) { + await this.putAccount(address, new Account()) + } + await this.modifyAccountFields(address, { + codeHash: (this.common?.customCrypto.keccak256 ?? keccak256)(value), + }) + } + + async getContractCodeSize(address: Address): Promise { + const contractCode = await this.getContractCode(address) + return contractCode.length + } + + async getContractStorage(address: Address, key: Uint8Array): Promise { + return ( + this.topStorageStack().get(`${address.toString()}_${bytesToHex(key)}`) ?? new Uint8Array(0) + ) + } + + async putContractStorage(address: Address, key: Uint8Array, value: Uint8Array): Promise { + this.topStorageStack().set(`${address.toString()}_${bytesToHex(key)}`, value) + } + + async checkpoint(): Promise { + this.checkpointSync() + } + async commit(): Promise { + this.accountStack.splice(-2, 1) + this.codeStack.splice(-2, 1) + this.storageStack.splice(-2, 1) + } + + async revert(): Promise { + this.accountStack.pop() + this.codeStack.pop() + this.storageStack.pop() + } + + async flush(): Promise {} + clearCaches(): void {} + + shallowCopy(): EVMStateManagerInterface { + const copy = new SimpleStateManager({ common: this.common }) + for (let i = 0; i < this.accountStack.length; i++) { + copy.accountStack.push(new Map(this.accountStack[i])) + copy.codeStack.push(new Map(this.codeStack[i])) + copy.storageStack.push(new Map(this.storageStack[i])) + } + return copy + } + + // State root functionality not implemented + getStateRoot(): Promise { + throw new Error('Method not implemented.') + } + setStateRoot(): Promise { + throw new Error('Method not implemented.') + } + hasStateRoot(): Promise { + throw new Error('Method not implemented.') + } + + // Only goes for long term create situations, skip + async clearContractStorage(): Promise {} + + // Only "core" methods implemented + checkChunkWitnessPresent?(): Promise { + throw new Error('Method not implemented.') + } + dumpStorage(): Promise { + throw new Error('Method not implemented.') + } + dumpStorageRange(): Promise { + throw new Error('Method not implemented.') + } + generateCanonicalGenesis(): Promise { + throw new Error('Method not implemented.') + } + getProof(): Promise { + throw new Error('Method not implemented.') + } + getAppliedKey?(): Uint8Array { + throw new Error('Method not implemented.') + } +} diff --git a/packages/statemanager/src/stateManager.ts b/packages/statemanager/src/stateManager.ts index 0f4292521f..24769b8320 100644 --- a/packages/statemanager/src/stateManager.ts +++ b/packages/statemanager/src/stateManager.ts @@ -25,14 +25,22 @@ import { import debugDefault from 'debug' import { keccak256 } from 'ethereum-cryptography/keccak.js' -import { AccountCache, CacheType, CodeCache, StorageCache } from './cache/index.js' -import { OriginalStorageCache } from './cache/originalStorageCache.js' - -import type { AccountFields, EVMStateManagerInterface, StorageDump } from '@ethereumjs/common' -import type { StorageRange } from '@ethereumjs/common/src' +import { + AccountCache, + CacheType, + CodeCache, + OriginalStorageCache, + StorageCache, +} from './cache/index.js' + +import type { + AccountFields, + EVMStateManagerInterface, + StorageDump, + StorageRange, +} from '@ethereumjs/common' import type { DB, PrefixedHexString } from '@ethereumjs/util' import type { Debugger } from 'debug' -const { debug: createDebugLogger } = debugDefault export type StorageProof = { key: PrefixedHexString @@ -152,6 +160,11 @@ export interface DefaultStateManagerOpts { * * The default state manager implementation uses a * `@ethereumjs/trie` trie as a data backend. + * + * Note that there is a `SimpleStateManager` dependency-free state + * manager implementation available shipped with the `@ethereumjs/statemanager` + * package which might be an alternative to this implementation + * for many basic use cases. */ export class DefaultStateManager implements EVMStateManagerInterface { protected _debug: Debugger @@ -195,7 +208,7 @@ export class DefaultStateManager implements EVMStateManagerInterface { this.DEBUG = typeof window === 'undefined' ? process?.env?.DEBUG?.includes('ethjs') ?? false : false - this._debug = createDebugLogger('statemanager:statemanager') + this._debug = debugDefault('statemanager:statemanager') this.common = opts.common ?? new Common({ chain: Chain.Mainnet }) diff --git a/packages/statemanager/src/statelessVerkleStateManager.ts b/packages/statemanager/src/statelessVerkleStateManager.ts index ba10d55bdf..7c2eaebf5f 100644 --- a/packages/statemanager/src/statelessVerkleStateManager.ts +++ b/packages/statemanager/src/statelessVerkleStateManager.ts @@ -23,8 +23,13 @@ import debugDefault from 'debug' import { keccak256 } from 'ethereum-cryptography/keccak.js' import { AccessWitness, AccessedStateType, decodeValue } from './accessWitness.js' -import { AccountCache, CacheType, CodeCache, StorageCache } from './cache/index.js' -import { OriginalStorageCache } from './cache/originalStorageCache.js' +import { + AccountCache, + CacheType, + CodeCache, + OriginalStorageCache, + StorageCache, +} from './cache/index.js' import type { AccessedStateWithAddress } from './accessWitness.js' import type { DefaultStateManager } from './stateManager.js' @@ -44,9 +49,7 @@ import type { VerkleProof, } from '@ethereumjs/util' -const { debug: createDebugLogger } = debugDefault - -const debug = createDebugLogger('statemanager:verkle') +const debug = debugDefault('statemanager:verkle') export interface VerkleState { [key: PrefixedHexString]: PrefixedHexString | null diff --git a/packages/statemanager/test/checkpointing.account.spec.ts b/packages/statemanager/test/checkpointing.account.spec.ts index 808466a3a2..95074d2d59 100644 --- a/packages/statemanager/test/checkpointing.account.spec.ts +++ b/packages/statemanager/test/checkpointing.account.spec.ts @@ -1,13 +1,15 @@ import { Account, Address, hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { DefaultStateManager } from '../src/index.js' +import { DefaultStateManager, SimpleStateManager } from '../src/index.js' + +import type { StateManagerInterface } from '@ethereumjs/common' /** * Compares account read to none or undefined */ const accountEval = async ( - sm: DefaultStateManager, + sm: StateManagerInterface, address: Address, compare: bigint | undefined ) => { @@ -28,6 +30,8 @@ type CompareList = [Account | undefined, bigint | undefined] describe('StateManager -> Account Checkpointing', () => { const address = new Address(hexToBytes(`0x${'11'.repeat(20)}`)) + const stateManagers = [DefaultStateManager, SimpleStateManager] + const accountN1: CompareList = [ Account.fromAccountData({ nonce: 1, @@ -112,245 +116,247 @@ describe('StateManager -> Account Checkpointing', () => { }, ] - for (const as of accountSets) { - it(`No CP -> A1 -> Flush() (-> A1)`, async () => { - const sm = new DefaultStateManager() - - await sm.putAccount(address, as.a1[0]) - await sm.flush() - assert.ok(await accountEval(sm, address, as.a1[1])) - - sm.clearCaches() - assert.ok(await accountEval(sm, address, as.a1[1])) - }) - - it(`CP -> A1.1 -> Commit -> Flush() (-> A1.1)`, async () => { - const sm = new DefaultStateManager() - - await sm.checkpoint() - await sm.putAccount(address, as.a1[0]) - await sm.commit() - await sm.flush() - assert.ok(await accountEval(sm, address, as.a1[1])) - - sm.clearCaches() - assert.ok(await accountEval(sm, address, as.a1[1])) - }) - - it(`CP -> A1.1 -> Revert -> Flush() (-> Undefined)`, async () => { - const sm = new DefaultStateManager() - - await sm.checkpoint() - await sm.putAccount(address, as.a1[0]) - await sm.revert() - await sm.flush() - assert.ok(await accountEval(sm, address, undefined)) - - sm.clearCaches() - assert.ok(await accountEval(sm, address, undefined)) - }) - - it(`A1.1 -> CP -> Commit -> Flush() (-> A1.1)`, async () => { - const sm = new DefaultStateManager() - - await sm.putAccount(address, as.a1[0]) - await sm.checkpoint() - await sm.commit() - await sm.flush() - assert.ok(await accountEval(sm, address, as.a1[1])) - - sm.clearCaches() - assert.ok(await accountEval(sm, address, as.a1[1])) - }) - - it(`A1.1 -> CP -> Revert -> Flush() (-> A1.1)`, async () => { - const sm = new DefaultStateManager() - - await sm.putAccount(address, as.a1[0]) - await sm.checkpoint() - await sm.revert() - await sm.flush() - assert.ok(await accountEval(sm, address, as.a1[1])) - - sm.clearCaches() - assert.ok(await accountEval(sm, address, as.a1[1])) - }) - - it(`A1.1 -> CP -> A1.2 -> Commit -> Flush() (-> A1.2)`, async () => { - const sm = new DefaultStateManager() - - await sm.putAccount(address, as.a1[0]) - await sm.checkpoint() - await sm.putAccount(address, as.a2[0]) - await sm.commit() - await sm.flush() - assert.ok(await accountEval(sm, address, as.a2[1])) - - sm.clearCaches() - assert.ok(await accountEval(sm, address, as.a2[1])) - }) - - it(`A1.1 -> CP -> A1.2 -> Commit -> A1.3 -> Flush() (-> A1.3)`, async () => { - const sm = new DefaultStateManager() - - await sm.putAccount(address, as.a1[0]) - await sm.checkpoint() - await sm.putAccount(address, as.a2[0]) - await sm.commit() - await sm.putAccount(address, as.a3[0]) - await sm.flush() - assert.ok(await accountEval(sm, address, as.a3[1])) - - sm.clearCaches() - assert.ok(await accountEval(sm, address, as.a3[1])) - }) - - it(`A1.1 -> CP -> A1.2 -> A1.3 -> Commit -> Flush() (-> A1.3)`, async () => { - const sm = new DefaultStateManager() - - await sm.putAccount(address, as.a1[0]) - await sm.checkpoint() - await sm.putAccount(address, as.a2[0]) - await sm.putAccount(address, as.a3[0]) - await sm.commit() - await sm.flush() - assert.ok(await accountEval(sm, address, as.a3[1])) - - sm.clearCaches() - assert.ok(await accountEval(sm, address, as.a3[1])) - }) - - it(`CP -> A1.1 -> A1.2 -> Commit -> Flush() (-> A1.2)`, async () => { - const sm = new DefaultStateManager() - - await sm.checkpoint() - await sm.putAccount(address, as.a1[0]) - await sm.putAccount(address, as.a2[0]) - await sm.commit() - await sm.flush() - assert.ok(await accountEval(sm, address, as.a2[1])) - - sm.clearCaches() - assert.ok(await accountEval(sm, address, as.a2[1])) - }) - - it(`CP -> A1.1 -> A1.2 -> Revert -> Flush() (-> Undefined)`, async () => { - const sm = new DefaultStateManager() - - await sm.checkpoint() - await sm.putAccount(address, as.a1[0]) - - await sm.putAccount(address, as.a2[0]) - await sm.revert() - await sm.flush() - assert.ok(await accountEval(sm, address, undefined)) - - sm.clearCaches() - assert.ok(await accountEval(sm, address, undefined)) - }) - - it(`A1.1 -> CP -> A1.2 -> Revert -> Flush() (-> A1.1)`, async () => { - const sm = new DefaultStateManager() - - await sm.putAccount(address, as.a1[0]) - await sm.checkpoint() - await sm.putAccount(address, as.a2[0]) - await sm.revert() - await sm.flush() - assert.ok(await accountEval(sm, address, as.a1[1])) - - sm.clearCaches() - assert.ok(await accountEval(sm, address, as.a1[1])) - }) - - it('A1.1 -> CP -> A1.2 -> CP -> A1.3 -> Commit -> Commit -> Flush() (-> A1.3)', async () => { - const sm = new DefaultStateManager() - - await sm.putAccount(address, as.a1[0]) - await sm.checkpoint() - await sm.putAccount(address, as.a2[0]) - await sm.checkpoint() - await sm.putAccount(address, as.a3[0]) - await sm.commit() - await sm.commit() - await sm.flush() - assert.ok(await accountEval(sm, address, as.a3[1])) - - sm.clearCaches() - assert.ok(await accountEval(sm, address, as.a3[1])) - }) - - it('A1.1 -> CP -> A1.2 -> CP -> A1.3 -> Commit -> Revert -> Flush() (-> A1.1)', async () => { - const sm = new DefaultStateManager() - - await sm.putAccount(address, as.a1[0]) - await sm.checkpoint() - await sm.putAccount(address, as.a2[0]) - await sm.checkpoint() - await sm.putAccount(address, as.a3[0]) - await sm.commit() - await sm.revert() - await sm.flush() - assert.ok(await accountEval(sm, address, as.a1[1])) - - sm.clearCaches() - assert.ok(await accountEval(sm, address, as.a1[1])) - }) - - it('A1.1 -> CP -> A1.2 -> CP -> A1.3 -> Revert -> Commit -> Flush() (-> A1.2)', async () => { - const sm = new DefaultStateManager() - - await sm.putAccount(address, as.a1[0]) - await sm.checkpoint() - await sm.putAccount(address, as.a2[0]) - await sm.checkpoint() - await sm.putAccount(address, as.a3[0]) - await sm.revert() - await sm.commit() - await sm.flush() - assert.ok(await accountEval(sm, address, as.a2[1])) - - sm.clearCaches() - assert.ok(await accountEval(sm, address, as.a2[1])) - }) - - it('A1.1 -> CP -> A1.2 -> CP -> A1.3 -> Revert -> A1.4 -> Commit -> Flush() (-> A1.4)', async () => { - const sm = new DefaultStateManager() - - await sm.putAccount(address, as.a1[0]) - await sm.checkpoint() - await sm.putAccount(address, as.a2[0]) - await sm.checkpoint() - await sm.putAccount(address, as.a3[0]) - await sm.revert() - await sm.putAccount(address, as.a4[0]) - await sm.commit() - await sm.flush() - assert.ok(await accountEval(sm, address, as.a4[1])) - - sm.clearCaches() - assert.ok(await accountEval(sm, address, as.a4[1])) - }) - - it('A1.1 -> CP -> A1.2 -> CP -> A1.3 -> Revert -> A1.4 -> CP -> A1.5 -> Commit -> Commit -> Flush() (-> A1.5)', async () => { - const sm = new DefaultStateManager() - - await sm.putAccount(address, as.a1[0]) - await sm.checkpoint() - await sm.putAccount(address, as.a2[0]) - await sm.checkpoint() - await sm.putAccount(address, as.a3[0]) - await sm.revert() - await sm.putAccount(address, as.a4[0]) - await sm.checkpoint() - await sm.putAccount(address, as.a5[0]) - await sm.commit() - await sm.commit() - await sm.flush() - assert.ok(await accountEval(sm, address, as.a5[1])) - - sm.clearCaches() - assert.ok(await accountEval(sm, address, as.a5[1])) - }) + for (const SM of stateManagers) { + for (const as of accountSets) { + it(`No CP -> A1 -> Flush() (-> A1)`, async () => { + const sm = new SM() + + await sm.putAccount(address, as.a1[0]) + await sm.flush() + assert.ok(await accountEval(sm, address, as.a1[1])) + + sm.clearCaches() + assert.ok(await accountEval(sm, address, as.a1[1])) + }) + + it(`CP -> A1.1 -> Commit -> Flush() (-> A1.1)`, async () => { + const sm = new SM() + + await sm.checkpoint() + await sm.putAccount(address, as.a1[0]) + await sm.commit() + await sm.flush() + assert.ok(await accountEval(sm, address, as.a1[1])) + + sm.clearCaches() + assert.ok(await accountEval(sm, address, as.a1[1])) + }) + + it(`CP -> A1.1 -> Revert -> Flush() (-> Undefined)`, async () => { + const sm = new SM() + + await sm.checkpoint() + await sm.putAccount(address, as.a1[0]) + await sm.revert() + await sm.flush() + assert.ok(await accountEval(sm, address, undefined)) + + sm.clearCaches() + assert.ok(await accountEval(sm, address, undefined)) + }) + + it(`A1.1 -> CP -> Commit -> Flush() (-> A1.1)`, async () => { + const sm = new SM() + + await sm.putAccount(address, as.a1[0]) + await sm.checkpoint() + await sm.commit() + await sm.flush() + assert.ok(await accountEval(sm, address, as.a1[1])) + + sm.clearCaches() + assert.ok(await accountEval(sm, address, as.a1[1])) + }) + + it(`A1.1 -> CP -> Revert -> Flush() (-> A1.1)`, async () => { + const sm = new SM() + + await sm.putAccount(address, as.a1[0]) + await sm.checkpoint() + await sm.revert() + await sm.flush() + assert.ok(await accountEval(sm, address, as.a1[1])) + + sm.clearCaches() + assert.ok(await accountEval(sm, address, as.a1[1])) + }) + + it(`A1.1 -> CP -> A1.2 -> Commit -> Flush() (-> A1.2)`, async () => { + const sm = new SM() + + await sm.putAccount(address, as.a1[0]) + await sm.checkpoint() + await sm.putAccount(address, as.a2[0]) + await sm.commit() + await sm.flush() + assert.ok(await accountEval(sm, address, as.a2[1])) + + sm.clearCaches() + assert.ok(await accountEval(sm, address, as.a2[1])) + }) + + it(`A1.1 -> CP -> A1.2 -> Commit -> A1.3 -> Flush() (-> A1.3)`, async () => { + const sm = new SM() + + await sm.putAccount(address, as.a1[0]) + await sm.checkpoint() + await sm.putAccount(address, as.a2[0]) + await sm.commit() + await sm.putAccount(address, as.a3[0]) + await sm.flush() + assert.ok(await accountEval(sm, address, as.a3[1])) + + sm.clearCaches() + assert.ok(await accountEval(sm, address, as.a3[1])) + }) + + it(`A1.1 -> CP -> A1.2 -> A1.3 -> Commit -> Flush() (-> A1.3)`, async () => { + const sm = new SM() + + await sm.putAccount(address, as.a1[0]) + await sm.checkpoint() + await sm.putAccount(address, as.a2[0]) + await sm.putAccount(address, as.a3[0]) + await sm.commit() + await sm.flush() + assert.ok(await accountEval(sm, address, as.a3[1])) + + sm.clearCaches() + assert.ok(await accountEval(sm, address, as.a3[1])) + }) + + it(`CP -> A1.1 -> A1.2 -> Commit -> Flush() (-> A1.2)`, async () => { + const sm = new SM() + + await sm.checkpoint() + await sm.putAccount(address, as.a1[0]) + await sm.putAccount(address, as.a2[0]) + await sm.commit() + await sm.flush() + assert.ok(await accountEval(sm, address, as.a2[1])) + + sm.clearCaches() + assert.ok(await accountEval(sm, address, as.a2[1])) + }) + + it(`CP -> A1.1 -> A1.2 -> Revert -> Flush() (-> Undefined)`, async () => { + const sm = new SM() + + await sm.checkpoint() + await sm.putAccount(address, as.a1[0]) + + await sm.putAccount(address, as.a2[0]) + await sm.revert() + await sm.flush() + assert.ok(await accountEval(sm, address, undefined)) + + sm.clearCaches() + assert.ok(await accountEval(sm, address, undefined)) + }) + + it(`A1.1 -> CP -> A1.2 -> Revert -> Flush() (-> A1.1)`, async () => { + const sm = new SM() + + await sm.putAccount(address, as.a1[0]) + await sm.checkpoint() + await sm.putAccount(address, as.a2[0]) + await sm.revert() + await sm.flush() + assert.ok(await accountEval(sm, address, as.a1[1])) + + sm.clearCaches() + assert.ok(await accountEval(sm, address, as.a1[1])) + }) + + it('A1.1 -> CP -> A1.2 -> CP -> A1.3 -> Commit -> Commit -> Flush() (-> A1.3)', async () => { + const sm = new SM() + + await sm.putAccount(address, as.a1[0]) + await sm.checkpoint() + await sm.putAccount(address, as.a2[0]) + await sm.checkpoint() + await sm.putAccount(address, as.a3[0]) + await sm.commit() + await sm.commit() + await sm.flush() + assert.ok(await accountEval(sm, address, as.a3[1])) + + sm.clearCaches() + assert.ok(await accountEval(sm, address, as.a3[1])) + }) + + it('A1.1 -> CP -> A1.2 -> CP -> A1.3 -> Commit -> Revert -> Flush() (-> A1.1)', async () => { + const sm = new SM() + + await sm.putAccount(address, as.a1[0]) + await sm.checkpoint() + await sm.putAccount(address, as.a2[0]) + await sm.checkpoint() + await sm.putAccount(address, as.a3[0]) + await sm.commit() + await sm.revert() + await sm.flush() + assert.ok(await accountEval(sm, address, as.a1[1])) + + sm.clearCaches() + assert.ok(await accountEval(sm, address, as.a1[1])) + }) + + it('A1.1 -> CP -> A1.2 -> CP -> A1.3 -> Revert -> Commit -> Flush() (-> A1.2)', async () => { + const sm = new SM() + + await sm.putAccount(address, as.a1[0]) + await sm.checkpoint() + await sm.putAccount(address, as.a2[0]) + await sm.checkpoint() + await sm.putAccount(address, as.a3[0]) + await sm.revert() + await sm.commit() + await sm.flush() + assert.ok(await accountEval(sm, address, as.a2[1])) + + sm.clearCaches() + assert.ok(await accountEval(sm, address, as.a2[1])) + }) + + it('A1.1 -> CP -> A1.2 -> CP -> A1.3 -> Revert -> A1.4 -> Commit -> Flush() (-> A1.4)', async () => { + const sm = new SM() + + await sm.putAccount(address, as.a1[0]) + await sm.checkpoint() + await sm.putAccount(address, as.a2[0]) + await sm.checkpoint() + await sm.putAccount(address, as.a3[0]) + await sm.revert() + await sm.putAccount(address, as.a4[0]) + await sm.commit() + await sm.flush() + assert.ok(await accountEval(sm, address, as.a4[1])) + + sm.clearCaches() + assert.ok(await accountEval(sm, address, as.a4[1])) + }) + + it('A1.1 -> CP -> A1.2 -> CP -> A1.3 -> Revert -> A1.4 -> CP -> A1.5 -> Commit -> Commit -> Flush() (-> A1.5)', async () => { + const sm = new SM() + + await sm.putAccount(address, as.a1[0]) + await sm.checkpoint() + await sm.putAccount(address, as.a2[0]) + await sm.checkpoint() + await sm.putAccount(address, as.a3[0]) + await sm.revert() + await sm.putAccount(address, as.a4[0]) + await sm.checkpoint() + await sm.putAccount(address, as.a5[0]) + await sm.commit() + await sm.commit() + await sm.flush() + assert.ok(await accountEval(sm, address, as.a5[1])) + + sm.clearCaches() + assert.ok(await accountEval(sm, address, as.a5[1])) + }) + } } }) diff --git a/packages/statemanager/test/checkpointing.code.spec.ts b/packages/statemanager/test/checkpointing.code.spec.ts index dd452dadf6..673451c1c3 100644 --- a/packages/statemanager/test/checkpointing.code.spec.ts +++ b/packages/statemanager/test/checkpointing.code.spec.ts @@ -1,10 +1,11 @@ +import { type StateManagerInterface } from '@ethereumjs/common' import { Account, Address, hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { DefaultStateManager } from '../src/index.js' +import { DefaultStateManager, SimpleStateManager } from '../src/index.js' const codeEval = async ( - sm: DefaultStateManager, + sm: StateManagerInterface, address: Address, value: Uint8Array, root: Uint8Array @@ -16,7 +17,8 @@ const codeEval = async ( describe('StateManager -> Code Checkpointing', () => { const address = new Address(hexToBytes(`0x${'11'.repeat(20)}`)) - const account = new Account() + + const stateManagers = [DefaultStateManager, SimpleStateManager] const value = hexToBytes('0x01') const root = hexToBytes('0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2') @@ -88,265 +90,267 @@ describe('StateManager -> Code Checkpointing', () => { }, ] - for (const c of codeSets) { - it(`No CP -> C1 -> Flush() (-> C1)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractCode(address, c.c1.value) - - await sm.flush() - await codeEval(sm, address, c.c1.value, c.c1.root) - - sm.clearCaches() - assert.deepEqual(await sm.getContractCode(address), c.c1.value) - await codeEval(sm, address, c.c1.value, c.c1.root) - }) - - it(`CP -> C1.1 -> Commit -> Flush() (-> C1.1)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.checkpoint() - await sm.putContractCode(address, c.c1.value) - await sm.commit() - await sm.flush() - await codeEval(sm, address, c.c1.value, c.c1.root) - - sm.clearCaches() - await codeEval(sm, address, c.c1.value, c.c1.root) - }) - - it(`CP -> C1.1 -> Revert -> Flush() (-> Undefined)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.checkpoint() - await sm.putContractCode(address, c.c1.value) - - await sm.revert() - await sm.flush() - await codeEval(sm, address, valueEmpty, rootEmpty) - - sm.clearCaches() - - await codeEval(sm, address, valueEmpty, rootEmpty) - }) - - it(`C1.1 -> CP -> Commit -> Flush() (-> C1.1)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractCode(address, c.c1.value) - await sm.checkpoint() - await sm.commit() - await sm.flush() - await codeEval(sm, address, c.c1.value, c.c1.root) - - sm.clearCaches() - await codeEval(sm, address, c.c1.value, c.c1.root) - }) - - it(`C1.1 -> CP -> Revert -> Flush() (-> C1.1)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractCode(address, c.c1.value) - await sm.checkpoint() - await sm.revert() - await sm.flush() - await codeEval(sm, address, c.c1.value, c.c1.root) - - sm.clearCaches() - await codeEval(sm, address, c.c1.value, c.c1.root) - }) - - it(`C1.1 -> CP -> C1.2 -> Commit -> Flush() (-> C1.2)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractCode(address, c.c1.value) - await sm.checkpoint() - await sm.putContractCode(address, c.c2.value) - await sm.commit() - await sm.flush() - await codeEval(sm, address, c.c2.value, c.c2.root) - - sm.clearCaches() - await codeEval(sm, address, c.c2.value, c.c2.root) - }) - - it(`C1.1 -> CP -> C1.2 -> Commit -> C1.3 -> Flush() (-> C1.3)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractCode(address, c.c1.value) - await sm.checkpoint() - await sm.putContractCode(address, c.c2.value) - await sm.commit() - await sm.putContractCode(address, c.c3.value) - await sm.flush() - await codeEval(sm, address, c.c3.value, c.c3.root) - - sm.clearCaches() - await codeEval(sm, address, c.c3.value, c.c3.root) - }) - - it(`C1.1 -> CP -> C1.2 -> C1.3 -> Commit -> Flush() (-> C1.3)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractCode(address, c.c1.value) - await sm.checkpoint() - await sm.putContractCode(address, c.c2.value) - await sm.putContractCode(address, c.c3.value) - await sm.commit() - await sm.flush() - await codeEval(sm, address, c.c3.value, c.c3.root) - - sm.clearCaches() - await codeEval(sm, address, c.c3.value, c.c3.root) - }) - - it(`CP -> C1.1 -> C1.2 -> Commit -> Flush() (-> C1.2)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.checkpoint() - await sm.putContractCode(address, c.c1.value) - await sm.putContractCode(address, c.c2.value) - await sm.commit() - await sm.flush() - await codeEval(sm, address, c.c2.value, c.c2.root) - - sm.clearCaches() - await codeEval(sm, address, c.c2.value, c.c2.root) - }) - - it(`CP -> C1.1 -> C1.2 -> Revert -> Flush() (-> Undefined)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.checkpoint() - await sm.putContractCode(address, c.c1.value) - - await sm.putContractCode(address, c.c2.value) - await sm.revert() - await sm.flush() - await codeEval(sm, address, valueEmpty, rootEmpty) - - sm.clearCaches() - await codeEval(sm, address, valueEmpty, rootEmpty) - }) - - it(`C1.1 -> CP -> C1.2 -> Revert -> Flush() (-> C1.1)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractCode(address, c.c1.value) - await sm.checkpoint() - await sm.putContractCode(address, c.c2.value) - await sm.revert() - await sm.flush() - await codeEval(sm, address, c.c1.value, c.c1.root) - - sm.clearCaches() - await codeEval(sm, address, c.c1.value, c.c1.root) - }) - - it('C1.1 -> CP -> C1.2 -> CP -> C1.3 -> Commit -> Commit -> Flush() (-> C1.3)', async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractCode(address, c.c1.value) - await sm.checkpoint() - await sm.putContractCode(address, c.c2.value) - await sm.checkpoint() - await sm.putContractCode(address, c.c3.value) - await sm.commit() - await sm.commit() - await sm.flush() - await codeEval(sm, address, c.c3.value, c.c3.root) - - sm.clearCaches() - await codeEval(sm, address, c.c3.value, c.c3.root) - }) - - it('C1.1 -> CP -> C1.2 -> CP -> C1.3 -> Commit -> Revert -> Flush() (-> C1.1)', async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractCode(address, c.c1.value) - await sm.checkpoint() - await sm.putContractCode(address, c.c2.value) - await sm.checkpoint() - await sm.putContractCode(address, c.c3.value) - await sm.commit() - await sm.revert() - await sm.flush() - await codeEval(sm, address, c.c1.value, c.c1.root) - - sm.clearCaches() - await codeEval(sm, address, c.c1.value, c.c1.root) - }) - - it('C1.1 -> CP -> C1.2 -> CP -> C1.3 -> Revert -> Commit -> Flush() (-> C1.2)', async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractCode(address, c.c1.value) - await sm.checkpoint() - await sm.putContractCode(address, c.c2.value) - await sm.checkpoint() - await sm.putContractCode(address, c.c3.value) - await sm.revert() - await sm.commit() - await sm.flush() - await codeEval(sm, address, c.c2.value, c.c2.root) - - sm.clearCaches() - await codeEval(sm, address, c.c2.value, c.c2.root) - }) - - it('C1.1 -> CP -> C1.2 -> CP -> C1.3 -> Revert -> C1.4 -> Commit -> Flush() (-> C1.4)', async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractCode(address, c.c1.value) - await sm.checkpoint() - await sm.putContractCode(address, c.c2.value) - await sm.checkpoint() - await sm.putContractCode(address, c.c3.value) - await sm.revert() - await sm.putContractCode(address, c.c4.value) - await sm.commit() - await sm.flush() - await codeEval(sm, address, c.c4.value, c.c4.root) - - sm.clearCaches() - await codeEval(sm, address, c.c4.value, c.c4.root) - }) - - it('C1.1 -> CP -> C1.2 -> CP -> C1.3 -> Revert -> C1.4 -> CP -> C1.5 -> Commit -> Commit -> Flush() (-> C1.5)', async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractCode(address, c.c1.value) - await sm.checkpoint() - await sm.putContractCode(address, c.c2.value) - await sm.checkpoint() - await sm.putContractCode(address, c.c3.value) - await sm.revert() - await sm.putContractCode(address, c.c4.value) - await sm.checkpoint() - await sm.putContractCode(address, c.c5.value) - await sm.commit() - await sm.commit() - await sm.flush() - await codeEval(sm, address, c.c5.value, c.c5.root) - - sm.clearCaches() - await codeEval(sm, address, c.c5.value, c.c5.root) - }) + for (const SM of stateManagers) { + for (const c of codeSets) { + it(`No CP -> C1 -> Flush() (-> C1)`, async () => { + const sm = new SM() + await sm.putAccount(address, new Account()) + + await sm.putContractCode(address, c.c1.value) + + await sm.flush() + await codeEval(sm, address, c.c1.value, c.c1.root) + + sm.clearCaches() + assert.deepEqual(await sm.getContractCode(address), c.c1.value) + await codeEval(sm, address, c.c1.value, c.c1.root) + }) + + it(`CP -> C1.1 -> Commit -> Flush() (-> C1.1)`, async () => { + const sm = new SM() + await sm.putAccount(address, new Account()) + + await sm.checkpoint() + await sm.putContractCode(address, c.c1.value) + await sm.commit() + await sm.flush() + await codeEval(sm, address, c.c1.value, c.c1.root) + + sm.clearCaches() + await codeEval(sm, address, c.c1.value, c.c1.root) + }) + + it(`CP -> C1.1 -> Revert -> Flush() (-> Undefined)`, async () => { + const sm = new SM() + await sm.putAccount(address, new Account()) + + await sm.checkpoint() + await sm.putContractCode(address, c.c1.value) + + await sm.revert() + await sm.flush() + await codeEval(sm, address, valueEmpty, rootEmpty) + + sm.clearCaches() + + await codeEval(sm, address, valueEmpty, rootEmpty) + }) + + it(`C1.1 -> CP -> Commit -> Flush() (-> C1.1)`, async () => { + const sm = new SM() + await sm.putAccount(address, new Account()) + + await sm.putContractCode(address, c.c1.value) + await sm.checkpoint() + await sm.commit() + await sm.flush() + await codeEval(sm, address, c.c1.value, c.c1.root) + + sm.clearCaches() + await codeEval(sm, address, c.c1.value, c.c1.root) + }) + + it(`C1.1 -> CP -> Revert -> Flush() (-> C1.1)`, async () => { + const sm = new SM() + await sm.putAccount(address, new Account()) + + await sm.putContractCode(address, c.c1.value) + await sm.checkpoint() + await sm.revert() + await sm.flush() + await codeEval(sm, address, c.c1.value, c.c1.root) + + sm.clearCaches() + await codeEval(sm, address, c.c1.value, c.c1.root) + }) + + it(`C1.1 -> CP -> C1.2 -> Commit -> Flush() (-> C1.2)`, async () => { + const sm = new SM() + await sm.putAccount(address, new Account()) + + await sm.putContractCode(address, c.c1.value) + await sm.checkpoint() + await sm.putContractCode(address, c.c2.value) + await sm.commit() + await sm.flush() + await codeEval(sm, address, c.c2.value, c.c2.root) + + sm.clearCaches() + await codeEval(sm, address, c.c2.value, c.c2.root) + }) + + it(`C1.1 -> CP -> C1.2 -> Commit -> C1.3 -> Flush() (-> C1.3)`, async () => { + const sm = new SM() + await sm.putAccount(address, new Account()) + + await sm.putContractCode(address, c.c1.value) + await sm.checkpoint() + await sm.putContractCode(address, c.c2.value) + await sm.commit() + await sm.putContractCode(address, c.c3.value) + await sm.flush() + await codeEval(sm, address, c.c3.value, c.c3.root) + + sm.clearCaches() + await codeEval(sm, address, c.c3.value, c.c3.root) + }) + + it(`C1.1 -> CP -> C1.2 -> C1.3 -> Commit -> Flush() (-> C1.3)`, async () => { + const sm = new SM() + await sm.putAccount(address, new Account()) + + await sm.putContractCode(address, c.c1.value) + await sm.checkpoint() + await sm.putContractCode(address, c.c2.value) + await sm.putContractCode(address, c.c3.value) + await sm.commit() + await sm.flush() + await codeEval(sm, address, c.c3.value, c.c3.root) + + sm.clearCaches() + await codeEval(sm, address, c.c3.value, c.c3.root) + }) + + it(`CP -> C1.1 -> C1.2 -> Commit -> Flush() (-> C1.2)`, async () => { + const sm = new SM() + await sm.putAccount(address, new Account()) + + await sm.checkpoint() + await sm.putContractCode(address, c.c1.value) + await sm.putContractCode(address, c.c2.value) + await sm.commit() + await sm.flush() + await codeEval(sm, address, c.c2.value, c.c2.root) + + sm.clearCaches() + await codeEval(sm, address, c.c2.value, c.c2.root) + }) + + it(`CP -> C1.1 -> C1.2 -> Revert -> Flush() (-> Undefined)`, async () => { + const sm = new SM() + await sm.putAccount(address, new Account()) + + await sm.checkpoint() + await sm.putContractCode(address, c.c1.value) + + await sm.putContractCode(address, c.c2.value) + await sm.revert() + await sm.flush() + await codeEval(sm, address, valueEmpty, rootEmpty) + + sm.clearCaches() + await codeEval(sm, address, valueEmpty, rootEmpty) + }) + + it(`C1.1 -> CP -> C1.2 -> Revert -> Flush() (-> C1.1)`, async () => { + const sm = new SM() + await sm.putAccount(address, new Account()) + + await sm.putContractCode(address, c.c1.value) + await sm.checkpoint() + await sm.putContractCode(address, c.c2.value) + await sm.revert() + await sm.flush() + await codeEval(sm, address, c.c1.value, c.c1.root) + + sm.clearCaches() + await codeEval(sm, address, c.c1.value, c.c1.root) + }) + + it('C1.1 -> CP -> C1.2 -> CP -> C1.3 -> Commit -> Commit -> Flush() (-> C1.3)', async () => { + const sm = new SM() + await sm.putAccount(address, new Account()) + + await sm.putContractCode(address, c.c1.value) + await sm.checkpoint() + await sm.putContractCode(address, c.c2.value) + await sm.checkpoint() + await sm.putContractCode(address, c.c3.value) + await sm.commit() + await sm.commit() + await sm.flush() + await codeEval(sm, address, c.c3.value, c.c3.root) + + sm.clearCaches() + await codeEval(sm, address, c.c3.value, c.c3.root) + }) + + it('C1.1 -> CP -> C1.2 -> CP -> C1.3 -> Commit -> Revert -> Flush() (-> C1.1)', async () => { + const sm = new SM() + await sm.putAccount(address, new Account()) + + await sm.putContractCode(address, c.c1.value) + await sm.checkpoint() + await sm.putContractCode(address, c.c2.value) + await sm.checkpoint() + await sm.putContractCode(address, c.c3.value) + await sm.commit() + await sm.revert() + await sm.flush() + await codeEval(sm, address, c.c1.value, c.c1.root) + + sm.clearCaches() + await codeEval(sm, address, c.c1.value, c.c1.root) + }) + + it('C1.1 -> CP -> C1.2 -> CP -> C1.3 -> Revert -> Commit -> Flush() (-> C1.2)', async () => { + const sm = new SM() + await sm.putAccount(address, new Account()) + + await sm.putContractCode(address, c.c1.value) + await sm.checkpoint() + await sm.putContractCode(address, c.c2.value) + await sm.checkpoint() + await sm.putContractCode(address, c.c3.value) + await sm.revert() + await sm.commit() + await sm.flush() + await codeEval(sm, address, c.c2.value, c.c2.root) + + sm.clearCaches() + await codeEval(sm, address, c.c2.value, c.c2.root) + }) + + it('C1.1 -> CP -> C1.2 -> CP -> C1.3 -> Revert -> C1.4 -> Commit -> Flush() (-> C1.4)', async () => { + const sm = new SM() + await sm.putAccount(address, new Account()) + + await sm.putContractCode(address, c.c1.value) + await sm.checkpoint() + await sm.putContractCode(address, c.c2.value) + await sm.checkpoint() + await sm.putContractCode(address, c.c3.value) + await sm.revert() + await sm.putContractCode(address, c.c4.value) + await sm.commit() + await sm.flush() + await codeEval(sm, address, c.c4.value, c.c4.root) + + sm.clearCaches() + await codeEval(sm, address, c.c4.value, c.c4.root) + }) + + it('C1.1 -> CP -> C1.2 -> CP -> C1.3 -> Revert -> C1.4 -> CP -> C1.5 -> Commit -> Commit -> Flush() (-> C1.5)', async () => { + const sm = new SM() + await sm.putAccount(address, new Account()) + + await sm.putContractCode(address, c.c1.value) + await sm.checkpoint() + await sm.putContractCode(address, c.c2.value) + await sm.checkpoint() + await sm.putContractCode(address, c.c3.value) + await sm.revert() + await sm.putContractCode(address, c.c4.value) + await sm.checkpoint() + await sm.putContractCode(address, c.c5.value) + await sm.commit() + await sm.commit() + await sm.flush() + await codeEval(sm, address, c.c5.value, c.c5.root) + + sm.clearCaches() + await codeEval(sm, address, c.c5.value, c.c5.root) + }) + } } }) diff --git a/packages/statemanager/test/checkpointing.storage.spec.ts b/packages/statemanager/test/checkpointing.storage.spec.ts index ca9afe8d35..0e6d5a2a1e 100644 --- a/packages/statemanager/test/checkpointing.storage.spec.ts +++ b/packages/statemanager/test/checkpointing.storage.spec.ts @@ -1,27 +1,42 @@ import { Account, Address, hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { DefaultStateManager } from '../src/index.js' +import { DefaultStateManager, SimpleStateManager } from '../src/index.js' + +import type { StateManagerInterface } from '@ethereumjs/common' const storageEval = async ( - sm: DefaultStateManager, + sm: StateManagerInterface, address: Address, key: Uint8Array, value: Uint8Array, - root: Uint8Array + root: Uint8Array, + rootCheck = true ) => { assert.deepEqual( await sm.getContractStorage(address, key), value, 'storage value should be equal' ) - const accountCMP = await sm.getAccount(address) - assert.deepEqual(accountCMP!.storageRoot, root, 'account storage root should be equal') + if (rootCheck) { + const accountCMP = await sm.getAccount(address) + assert.deepEqual(accountCMP!.storageRoot, root, 'account storage root should be equal') + } } describe('StateManager -> Storage Checkpointing', () => { const address = new Address(hexToBytes(`0x${'11'.repeat(20)}`)) - const account = new Account() + + const stateManagers = [ + { + SM: DefaultStateManager, + rootCheck: true, + }, + { + SM: SimpleStateManager, + rootCheck: false, + }, + ] const key = hexToBytes(`0x${'01'.repeat(32)}`) @@ -95,264 +110,266 @@ describe('StateManager -> Storage Checkpointing', () => { }, ] - for (const s of storageSets) { - it(`No CP -> S1 -> Flush() (-> S1)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractStorage(address, key, s.s1.value) - await sm.flush() - await storageEval(sm, address, key, s.s1.value, s.s1.root) - - sm.clearCaches() - assert.deepEqual(await sm.getContractStorage(address, key), s.s1.value) - await storageEval(sm, address, key, s.s1.value, s.s1.root) - }) - - it(`CP -> S1.1 -> Commit -> Flush() (-> S1.1)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s1.value) - await sm.commit() - await sm.flush() - await storageEval(sm, address, key, s.s1.value, s.s1.root) - - sm.clearCaches() - await storageEval(sm, address, key, s.s1.value, s.s1.root) - }) - - it(`CP -> S1.1 -> Revert -> Flush() (-> Undefined)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s1.value) - - await sm.revert() - await sm.flush() - await storageEval(sm, address, key, valueEmpty, rootEmpty) - - sm.clearCaches() - - await storageEval(sm, address, key, valueEmpty, rootEmpty) - }) - - it(`S1.1 -> CP -> Commit -> Flush() (-> S1.1)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractStorage(address, key, s.s1.value) - await sm.checkpoint() - await sm.commit() - await sm.flush() - await storageEval(sm, address, key, s.s1.value, s.s1.root) - - sm.clearCaches() - await storageEval(sm, address, key, s.s1.value, s.s1.root) - }) - - it(`S1.1 -> CP -> Revert -> Flush() (-> S1.1)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractStorage(address, key, s.s1.value) - await sm.checkpoint() - await sm.revert() - await sm.flush() - await storageEval(sm, address, key, s.s1.value, s.s1.root) - - sm.clearCaches() - await storageEval(sm, address, key, s.s1.value, s.s1.root) - }) - - it(`S1.1 -> CP -> S1.2 -> Commit -> Flush() (-> S1.2)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractStorage(address, key, s.s1.value) - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s2.value) - await sm.commit() - await sm.flush() - await storageEval(sm, address, key, s.s2.value, s.s2.root) - - sm.clearCaches() - await storageEval(sm, address, key, s.s2.value, s.s2.root) - }) - - it(`S1.1 -> CP -> S1.2 -> Commit -> S1.3 -> Flush() (-> S1.3)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractStorage(address, key, s.s1.value) - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s2.value) - await sm.commit() - await sm.putContractStorage(address, key, s.s3.value) - await sm.flush() - await storageEval(sm, address, key, s.s3.value, s.s3.root) - - sm.clearCaches() - await storageEval(sm, address, key, s.s3.value, s.s3.root) - }) - - it(`S1.1 -> CP -> S1.2 -> S1.3 -> Commit -> Flush() (-> S1.3)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractStorage(address, key, s.s1.value) - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s2.value) - await sm.putContractStorage(address, key, s.s3.value) - await sm.commit() - await sm.flush() - await storageEval(sm, address, key, s.s3.value, s.s3.root) - - sm.clearCaches() - await storageEval(sm, address, key, s.s3.value, s.s3.root) - }) - - it(`CP -> S1.1 -> S1.2 -> Commit -> Flush() (-> S1.2)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s1.value) - await sm.putContractStorage(address, key, s.s2.value) - await sm.commit() - await sm.flush() - await storageEval(sm, address, key, s.s2.value, s.s2.root) - - sm.clearCaches() - await storageEval(sm, address, key, s.s2.value, s.s2.root) - }) - - it(`CP -> S1.1 -> S1.2 -> Revert -> Flush() (-> Undefined)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s1.value) - - await sm.putContractStorage(address, key, s.s2.value) - await sm.revert() - await sm.flush() - await storageEval(sm, address, key, valueEmpty, rootEmpty) - - sm.clearCaches() - await storageEval(sm, address, key, valueEmpty, rootEmpty) - }) - - it(`S1.1 -> CP -> S1.2 -> Revert -> Flush() (-> S1.1)`, async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractStorage(address, key, s.s1.value) - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s2.value) - await sm.revert() - await sm.flush() - await storageEval(sm, address, key, s.s1.value, s.s1.root) - - sm.clearCaches() - await storageEval(sm, address, key, s.s1.value, s.s1.root) - }) - - it('S1.1 -> CP -> S1.2 -> CP -> S1.3 -> Commit -> Commit -> Flush() (-> S1.3)', async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractStorage(address, key, s.s1.value) - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s2.value) - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s3.value) - await sm.commit() - await sm.commit() - await sm.flush() - await storageEval(sm, address, key, s.s3.value, s.s3.root) - - sm.clearCaches() - await storageEval(sm, address, key, s.s3.value, s.s3.root) - }) - - it('S1.1 -> CP -> S1.2 -> CP -> S1.3 -> Commit -> Revert -> Flush() (-> S1.1)', async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractStorage(address, key, s.s1.value) - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s2.value) - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s3.value) - await sm.commit() - await sm.revert() - await sm.flush() - await storageEval(sm, address, key, s.s1.value, s.s1.root) - - sm.clearCaches() - await storageEval(sm, address, key, s.s1.value, s.s1.root) - }) - - it('S1.1 -> CP -> S1.2 -> CP -> S1.3 -> Revert -> Commit -> Flush() (-> S1.2)', async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractStorage(address, key, s.s1.value) - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s2.value) - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s3.value) - await sm.revert() - await sm.commit() - await sm.flush() - await storageEval(sm, address, key, s.s2.value, s.s2.root) - - sm.clearCaches() - await storageEval(sm, address, key, s.s2.value, s.s2.root) - }) - - it('S1.1 -> CP -> S1.2 -> CP -> S1.3 -> Revert -> S1.4 -> Commit -> Flush() (-> S1.4)', async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractStorage(address, key, s.s1.value) - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s2.value) - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s3.value) - await sm.revert() - await sm.putContractStorage(address, key, s.s4.value) - await sm.commit() - await sm.flush() - await storageEval(sm, address, key, s.s4.value, s.s4.root) - - sm.clearCaches() - await storageEval(sm, address, key, s.s4.value, s.s4.root) - }) - - it('S1.1 -> CP -> S1.2 -> CP -> S1.3 -> Revert -> S1.4 -> CP -> S1.5 -> Commit -> Commit -> Flush() (-> S1.5)', async () => { - const sm = new DefaultStateManager() - await sm.putAccount(address, account) - - await sm.putContractStorage(address, key, s.s1.value) - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s2.value) - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s3.value) - await sm.revert() - await sm.putContractStorage(address, key, s.s4.value) - await sm.checkpoint() - await sm.putContractStorage(address, key, s.s5.value) - await sm.commit() - await sm.commit() - await sm.flush() - await storageEval(sm, address, key, s.s5.value, s.s5.root) - - sm.clearCaches() - await storageEval(sm, address, key, s.s5.value, s.s5.root) - }) + for (const SMDict of stateManagers) { + for (const s of storageSets) { + it(`No CP -> S1 -> Flush() (-> S1)`, async () => { + const sm = new SMDict.SM() + await sm.putAccount(address, new Account()) + + await sm.putContractStorage(address, key, s.s1.value) + await sm.flush() + await storageEval(sm, address, key, s.s1.value, s.s1.root, SMDict.rootCheck) + + sm.clearCaches() + assert.deepEqual(await sm.getContractStorage(address, key), s.s1.value) + await storageEval(sm, address, key, s.s1.value, s.s1.root, SMDict.rootCheck) + }) + + it(`CP -> S1.1 -> Commit -> Flush() (-> S1.1)`, async () => { + const sm = new SMDict.SM() + await sm.putAccount(address, new Account()) + + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s1.value) + await sm.commit() + await sm.flush() + await storageEval(sm, address, key, s.s1.value, s.s1.root, SMDict.rootCheck) + + sm.clearCaches() + await storageEval(sm, address, key, s.s1.value, s.s1.root, SMDict.rootCheck) + }) + + it(`CP -> S1.1 -> Revert -> Flush() (-> Undefined)`, async () => { + const sm = new SMDict.SM() + await sm.putAccount(address, new Account()) + + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s1.value) + + await sm.revert() + await sm.flush() + await storageEval(sm, address, key, valueEmpty, rootEmpty) + + sm.clearCaches() + + await storageEval(sm, address, key, valueEmpty, rootEmpty) + }) + + it(`S1.1 -> CP -> Commit -> Flush() (-> S1.1)`, async () => { + const sm = new SMDict.SM() + await sm.putAccount(address, new Account()) + + await sm.putContractStorage(address, key, s.s1.value) + await sm.checkpoint() + await sm.commit() + await sm.flush() + await storageEval(sm, address, key, s.s1.value, s.s1.root, SMDict.rootCheck) + + sm.clearCaches() + await storageEval(sm, address, key, s.s1.value, s.s1.root, SMDict.rootCheck) + }) + + it(`S1.1 -> CP -> Revert -> Flush() (-> S1.1)`, async () => { + const sm = new SMDict.SM() + await sm.putAccount(address, new Account()) + + await sm.putContractStorage(address, key, s.s1.value) + await sm.checkpoint() + await sm.revert() + await sm.flush() + await storageEval(sm, address, key, s.s1.value, s.s1.root, SMDict.rootCheck) + + sm.clearCaches() + await storageEval(sm, address, key, s.s1.value, s.s1.root, SMDict.rootCheck) + }) + + it(`S1.1 -> CP -> S1.2 -> Commit -> Flush() (-> S1.2)`, async () => { + const sm = new SMDict.SM() + await sm.putAccount(address, new Account()) + + await sm.putContractStorage(address, key, s.s1.value) + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s2.value) + await sm.commit() + await sm.flush() + await storageEval(sm, address, key, s.s2.value, s.s2.root, SMDict.rootCheck) + + sm.clearCaches() + await storageEval(sm, address, key, s.s2.value, s.s2.root, SMDict.rootCheck) + }) + + it(`S1.1 -> CP -> S1.2 -> Commit -> S1.3 -> Flush() (-> S1.3)`, async () => { + const sm = new SMDict.SM() + await sm.putAccount(address, new Account()) + + await sm.putContractStorage(address, key, s.s1.value) + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s2.value) + await sm.commit() + await sm.putContractStorage(address, key, s.s3.value) + await sm.flush() + await storageEval(sm, address, key, s.s3.value, s.s3.root, SMDict.rootCheck) + + sm.clearCaches() + await storageEval(sm, address, key, s.s3.value, s.s3.root, SMDict.rootCheck) + }) + + it(`S1.1 -> CP -> S1.2 -> S1.3 -> Commit -> Flush() (-> S1.3)`, async () => { + const sm = new SMDict.SM() + await sm.putAccount(address, new Account()) + + await sm.putContractStorage(address, key, s.s1.value) + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s2.value) + await sm.putContractStorage(address, key, s.s3.value) + await sm.commit() + await sm.flush() + await storageEval(sm, address, key, s.s3.value, s.s3.root, SMDict.rootCheck) + + sm.clearCaches() + await storageEval(sm, address, key, s.s3.value, s.s3.root, SMDict.rootCheck) + }) + + it(`CP -> S1.1 -> S1.2 -> Commit -> Flush() (-> S1.2)`, async () => { + const sm = new SMDict.SM() + await sm.putAccount(address, new Account()) + + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s1.value) + await sm.putContractStorage(address, key, s.s2.value) + await sm.commit() + await sm.flush() + await storageEval(sm, address, key, s.s2.value, s.s2.root, SMDict.rootCheck) + + sm.clearCaches() + await storageEval(sm, address, key, s.s2.value, s.s2.root, SMDict.rootCheck) + }) + + it(`CP -> S1.1 -> S1.2 -> Revert -> Flush() (-> Undefined)`, async () => { + const sm = new SMDict.SM() + await sm.putAccount(address, new Account()) + + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s1.value) + + await sm.putContractStorage(address, key, s.s2.value) + await sm.revert() + await sm.flush() + await storageEval(sm, address, key, valueEmpty, rootEmpty) + + sm.clearCaches() + await storageEval(sm, address, key, valueEmpty, rootEmpty) + }) + + it(`S1.1 -> CP -> S1.2 -> Revert -> Flush() (-> S1.1)`, async () => { + const sm = new SMDict.SM() + await sm.putAccount(address, new Account()) + + await sm.putContractStorage(address, key, s.s1.value) + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s2.value) + await sm.revert() + await sm.flush() + await storageEval(sm, address, key, s.s1.value, s.s1.root, SMDict.rootCheck) + + sm.clearCaches() + await storageEval(sm, address, key, s.s1.value, s.s1.root, SMDict.rootCheck) + }) + + it('S1.1 -> CP -> S1.2 -> CP -> S1.3 -> Commit -> Commit -> Flush() (-> S1.3)', async () => { + const sm = new SMDict.SM() + await sm.putAccount(address, new Account()) + + await sm.putContractStorage(address, key, s.s1.value) + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s2.value) + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s3.value) + await sm.commit() + await sm.commit() + await sm.flush() + await storageEval(sm, address, key, s.s3.value, s.s3.root, SMDict.rootCheck) + + sm.clearCaches() + await storageEval(sm, address, key, s.s3.value, s.s3.root, SMDict.rootCheck) + }) + + it('S1.1 -> CP -> S1.2 -> CP -> S1.3 -> Commit -> Revert -> Flush() (-> S1.1)', async () => { + const sm = new SMDict.SM() + await sm.putAccount(address, new Account()) + + await sm.putContractStorage(address, key, s.s1.value) + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s2.value) + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s3.value) + await sm.commit() + await sm.revert() + await sm.flush() + await storageEval(sm, address, key, s.s1.value, s.s1.root, SMDict.rootCheck) + + sm.clearCaches() + await storageEval(sm, address, key, s.s1.value, s.s1.root, SMDict.rootCheck) + }) + + it('S1.1 -> CP -> S1.2 -> CP -> S1.3 -> Revert -> Commit -> Flush() (-> S1.2)', async () => { + const sm = new SMDict.SM() + await sm.putAccount(address, new Account()) + + await sm.putContractStorage(address, key, s.s1.value) + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s2.value) + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s3.value) + await sm.revert() + await sm.commit() + await sm.flush() + await storageEval(sm, address, key, s.s2.value, s.s2.root, SMDict.rootCheck) + + sm.clearCaches() + await storageEval(sm, address, key, s.s2.value, s.s2.root, SMDict.rootCheck) + }) + + it('S1.1 -> CP -> S1.2 -> CP -> S1.3 -> Revert -> S1.4 -> Commit -> Flush() (-> S1.4)', async () => { + const sm = new SMDict.SM() + await sm.putAccount(address, new Account()) + + await sm.putContractStorage(address, key, s.s1.value) + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s2.value) + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s3.value) + await sm.revert() + await sm.putContractStorage(address, key, s.s4.value) + await sm.commit() + await sm.flush() + await storageEval(sm, address, key, s.s4.value, s.s4.root, SMDict.rootCheck) + + sm.clearCaches() + await storageEval(sm, address, key, s.s4.value, s.s4.root, SMDict.rootCheck) + }) + + it('S1.1 -> CP -> S1.2 -> CP -> S1.3 -> Revert -> S1.4 -> CP -> S1.5 -> Commit -> Commit -> Flush() (-> S1.5)', async () => { + const sm = new SMDict.SM() + await sm.putAccount(address, new Account()) + + await sm.putContractStorage(address, key, s.s1.value) + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s2.value) + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s3.value) + await sm.revert() + await sm.putContractStorage(address, key, s.s4.value) + await sm.checkpoint() + await sm.putContractStorage(address, key, s.s5.value) + await sm.commit() + await sm.commit() + await sm.flush() + await storageEval(sm, address, key, s.s5.value, s.s5.root, SMDict.rootCheck) + + sm.clearCaches() + await storageEval(sm, address, key, s.s5.value, s.s5.root, SMDict.rootCheck) + }) + } } }) diff --git a/packages/statemanager/test/rpcStateManager.spec.ts b/packages/statemanager/test/rpcStateManager.spec.ts index 74d6f8cd0f..843698196f 100644 --- a/packages/statemanager/test/rpcStateManager.spec.ts +++ b/packages/statemanager/test/rpcStateManager.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromJsonRpcProvider, createBlockFromRPC } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { EVM, type EVMRunCallOpts } from '@ethereumjs/evm' import { FeeMarketEIP1559Transaction, TransactionFactory } from '@ethereumjs/tx' @@ -206,7 +206,7 @@ describe('RPC State Manager API tests', () => { assert.deepEqual({}, clearedStorage, 'storage cache should be empty after clear') try { - await Block.fromJsonRpcProvider(provider, 'fakeBlockTag', {} as any) + await createBlockFromJsonRpcProvider(provider, 'fakeBlockTag', {} as any) assert.fail('should have thrown') } catch (err: any) { assert.ok( @@ -295,7 +295,7 @@ describe('runBlock test', () => { common.setHardforkBy({ blockNumber: blockTag - 1n }) const vm = await VM.create({ common, stateManager: state }) - const block = Block.fromRPC(blockData as JsonRpcBlock, [], { common }) + const block = createBlockFromRPC(blockData as JsonRpcBlock, [], { common }) try { const res = await vm.runBlock({ block, @@ -327,7 +327,7 @@ describe('blockchain', () => await evm.stateManager.setStateRoot( hexToBytes('0xf8506f559699a58a4724df4fcf2ad4fd242d20324db541823f128f5974feb6c7') ) - const block = await Block.fromJsonRpcProvider(provider, 500000n, { setHardfork: true }) + const block = await createBlockFromJsonRpcProvider(provider, 500000n, { setHardfork: true }) await evm.stateManager.putContractCode(contractAddress, hexToBytes(code)) const runCallArgs: Partial = { caller, diff --git a/packages/statemanager/test/statelessVerkleStateManager.spec.ts b/packages/statemanager/test/statelessVerkleStateManager.spec.ts index 94270c9128..7e4ca0f915 100644 --- a/packages/statemanager/test/statelessVerkleStateManager.spec.ts +++ b/packages/statemanager/test/statelessVerkleStateManager.spec.ts @@ -1,5 +1,5 @@ -import { Block } from '@ethereumjs/block' -import { Common } from '@ethereumjs/common' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { createCommonFromGethGenesis } from '@ethereumjs/common' import { TransactionFactory } from '@ethereumjs/tx' import { Account, @@ -28,16 +28,19 @@ describe('StatelessVerkleStateManager: Kaustinen Verkle Block', () => { beforeAll(async () => { verkleCrypto = await loadVerkleCrypto() }) - const common = Common.fromGethGenesis(testnetVerkleKaustinen, { + const common = createCommonFromGethGenesis(testnetVerkleKaustinen, { chain: 'customChain', eips: [2935, 4895, 6800], }) const decodedTxs = verkleBlockJSON.transactions.map((tx) => TransactionFactory.fromSerializedData(hexToBytes(tx as PrefixedHexString)) ) - const block = Block.fromBlockData({ ...verkleBlockJSON, transactions: decodedTxs } as BlockData, { - common, - }) + const block = createBlockFromBlockData( + { ...verkleBlockJSON, transactions: decodedTxs } as BlockData, + { + common, + } + ) it('initPreState()', async () => { const stateManager = new StatelessVerkleStateManager({ verkleCrypto }) diff --git a/packages/trie/examples/createFromProof.ts b/packages/trie/examples/createFromProof.ts index 90a56cf4f3..7b41410c42 100644 --- a/packages/trie/examples/createFromProof.ts +++ b/packages/trie/examples/createFromProof.ts @@ -1,6 +1,6 @@ import { Trie } from '@ethereumjs/trie' import { bytesToUtf8 } from '@ethereumjs/util' -import { utf8ToBytes } from 'ethereum-cryptography/utils' +import { utf8ToBytes } from '@ethereumjs/util' async function main() { const k1 = utf8ToBytes('keyOne') diff --git a/packages/trie/examples/logDemo.ts b/packages/trie/examples/logDemo.ts index d6638490dc..f3b77fe7a5 100644 --- a/packages/trie/examples/logDemo.ts +++ b/packages/trie/examples/logDemo.ts @@ -1,8 +1,8 @@ -import { utf8ToBytes } from 'ethereum-cryptography/utils' -import { Trie } from '../dist/cjs/index.js' -import { debug } from 'debug' - -debug.enable('*') +/** + * Run with DEBUG=ethjs,trie:* to see debug log ouput + */ +import { utf8ToBytes } from '@ethereumjs/util' +import { Trie } from '@ethereumjs/trie' const trie_entries: [string, string | null][] = [ ['do', 'verb'], @@ -16,7 +16,6 @@ const trie_entries: [string, string | null][] = [ ] const main = async () => { - process.env.DEBUG = 'ethjs,*trie*' const trie = new Trie({ useRootPersistence: true, }) diff --git a/packages/trie/examples/trieWalking.ts b/packages/trie/examples/trieWalking.ts index a2dc2d1afb..e99eb93543 100644 --- a/packages/trie/examples/trieWalking.ts +++ b/packages/trie/examples/trieWalking.ts @@ -1,5 +1,5 @@ import { Trie } from '@ethereumjs/trie' -import { utf8ToBytes } from 'ethereum-cryptography/utils' +import { utf8ToBytes } from '@ethereumjs/util' async function main() { const trie = await Trie.create() diff --git a/packages/trie/package.json b/packages/trie/package.json index 14d5fd38f3..17251fd4de 100644 --- a/packages/trie/package.json +++ b/packages/trie/package.json @@ -21,8 +21,9 @@ "contributors": [ "Aaron Kumavis (https://github.com/kumavis)" ], + "type": "module", + "sideEffects": false, "main": "dist/cjs/index.js", - "type": "commonjs", "module": "dist/esm/index.js", "exports": { ".": { diff --git a/packages/trie/src/util/view.ts b/packages/trie/scripts/view.ts similarity index 99% rename from packages/trie/src/util/view.ts rename to packages/trie/scripts/view.ts index fef2086f61..5d2f600027 100644 --- a/packages/trie/src/util/view.ts +++ b/packages/trie/scripts/view.ts @@ -1,5 +1,5 @@ import { debug as _debug } from 'debug' -import { bytesToHex, equalsBytes, hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils' +import { bytesToHex, equalsBytes, hexToBytes, utf8ToBytes } from '@ethereumjs/util' import { BranchNode, ExtensionNode, LeafNode } from '../node/index.js' import { Trie } from '../trie.js' diff --git a/packages/trie/examples/walkDemo.ts b/packages/trie/scripts/walkDemo.ts similarity index 100% rename from packages/trie/examples/walkDemo.ts rename to packages/trie/scripts/walkDemo.ts diff --git a/packages/trie/src/util/asyncWalk.ts b/packages/trie/src/util/asyncWalk.ts index f23cc33fc0..2074302193 100644 --- a/packages/trie/src/util/asyncWalk.ts +++ b/packages/trie/src/util/asyncWalk.ts @@ -1,5 +1,7 @@ import { RLP } from '@ethereumjs/rlp' -import { equalsBytes, toHex } from 'ethereum-cryptography/utils' +import { equalsBytes } from '@ethereumjs/util' +// TODO: replace with bytesToHex from @ethereumjs/util +import { toHex } from 'ethereum-cryptography/utils.js' // eslint-disable-line import { BranchNode } from '../node/branch.js' import { ExtensionNode } from '../node/extension.js' diff --git a/packages/trie/test/util/log.spec.ts b/packages/trie/test/util/log.spec.ts index a5c4b801f0..bb58340d2b 100644 --- a/packages/trie/test/util/log.spec.ts +++ b/packages/trie/test/util/log.spec.ts @@ -1,4 +1,4 @@ -import { utf8ToBytes } from 'ethereum-cryptography/utils' +import { utf8ToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' import { Trie } from '../../src/trie.js' diff --git a/packages/tx/examples/custom-chain-id-tx.ts b/packages/tx/examples/custom-chain-id-tx.ts index 1ee9d68923..4d9b911a89 100644 --- a/packages/tx/examples/custom-chain-id-tx.ts +++ b/packages/tx/examples/custom-chain-id-tx.ts @@ -1,12 +1,12 @@ import { LegacyTransaction } from '../dist/cjs' import { toBytes } from '@ethereumjs/util' -import { Common, Hardfork } from '@ethereumjs/common' +import { Common, createCustomCommon, Hardfork } from '@ethereumjs/common' const txData = toBytes( '0xf9010b82930284d09dc30083419ce0942d18de92e0f9aee1a29770c3b15c6cf8ac5498e580b8a42f43f4fb0000000000000000000000000000000000000000000000000000016b78998da900000000000000000000000000000000000000000000000000000000000cb1b70000000000000000000000000000000000000000000000000000000000000fa00000000000000000000000000000000000000000000000000000000001363e4f00000000000000000000000000000000000000000000000000000000000186a029a0fac36e66d329af0e831b2e61179b3ec8d7c7a8a2179e303cfed3364aff2bc3e4a07cb73d56e561ccbd838818dd3dea5fa0b5158577ffc61c0e6ec1f0ed55716891' ) -const common = Common.custom({ chainId: 3 }) +const common = createCustomCommon({ chainId: 3 }) common.setHardfork(Hardfork.Petersburg) const tx = LegacyTransaction.fromSerializedTx(txData, { common }) diff --git a/packages/tx/examples/custom-chain-tx.ts b/packages/tx/examples/custom-chain-tx.ts index 71e738c7e5..b4f4c64a7c 100644 --- a/packages/tx/examples/custom-chain-tx.ts +++ b/packages/tx/examples/custom-chain-tx.ts @@ -1,5 +1,5 @@ import { Address } from '@ethereumjs/util' -import { Common } from '@ethereumjs/common' +import { Common, createCustomCommon } from '@ethereumjs/common' import { LegacyTransaction } from '../dist/cjs/index' import { hexToBytes } from '@ethereumjs/util' @@ -8,7 +8,7 @@ import { hexToBytes } from '@ethereumjs/util' // This custom network has the same params as mainnet, // except for name, chainId, and networkId, // so we use the `Common.custom` method. -const customCommon = Common.custom( +const customCommon = createCustomCommon( { name: 'my-network', networkId: 123, diff --git a/packages/tx/examples/l2tx.ts b/packages/tx/examples/l2tx.ts index f518d7a61c..427f2dc425 100644 --- a/packages/tx/examples/l2tx.ts +++ b/packages/tx/examples/l2tx.ts @@ -1,10 +1,10 @@ -import { Common, CustomChain } from '@ethereumjs/common' +import { Common, createCustomCommon, CustomChain } from '@ethereumjs/common' import { LegacyTransaction } from '@ethereumjs/tx' import { Address, bytesToHex, hexToBytes } from '@ethereumjs/util' const pk = hexToBytes('0x076247989df60a82f6e86e58104368676096f84e60972282ee00d4673a2bc9b9') const to = Address.fromString('0x256e8f0ba532ad83a0debde7501669511a41a1f3') -const common = Common.custom(CustomChain.xDaiChain) +const common = createCustomCommon(CustomChain.xDaiChain) const txData = { nonce: 0, diff --git a/packages/tx/package.json b/packages/tx/package.json index d1ec3b5239..2ca85c6749 100644 --- a/packages/tx/package.json +++ b/packages/tx/package.json @@ -27,7 +27,8 @@ "hireable": true } ], - "type": "commonjs", + "type": "module", + "sideEffects": false, "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "exports": { diff --git a/packages/tx/src/baseTransaction.ts b/packages/tx/src/baseTransaction.ts index d13c830263..9f8c70f80d 100644 --- a/packages/tx/src/baseTransaction.ts +++ b/packages/tx/src/baseTransaction.ts @@ -1,4 +1,4 @@ -import { Chain, Common } from '@ethereumjs/common' +import { Chain, Common, createCustomCommon, isSupportedChainId } from '@ethereumjs/common' import { Address, BIGINT_0, @@ -228,7 +228,7 @@ export abstract class BaseTransaction * Returns a Uint8Array Array of the raw Bytes of this transaction, in order. * * Use {@link BaseTransaction.serialize} to add a transaction to a block - * with {@link Block.fromValuesArray}. + * with {@link createBlockFromValuesArray}. * * For an unsigned tx this method uses the empty Bytes values for the * signature parameters `v`, `r` and `s` for encoding. For an EIP-155 compliant @@ -344,6 +344,8 @@ export abstract class BaseTransaction v: this.v !== undefined ? bigIntToHex(this.v) : undefined, r: this.r !== undefined ? bigIntToHex(this.r) : undefined, s: this.s !== undefined ? bigIntToHex(this.s) : undefined, + chainId: bigIntToHex(this.common.chainId()), + yParity: this.v === 0n || this.v === 1n ? bigIntToHex(this.v) : undefined, } } @@ -386,14 +388,14 @@ export abstract class BaseTransaction // -> Return provided Common return common.copy() } else { - if (Common.isSupportedChainId(chainIdBigInt)) { + if (isSupportedChainId(chainIdBigInt)) { // No Common, chain ID supported by Common // -> Instantiate Common with chain ID return new Common({ chain: chainIdBigInt }) } else { // No Common, chain ID not supported by Common // -> Instantiate custom Common derived from DEFAULT_CHAIN - return Common.custom( + return createCustomCommon( { name: 'custom-chain', networkId: chainIdBigInt, diff --git a/packages/tx/src/eip1559Transaction.ts b/packages/tx/src/eip1559Transaction.ts index a0892e80d1..b33d12e1c5 100644 --- a/packages/tx/src/eip1559Transaction.ts +++ b/packages/tx/src/eip1559Transaction.ts @@ -227,7 +227,7 @@ export class FeeMarketEIP1559Transaction extends BaseTransaction { * Format: `[nonce, gasPrice, gasLimit, to, value, data, v, r, s]` * * For legacy txs this is also the correct format to add transactions - * to a block with {@link Block.fromValuesArray} (use the `serialize()` method + * to a block with {@link createBlockFromValuesArray} (use the `serialize()` method * for typed txs). * * For an unsigned tx this method returns the empty Bytes values diff --git a/packages/tx/src/types.ts b/packages/tx/src/types.ts index 481afbe4b6..f37ee76b9a 100644 --- a/packages/tx/src/types.ts +++ b/packages/tx/src/types.ts @@ -542,6 +542,7 @@ export interface JsonTx { maxFeePerGas?: PrefixedHexString maxFeePerBlobGas?: PrefixedHexString blobVersionedHashes?: PrefixedHexString[] + yParity?: PrefixedHexString } export type JsonBlobTxNetworkWrapper = JsonTx & { @@ -575,4 +576,5 @@ export interface JsonRpcTx { s: string // DATA, 32 Bytes - ECDSA signature s maxFeePerBlobGas?: string // QUANTITY - max data fee for blob transactions blobVersionedHashes?: string[] // DATA - array of 32 byte versioned hashes for blob transactions + yParity?: string // DATA - parity of the y-coordinate of the public key } diff --git a/packages/tx/test/eip1559.spec.ts b/packages/tx/test/eip1559.spec.ts index 20b0dd4083..a0833bfef6 100644 --- a/packages/tx/test/eip1559.spec.ts +++ b/packages/tx/test/eip1559.spec.ts @@ -1,4 +1,4 @@ -import { Common, Hardfork } from '@ethereumjs/common' +import { Hardfork, createCustomCommon } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { TWO_POW256, ecsign, equalsBytes, hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' @@ -10,7 +10,7 @@ import testdata from './json/eip1559.json' // Source: Besu import type { FeeMarketEIP1559TxData, JsonTx } from '../src/index.js' import type { PrefixedHexString } from '@ethereumjs/util' -const common = Common.custom({ chainId: 4 }) +const common = createCustomCommon({ chainId: 4 }) common.setHardfork(Hardfork.London) const validAddress = hexToBytes(`0x${'01'.repeat(20)}`) @@ -197,7 +197,7 @@ describe('[FeeMarketEIP1559Transaction]', () => { freeze: false, }) - const newCommon = Common.custom({ chainId: 4 }) + const newCommon = createCustomCommon({ chainId: 4 }) newCommon.setHardfork(Hardfork.Paris) assert.notDeepEqual(newCommon, common, 'new common is different than original common') @@ -259,6 +259,7 @@ describe('[FeeMarketEIP1559Transaction]', () => { v: '0x0', r: '0xf924cb68412c8f1cfd74d9b581c71eeaf94fff6abdde3e5b02ca6b2931dcf47', s: '0x7dd1c50027c3e31f8b565e25ce68a5072110f61fce5eee81b195dd51273c2f83', + yParity: '0x0', } assert.deepEqual(json, expectedJSON, 'Should return expected JSON dict') }) diff --git a/packages/tx/test/eip4844.spec.ts b/packages/tx/test/eip4844.spec.ts index 809b074f36..f3aca67c40 100644 --- a/packages/tx/test/eip4844.spec.ts +++ b/packages/tx/test/eip4844.spec.ts @@ -1,4 +1,4 @@ -import { Common, Hardfork } from '@ethereumjs/common' +import { Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common' import { Address, blobsToCommitments, @@ -21,6 +21,7 @@ import { BlobEIP4844Transaction, TransactionFactory } from '../src/index.js' import blobTx from './json/serialized4844tx.json' import type { BlobEIP4844TxData } from '../src/index.js' +import type { Common } from '@ethereumjs/common' import type { Kzg, PrefixedHexString } from '@ethereumjs/util' const pk = randomBytes(32) @@ -28,7 +29,7 @@ describe('EIP4844 addSignature tests', () => { let common: Common beforeAll(async () => { const kzg = await loadKZG() - common = Common.fromGethGenesis(gethGenesis, { + common = createCommonFromGethGenesis(gethGenesis, { chain: 'customChain', hardfork: Hardfork.Cancun, customCrypto: { kzg }, @@ -92,7 +93,7 @@ describe('EIP4844 constructor tests - valid scenarios', () => { let common: Common beforeAll(async () => { const kzg = await loadKZG() - common = Common.fromGethGenesis(gethGenesis, { + common = createCommonFromGethGenesis(gethGenesis, { chain: 'customChain', hardfork: Hardfork.Cancun, customCrypto: { kzg }, @@ -130,7 +131,7 @@ describe('fromTxData using from a json', () => { let common: Common beforeAll(async () => { const kzg = await loadKZG() - common = Common.fromGethGenesis(gethGenesis, { + common = createCommonFromGethGenesis(gethGenesis, { chain: 'customChain', hardfork: Hardfork.Cancun, customCrypto: { kzg }, @@ -154,6 +155,7 @@ describe('fromTxData using from a json', () => { accessList: null, maxFeePerBlobGas: '0xb2d05e00', blobVersionedHashes: ['0x01b0a4cdd5f55589f5c5b4d46c76704bb6ce95c0a8c09f77f197a57808dded28'], + yParity: '0x0', } const txMeta = { hash: '0xe5e02be0667b6d31895d1b5a8b916a6761cbc9865225c6144a3e2c50936d173e', @@ -201,7 +203,7 @@ describe('EIP4844 constructor tests - invalid scenarios', () => { let common: Common beforeAll(async () => { const kzg = await loadKZG() - common = Common.fromGethGenesis(gethGenesis, { + common = createCommonFromGethGenesis(gethGenesis, { chain: 'customChain', hardfork: Hardfork.Cancun, customCrypto: { kzg }, @@ -258,7 +260,7 @@ describe('Network wrapper tests', () => { let common: Common beforeAll(async () => { kzg = await loadKZG() - common = Common.fromGethGenesis(gethGenesis, { + common = createCommonFromGethGenesis(gethGenesis, { chain: 'customChain', hardfork: Hardfork.Cancun, customCrypto: { kzg }, @@ -530,7 +532,7 @@ describe('hash() and signature verification', () => { let common: Common beforeAll(async () => { const kzg = await loadKZG() - common = Common.fromGethGenesis(gethGenesis, { + common = createCommonFromGethGenesis(gethGenesis, { chain: 'customChain', hardfork: Hardfork.Cancun, customCrypto: { kzg }, @@ -578,7 +580,7 @@ describe('hash() and signature verification', () => { it('getEffectivePriorityFee()', async () => { const kzg = await loadKZG() - const common = Common.fromGethGenesis(gethGenesis, { + const common = createCommonFromGethGenesis(gethGenesis, { chain: 'customChain', hardfork: Hardfork.Cancun, customCrypto: { kzg }, @@ -606,7 +608,7 @@ describe('Network wrapper deserialization test', () => { let common: Common beforeAll(async () => { kzg = await loadKZG() - common = Common.fromGethGenesis(gethGenesis, { + common = createCommonFromGethGenesis(gethGenesis, { chain: 'customChain', hardfork: Hardfork.Cancun, customCrypto: { @@ -631,6 +633,7 @@ describe('Network wrapper deserialization test', () => { accessList: [], maxFeePerBlobGas: '0x5f5e100', blobVersionedHashes: ['0x0172ff1d4f354eebdb3cd0cb64e41ac584359094373fd5f979bcccbd6072d936'], + yParity: '0x0', } const txMeta = { sender: '0x652a2b04934d96c26c4710853021779fb9f525d2', diff --git a/packages/tx/test/legacy.spec.ts b/packages/tx/test/legacy.spec.ts index 2194a5dcdc..c3d23bfc7a 100644 --- a/packages/tx/test/legacy.spec.ts +++ b/packages/tx/test/legacy.spec.ts @@ -1,4 +1,4 @@ -import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { Chain, Common, Hardfork, createCustomCommon } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { bytesToBigInt, @@ -399,7 +399,7 @@ describe('[Transaction]', () => { const privateKey = hexToBytes( '0xDE3128752F183E8930D7F00A2AAA302DCB5E700B2CBA2D8CA5795660F07DEFD5' ) - const common = Common.custom({ chainId: 3 }) + const common = createCustomCommon({ chainId: 3 }) const tx = LegacyTransaction.fromValuesArray( txRaw.map((rawTxData) => hexToBytes(rawTxData as PrefixedHexString)), { common } diff --git a/packages/tx/test/typedTxsAndEIP2930.spec.ts b/packages/tx/test/typedTxsAndEIP2930.spec.ts index 32cde1fd69..63199482ce 100644 --- a/packages/tx/test/typedTxsAndEIP2930.spec.ts +++ b/packages/tx/test/typedTxsAndEIP2930.spec.ts @@ -1,4 +1,4 @@ -import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { Chain, Common, Hardfork, createCustomCommon } from '@ethereumjs/common' import { Address, MAX_INTEGER, @@ -605,7 +605,7 @@ describe('[AccessListEIP2930Transaction] -> Class Specific Tests', () => { chainId: txData.chainId, eips: [2718, 2929, 2930], } - const usedCommon = Common.custom(customChainParams, { + const usedCommon = createCustomCommon(customChainParams, { baseChain: Chain.Mainnet, hardfork: Hardfork.Berlin, }) @@ -664,6 +664,7 @@ describe('[AccessListEIP2930Transaction] -> Class Specific Tests', () => { v: '0x0', r: '0x294ac94077b35057971e6b4b06dfdf55a6fbed819133a6c1d31e187f1bca938d', s: '0xbe950468ba1c25a5cb50e9f6d8aa13c8cd21f24ba909402775b262ac76d374d', + yParity: '0x0', } assert.deepEqual(signed.toJSON(), expectedJSON) diff --git a/packages/util/package.json b/packages/util/package.json index 3d0906d621..785eb9a4d7 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -61,7 +61,8 @@ "url": "https://github.com/asinyagin" } ], - "type": "commonjs", + "type": "module", + "sideEffects": false, "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "exports": { diff --git a/packages/verkle/package.json b/packages/verkle/package.json index 84a04bc501..e97a1a9baf 100644 --- a/packages/verkle/package.json +++ b/packages/verkle/package.json @@ -24,8 +24,9 @@ "url": "https://github.com/gabrocheleau" } ], + "type": "module", + "sideEffects": false, "main": "dist/cjs/index.js", - "type": "commonjs", "module": "dist/esm/index.js", "exports": { ".": { diff --git a/packages/vm/benchmarks/mainnetBlocks.ts b/packages/vm/benchmarks/mainnetBlocks.ts index 64f19b464d..dc806da008 100644 --- a/packages/vm/benchmarks/mainnetBlocks.ts +++ b/packages/vm/benchmarks/mainnetBlocks.ts @@ -1,9 +1,9 @@ import { readFileSync } from 'fs' import Benchmark from 'benchmark' import { Chain, Common, Hardfork } from '@ethereumjs/common' -import { Block } from '@ethereumjs/block' -import { VM } from '../dist/cjs' -import { getPreState, getBlockchain, verifyResult } from './util' +import { Block, createBlockFromRPC } from '@ethereumjs/block' +import { VM } from '@ethereumjs/vm' +import { getPreState, getBlockchain, verifyResult } from './util.js' const BLOCK_FIXTURE = 'benchmarks/fixture/blocks-prestate.json' @@ -30,7 +30,7 @@ export async function mainnetBlocks(suite?: Benchmark.Suite, numSamples?: number const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.MuirGlacier }) for (const blockData of data) { - const block = Block.fromRPC(blockData.block, [], { common }) + const block = createBlockFromRPC(blockData.block, [], { common }) const blockNumber = Number(block.header.number) const { receipts, preState, blockhashes } = blockData diff --git a/packages/vm/benchmarks/run.ts b/packages/vm/benchmarks/run.ts index 84590b3918..3ed25d1a8a 100644 --- a/packages/vm/benchmarks/run.ts +++ b/packages/vm/benchmarks/run.ts @@ -1,6 +1,6 @@ import Benchmark from 'benchmark' -import { BenchmarksType } from './util' -import { mainnetBlocks } from './mainnetBlocks' +import { BenchmarksType } from './util.js' +import { mainnetBlocks } from './mainnetBlocks.js' // Add an import and a BENCHMARKS entry to list a new benchmark const BENCHMARKS: BenchmarksType = { diff --git a/packages/vm/benchmarks/util.ts b/packages/vm/benchmarks/util.ts index 4eb20c7bce..460f29ce30 100644 --- a/packages/vm/benchmarks/util.ts +++ b/packages/vm/benchmarks/util.ts @@ -11,8 +11,8 @@ import { import { Common } from '@ethereumjs/common' import { Block } from '@ethereumjs/block' import { DefaultStateManager } from '@ethereumjs/statemanager' -import { RunBlockResult } from '../dist/cjs/types' -import { Mockchain } from './mockchain' +import { RunBlockResult } from '@ethereumjs/vm' +import { Mockchain } from './mockchain.js' export interface BenchmarkType { [key: string]: Function diff --git a/packages/vm/examples/buildBlock.ts b/packages/vm/examples/buildBlock.ts index 3be0f55ed9..748a30ca14 100644 --- a/packages/vm/examples/buildBlock.ts +++ b/packages/vm/examples/buildBlock.ts @@ -1,14 +1,14 @@ -import { Block } from '@ethereumjs/block' -import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { Chain, Common } from '@ethereumjs/common' import { LegacyTransaction } from '@ethereumjs/tx' -import { Account, Address, bytesToHex, hexToBytes, randomBytes } from '@ethereumjs/util' +import { Account, Address, bytesToHex, hexToBytes } from '@ethereumjs/util' import { VM } from '@ethereumjs/vm' const main = async () => { const common = new Common({ chain: Chain.Mainnet }) const vm = await VM.create({ common }) - const parentBlock = Block.fromBlockData( + const parentBlock = createBlockFromBlockData( { header: { number: 1n } }, { skipConsensusFormatValidation: true } ) diff --git a/packages/vm/examples/helpers/account-utils.cts b/packages/vm/examples/helpers/account-utils.ts similarity index 95% rename from packages/vm/examples/helpers/account-utils.cts rename to packages/vm/examples/helpers/account-utils.ts index 1957a42d74..c5db9a3c3e 100644 --- a/packages/vm/examples/helpers/account-utils.cts +++ b/packages/vm/examples/helpers/account-utils.ts @@ -1,4 +1,4 @@ -import { VM } from '../../dist/cjs/vm' +import { VM } from '@ethereumjs/vm' import { Account, Address } from '@ethereumjs/util' export const keyPair = { diff --git a/packages/vm/examples/helpers/tx-builder.cts b/packages/vm/examples/helpers/tx-builder.ts similarity index 76% rename from packages/vm/examples/helpers/tx-builder.cts rename to packages/vm/examples/helpers/tx-builder.ts index 27748af4e2..b00be99f7b 100644 --- a/packages/vm/examples/helpers/tx-builder.cts +++ b/packages/vm/examples/helpers/tx-builder.ts @@ -1,7 +1,5 @@ import { Interface, defaultAbiCoder as AbiCoder } from '@ethersproject/abi' -import { AccessListEIP2930TxData, FeeMarketEIP1559TxData, TxData } from '@ethereumjs/tx' - -type TransactionsData = TxData | AccessListEIP2930TxData | FeeMarketEIP1559TxData +import { LegacyTxData } from '@ethereumjs/tx' export const encodeFunction = ( method: string, @@ -33,8 +31,8 @@ export const encodeDeployment = ( return deploymentData } -export const buildTransaction = (data: Partial): TransactionsData => { - const defaultData: Partial = { +export const buildTransaction = (data: Partial): LegacyTxData => { + const defaultData: Partial = { nonce: BigInt(0), gasLimit: 2_000_000, // We assume that 2M is enough, gasPrice: 1, diff --git a/packages/vm/examples/run-blockchain.ts b/packages/vm/examples/run-blockchain.ts index 0ed588ba9a..e86bcde817 100644 --- a/packages/vm/examples/run-blockchain.ts +++ b/packages/vm/examples/run-blockchain.ts @@ -7,21 +7,25 @@ // 5. Runs the Blockchain on the VM. import { Account, Address, toBytes, setLengthLeft, bytesToHex, hexToBytes } from '@ethereumjs/util' -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { + Block, + createBlockFromBlockData, + createBlockFromRLPSerializedBlock, +} from '@ethereumjs/block' +import { Blockchain, createBlockchain } from '@ethereumjs/blockchain' import { Common, ConsensusType } from '@ethereumjs/common' import { VM } from '@ethereumjs/vm' -//import testData from './helpers/blockchain-mock-data.json' -const testData = require('./helpers/blockchain-mock-data.json') +import testData from './helpers/blockchain-mock-data.json' + async function main() { const common = new Common({ chain: 1, hardfork: testData.network.toLowerCase() }) const validatePow = common.consensusType() === ConsensusType.ProofOfWork const validateBlocks = true - const genesisBlock = Block.fromBlockData({ header: testData.genesisBlockHeader }, { common }) + const genesisBlock = createBlockFromBlockData({ header: testData.genesisBlockHeader }, { common }) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateConsensus: validatePow, validateBlocks, @@ -74,7 +78,7 @@ async function setupPreConditions(vm: VM, data: any) { async function putBlocks(blockchain: Blockchain, common: Common, data: typeof testData) { for (const blockData of data.blocks) { const blockRlp = toBytes(blockData.rlp) - const block = Block.fromRLPSerializedBlock(blockRlp, { common }) + const block = createBlockFromRLPSerializedBlock(blockRlp, { common }) await blockchain.putBlock(block) } } diff --git a/packages/vm/examples/run-solidity-contract.ts b/packages/vm/examples/run-solidity-contract.ts index 89d7acb529..88c4346c77 100644 --- a/packages/vm/examples/run-solidity-contract.ts +++ b/packages/vm/examples/run-solidity-contract.ts @@ -1,20 +1,24 @@ -import { join } from 'path' +import path from 'path' +import { fileURLToPath } from 'url' import { readFileSync } from 'fs' import { defaultAbiCoder as AbiCoder, Interface } from '@ethersproject/abi' import { Address, bytesToHex, hexToBytes } from '@ethereumjs/util' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { LegacyTransaction } from '@ethereumjs/tx' import { VM } from '@ethereumjs/vm' -import { buildTransaction, encodeDeployment, encodeFunction } from './helpers/tx-builder.cjs' -import { getAccountNonce, insertAccount } from './helpers/account-utils.cjs' -import { Block } from '@ethereumjs/block' -const solc = require('solc') +import { buildTransaction, encodeDeployment, encodeFunction } from './helpers/tx-builder.js' +import { getAccountNonce, insertAccount } from './helpers/account-utils.js' +import { createBlockFromBlockData } from '@ethereumjs/block' +import solc from 'solc' const INITIAL_GREETING = 'Hello, World!' const SECOND_GREETING = 'Hola, Mundo!' const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) -const block = Block.fromBlockData({ header: { extraData: new Uint8Array(97) } }, { common }) +const block = createBlockFromBlockData({ header: { extraData: new Uint8Array(97) } }, { common }) + +const __filename = fileURLToPath(import.meta.url) // get the resolved path to the file +const __dirname = path.dirname(__filename) // get the name of the directory /** * This function creates the input for the Solidity compiler. @@ -30,7 +34,7 @@ function getSolcInput() { language: 'Solidity', sources: { 'helpers/Greeter.sol': { - content: readFileSync(join(__dirname, 'helpers', 'Greeter.sol'), 'utf8'), + content: readFileSync(path.join(__dirname, 'helpers', 'Greeter.sol'), 'utf8'), }, // If more contracts were to be compiled, they should have their own entries here }, @@ -101,7 +105,7 @@ async function deployContract( nonce: await getAccountNonce(vm, senderPrivateKey), } - const tx = LegacyTransaction.fromTxData(buildTransaction(txData), { common }).sign( + const tx = LegacyTransaction.fromTxData(buildTransaction(txData as any), { common }).sign( senderPrivateKey ) @@ -128,10 +132,10 @@ async function setGreeting( const txData = { to: contractAddress, data, - nonce: await getAccountNonce(vm, senderPrivateKey), + nonce: await getAccountNonce(vm as any, senderPrivateKey), } - const tx = LegacyTransaction.fromTxData(buildTransaction(txData), { common }).sign( + const tx = LegacyTransaction.fromTxData(buildTransaction(txData as any), { common }).sign( senderPrivateKey ) diff --git a/packages/vm/examples/runGoerliBlock.ts b/packages/vm/examples/runGoerliBlock.ts index d6c079a33e..a10bb06692 100644 --- a/packages/vm/examples/runGoerliBlock.ts +++ b/packages/vm/examples/runGoerliBlock.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { Block, createBlockFromRPC } from '@ethereumjs/block' import { Chain, Common } from '@ethereumjs/common' import { bytesToHex, hexToBytes } from '@ethereumjs/util' import { VM } from '../src/vm.js' @@ -8,7 +8,7 @@ const main = async () => { const common = new Common({ chain: Chain.Goerli, hardfork: 'london' }) const vm = await VM.create({ common, setHardfork: true }) - const block = Block.fromRPC(goerliBlock2, undefined, { common }) + const block = createBlockFromRPC(goerliBlock2, undefined, { common }) const result = await vm.runBlock({ block, generate: true, skipHeaderValidation: true }) // we skip header validaiton since we are running a block without the full Ethereum history available console.log(`The state root for Goerli block 2 is ${bytesToHex(result.stateRoot)}`) } diff --git a/packages/vm/examples/vmWithGenesisState.ts b/packages/vm/examples/vmWithGenesisState.ts index db1cd203ec..387bd11953 100644 --- a/packages/vm/examples/vmWithGenesisState.ts +++ b/packages/vm/examples/vmWithGenesisState.ts @@ -1,4 +1,4 @@ -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockchain } from '@ethereumjs/blockchain' import { Chain } from '@ethereumjs/common' import { getGenesis } from '@ethereumjs/genesis' import { Address } from '@ethereumjs/util' @@ -7,7 +7,7 @@ import { VM } from '@ethereumjs/vm' const main = async () => { const genesisState = getGenesis(Chain.Mainnet) - const blockchain = await Blockchain.create({ genesisState }) + const blockchain = await createBlockchain({ genesisState }) const vm = await VM.create({ blockchain, genesisState }) const account = await vm.stateManager.getAccount( Address.fromString('0x000d836201318ec6899a67540690382780743280') diff --git a/packages/vm/package.json b/packages/vm/package.json index 111df4c242..4179f435db 100644 --- a/packages/vm/package.json +++ b/packages/vm/package.json @@ -19,7 +19,8 @@ "contributors": [ "Alex Beregszaszi " ], - "type": "commonjs", + "type": "module", + "sideEffects": false, "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "exports": { diff --git a/packages/vm/src/buildBlock.ts b/packages/vm/src/buildBlock.ts index 8eaacdf6e4..984ff7fbf5 100644 --- a/packages/vm/src/buildBlock.ts +++ b/packages/vm/src/buildBlock.ts @@ -1,4 +1,9 @@ -import { Block } from '@ethereumjs/block' +import { + createBlockFromBlockData, + genRequestsTrieRoot, + genTransactionsTrieRoot, + genWithdrawalsTrieRoot, +} from '@ethereumjs/block' import { ConsensusType, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { Trie } from '@ethereumjs/trie' @@ -29,7 +34,7 @@ import { import type { BuildBlockOpts, BuilderOpts, RunTxResult, SealBlockOpts } from './types.js' import type { VM } from './vm.js' -import type { HeaderData } from '@ethereumjs/block' +import type { Block, HeaderData } from '@ethereumjs/block' import type { TypedTransaction } from '@ethereumjs/tx' export enum BuildStatus { @@ -134,7 +139,7 @@ export class BlockBuilder { * Calculates and returns the transactionsTrie for the block. */ public async transactionsTrie() { - return Block.genTransactionsTrieRoot(this.transactions, new Trie({ common: this.vm.common })) + return genTransactionsTrieRoot(this.transactions, new Trie({ common: this.vm.common })) } /** @@ -249,7 +254,7 @@ export class BlockBuilder { } const blockData = { header, transactions: this.transactions } - const block = Block.fromBlockData(blockData, this.blockOpts) + const block = createBlockFromBlockData(blockData, this.blockOpts) const result = await this.vm.runTx({ tx, block, skipHardForkValidation }) @@ -306,7 +311,7 @@ export class BlockBuilder { const transactionsTrie = await this.transactionsTrie() const withdrawalsRoot = this.withdrawals - ? await Block.genWithdrawalsTrieRoot(this.withdrawals, new Trie({ common: this.vm.common })) + ? await genWithdrawalsTrieRoot(this.withdrawals, new Trie({ common: this.vm.common })) : undefined const receiptTrie = await this.receiptTrie() const logsBloom = this.logsBloom() @@ -323,7 +328,7 @@ export class BlockBuilder { let requestsRoot if (this.vm.common.isActivatedEIP(7685)) { requests = await accumulateRequests(this.vm, this.transactionResults) - requestsRoot = await Block.genRequestsTrieRoot(requests) + requestsRoot = await genRequestsTrieRoot(requests) // Do other validations per request type } @@ -355,7 +360,7 @@ export class BlockBuilder { requests, } - const block = Block.fromBlockData(blockData, blockOpts) + const block = createBlockFromBlockData(blockData, blockOpts) if (this.blockOpts.putBlockIntoBlockchain === true) { await this.vm.blockchain.putBlock(block) diff --git a/packages/vm/src/requests.ts b/packages/vm/src/requests.ts index 7141fa8c97..b8e095afbe 100644 --- a/packages/vm/src/requests.ts +++ b/packages/vm/src/requests.ts @@ -1,4 +1,4 @@ -import { Common } from '@ethereumjs/common' +import { getInitializedChains } from '@ethereumjs/common' import { Address, ConsolidationRequest, @@ -13,7 +13,7 @@ import { unpadBytes, } from '@ethereumjs/util' -import type { RunTxResult } from './types' +import type { RunTxResult } from './types.js' import type { VM } from './vm.js' import type { CLRequest, CLRequestType } from '@ethereumjs/util' @@ -33,7 +33,7 @@ export const accumulateRequests = async ( if (common.isActivatedEIP(6110)) { const depositContractAddress = vm.common['_chainParams'].depositContractAddress ?? - Common.getInitializedChains().mainnet.depositContractAddress + getInitializedChains().mainnet.depositContractAddress if (depositContractAddress === undefined) throw new Error('deposit contract address required with EIP 6110') await accumulateDeposits(depositContractAddress, txResults, requests) @@ -78,7 +78,7 @@ const accumulateEIP7002Requests = async ( const systemAddressBytes = bigIntToAddressBytes(vm.common.param('vm', 'systemAddress')) const systemAddress = Address.fromString(bytesToHex(systemAddressBytes)) - const addrIsEmpty = (await vm.stateManager.getAccount(systemAddress)) === undefined + const originalAccount = await vm.stateManager.getAccount(systemAddress) const results = await vm.evm.runCall({ caller: systemAddress, @@ -98,8 +98,11 @@ const accumulateEIP7002Requests = async ( } } - if (addrIsEmpty) { + if (originalAccount === undefined) { await vm.stateManager.deleteAccount(systemAddress) + } else { + // Restore the original account (the `runCall` updates the nonce) + await vm.stateManager.putAccount(systemAddress, originalAccount) } } @@ -125,7 +128,7 @@ const accumulateEIP7251Requests = async ( const systemAddressBytes = bigIntToAddressBytes(vm.common.param('vm', 'systemAddress')) const systemAddress = Address.fromString(bytesToHex(systemAddressBytes)) - const addrIsEmpty = (await vm.stateManager.getAccount(systemAddress)) === undefined + const originalAccount = await vm.stateManager.getAccount(systemAddress) const results = await vm.evm.runCall({ caller: systemAddress, @@ -147,8 +150,11 @@ const accumulateEIP7251Requests = async ( } } - if (addrIsEmpty) { + if (originalAccount === undefined) { await vm.stateManager.deleteAccount(systemAddress) + } else { + // Restore the original account (the `runCall` updates the nonce) + await vm.stateManager.putAccount(systemAddress, originalAccount) } } diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index a5660c2913..2ca022323a 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData, genRequestsTrieRoot } from '@ethereumjs/block' import { ConsensusType, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { StatelessVerkleStateManager } from '@ethereumjs/statemanager' @@ -40,13 +40,12 @@ import type { TxReceipt, } from './types.js' import type { VM } from './vm.js' +import type { Block } from '@ethereumjs/block' import type { Common } from '@ethereumjs/common' import type { EVM, EVMInterface } from '@ethereumjs/evm' import type { CLRequest, CLRequestType, PrefixedHexString } from '@ethereumjs/util' -const { debug: createDebugLogger } = debugDefault - -const debug = createDebugLogger('vm:block') +const debug = debugDefault('vm:block') const parentBeaconBlockRootAddress = Address.fromString( '0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02' @@ -208,7 +207,7 @@ export async function runBlock(this: VM, opts: RunBlockOpts): Promise[] | undefined if (block.common.isActivatedEIP(7685)) { requests = await accumulateRequests(this, result.results) - requestsRoot = await Block.genRequestsTrieRoot(requests) + requestsRoot = await genRequestsTrieRoot(requests) } // Persist state @@ -240,12 +239,12 @@ export async function runBlock(this: VM, opts: RunBlockOpts): Promise { } // create a reasonable default if no block is given - opts.block = opts.block ?? Block.fromBlockData({}, { common: this.common }) + opts.block = opts.block ?? createBlockFromBlockData({}, { common: this.common }) if (opts.skipHardForkValidation !== true) { // Find and set preMerge hf for easy access later diff --git a/packages/vm/src/vm.ts b/packages/vm/src/vm.ts index 39fc643f26..6d30a9f101 100644 --- a/packages/vm/src/vm.ts +++ b/packages/vm/src/vm.ts @@ -1,4 +1,4 @@ -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockchain } from '@ethereumjs/blockchain' import { Chain, Common } from '@ethereumjs/common' import { EVM, getActivePrecompiles } from '@ethereumjs/evm' import { DefaultStateManager } from '@ethereumjs/statemanager' @@ -20,8 +20,7 @@ import type { } from './types.js' import type { BlockchainInterface } from '@ethereumjs/blockchain' import type { EVMStateManagerInterface } from '@ethereumjs/common' -import type { EVMInterface } from '@ethereumjs/evm' -import type { EVMPerformanceLogOutput } from '@ethereumjs/evm/dist/cjs/logger.js' +import type { EVMInterface, EVMPerformanceLogOutput } from '@ethereumjs/evm' import type { BigIntLike } from '@ethereumjs/util' /** @@ -92,7 +91,7 @@ export class VM { } if (opts.blockchain === undefined) { - opts.blockchain = await Blockchain.create({ common: opts.common }) + opts.blockchain = await createBlockchain({ common: opts.common }) } const genesisState = opts.genesisState ?? {} diff --git a/packages/vm/test/api/EIPs/eip-1559-FeeMarket.spec.ts b/packages/vm/test/api/EIPs/eip-1559-FeeMarket.spec.ts index 834bcde51a..df2d83d671 100644 --- a/packages/vm/test/api/EIPs/eip-1559-FeeMarket.spec.ts +++ b/packages/vm/test/api/EIPs/eip-1559-FeeMarket.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { AccessListEIP2930Transaction, @@ -53,7 +53,7 @@ function makeBlock(baseFee: bigint, transaction: TypedTransaction, txType: Trans const signed = transaction.sign(pkey) const json = signed.toJSON() json.type = txType - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { number: BigInt(1), 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 f6676ac6ea..0518294d21 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 @@ -1,6 +1,6 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' -import { Common, Hardfork } from '@ethereumjs/common' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' +import { Hardfork, createCustomCommon } from '@ethereumjs/common' import { LegacyTransaction } from '@ethereumjs/tx' import { Account, @@ -17,11 +17,13 @@ import { import { hexToBytes } from 'ethereum-cryptography/utils' import { assert, describe, it } from 'vitest' -import { bytesToBigInt } from '../../../../util/src/bytes' -import { BIGINT_0 } from '../../../../util/src/constants' -import { VM } from '../../../src/vm' +import { bytesToBigInt } from '../../../../util/src/bytes.js' +import { BIGINT_0 } from '../../../../util/src/constants.js' +import { VM } from '../../../src/vm.js' -function eip2935ActiveAtCommon(timestamp: number) { +import type { Block } from '@ethereumjs/block' + +function eip2935ActiveAtCommon(timestamp: number, address: bigint) { const hfs = [ Hardfork.Chainstart, Hardfork.Homestead, @@ -54,7 +56,7 @@ function eip2935ActiveAtCommon(timestamp: number) { block: null, timestamp, }) - const c = Common.custom({ + const c = createCustomCommon({ customHardforks: { testEIP2935Hardfork: { name: 'testEIP2935Hardfork', @@ -62,6 +64,9 @@ function eip2935ActiveAtCommon(timestamp: number) { url: '', status: 'final', eips: [2935, 7709], + vm: { + historyStorageAddress: address, + }, }, }, hardforks, @@ -83,21 +88,6 @@ const PREBALANCE = BigInt(10000000) // array of different deployment configurations const deploymentConfigs = [ - // original configuration - [ - // contract code - '0x60203611603157600143035f35116029575f356120000143116029576120005f3506545f5260205ff35b5f5f5260205ff35b5f5ffd00', - // deployment tx input - '0x60368060095f395ff360203611603157600143035f35116029575f356120000143116029576120005f3506545f5260205ff35b5f5f5260205ff35b5f5ffd00', - // v r s - ['0x1b', '0x539', '0x1b9b6eb1f0'], - // sender, hash, deployed address - [ - '0xa4690f0ed0d089faa1e0ad94c8f1b4a2fd4b0734', - '0x7ba81426bfa88a2cf4ea5c9abbbe83619505acd1173bc8450f93cf17cde3784b', - '0x25a219378dad9b3503c8268c9ca836a52427a4fb', - ], - ], // may 25 configuration with set on the lines of 4788 [ // contract code @@ -125,6 +115,7 @@ describe('EIP 2935: historical block hashes', () => { ] = deploymentConfig const historyAddress = Address.fromString(deployedToAddress) + const historyAddressBigInt = bytesToBigInt(historyAddress.bytes) const contract2935Code = hexToBytes(contract2935CodeHex) // eslint-disable-next-line no-inner-declarations @@ -177,8 +168,8 @@ describe('EIP 2935: historical block hashes', () => { }) it('should save genesis block hash to the history block hash contract', async () => { - const commonGenesis = eip2935ActiveAtCommon(1) - const blockchain = await Blockchain.create({ + const commonGenesis = eip2935ActiveAtCommon(1, historyAddressBigInt) + const blockchain = await createBlockchain({ common: commonGenesis, validateBlocks: false, validateConsensus: false, @@ -187,10 +178,6 @@ describe('EIP 2935: historical block hashes', () => { commonGenesis.setHardforkBy({ timestamp: 1, }) - commonGenesis['_paramsCache']['vm']['historyStorageAddress'].v = bytesToBigInt( - historyAddress.bytes - ) - const genesis = await vm.blockchain.getBlock(0) const block = await ( await vm.buildBlock({ @@ -214,12 +201,12 @@ describe('EIP 2935: historical block hashes', () => { const blocksActivation = 256 // This ensures that only block 255 gets stored into the hash contract // More than blocks activation to build, so we can ensure that we can also retrieve block 0 or block 1 hash at block 300 const blocksToBuild = 500 - const commonGetHistoryServeWindow = eip2935ActiveAtCommon(0) + const commonGetHistoryServeWindow = eip2935ActiveAtCommon(0, historyAddressBigInt) commonGetHistoryServeWindow.setEIPs([2935]) - const common = eip2935ActiveAtCommon(blocksActivation) + const common = eip2935ActiveAtCommon(blocksActivation, historyAddressBigInt) const historyServeWindow = commonGetHistoryServeWindow.param('vm', 'historyServeWindow') - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateBlocks: false, validateConsensus: false, @@ -249,7 +236,7 @@ describe('EIP 2935: historical block hashes', () => { } // swap out the blockchain to test from storage - const blockchainEmpty = await Blockchain.create({ + const blockchainEmpty = await createBlockchain({ common, validateBlocks: false, validateConsensus: false, @@ -294,7 +281,7 @@ describe('EIP 2935: historical block hashes', () => { // validate the contract code cases // const result = await vm.runTx({ tx, block, skipHardForkValidation: true }) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { baseFeePerGas: BigInt(7), diff --git a/packages/vm/test/api/EIPs/eip-3074-authcall.spec.ts b/packages/vm/test/api/EIPs/eip-3074-authcall.spec.ts index 1a20d87ee7..6bab91c1a4 100644 --- a/packages/vm/test/api/EIPs/eip-3074-authcall.spec.ts +++ b/packages/vm/test/api/EIPs/eip-3074-authcall.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { EVMErrorMessage } from '@ethereumjs/evm' import { LegacyTransaction } from '@ethereumjs/tx' @@ -35,7 +35,7 @@ const common = new Common({ const privateKey = hexToBytes('0xe331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109') const authAddress = new Address(privateToAddress(privateKey)) -const block = Block.fromBlockData( +const block = createBlockFromBlockData( { header: { baseFeePerGas: BigInt(7), diff --git a/packages/vm/test/api/EIPs/eip-3198-BaseFee.spec.ts b/packages/vm/test/api/EIPs/eip-3198-BaseFee.spec.ts index efba708419..45cf5f2587 100644 --- a/packages/vm/test/api/EIPs/eip-3198-BaseFee.spec.ts +++ b/packages/vm/test/api/EIPs/eip-3198-BaseFee.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx' import { Address, hexToBytes, privateToAddress } from '@ethereumjs/util' @@ -43,7 +43,7 @@ function makeBlock(baseFee: bigint, transaction: TypedTransaction) { const signed = transaction.sign(pkey) const json = signed.toJSON() - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { number: BigInt(1), diff --git a/packages/vm/test/api/EIPs/eip-3651-warm-coinbase.spec.ts b/packages/vm/test/api/EIPs/eip-3651-warm-coinbase.spec.ts index 5aa4fd483f..6baa7b004d 100644 --- a/packages/vm/test/api/EIPs/eip-3651-warm-coinbase.spec.ts +++ b/packages/vm/test/api/EIPs/eip-3651-warm-coinbase.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { LegacyTransaction } from '@ethereumjs/tx' import { Account, Address, hexToBytes, privateToAddress } from '@ethereumjs/util' @@ -17,7 +17,7 @@ const common = new Common({ eips: [3651], }) -const block = Block.fromBlockData( +const block = createBlockFromBlockData( { header: { baseFeePerGas: 7, diff --git a/packages/vm/test/api/EIPs/eip-4399-supplant-difficulty-opcode-with-prevrando.spec.ts b/packages/vm/test/api/EIPs/eip-4399-supplant-difficulty-opcode-with-prevrando.spec.ts index 147a5e36e5..92c765feca 100644 --- a/packages/vm/test/api/EIPs/eip-4399-supplant-difficulty-opcode-with-prevrando.spec.ts +++ b/packages/vm/test/api/EIPs/eip-4399-supplant-difficulty-opcode-with-prevrando.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { bytesToBigInt, hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' @@ -19,7 +19,7 @@ describe('EIP-4399 -> 0x44 (DIFFICULTY) should return PREVRANDAO', () => { timestamp: genesis.header.timestamp + BigInt(1), gasLimit: genesis.header.gasLimit, } - let block = Block.fromBlockData( + let block = createBlockFromBlockData( { header }, { common, calcDifficultyFromHeader: genesis.header } ) @@ -41,7 +41,7 @@ describe('EIP-4399 -> 0x44 (DIFFICULTY) should return PREVRANDAO', () => { common.setHardfork(Hardfork.Paris) const prevRandao = bytesToBigInt(new Uint8Array(32).fill(1)) - block = Block.fromBlockData( + block = createBlockFromBlockData( { header: { ...header, diff --git a/packages/vm/test/api/EIPs/eip-4788-beaconroot.spec.ts b/packages/vm/test/api/EIPs/eip-4788-beaconroot.spec.ts index f288df2c01..c39117a4bc 100644 --- a/packages/vm/test/api/EIPs/eip-4788-beaconroot.spec.ts +++ b/packages/vm/test/api/EIPs/eip-4788-beaconroot.spec.ts @@ -9,7 +9,7 @@ * - Input length < 32 bytes (reverts) */ -import { Block, BlockHeader } from '@ethereumjs/block' +import { BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { TransactionFactory } from '@ethereumjs/tx' import { @@ -25,6 +25,7 @@ import { assert, describe, it } from 'vitest' import { VM } from '../../../src' +import type { Block } from '@ethereumjs/block' import type { TransactionType, TxData } from '@ethereumjs/tx' import type { BigIntLike, PrefixedHexString } from '@ethereumjs/util' @@ -63,7 +64,7 @@ function beaconrootBlock( }, { common, freeze: false } ) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header, transactions: newTxData, diff --git a/packages/vm/test/api/EIPs/eip-4844-blobs.spec.ts b/packages/vm/test/api/EIPs/eip-4844-blobs.spec.ts index 54c7654558..5afdeba5c0 100644 --- a/packages/vm/test/api/EIPs/eip-4844-blobs.spec.ts +++ b/packages/vm/test/api/EIPs/eip-4844-blobs.spec.ts @@ -1,6 +1,6 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' -import { Common, Hardfork } from '@ethereumjs/common' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' +import { Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common' import { BlobEIP4844Transaction } from '@ethereumjs/tx' import { Address, @@ -17,8 +17,8 @@ import { loadKZG } from 'kzg-wasm' import { assert, describe, it } from 'vitest' import * as genesisJSON from '../../../../client/test/testdata/geth-genesis/eip4844.json' -import { VM } from '../../../src/vm' -import { setBalance } from '../utils' +import { VM } from '../../../src/vm.js' +import { setBalance } from '../utils.js' const pk = hexToBytes(`0x${'20'.repeat(32)}`) const sender = bytesToHex(privateToAddress(pk)) @@ -27,16 +27,16 @@ describe('EIP4844 tests', () => { it('should build a block correctly with blobs', async () => { const kzg = await loadKZG() - const common = Common.fromGethGenesis(genesisJSON, { + const common = createCommonFromGethGenesis(genesisJSON, { chain: 'eip4844', hardfork: Hardfork.Cancun, customCrypto: { kzg }, }) - const genesisBlock = Block.fromBlockData( + const genesisBlock = createBlockFromBlockData( { header: { gasLimit: 50000, parentBeaconBlockRoot: zeros(32) } }, { common } ) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ genesisBlock, common, validateBlocks: false, 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 37cccc795f..5fa48e09ce 100644 --- a/packages/vm/test/api/EIPs/eip-4895-withdrawals.spec.ts +++ b/packages/vm/test/api/EIPs/eip-4895-withdrawals.spec.ts @@ -1,6 +1,6 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' -import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { createBlockFromBlockData, genWithdrawalsTrieRoot } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' +import { Chain, Common, Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common' import { decode } from '@ethereumjs/rlp' import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx' import { @@ -19,6 +19,7 @@ import { assert, describe, it } from 'vitest' import * as genesisJSON from '../../../../client/test/testdata/geth-genesis/withdrawals.json' import { VM } from '../../../src/vm' +import type { Block } from '@ethereumjs/block' import type { WithdrawalBytes, WithdrawalData } from '@ethereumjs/util' const common = new Common({ @@ -89,7 +90,7 @@ describe('EIP4895 tests', () => { }) index++ } - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { baseFeePerGas: BigInt(7), @@ -146,11 +147,11 @@ describe('EIP4895 tests', () => { let postState: string // construct a block with just the 0th withdrawal should have no effect on state - block = Block.fromBlockData( + block = createBlockFromBlockData( { header: { baseFeePerGas: BigInt(7), - withdrawalsRoot: await Block.genWithdrawalsTrieRoot(withdrawals.slice(0, 1)), + withdrawalsRoot: await genWithdrawalsTrieRoot(withdrawals.slice(0, 1)), transactionsTrie: KECCAK256_RLP, }, transactions: [], @@ -168,11 +169,11 @@ describe('EIP4895 tests', () => { ) // construct a block with all the withdrawals - block = Block.fromBlockData( + block = createBlockFromBlockData( { header: { baseFeePerGas: BigInt(7), - withdrawalsRoot: await Block.genWithdrawalsTrieRoot(withdrawals), + withdrawalsRoot: await genWithdrawalsTrieRoot(withdrawals), transactionsTrie: KECCAK256_RLP, }, transactions: [], @@ -190,10 +191,10 @@ describe('EIP4895 tests', () => { }) it('should build a block correctly with withdrawals', async () => { - const common = Common.fromGethGenesis(genesisJSON, { chain: 'custom' }) + const common = createCommonFromGethGenesis(genesisJSON, { chain: 'custom' }) common.setHardfork(Hardfork.Shanghai) const genesisState = parseGethGenesisState(genesisJSON) - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ common, validateBlocks: false, validateConsensus: false, diff --git a/packages/vm/test/api/EIPs/eip-6110.spec.ts b/packages/vm/test/api/EIPs/eip-6110.spec.ts index f2c94aba67..5e2a065c70 100644 --- a/packages/vm/test/api/EIPs/eip-6110.spec.ts +++ b/packages/vm/test/api/EIPs/eip-6110.spec.ts @@ -1,5 +1,5 @@ -import { Block } from '@ethereumjs/block' -import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { Chain, Common, Hardfork, getInitializedChains } from '@ethereumjs/common' import { TransactionFactory } from '@ethereumjs/tx' import { Account, Address, bytesToHex, hexToBytes, randomBytes } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak.js' @@ -22,7 +22,7 @@ common['_activatedEIPsCache'] = [ 2565, 2929, 2718, 2930, 1559, 3198, 3529, 3541, 4345, 5133, 3675, 4399, 3651, 3855, 3860, 4895, 1153, 4844, 4788, 5656, 6780, 7516, 2537, 3074, 6110, 7685, ] -const DEPOSIT_CONTRACT_ADDRESS = Common.getInitializedChains().mainnet +const DEPOSIT_CONTRACT_ADDRESS = getInitializedChains().mainnet .depositContractAddress! as PrefixedHexString const pubkey = @@ -51,7 +51,7 @@ describe('EIP-6110 runBlock tests', () => { sender, Account.fromAccountData({ balance: 540000000030064771065n }) ) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { transactions: [depositTx], }, @@ -87,7 +87,7 @@ describe('EIP-7685 buildBlock tests', () => { sender, Account.fromAccountData({ balance: 540000000030064771065n }) ) - const block = Block.fromBlockData({}, { common }) + const block = createBlockFromBlockData({}, { common }) ;(vm.blockchain as any)['dbManager']['getHeader'] = () => block.header const blockBuilder = await vm.buildBlock({ parentBlock: block }) await blockBuilder.addTransaction(depositTx) diff --git a/packages/vm/test/api/EIPs/eip-6800-verkle.spec.ts b/packages/vm/test/api/EIPs/eip-6800-verkle.spec.ts index 1f7d5657cc..a13cd32fc9 100644 --- a/packages/vm/test/api/EIPs/eip-6800-verkle.spec.ts +++ b/packages/vm/test/api/EIPs/eip-6800-verkle.spec.ts @@ -1,5 +1,5 @@ -import { Block } from '@ethereumjs/block' -import { Common, Hardfork } from '@ethereumjs/common' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { Hardfork, createCustomCommon } from '@ethereumjs/common' import { EVM } from '@ethereumjs/evm' import { StatelessVerkleStateManager } from '@ethereumjs/statemanager' import { TransactionFactory } from '@ethereumjs/tx' @@ -14,7 +14,7 @@ import type { BlockData } from '@ethereumjs/block' import type { PrefixedHexString } from '@ethereumjs/util' const customChainParams = { name: 'custom', chainId: 69420, networkId: 678 } -const common = Common.custom(customChainParams, { +const common = createCustomCommon(customChainParams, { hardfork: Hardfork.Cancun, eips: [2935, 4895, 6800], }) @@ -26,9 +26,12 @@ const parentStateRoot = hexToBytes( '0x64e1a647f42e5c2e3c434531ccf529e1b3e93363a40db9fc8eec81f492123510' ) -const block = Block.fromBlockData({ ...verkleBlockJSON, transactions: decodedTxs } as BlockData, { - common, -}) +const block = createBlockFromBlockData( + { ...verkleBlockJSON, transactions: decodedTxs } as BlockData, + { + common, + } +) describe('EIP 6800 tests', () => { it('successfully run transactions statelessly using the block witness', async () => { diff --git a/packages/vm/test/api/EIPs/eip-7002.spec.ts b/packages/vm/test/api/EIPs/eip-7002.spec.ts index 0a52aa610d..a3815d348f 100644 --- a/packages/vm/test/api/EIPs/eip-7002.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7002.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { LegacyTransaction } from '@ethereumjs/tx' @@ -18,6 +18,8 @@ import { assert, describe, it } from 'vitest' import { bytesToBigInt } from '../../../../util/src/bytes.js' import { setupVM } from '../utils.js' +import type { Block } from '@ethereumjs/block' + const pkey = hexToBytes(`0x${'20'.repeat(32)}`) const addr = Address.fromPrivateKey(pkey) @@ -67,7 +69,7 @@ function generateTx(nonce: bigint) { describe('EIP-7002 tests', () => { it('should correctly create requests', async () => { const vm = await setupVM({ common }) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { number: 1, @@ -92,7 +94,7 @@ describe('EIP-7002 tests', () => { const tx = generateTx(BigInt(0)) // Call withdrawals contract with a withdrawals request - const block2 = Block.fromBlockData( + const block2 = createBlockFromBlockData( { header: { number: 2, @@ -138,7 +140,7 @@ describe('EIP-7002 tests', () => { const tx2 = generateTx(BigInt(1)) const tx3 = generateTx(BigInt(2)) - const block3 = Block.fromBlockData( + const block3 = createBlockFromBlockData( { header: { number: 3, @@ -163,7 +165,7 @@ describe('EIP-7002 tests', () => { it('should throw when contract is not deployed', async () => { const vm = await setupVM({ common }) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { number: 1, diff --git a/packages/vm/test/api/EIPs/eip-7685.spec.ts b/packages/vm/test/api/EIPs/eip-7685.spec.ts index ebbbed760e..1053058f4c 100644 --- a/packages/vm/test/api/EIPs/eip-7685.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7685.spec.ts @@ -1,5 +1,5 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockFromBlockData, genRequestsTrieRoot } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { DepositRequest, @@ -34,7 +34,7 @@ const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Cancun, eip describe('EIP-7685 runBlock tests', () => { it('should not error when a valid requestsRoot is provided', async () => { const vm = await setupVM({ common }) - const emptyBlock = Block.fromBlockData({}, { common }) + const emptyBlock = createBlockFromBlockData({}, { common }) const res = await vm.runBlock({ block: emptyBlock, generate: true, @@ -44,7 +44,7 @@ describe('EIP-7685 runBlock tests', () => { it('should error when an invalid requestsRoot is provided', async () => { const vm = await setupVM({ common }) - const emptyBlock = Block.fromBlockData( + const emptyBlock = createBlockFromBlockData( { header: { requestsRoot: invalidRequestsRoot } }, { common } ) @@ -57,8 +57,8 @@ describe('EIP-7685 runBlock tests', () => { it('should not throw invalid requestsRoot error when valid requests are provided', async () => { const vm = await setupVM({ common }) const request = getRandomDepositRequest() - const requestsRoot = await Block.genRequestsTrieRoot([request]) - const block = Block.fromBlockData( + const requestsRoot = await genRequestsTrieRoot([request]) + const block = createBlockFromBlockData( { requests: [request], header: { requestsRoot }, @@ -70,7 +70,7 @@ describe('EIP-7685 runBlock tests', () => { it('should error when requestsRoot does not match requests provided', async () => { const vm = await setupVM({ common }) const request = getRandomDepositRequest() - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { requests: [request], header: { requestsRoot: invalidRequestsRoot }, @@ -88,11 +88,11 @@ describe('EIP 7685 buildBlock tests', () => { hardfork: Hardfork.Cancun, eips: [7685, 1559, 4895, 4844, 4788], }) - const genesisBlock = Block.fromBlockData( + const genesisBlock = createBlockFromBlockData( { header: { gasLimit: 50000, baseFeePerGas: 100 } }, { common } ) - const blockchain = await Blockchain.create({ genesisBlock, common, validateConsensus: false }) + const blockchain = await createBlockchain({ genesisBlock, common, validateConsensus: false }) const vm = await VM.create({ common, blockchain }) const blockBuilder = await vm.buildBlock({ parentBlock: genesisBlock, diff --git a/packages/vm/test/api/buildBlock.spec.ts b/packages/vm/test/api/buildBlock.spec.ts index 66a63835cb..6d8a4af233 100644 --- a/packages/vm/test/api/buildBlock.spec.ts +++ b/packages/vm/test/api/buildBlock.spec.ts @@ -1,6 +1,6 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' -import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' +import { Chain, Common, Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common' import { FeeMarketEIP1559Transaction, LegacyTransaction } from '@ethereumjs/tx' import { Account, Address, concatBytes, hexToBytes } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' @@ -15,8 +15,8 @@ const pKeyAddress = Address.fromPrivateKey(privateKey) describe('BlockBuilder', () => { it('should build a valid block', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const genesisBlock = Block.fromBlockData({ header: { gasLimit: 50000 } }, { common }) - const blockchain = await Blockchain.create({ genesisBlock, common, validateConsensus: false }) + const genesisBlock = createBlockFromBlockData({ header: { gasLimit: 50000 } }, { common }) + const blockchain = await createBlockchain({ genesisBlock, common, validateConsensus: false }) const vm = await VM.create({ common, blockchain }) await setBalance(vm, pKeyAddress) @@ -52,7 +52,7 @@ describe('BlockBuilder', () => { it('should throw if adding a transaction exceeds the block gas limit', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) const vm = await VM.create({ common }) - const genesis = Block.fromBlockData({}, { common }) + const genesis = createBlockFromBlockData({}, { common }) const blockBuilder = await vm.buildBlock({ parentBlock: genesis }) const gasLimit = genesis.header.gasLimit + BigInt(1) @@ -80,8 +80,8 @@ describe('BlockBuilder', () => { it('should correctly seal a PoW block', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const genesisBlock = Block.fromBlockData({ header: { gasLimit: 50000 } }, { common }) - const blockchain = await Blockchain.create({ genesisBlock, common, validateConsensus: false }) + const genesisBlock = createBlockFromBlockData({ header: { gasLimit: 50000 } }, { common }) + const blockchain = await createBlockchain({ genesisBlock, common, validateConsensus: false }) const vm = await VM.create({ common, blockchain }) await setBalance(vm, pKeyAddress) @@ -166,7 +166,7 @@ describe('BlockBuilder', () => { extraData: extraData2, alloc: { [addr]: { balance: '0x10000000000000000000' } }, } - const common = Common.fromGethGenesis(chainData, { + const common = createCommonFromGethGenesis(chainData, { chain: 'devnet', hardfork: Hardfork.Istanbul, }) @@ -174,11 +174,11 @@ describe('BlockBuilder', () => { // extraData: [vanity, activeSigner, seal] const extraData = concatBytes(new Uint8Array(32), signer.address.toBytes(), new Uint8Array(65)) const cliqueSigner = signer.privateKey - const genesisBlock = Block.fromBlockData( + const genesisBlock = createBlockFromBlockData( { header: { gasLimit: 50000, extraData } }, { common, cliqueSigner } ) - const blockchain = await Blockchain.create({ genesisBlock, common }) + const blockchain = await createBlockchain({ genesisBlock, common }) const vm = await VM.create({ common, blockchain }) // add balance for tx @@ -210,8 +210,8 @@ describe('BlockBuilder', () => { it('should throw if block already built or reverted', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const genesisBlock = Block.fromBlockData({ header: { gasLimit: 50000 } }, { common }) - const blockchain = await Blockchain.create({ genesisBlock, common, validateConsensus: false }) + const genesisBlock = createBlockFromBlockData({ header: { gasLimit: 50000 } }, { common }) + const blockchain = await createBlockchain({ genesisBlock, common, validateConsensus: false }) const vm = await VM.create({ common, blockchain }) await setBalance(vm, pKeyAddress) @@ -264,8 +264,8 @@ describe('BlockBuilder', () => { it('should build a block without any txs', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const genesisBlock = Block.fromBlockData({ header: { gasLimit: 50000 } }, { common }) - const blockchain = await Blockchain.create({ genesisBlock, common, validateConsensus: false }) + const genesisBlock = createBlockFromBlockData({ header: { gasLimit: 50000 } }, { common }) + const blockchain = await createBlockchain({ genesisBlock, common, validateConsensus: false }) const vm = await VM.create({ common, blockchain }) const vmCopy = await vm.shallowCopy() @@ -286,11 +286,11 @@ describe('BlockBuilder', () => { it('should build a 1559 block with legacy and 1559 txs', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London, eips: [1559] }) - const genesisBlock = Block.fromBlockData( + const genesisBlock = createBlockFromBlockData( { header: { gasLimit: 50000, baseFeePerGas: 100 } }, { common } ) - const blockchain = await Blockchain.create({ genesisBlock, common, validateConsensus: false }) + const blockchain = await createBlockchain({ genesisBlock, common, validateConsensus: false }) const vm = await VM.create({ common, blockchain }) await setBalance(vm, pKeyAddress) diff --git a/packages/vm/test/api/customChain.spec.ts b/packages/vm/test/api/customChain.spec.ts index 3620a03fe2..d95f78d2d7 100644 --- a/packages/vm/test/api/customChain.spec.ts +++ b/packages/vm/test/api/customChain.spec.ts @@ -1,5 +1,5 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockFromBlockData } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' import { Common, Hardfork } from '@ethereumjs/common' import { TransactionFactory } from '@ethereumjs/tx' import { Address, bytesToHex, hexToBytes } from '@ethereumjs/util' @@ -49,7 +49,7 @@ const common = new Common({ hardfork: Hardfork.Chainstart, customChains: [testChain] as ChainConfig[], }) -const block = Block.fromBlockData( +const block = createBlockFromBlockData( { header: { gasLimit: 21_000, @@ -63,7 +63,7 @@ const privateKey = hexToBytes('0xe331b6d69882b4cb4ea581d88e0b604039a3de5967688d3 describe('VM initialized with custom state', () => { it('should transfer eth from already existent account', async () => { - const blockchain = await Blockchain.create({ common, genesisState }) + const blockchain = await createBlockchain({ common, genesisState }) const vm = await VM.create({ blockchain, common, genesisState }) const to = '0x00000000000000000000000000000000000000ff' @@ -90,7 +90,7 @@ describe('VM initialized with custom state', () => { }) it('should retrieve value from storage', async () => { - const blockchain = await Blockchain.create({ common, genesisState }) + const blockchain = await createBlockchain({ common, genesisState }) common.setHardfork(Hardfork.London) const vm = await VM.create({ blockchain, common, genesisState }) const sigHash = new Interface(['function retrieve()']).getSighash( diff --git a/packages/vm/test/api/genesis.spec.ts b/packages/vm/test/api/genesis.spec.ts index d9df6ae020..90e15ac6b5 100644 --- a/packages/vm/test/api/genesis.spec.ts +++ b/packages/vm/test/api/genesis.spec.ts @@ -1,4 +1,4 @@ -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockchain } from '@ethereumjs/blockchain' import { Chain } from '@ethereumjs/common' import { getGenesis } from '@ethereumjs/genesis' import { assert, describe, it } from 'vitest' @@ -10,7 +10,7 @@ describe('genesis', () => { const f = async () => { const genesisState = getGenesis(Chain.Mainnet) - const blockchain = await Blockchain.create({ genesisState }) + const blockchain = await createBlockchain({ genesisState }) await VM.create({ blockchain, genesisState }) } diff --git a/packages/vm/test/api/index.spec.ts b/packages/vm/test/api/index.spec.ts index dcad18f364..f83380be60 100644 --- a/packages/vm/test/api/index.spec.ts +++ b/packages/vm/test/api/index.spec.ts @@ -1,4 +1,4 @@ -import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { Chain, Common, Hardfork, createCustomCommon } from '@ethereumjs/common' import { EVM } from '@ethereumjs/evm' import { Account, Address, KECCAK256_RLP, hexToBytes } from '@ethereumjs/util' import * as util from 'util' // eslint-disable-line @typescript-eslint/no-unused-vars @@ -153,7 +153,7 @@ describe('VM -> common (chain, HFs, EIPs)', () => { it('should only accept valid chain and fork', async () => { // let common = new Common({ chain: Chain.Ropsten, hardfork: Hardfork.Byzantium }) - let common = Common.custom({ chainId: 3 }) + let common = createCustomCommon({ chainId: 3 }) common.setHardfork(Hardfork.Byzantium) let vm = await VM.create({ common }) assert.equal(vm.common.param('gasPrices', 'ecAdd'), BigInt(500)) @@ -181,9 +181,9 @@ describe('VM -> common (chain, HFs, EIPs)', () => { } }) - it('should accept a custom chain config (Common.custom() static constructor)', async () => { + it('should accept a custom chain config (createCustomCommon() static constructor)', async () => { const customChainParams = { name: 'custom', chainId: 123, networkId: 678 } - const common = Common.custom(customChainParams, { + const common = createCustomCommon(customChainParams, { baseChain: 'mainnet', hardfork: 'byzantium', }) diff --git a/packages/vm/test/api/runBlock.spec.ts b/packages/vm/test/api/runBlock.spec.ts index cfc78497b4..78702db2ce 100644 --- a/packages/vm/test/api/runBlock.spec.ts +++ b/packages/vm/test/api/runBlock.spec.ts @@ -1,5 +1,9 @@ -import { Block } from '@ethereumjs/block' -import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { + createBlockFromBlockData, + createBlockFromRLPSerializedBlock, + createBlockFromValuesArray, +} from '@ethereumjs/block' +import { Chain, Common, Hardfork, createCustomCommon } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { AccessListEIP2930Transaction, @@ -40,7 +44,7 @@ import type { PreByzantiumTxReceipt, RunBlockOpts, } from '../../src/types' -import type { BlockBytes } from '@ethereumjs/block' +import type { Block, BlockBytes } from '@ethereumjs/block' import type { AuthorizationListBytesItem, ChainConfig } from '@ethereumjs/common' import type { DefaultStateManager } from '@ethereumjs/statemanager' import type { TypedTransaction } from '@ethereumjs/tx' @@ -51,10 +55,10 @@ describe('runBlock() -> successful API parameter usage', async () => { async function simpleRun(vm: VM) { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) const genesisRlp = hexToBytes(testData.genesisRLP as PrefixedHexString) - const genesis = Block.fromRLPSerializedBlock(genesisRlp, { common }) + const genesis = createBlockFromRLPSerializedBlock(genesisRlp, { common }) const blockRlp = hexToBytes(testData.blocks[0].rlp as PrefixedHexString) - const block = Block.fromRLPSerializedBlock(blockRlp, { common }) + const block = createBlockFromRLPSerializedBlock(blockRlp, { common }) await setupPreConditions(vm.stateManager, testData) @@ -85,7 +89,7 @@ describe('runBlock() -> successful API parameter usage', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) const block1Rlp = hexToBytes(testData.blocks[0].rlp as PrefixedHexString) - const block1 = Block.fromRLPSerializedBlock(block1Rlp, { common }) + const block1 = createBlockFromRLPSerializedBlock(block1Rlp, { common }) await vm.runBlock({ block: block1, root: (vm.stateManager as DefaultStateManager)['_trie'].root(), @@ -94,7 +98,7 @@ describe('runBlock() -> successful API parameter usage', async () => { }) const block2Rlp = hexToBytes(testData.blocks[1].rlp as PrefixedHexString) - const block2 = Block.fromRLPSerializedBlock(block2Rlp, { common }) + const block2 = createBlockFromRLPSerializedBlock(block2Rlp, { common }) await vm.runBlock({ block: block2, @@ -104,7 +108,7 @@ describe('runBlock() -> successful API parameter usage', async () => { }) const block3Rlp = toBytes(testData.blocks[2].rlp as PrefixedHexString) - const block3 = Block.fromRLPSerializedBlock(block3Rlp, { common }) + const block3 = createBlockFromRLPSerializedBlock(block3Rlp, { common }) await vm.runBlock({ block: block3, @@ -134,9 +138,12 @@ describe('runBlock() -> successful API parameter usage', async () => { await uncleRun(vm) }) - it('PoW block, Common custom chain (Common.custom() static constructor)', async () => { + it('PoW block, Common custom chain (createCustomCommon() static constructor)', async () => { const customChainParams = { name: 'custom', chainId: 123, networkId: 678 } - const common = Common.custom(customChainParams, { baseChain: 'mainnet', hardfork: 'berlin' }) + const common = createCustomCommon(customChainParams, { + baseChain: 'mainnet', + hardfork: 'berlin', + }) const vm = await setupVM({ common }) await simpleRun(vm) }) @@ -165,7 +172,7 @@ describe('runBlock() -> successful API parameter usage', async () => { ) function getBlock(common: Common): Block { - return Block.fromBlockData( + return createBlockFromBlockData( { header: { number: BigInt(10000000), @@ -216,7 +223,7 @@ describe('runBlock() -> API parameter usage/data errors', async () => { it('should fail when runTx fails', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) const blockRlp = hexToBytes(testData.blocks[0].rlp as PrefixedHexString) - const block = Block.fromRLPSerializedBlock(blockRlp, { common }) + const block = createBlockFromRLPSerializedBlock(blockRlp, { common }) // The mocked VM uses a mocked runTx // which always returns an error. @@ -229,7 +236,7 @@ describe('runBlock() -> API parameter usage/data errors', async () => { it('should fail when block gas limit higher than 2^63-1', async () => { const vm = await VM.create({ common }) - const block = Block.fromBlockData({ + const block = createBlockFromBlockData({ header: { gasLimit: hexToBytes('0x8000000000000000'), }, @@ -244,7 +251,7 @@ describe('runBlock() -> API parameter usage/data errors', async () => { const vm = await VM.create({ common }) const blockRlp = hexToBytes(testData.blocks[0].rlp as PrefixedHexString) - const block = Object.create(Block.fromRLPSerializedBlock(blockRlp, { common })) + const block = Object.create(createBlockFromRLPSerializedBlock(blockRlp, { common })) await vm .runBlock({ block }) @@ -260,7 +267,7 @@ describe('runBlock() -> API parameter usage/data errors', async () => { it('should fail when no `validateHeader` method exists on blockchain class', async () => { const vm = await VM.create({ common }) const blockRlp = hexToBytes(testData.blocks[0].rlp as PrefixedHexString) - const block = Object.create(Block.fromRLPSerializedBlock(blockRlp, { common })) + const block = Object.create(createBlockFromRLPSerializedBlock(blockRlp, { common })) ;(vm.blockchain as any).validateHeader = undefined try { await vm.runBlock({ block }) @@ -277,7 +284,7 @@ describe('runBlock() -> API parameter usage/data errors', async () => { const vm = await VM.create({ common }) const blockRlp = hexToBytes(testData.blocks[0].rlp as PrefixedHexString) - const block = Object.create(Block.fromRLPSerializedBlock(blockRlp, { common })) + const block = Object.create(createBlockFromRLPSerializedBlock(blockRlp, { common })) // modify first tx's gasLimit const { nonce, gasPrice, to, value, data, v, r, s } = block.transactions[0] @@ -305,7 +312,7 @@ describe('runBlock() -> runtime behavior', async () => { const block1 = RLP.decode(testData.blocks[0].rlp as PrefixedHexString) as NestedUint8Array // edit extra data of this block to "dao-hard-fork" block1[0][12] = utf8ToBytes('dao-hard-fork') - const block = Block.fromValuesArray(block1 as BlockBytes, { common }) + const block = createBlockFromValuesArray(block1 as BlockBytes, { common }) await setupPreConditions(vm.stateManager, testData) // fill two original DAO child-contracts with funds and the recovery account with funds in order to verify that the balance gets summed correctly @@ -376,7 +383,7 @@ describe('runBlock() -> runtime behavior', async () => { ).sign(otherUser.privateKey) // create block with the signer and txs - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { extraData: new Uint8Array(97) }, transactions: [tx, tx] }, { common, cliqueSigner: signer.privateKey } ) @@ -420,7 +427,7 @@ it('should correctly reflect generated fields', async () => { // get a receipt trie root of for the empty receipts set, // which is a well known constant. const bytes32Zeros = new Uint8Array(32) - const block = Block.fromBlockData({ + const block = createBlockFromBlockData({ header: { receiptTrie: bytes32Zeros, transactionsTrie: bytes32Zeros, gasUsed: BigInt(1) }, }) @@ -440,7 +447,7 @@ async function runWithHf(hardfork: string) { const vm = await setupVM({ common }) const blockRlp = hexToBytes(testData.blocks[0].rlp as PrefixedHexString) - const block = Block.fromRLPSerializedBlock(blockRlp, { common }) + const block = createBlockFromRLPSerializedBlock(blockRlp, { common }) await setupPreConditions(vm.stateManager, testData) @@ -476,7 +483,7 @@ describe('runBlock() -> tx types', async () => { const common = vm.common const blockRlp = hexToBytes(testData.blocks[0].rlp as PrefixedHexString) - const block = Block.fromRLPSerializedBlock(blockRlp, { common, freeze: false }) + const block = createBlockFromRLPSerializedBlock(blockRlp, { common, freeze: false }) //@ts-ignore read-only property block.transactions = transactions @@ -657,7 +664,7 @@ describe('runBlock() -> tx types', async () => { }, { common } ).sign(defaultSenderPkey) - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { transactions: [tx1, tx2], }, diff --git a/packages/vm/test/api/runTx.spec.ts b/packages/vm/test/api/runTx.spec.ts index d072511525..a8c05dc32a 100644 --- a/packages/vm/test/api/runTx.spec.ts +++ b/packages/vm/test/api/runTx.spec.ts @@ -1,6 +1,6 @@ -import { Block, BlockHeader } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' -import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { BlockHeader, createBlockFromBlockData } from '@ethereumjs/block' +import { Blockchain, createBlockchain } from '@ethereumjs/blockchain' +import { Chain, Common, Hardfork, createCommonFromGethGenesis } from '@ethereumjs/common' import { BlobEIP4844Transaction, FeeMarketEIP1559Transaction, @@ -55,7 +55,7 @@ describe('runTx() -> successful API parameter usage', async () => { let block if (vm.common.consensusType() === 'poa') { // Setup block with correct extraData for POA - block = Block.fromBlockData( + block = createBlockFromBlockData( { header: { extraData: new Uint8Array(97) } }, { common: vm.common } ) @@ -74,7 +74,7 @@ describe('runTx() -> successful API parameter usage', async () => { common = new Common({ chain: Chain.Goerli, hardfork: Hardfork.London }) vm = await VM.create({ common, - blockchain: await Blockchain.create({ validateConsensus: false, validateBlocks: false }), + blockchain: await createBlockchain({ validateConsensus: false, validateBlocks: false }), }) await simpleRun(vm, 'goerli (PoA), london HF, default SM - should run without errors') }) @@ -83,13 +83,13 @@ describe('runTx() -> successful API parameter usage', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) const vm = await VM.create({ common, - blockchain: await Blockchain.create({ validateConsensus: false, validateBlocks: false }), + blockchain: await createBlockchain({ validateConsensus: false, validateBlocks: false }), }) const tx = getTransaction(vm.common, 0, true) const caller = tx.getSenderAddress() const acc = createAccount() await vm.stateManager.putAccount(caller, acc) - const block = Block.fromBlockData({}, { common: vm.common.copy() }) + const block = createBlockFromBlockData({}, { common: vm.common.copy() }) await vm.runTx({ tx, block }) assert.ok(true, 'matched hardfork should run without throwing') }) @@ -98,13 +98,13 @@ describe('runTx() -> successful API parameter usage', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) const vm = await VM.create({ common, - blockchain: await Blockchain.create({ validateConsensus: false, validateBlocks: false }), + blockchain: await createBlockchain({ validateConsensus: false, validateBlocks: false }), }) const tx = getTransaction(vm.common, 0, true) const caller = tx.getSenderAddress() const acc = createAccount() await vm.stateManager.putAccount(caller, acc) - const block = Block.fromBlockData({}, { common: vm.common.copy() }) + const block = createBlockFromBlockData({}, { common: vm.common.copy() }) block.common.setHardfork(Hardfork.Paris) try { @@ -141,13 +141,13 @@ describe('runTx() -> successful API parameter usage', async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Paris }) const vm = await VM.create({ common, - blockchain: await Blockchain.create({ validateConsensus: false, validateBlocks: false }), + blockchain: await createBlockchain({ validateConsensus: false, validateBlocks: false }), }) const tx = getTransaction(vm.common, 0, true) const caller = tx.getSenderAddress() const acc = createAccount() await vm.stateManager.putAccount(caller, acc) - const block = Block.fromBlockData({}, { common: vm.common.copy() }) + const block = createBlockFromBlockData({}, { common: vm.common.copy() }) tx.common.setHardfork(Hardfork.GrayGlacier) block.common.setHardfork(Hardfork.GrayGlacier) @@ -227,7 +227,7 @@ describe('runTx() -> successful API parameter usage', async () => { const tx = unsignedTx.sign(privateKey) const coinbase = hexToBytes('0x00000000000000000000000000000000000000ff') - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: { gasLimit: transferCost - 1, @@ -432,7 +432,7 @@ describe('runTx() -> API parameter usage/data errors', () => { for (const txType of TRANSACTION_TYPES) { const vm = await VM.create({ common }) const tx = getTransaction(vm.common, txType.type, true) - const block = Block.fromBlockData({ header: { baseFeePerGas: 100000 } }, { common }) + const block = createBlockFromBlockData({ header: { baseFeePerGas: 100000 } }, { common }) try { await vm.runTx({ tx, block }) assert.fail('should fail') @@ -714,7 +714,7 @@ describe('runTx() -> consensus bugs', () => { const tx = FeeMarketEIP1559Transaction.fromTxData(txData, { common }).sign(pkey) - const block = Block.fromBlockData({ header: { baseFeePerGas: 0x0c } }, { common }) + const block = createBlockFromBlockData({ header: { baseFeePerGas: 0x0c } }, { common }) const result = await vm.runTx({ tx, block }) assert.equal( @@ -881,7 +881,7 @@ describe('EIP 4844 transaction tests', () => { const kzg = await loadKZG() const genesisJson = await import('../../../block/test/testdata/4844-hardfork.json') - const common = Common.fromGethGenesis(genesisJson, { + const common = createCommonFromGethGenesis(genesisJson, { chain: 'customChain', hardfork: Hardfork.Cancun, customCrypto: { kzg }, @@ -892,7 +892,7 @@ describe('EIP 4844 transaction tests', () => { // Stub getBlock to produce a valid parent header under EIP 4844 Blockchain.prototype.getBlock = async () => { - return Block.fromBlockData( + return createBlockFromBlockData( { header: BlockHeader.fromHeaderData( { @@ -912,7 +912,7 @@ describe('EIP 4844 transaction tests', () => { } ) } - const blockchain = await Blockchain.create({ + const blockchain = await createBlockchain({ validateBlocks: false, validateConsensus: false, }) @@ -920,7 +920,7 @@ describe('EIP 4844 transaction tests', () => { const tx = getTransaction(common, 3, true) as BlobEIP4844Transaction - const block = Block.fromBlockData( + const block = createBlockFromBlockData( { header: BlockHeader.fromHeaderData( { diff --git a/packages/vm/test/api/types.spec.ts b/packages/vm/test/api/types.spec.ts index e484ff6170..e223ba8d8b 100644 --- a/packages/vm/test/api/types.spec.ts +++ b/packages/vm/test/api/types.spec.ts @@ -1,4 +1,4 @@ -import { Block } from '@ethereumjs/block' +import { createBlockFromBlockData } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { AccessListEIP2930Transaction, LegacyTransaction } from '@ethereumjs/tx' import { assert, describe, it } from 'vitest' @@ -24,7 +24,7 @@ describe('[Types]', () => { const block: Omit< Required, 'withdrawals' | 'executionWitness' - > = Block.fromBlockData({}, { common }) + > = createBlockFromBlockData({}, { common }) assert.ok(block, 'block') // Transactions diff --git a/packages/vm/test/api/utils.ts b/packages/vm/test/api/utils.ts index 82f6c30263..4f138b97bb 100644 --- a/packages/vm/test/api/utils.ts +++ b/packages/vm/test/api/utils.ts @@ -1,4 +1,4 @@ -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockchain } from '@ethereumjs/blockchain' import { TransactionFactory, TransactionType } from '@ethereumjs/tx' import { Account, @@ -33,7 +33,7 @@ export async function setupVM(opts: VMOpts & { genesisBlock?: Block } = {}) { const db: any = new LevelDB(new MemoryLevel()) const { common, genesisBlock } = opts if (opts.blockchain === undefined) { - opts.blockchain = await Blockchain.create({ + opts.blockchain = await createBlockchain({ db, validateBlocks: false, validateConsensus: false, diff --git a/packages/vm/test/tester/config.ts b/packages/vm/test/tester/config.ts index 6cccaa4a76..3aa1f00701 100644 --- a/packages/vm/test/tester/config.ts +++ b/packages/vm/test/tester/config.ts @@ -1,4 +1,4 @@ -import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { Chain, Common, Hardfork, createCustomCommon } from '@ethereumjs/common' import * as path from 'path' import type { Kzg } from '@ethereumjs/util' @@ -265,7 +265,7 @@ function setupCommonWithNetworks(network: string, ttd?: number, timestamp?: numb } } } - const common = Common.custom( + const common = createCustomCommon( { hardforks: testHardforks, defaultHardfork: hfName, @@ -349,7 +349,7 @@ export function getCommon(network: string, kzg?: Kzg): Common { }) } } - const common = Common.custom( + const common = createCustomCommon( { hardforks: testHardforks, }, diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index a68a76f84e..b8f2b50928 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -1,5 +1,5 @@ -import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockFromBlockData, createBlockFromRLPSerializedBlock } from '@ethereumjs/block' +import { createBlockchain } from '@ethereumjs/blockchain' import { ConsensusAlgorithm } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { DefaultStateManager } from '@ethereumjs/statemanager' @@ -18,6 +18,7 @@ import { import { VM } from '../../../src/vm' import { setupPreConditions, verifyPostConditions } from '../../util' +import type { Block } from '@ethereumjs/block' import type { EthashConsensus } from '@ethereumjs/blockchain' import type { Common } from '@ethereumjs/common' import type { PrefixedHexString } from '@ethereumjs/util' @@ -65,14 +66,14 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes const header = formatBlockHeader(testData.genesisBlockHeader) const withdrawals = common.isActivatedEIP(4895) ? [] : undefined const blockData = { header, withdrawals } - const genesisBlock = Block.fromBlockData(blockData, { common }) + const genesisBlock = createBlockFromBlockData(blockData, { common }) if (typeof testData.genesisRLP === 'string') { const rlp = toBytes(testData.genesisRLP) t.deepEquals(genesisBlock.serialize(), rlp, 'correct genesis RLP') } - let blockchain = await Blockchain.create({ + let blockchain = await createBlockchain({ common, validateBlocks: true, validateConsensus: validatePow, @@ -186,7 +187,7 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes await blockBuilder.revert() // will only revert if checkpointed } - const block = Block.fromRLPSerializedBlock(blockRlp, { common, setHardfork: TD }) + const block = createBlockFromRLPSerializedBlock(blockRlp, { common, setHardfork: TD }) await blockchain.putBlock(block) // This is a trick to avoid generating the canonical genesis diff --git a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts index 647cf5d9a1..28edd7bf30 100644 --- a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts +++ b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts @@ -1,12 +1,12 @@ import { Block } from '@ethereumjs/block' -import { Blockchain } from '@ethereumjs/blockchain' +import { createBlockchain } from '@ethereumjs/blockchain' import { type InterpreterStep } from '@ethereumjs/evm' import { DefaultStateManager } from '@ethereumjs/statemanager' import { Trie } from '@ethereumjs/trie' import { Account, Address, bytesToHex, equalsBytes, toBytes } from '@ethereumjs/util' -import { VM } from '../../../src/vm' -import { makeBlockFromEnv, makeTx, setupPreConditions } from '../../util' +import { VM } from '../../../src/index.js' +import { makeBlockFromEnv, makeTx, setupPreConditions } from '../../util.js' import type * as tape from 'tape' @@ -72,7 +72,7 @@ async function runTestCase(options: any, testData: any, t: tape.Test) { // Have to create a blockchain with empty block as genesisBlock for Merge // Otherwise mainnet genesis will throw since this has difficulty nonzero const genesisBlock = new Block(undefined, undefined, undefined, undefined, { common }) - const blockchain = await Blockchain.create({ genesisBlock, common }) + const blockchain = await createBlockchain({ genesisBlock, common }) const state = new Trie({ useKeyHashing: true, common }) const stateManager = new DefaultStateManager({ trie: state, diff --git a/packages/vm/test/util.ts b/packages/vm/test/util.ts index c77739d7f3..8b7cd25c77 100644 --- a/packages/vm/test/util.ts +++ b/packages/vm/test/util.ts @@ -1,9 +1,10 @@ import { Block, BlockHeader } from '@ethereumjs/block' -import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { Chain, Common, Hardfork, createCustomCommon } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { AccessListEIP2930Transaction, BlobEIP4844Transaction, + EOACodeEIP7702Transaction, FeeMarketEIP1559Transaction, LegacyTransaction, } from '@ethereumjs/tx' @@ -18,6 +19,7 @@ import { isHexString, setLengthLeft, toBytes, + unpadBytes, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' @@ -116,12 +118,24 @@ export function makeTx( txData: any, opts?: TxOptions ): + | EOACodeEIP7702Transaction | BlobEIP4844Transaction | FeeMarketEIP1559Transaction | AccessListEIP2930Transaction | LegacyTransaction { let tx - if (txData.blobVersionedHashes !== undefined) { + if (txData.authorizationList !== undefined) { + // Convert `v` keys to `yParity` + for (const signature of txData.authorizationList) { + if (signature.v !== undefined) { + signature.yParity = bytesToHex(unpadBytes(hexToBytes(signature.v))) + } + if (signature.nonce !== undefined && signature.nonce[0] === '0x00') { + signature.nonce[0] = '0x' + } + } + tx = EOACodeEIP7702Transaction.fromTxData(txData, opts) + } else if (txData.blobVersionedHashes !== undefined) { tx = BlobEIP4844Transaction.fromTxData(txData, opts) } else if (txData.maxFeePerGas !== undefined) { tx = FeeMarketEIP1559Transaction.fromTxData(txData, opts) @@ -399,7 +413,7 @@ export function getDAOCommon(activationBlock: number) { editedForks.push(fork) } } - const DAOCommon = Common.custom( + const DAOCommon = createCustomCommon( { hardforks: editedForks, }, diff --git a/packages/vm/tsconfig.benchmarks.json b/packages/vm/tsconfig.benchmarks.json index ae8a7db8e2..e05b9c483f 100644 --- a/packages/vm/tsconfig.benchmarks.json +++ b/packages/vm/tsconfig.benchmarks.json @@ -1,5 +1,5 @@ { - "extends": "../../config/tsconfig.json", + "extends": "../../config/tsconfig.prod.esm.json", "compilerOptions": { "lib": ["dom"], "sourceMap": false, diff --git a/packages/wallet/package.json b/packages/wallet/package.json index 505e23e9a6..ee4a11d329 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -17,7 +17,8 @@ }, "license": "MIT", "author": "Alex Beregszaszi ", - "type": "commonjs", + "type": "module", + "sideEffects": false, "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "exports": {