From bcb48d20348a61c32cf0273bca7b3aa19ac4d866 Mon Sep 17 00:00:00 2001 From: harkamal Date: Fri, 27 Sep 2024 14:32:24 +0530 Subject: [PATCH 01/49] vm: update the system contract addresses for prague devnet4 --- .../test/rpc/engine/newPayloadV4.spec.ts | 20 +++++++++---------- packages/vm/src/params.ts | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/client/test/rpc/engine/newPayloadV4.spec.ts b/packages/client/test/rpc/engine/newPayloadV4.spec.ts index 557f106eb2..8de2af52a9 100644 --- a/packages/client/test/rpc/engine/newPayloadV4.spec.ts +++ b/packages/client/test/rpc/engine/newPayloadV4.spec.ts @@ -12,9 +12,9 @@ const [blockData] = beaconData const parentBeaconBlockRoot = '0x42942949c4ed512cd85c2cb54ca88591338cbb0564d3a2bea7961a639ef29d64' const validForkChoiceState = { - headBlockHash: '0x5040e6b0056398536751c187683a3ecde8aff8fd9ea1d3450d687d7032134caf', - safeBlockHash: '0x5040e6b0056398536751c187683a3ecde8aff8fd9ea1d3450d687d7032134caf', - finalizedBlockHash: '0x5040e6b0056398536751c187683a3ecde8aff8fd9ea1d3450d687d7032134caf', + headBlockHash: '0xdc1727ee420fead1d6a8dfe548758e5a5a48eed888b162278534b105b9909cf9', + safeBlockHash: '0xdc1727ee420fead1d6a8dfe548758e5a5a48eed888b162278534b105b9909cf9', + finalizedBlockHash: '0xdc1727ee420fead1d6a8dfe548758e5a5a48eed888b162278534b105b9909cf9', } const validPayloadAttributes = { timestamp: '0x64ba84fd', @@ -41,16 +41,16 @@ const electraGenesisContracts = { code: '0x3373fffffffffffffffffffffffffffffffffffffffe1460575767ffffffffffffffff5f3511605357600143035f3511604b575f35612000014311604b57611fff5f3516545f5260205ff35b5f5f5260205ff35b5f5ffd5b5f35611fff60014303165500', }, // consolidation requests contract - '0x00b42dbF2194e931E80326D950320f7d9Dbeac02': { + '0x00706203067988Ab3E2A2ab626EdCD6f28bDBbbb': { balance: '0', nonce: '1', - code: '0x3373fffffffffffffffffffffffffffffffffffffffe146098573615156028575f545f5260205ff35b36606014156101445760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061014457600154600101600155600354806004026004013381556001015f35815560010160203581556001016040359055600101600355005b6003546002548082038060011160ac575060015b5f5b81811460f15780607402838201600402600401805490600101805490600101805490600101549260601b84529083601401528260340152906054015260010160ae565b9101809214610103579060025561010e565b90505f6002555f6003555b5f548061049d141561011d57505f5b6001546001828201116101325750505f610138565b01600190035b5f555f6001556074025ff35b5f5ffd', + code: '0x3373fffffffffffffffffffffffffffffffffffffffe1460a8573615156028575f545f5260205ff35b36606014156101555760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061015557600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060011160bc575060015b5f5b8181146101025780607402838201600402600401805490600101805490600101805490600101549260601b84529083601401528260340152906054015260010160be565b9101809214610114579060025561011f565b90505f6002555f6003555b5f548061049d141561012e57505f5b6001546001828201116101435750505f610149565b01600190035b5f555f6001556074025ff35b5f5ffd', }, // withdrawals request contract - '0x00A3ca265EBcb825B45F985A16CEFB49958cE017': { + '0x05F27129610CB42103b665629CB5c8C00296AaAa': { balance: '0', nonce: '1', - code: '0x3373fffffffffffffffffffffffffffffffffffffffe146090573615156028575f545f5260205ff35b366038141561012e5760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061012e57600154600101600155600354806003026004013381556001015f3581556001016020359055600101600355005b6003546002548082038060101160a4575060105b5f5b81811460dd5780604c02838201600302600401805490600101805490600101549160601b83528260140152906034015260010160a6565b910180921460ed579060025560f8565b90505f6002555f6003555b5f548061049d141561010757505f5b60015460028282011161011c5750505f610122565b01600290035b5f555f600155604c025ff35b5f5ffd', + code: '0x3373fffffffffffffffffffffffffffffffffffffffe1460a0573615156028575f545f5260205ff35b366038141561013f5760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061013f57600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160b4575060105b5f5b81811460ed5780604c02838201600302600401805490600101805490600101549160601b83528260140152906034015260010160b6565b910180921460fe5790600255610109565b90505f6002555f6003555b5f548061049d141561011857505f5b60015460028282011161012d5750505f610133565b01600290035b5f555f600155604c025ff35b5f5ffd', storage: { '0x0000000000000000000000000000000000000000000000000000000000000000': '0x000000000000000000000000000000000000000000000000000000000000049d', @@ -163,9 +163,9 @@ describe(`${method}: call with executionPayloadV4`, () => { depositRequests: [], withdrawalRequests: [], consolidationRequests: [], - parentHash: '0x5040e6b0056398536751c187683a3ecde8aff8fd9ea1d3450d687d7032134caf', - stateRoot: '0xbde9840c609ffa39cae0a2c9e354ac673920fcc2a5e6faeef5b78817c7fba7dd', - blockHash: '0x6b3ee4bb75e316427142bb9b48629e3e87ed8eea9f6d42b6aae296a11ec920b3', + parentHash: '0xdc1727ee420fead1d6a8dfe548758e5a5a48eed888b162278534b105b9909cf9', + stateRoot: '0x42a2ea499b9805a99881ebb8057b7b336082a4890aac29f346064ca7a4777b82', + blockHash: '0xb8dd5003e1390c64eda06c87b931ede724905d4a33b77aba58c865bd2055c142', } const oldMethods = ['engine_newPayloadV1', 'engine_newPayloadV2', 'engine_newPayloadV3'] diff --git a/packages/vm/src/params.ts b/packages/vm/src/params.ts index 1d3a576b2c..0450a75af8 100644 --- a/packages/vm/src/params.ts +++ b/packages/vm/src/params.ts @@ -78,7 +78,7 @@ export const paramsVM: ParamsDict = { 7002: { // config systemAddress: '0xfffffffffffffffffffffffffffffffffffffffe', // The system address to perform operations on the withdrawal requests predeploy address - withdrawalRequestPredeployAddress: '0x00A3ca265EBcb825B45F985A16CEFB49958cE017', // Address of the validator excess address + withdrawalRequestPredeployAddress: '0x05F27129610CB42103b665629CB5c8C00296AaAa', // Address of the validator excess address }, /** @@ -87,6 +87,6 @@ export const paramsVM: ParamsDict = { 7251: { // config systemAddress: '0xfffffffffffffffffffffffffffffffffffffffe', // The system address to perform operations on the consolidation requests predeploy address - consolidationRequestPredeployAddress: '0x00b42dbF2194e931E80326D950320f7d9Dbeac02', // Address of the consolidations contract + consolidationRequestPredeployAddress: '0x00706203067988Ab3E2A2ab626EdCD6f28bDBbbb', // Address of the consolidations contract }, } From 1c30c2a1c58354d3a02e48047c07c80b32f6089c Mon Sep 17 00:00:00 2001 From: harkamal Date: Sun, 29 Sep 2024 14:25:35 +0530 Subject: [PATCH 02/49] change the requests root from trie root to flat root and update examples and spcs --- packages/block/CHANGELOG.md | 9 ++++++--- packages/block/README.md | 15 +++++++++------ packages/block/examples/6110Requests.ts | 5 +++-- packages/block/examples/7002Requests.ts | 5 +++-- packages/block/examples/7251Requests.ts | 5 +++-- packages/block/src/block/block.ts | 6 +++--- packages/block/src/block/constructors.ts | 8 ++++---- packages/block/src/helpers.ts | 18 +++++++++++++----- packages/block/src/index.ts | 2 +- packages/block/test/eip7685block.spec.ts | 11 ++++++----- packages/vm/src/buildBlock.ts | 6 ++++-- packages/vm/src/runBlock.ts | 9 ++++++--- packages/vm/test/api/EIPs/eip-7002.spec.ts | 4 ++-- packages/vm/test/api/EIPs/eip-7685.spec.ts | 5 +++-- 14 files changed, 66 insertions(+), 42 deletions(-) diff --git a/packages/block/CHANGELOG.md b/packages/block/CHANGELOG.md index 6424716c49..c24a5d79dc 100644 --- a/packages/block/CHANGELOG.md +++ b/packages/block/CHANGELOG.md @@ -26,6 +26,7 @@ import { type CLRequest, type CLRequestType, } from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak.js' const main = async () => { const common = new Common({ @@ -42,7 +43,7 @@ const main = async () => { } const request = DepositRequest.fromRequestData(depositRequestData) as CLRequest const requests = [request] - const requestsRoot = await Block.genRequestsTrieRoot(requests) + const requestsRoot = await Block.genRequestsRoot(requests, keccak256) const block = Block.fromBlockData( { @@ -77,6 +78,7 @@ import { type CLRequest, type CLRequestType, } from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak.js' const main = async () => { const common = new Common({ @@ -93,7 +95,7 @@ const main = async () => { withdrawalRequestData, ) as CLRequest const requests = [request] - const requestsRoot = await Block.genRequestsTrieRoot(requests) + const requestsRoot = await Block.genRequestsRoot(requests, keccak256) const block = Block.fromBlockData( { @@ -130,6 +132,7 @@ import { type CLRequest, type CLRequestType, } from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak.js' const main = async () => { const common = new Common({ @@ -146,7 +149,7 @@ const main = async () => { consolidationRequestData, ) as CLRequest const requests = [request] - const requestsRoot = await Block.genRequestsTrieRoot(requests) + const requestsRoot = await Block.genRequestsRoot(requests, keccak256) const block = Block.fromBlockData( { diff --git a/packages/block/README.md b/packages/block/README.md index e5ba6645e5..a057f2d43a 100644 --- a/packages/block/README.md +++ b/packages/block/README.md @@ -244,7 +244,7 @@ Starting with v5.3.0 this library supports requests to the consensus layer which ```ts // ./examples/6110Requests.ts -import { createBlock, genRequestsTrieRoot } from '@ethereumjs/block' +import { createBlock, genRequestsRoot } from '@ethereumjs/block' import { Common, Hardfork, Mainnet } from '@ethereumjs/common' import { type CLRequest, @@ -253,6 +253,7 @@ import { createDepositRequest, randomBytes, } from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak.js' const main = async () => { const common = new Common({ @@ -269,7 +270,7 @@ const main = async () => { } const request = createDepositRequest(depositRequestData) as CLRequest const requests = [request] - const requestsRoot = await genRequestsTrieRoot(requests) + const requestsRoot = await genRequestsRoot(requests, keccak256) const block = createBlock( { @@ -297,7 +298,7 @@ Have a look at the EIP for some guidance on how to use and fill in the various d ```ts // ./examples/7002Requests.ts -import { createBlock, genRequestsTrieRoot } from '@ethereumjs/block' +import { createBlock, genRequestsRoot } from '@ethereumjs/block' import { Common, Hardfork, Mainnet } from '@ethereumjs/common' import { type CLRequest, @@ -306,6 +307,7 @@ import { createWithdrawalRequest, randomBytes, } from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak.js' const main = async () => { const common = new Common({ @@ -320,7 +322,7 @@ const main = async () => { } const request = createWithdrawalRequest(withdrawalRequestData) as CLRequest const requests = [request] - const requestsRoot = await genRequestsTrieRoot(requests) + const requestsRoot = await genRequestsRoot(requests, keecak256) const block = createBlock( { @@ -348,7 +350,7 @@ Have a look at the EIP for some guidance on how to use and fill in the various w ```ts // ./examples/7251Requests.ts -import { createBlock, genRequestsTrieRoot } from '@ethereumjs/block' +import { createBlock, genRequestsRoot } from '@ethereumjs/block' import { Common, Hardfork, Mainnet } from '@ethereumjs/common' import { type CLRequest, @@ -356,6 +358,7 @@ import { createConsolidationRequest, randomBytes, } from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak.js' const main = async () => { const common = new Common({ @@ -370,7 +373,7 @@ const main = async () => { } const request = createConsolidationRequest(consolidationRequestData) as CLRequest const requests = [request] - const requestsRoot = await genRequestsTrieRoot(requests) + const requestsRoot = await genRequestsRoot(requests, keccak256) const block = createBlock( { diff --git a/packages/block/examples/6110Requests.ts b/packages/block/examples/6110Requests.ts index 8ecd10b796..e92feab960 100644 --- a/packages/block/examples/6110Requests.ts +++ b/packages/block/examples/6110Requests.ts @@ -1,4 +1,4 @@ -import { createBlock, genRequestsTrieRoot } from '@ethereumjs/block' +import { createBlock, genRequestsRoot } from '@ethereumjs/block' import { Common, Hardfork, Mainnet } from '@ethereumjs/common' import { type CLRequest, @@ -7,6 +7,7 @@ import { createDepositRequest, randomBytes, } from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak.js' const main = async () => { const common = new Common({ @@ -23,7 +24,7 @@ const main = async () => { } const request = createDepositRequest(depositRequestData) as CLRequest const requests = [request] - const requestsRoot = await genRequestsTrieRoot(requests) + const requestsRoot = await genRequestsRoot(requests, keccak256) const block = createBlock( { diff --git a/packages/block/examples/7002Requests.ts b/packages/block/examples/7002Requests.ts index 8a383ebc38..d3f04aae91 100644 --- a/packages/block/examples/7002Requests.ts +++ b/packages/block/examples/7002Requests.ts @@ -1,4 +1,4 @@ -import { createBlock, genRequestsTrieRoot } from '@ethereumjs/block' +import { createBlock, genRequestsRoot } from '@ethereumjs/block' import { Common, Hardfork, Mainnet } from '@ethereumjs/common' import { type CLRequest, @@ -7,6 +7,7 @@ import { createWithdrawalRequest, randomBytes, } from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak.js' const main = async () => { const common = new Common({ @@ -21,7 +22,7 @@ const main = async () => { } const request = createWithdrawalRequest(withdrawalRequestData) as CLRequest const requests = [request] - const requestsRoot = await genRequestsTrieRoot(requests) + const requestsRoot = await genRequestsRoot(requests, keccak256) const block = createBlock( { diff --git a/packages/block/examples/7251Requests.ts b/packages/block/examples/7251Requests.ts index a8e1f1fc9c..846fc4e8e0 100644 --- a/packages/block/examples/7251Requests.ts +++ b/packages/block/examples/7251Requests.ts @@ -1,4 +1,4 @@ -import { createBlock, genRequestsTrieRoot } from '@ethereumjs/block' +import { createBlock, genRequestsRoot } from '@ethereumjs/block' import { Common, Hardfork, Mainnet } from '@ethereumjs/common' import { type CLRequest, @@ -6,6 +6,7 @@ import { createConsolidationRequest, randomBytes, } from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak.js' const main = async () => { const common = new Common({ @@ -20,7 +21,7 @@ const main = async () => { } const request = createConsolidationRequest(consolidationRequestData) as CLRequest const requests = [request] - const requestsRoot = await genRequestsTrieRoot(requests) + const requestsRoot = await genRequestsRoot(requests, keccak256) const block = createBlock( { diff --git a/packages/block/src/block/block.ts b/packages/block/src/block/block.ts index b6613a2791..cf232e8ea3 100644 --- a/packages/block/src/block/block.ts +++ b/packages/block/src/block/block.ts @@ -19,7 +19,7 @@ import { keccak256 } from 'ethereum-cryptography/keccak.js' // See: https://github.com/microsoft/TypeScript/issues/47558 // (situation will eventually improve on Typescript and/or Eslint update) import { - genRequestsTrieRoot, + genRequestsRoot, genTransactionsTrieRoot, genWithdrawalsTrieRoot, BlockHeader, @@ -261,11 +261,11 @@ export class Block { if (requestsInput === undefined) { if (this.cache.requestsRoot === undefined) { - this.cache.requestsRoot = await genRequestsTrieRoot(this.requests!) + this.cache.requestsRoot = await genRequestsRoot(this.requests!, this.keccakFunction) } return equalsBytes(this.cache.requestsRoot, this.header.requestsRoot!) } else { - const reportedRoot = await genRequestsTrieRoot(requests) + const reportedRoot = await genRequestsRoot(requests, this.keccakFunction) return equalsBytes(reportedRoot, this.header.requestsRoot!) } } diff --git a/packages/block/src/block/constructors.ts b/packages/block/src/block/constructors.ts index ef0d9b478d..e9605cfeb9 100644 --- a/packages/block/src/block/constructors.ts +++ b/packages/block/src/block/constructors.ts @@ -23,9 +23,10 @@ import { intToHex, isHexString, } from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak.js' import { generateCliqueBlockExtraData } from '../consensus/clique.js' -import { genRequestsTrieRoot, genTransactionsTrieRoot, genWithdrawalsTrieRoot } from '../helpers.js' +import { genRequestsRoot, genTransactionsTrieRoot, genWithdrawalsTrieRoot } from '../helpers.js' import { Block, createBlockHeader, @@ -433,9 +434,8 @@ export async function createBlockFromExecutionPayload( } } - const requestsRoot = requests - ? await genRequestsTrieRoot(requests, new Trie({ common: opts?.common })) - : undefined + const keccakFunction = opts?.common?.customCrypto.keccak256 ?? keccak256 + const requestsRoot = requests ? await genRequestsRoot(requests, keccakFunction) : undefined const header: HeaderData = { ...payload, diff --git a/packages/block/src/helpers.ts b/packages/block/src/helpers.ts index 79538dd20d..35883026a9 100644 --- a/packages/block/src/helpers.ts +++ b/packages/block/src/helpers.ts @@ -151,7 +151,10 @@ export async function genTransactionsTrieRoot(txs: TypedTransaction[], emptyTrie * @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) { +export async function genRequestsRoot( + requests: CLRequest[], + keccakFunction: (msg: Uint8Array) => Uint8Array, +) { // 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) { @@ -160,9 +163,14 @@ export async function genRequestsTrieRoot(requests: CLRequest[], 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()) + + // def compute_requests_hash(list): + // return keccak256(rlp.encode([rlp.encode(req) for req in list])) + + const encodedRequests = [] + for (const req of requests) { + encodedRequests.push(RLP.encode(req.serialize())) } - return trie.root() + + return keccakFunction(RLP.encode(encodedRequests)) } diff --git a/packages/block/src/index.ts b/packages/block/src/index.ts index 074d1a1c04..e3745e9e7c 100644 --- a/packages/block/src/index.ts +++ b/packages/block/src/index.ts @@ -4,7 +4,7 @@ export * from './consensus/index.js' export { type BeaconPayloadJSON, executionPayloadFromBeaconPayload } from './from-beacon-payload.js' export * from './header/index.js' export { - genRequestsTrieRoot, + genRequestsRoot, genTransactionsTrieRoot, genWithdrawalsTrieRoot, getDifficulty, diff --git a/packages/block/test/eip7685block.spec.ts b/packages/block/test/eip7685block.spec.ts index 6b3f314f6c..4c0c21e646 100644 --- a/packages/block/test/eip7685block.spec.ts +++ b/packages/block/test/eip7685block.spec.ts @@ -6,9 +6,10 @@ import { createWithdrawalRequest, randomBytes, } from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak.js' import { assert, describe, expect, it } from 'vitest' -import { genRequestsTrieRoot } from '../src/helpers.js' +import { genRequestsRoot } from '../src/helpers.js' import { Block, createBlock, @@ -55,7 +56,7 @@ describe('7685 tests', () => { }) it('should instantiate a block with requests', async () => { const request = getRandomDepositRequest() - const requestsRoot = await genRequestsTrieRoot([request]) + const requestsRoot = await genRequestsRoot([request], keccak256) const block = createBlock( { requests: [request], @@ -83,7 +84,7 @@ describe('7685 tests', () => { const request2 = getRandomDepositRequest() const request3 = getRandomWithdrawalRequest() const requests = [request1, request2, request3] - const requestsRoot = await genRequestsTrieRoot(requests) + const requestsRoot = await genRequestsRoot(requests, keccak256) // Construct block with requests in correct order @@ -125,7 +126,7 @@ describe('createWithdrawalFromBytesArray tests', () => { const request2 = getRandomWithdrawalRequest() const request3 = getRandomWithdrawalRequest() const requests = [request1, request2, request3] - const requestsRoot = await genRequestsTrieRoot(requests) + const requestsRoot = await genRequestsRoot(requests, keccak256) const serializedRequests = [request1.serialize(), request2.serialize(), request3.serialize()] const block = createBlockFromBytesArray( @@ -145,7 +146,7 @@ describe('fromRPC tests', () => { const request2 = getRandomDepositRequest() const request3 = getRandomWithdrawalRequest() const requests = [request1, request2, request3] - const requestsRoot = await genRequestsTrieRoot(requests) + const requestsRoot = await genRequestsRoot(requests, keccak256) const serializedRequests = [request1.serialize(), request2.serialize(), request3.serialize()] const block = createBlockFromBytesArray( diff --git a/packages/vm/src/buildBlock.ts b/packages/vm/src/buildBlock.ts index 119b3b144d..801e85d995 100644 --- a/packages/vm/src/buildBlock.ts +++ b/packages/vm/src/buildBlock.ts @@ -1,7 +1,7 @@ import { createBlock, createSealedCliqueBlock, - genRequestsTrieRoot, + genRequestsRoot, genTransactionsTrieRoot, genWithdrawalsTrieRoot, } from '@ethereumjs/block' @@ -23,6 +23,7 @@ import { toType, zeros, } from '@ethereumjs/util' +import { keccak256 } from 'ethereum-cryptography/keccak.js' import { Bloom } from './bloom/index.js' import { accumulateRequests } from './requests.js' @@ -339,8 +340,9 @@ export class BlockBuilder { let requests let requestsRoot if (this.vm.common.isActivatedEIP(7685)) { + const keccakFunction = this.vm.common.customCrypto.keccak256 ?? keccak256 requests = await accumulateRequests(this.vm, this.transactionResults) - requestsRoot = await genRequestsTrieRoot(requests) + requestsRoot = await genRequestsRoot(requests, keccakFunction) // Do other validations per request type } diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index a5d9be682b..7e25fceaa6 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -1,4 +1,4 @@ -import { createBlock, genRequestsTrieRoot } from '@ethereumjs/block' +import { createBlock, genRequestsRoot } from '@ethereumjs/block' import { ConsensusType, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { StatelessVerkleStateManager, verifyVerkleStateProof } from '@ethereumjs/statemanager' @@ -26,6 +26,7 @@ import { unprefixedHexToBytes, } from '@ethereumjs/util' import debugDefault from 'debug' +import { keccak256 } from 'ethereum-cryptography/keccak.js' import { Bloom } from './bloom/index.js' import { emitEVMProfile } from './emitEVMProfile.js' @@ -215,8 +216,9 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise[] | undefined if (block.common.isActivatedEIP(7685)) { + const keccakFunction = vm.common.customCrypto.keccak256 ?? keccak256 requests = await accumulateRequests(vm, result.results) - requestsRoot = await genRequestsTrieRoot(requests) + requestsRoot = await genRequestsRoot(requests, keccakFunction) } // Persist state @@ -253,7 +255,8 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise { it('should not throw invalid requestsRoot error when valid requests are provided', async () => { const vm = await setupVM({ common }) const request = getRandomDepositRequest() - const requestsRoot = await genRequestsTrieRoot([request]) + const requestsRoot = await genRequestsRoot([request], keccak256) const block = createBlock( { requests: [request], From 73003e72d30eb81257cae232dc2ec4976d7a7c50 Mon Sep 17 00:00:00 2001 From: harkamal Date: Sun, 29 Sep 2024 17:43:05 +0530 Subject: [PATCH 03/49] convert requests to flat type across util,block and vm --- packages/block/README.md | 4 +- packages/block/examples/6110Requests.ts | 4 +- packages/block/src/block/block.ts | 44 ++------ packages/block/src/block/constructors.ts | 38 +------ packages/block/src/from-beacon-payload.ts | 51 +--------- packages/block/src/types.ts | 7 +- packages/block/test/eip7685block.spec.ts | 4 +- packages/util/src/request.ts | 113 ++++++++++----------- packages/vm/src/requests.ts | 34 +------ packages/vm/test/api/EIPs/eip-7685.spec.ts | 12 +-- 10 files changed, 78 insertions(+), 233 deletions(-) diff --git a/packages/block/README.md b/packages/block/README.md index a057f2d43a..aec0e2f64e 100644 --- a/packages/block/README.md +++ b/packages/block/README.md @@ -264,9 +264,9 @@ const main = async () => { const depositRequestData = { pubkey: randomBytes(48), withdrawalCredentials: randomBytes(32), - amount: bytesToBigInt(randomBytes(8)), + amount: (randomBytes(8)), signature: randomBytes(96), - index: bytesToBigInt(randomBytes(8)), + index: (randomBytes(8)), } const request = createDepositRequest(depositRequestData) as CLRequest const requests = [request] diff --git a/packages/block/examples/6110Requests.ts b/packages/block/examples/6110Requests.ts index e92feab960..56b831d9d2 100644 --- a/packages/block/examples/6110Requests.ts +++ b/packages/block/examples/6110Requests.ts @@ -18,9 +18,9 @@ const main = async () => { const depositRequestData = { pubkey: randomBytes(48), withdrawalCredentials: randomBytes(32), - amount: bytesToBigInt(randomBytes(8)), + amount: randomBytes(8), signature: randomBytes(96), - index: bytesToBigInt(randomBytes(8)), + index: randomBytes(8), } const request = createDepositRequest(depositRequestData) as CLRequest const requests = [request] diff --git a/packages/block/src/block/block.ts b/packages/block/src/block/block.ts index cf232e8ea3..47331a6c83 100644 --- a/packages/block/src/block/block.ts +++ b/packages/block/src/block/block.ts @@ -4,7 +4,6 @@ import { Trie } from '@ethereumjs/trie' import { Blob4844Tx, Capability } from '@ethereumjs/tx' import { BIGINT_0, - CLRequestType, KECCAK256_RLP, KECCAK256_RLP_ARRAY, bytesToHex, @@ -35,14 +34,7 @@ import { import type { BlockBytes, BlockOptions, ExecutionPayload, JSONBlock } from '../types.js' import type { Common } from '@ethereumjs/common' import type { FeeMarket1559Tx, LegacyTx, TypedTransaction } from '@ethereumjs/tx' -import type { - CLRequest, - ConsolidationRequest, - DepositRequest, - VerkleExecutionWitness, - Withdrawal, - WithdrawalRequest, -} from '@ethereumjs/util' +import type { CLRequest, CLRequestType, VerkleExecutionWitness, Withdrawal } from '@ethereumjs/util' /** * Class representing a block in the Ethereum network. The {@link BlockHeader} has its own @@ -545,6 +537,10 @@ export class Block { const header = blockJSON.header! const transactions = this.transactions.map((tx) => bytesToHex(tx.serialize())) ?? [] const withdrawalsArr = blockJSON.withdrawals ? { withdrawals: blockJSON.withdrawals } : {} + const executionRequestsArr = + this.requests !== undefined + ? { executionRequests: this.requests.map((req) => bytesToHex(req.serialize())) } + : undefined const executionPayload: ExecutionPayload = { blockNumber: header.number!, @@ -566,35 +562,7 @@ export class Block { ...withdrawalsArr, parentBeaconBlockRoot: header.parentBeaconBlockRoot, executionWitness: this.executionWitness, - - // lets add the request fields first and then iterate over requests to fill them up - depositRequests: this.common.isActivatedEIP(6110) ? [] : undefined, - withdrawalRequests: this.common.isActivatedEIP(7002) ? [] : undefined, - consolidationRequests: this.common.isActivatedEIP(7251) ? [] : undefined, - } - - if (this.requests !== undefined) { - for (const request of this.requests) { - switch (request.type) { - case CLRequestType.Deposit: - executionPayload.depositRequests!.push((request as DepositRequest).toJSON()) - continue - - case CLRequestType.Withdrawal: - executionPayload.withdrawalRequests!.push((request as WithdrawalRequest).toJSON()) - continue - - case CLRequestType.Consolidation: - executionPayload.consolidationRequests!.push((request as ConsolidationRequest).toJSON()) - continue - } - } - } else if ( - executionPayload.depositRequests !== undefined || - executionPayload.withdrawalRequests !== undefined || - executionPayload.consolidationRequests !== undefined - ) { - throw Error(`Undefined requests for activated deposit or withdrawal requests`) + ...executionRequestsArr, } return executionPayload diff --git a/packages/block/src/block/constructors.ts b/packages/block/src/block/constructors.ts index e9605cfeb9..a795094bf2 100644 --- a/packages/block/src/block/constructors.ts +++ b/packages/block/src/block/constructors.ts @@ -12,10 +12,7 @@ import { bigIntToHex, bytesToHex, bytesToUtf8, - createConsolidationRequestFromJSON, - createDepositRequestFromJSON, createWithdrawal, - createWithdrawalRequestFromJSON, equalsBytes, fetchFromProvider, getProvider, @@ -49,8 +46,6 @@ import type { } from '../types.js' import type { TypedTransaction } from '@ethereumjs/tx' import type { - CLRequest, - CLRequestType, EthersProvider, PrefixedHexString, RequestBytes, @@ -383,9 +378,7 @@ export async function createBlockFromExecutionPayload( feeRecipient: coinbase, transactions, withdrawals: withdrawalsData, - depositRequests, - withdrawalRequests, - consolidationRequests, + executionRequests, executionWitness, } = payload @@ -408,32 +401,9 @@ export async function createBlockFromExecutionPayload( ? 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(createDepositRequestFromJSON(dJSON)) - } - } - if (withdrawalRequests !== undefined && withdrawalRequests !== null) { - for (const wJSON of withdrawalRequests) { - requests!.push(createWithdrawalRequestFromJSON(wJSON)) - } - } - if (consolidationRequests !== undefined && consolidationRequests !== null) { - for (const cJSON of consolidationRequests) { - requests!.push(createConsolidationRequestFromJSON(cJSON)) - } - } - + const requests = executionRequests?.map((req) => + CLRequestFactory.fromSerializedRequest(hexToBytes(req)), + ) const keccakFunction = opts?.common?.customCrypto.keccak256 ?? keccak256 const requestsRoot = requests ? await genRequestsRoot(requests, keccakFunction) : undefined diff --git a/packages/block/src/from-beacon-payload.ts b/packages/block/src/from-beacon-payload.ts index d6d93467f6..a6ff816b8f 100644 --- a/packages/block/src/from-beacon-payload.ts +++ b/packages/block/src/from-beacon-payload.ts @@ -10,26 +10,6 @@ type BeaconWithdrawal = { amount: PrefixedHexString } -type BeaconDepositRequest = { - pubkey: PrefixedHexString - withdrawal_credentials: PrefixedHexString - amount: PrefixedHexString - signature: PrefixedHexString - index: PrefixedHexString -} - -type BeaconWithdrawalRequest = { - source_address: PrefixedHexString - validator_pubkey: PrefixedHexString - amount: PrefixedHexString -} - -type BeaconConsolidationRequest = { - source_address: PrefixedHexString - source_pubkey: PrefixedHexString - target_pubkey: PrefixedHexString -} - // Payload JSON that one gets using the beacon apis // curl localhost:5052/eth/v2/beacon/blocks/56610 | jq .data.message.body.execution_payload export type BeaconPayloadJSON = { @@ -52,10 +32,7 @@ export type BeaconPayloadJSON = { excess_blob_gas?: NumericString parent_beacon_block_root?: PrefixedHexString // requests data - deposit_requests?: BeaconDepositRequest[] - withdrawal_requests?: BeaconWithdrawalRequest[] - consolidation_requests?: BeaconConsolidationRequest[] - + execution_requests?: PrefixedHexString[] // the casing of VerkleExecutionWitness remains same camel case for now execution_witness?: VerkleExecutionWitness } @@ -158,30 +135,8 @@ export function executionPayloadFromBeaconPayload(payload: BeaconPayloadJSON): E } // requests - if (payload.deposit_requests !== undefined && payload.deposit_requests !== null) { - executionPayload.depositRequests = payload.deposit_requests.map((beaconRequest) => ({ - pubkey: beaconRequest.pubkey, - withdrawalCredentials: beaconRequest.withdrawal_credentials, - amount: beaconRequest.amount, - signature: beaconRequest.signature, - index: beaconRequest.index, - })) - } - if (payload.withdrawal_requests !== undefined && payload.withdrawal_requests !== null) { - executionPayload.withdrawalRequests = payload.withdrawal_requests.map((beaconRequest) => ({ - sourceAddress: beaconRequest.source_address, - validatorPubkey: beaconRequest.validator_pubkey, - amount: beaconRequest.amount, - })) - } - if (payload.consolidation_requests !== undefined && payload.consolidation_requests !== null) { - executionPayload.consolidationRequests = payload.consolidation_requests.map( - (beaconRequest) => ({ - sourceAddress: beaconRequest.source_address, - sourcePubkey: beaconRequest.source_pubkey, - targetPubkey: beaconRequest.target_pubkey, - }), - ) + if (payload.execution_requests !== undefined && payload.execution_requests !== null) { + executionPayload.executionRequests = payload.execution_requests } if (payload.execution_witness !== undefined && payload.execution_witness !== null) { diff --git a/packages/block/src/types.ts b/packages/block/src/types.ts index 145ef4ef20..50db8f09f2 100644 --- a/packages/block/src/types.ts +++ b/packages/block/src/types.ts @@ -7,8 +7,6 @@ import type { BytesLike, CLRequest, CLRequestType, - ConsolidationRequestV1, - DepositRequestV1, JSONRPCWithdrawal, NumericString, PrefixedHexString, @@ -16,7 +14,6 @@ import type { VerkleExecutionWitness, WithdrawalBytes, WithdrawalData, - WithdrawalRequestV1, } from '@ethereumjs/util' /** @@ -274,7 +271,5 @@ export type ExecutionPayload = { parentBeaconBlockRoot?: PrefixedHexString // QUANTITY, 64 Bits // VerkleExecutionWitness is already a hex serialized object executionWitness?: VerkleExecutionWitness | null // QUANTITY, 64 Bits, null implies not available - depositRequests?: DepositRequestV1[] // Array of 6110 deposit requests - withdrawalRequests?: WithdrawalRequestV1[] // Array of 7002 withdrawal requests - consolidationRequests?: ConsolidationRequestV1[] // Array of 7251 consolidation requests + executionRequests?: PrefixedHexString[] | null // null implies not available } diff --git a/packages/block/test/eip7685block.spec.ts b/packages/block/test/eip7685block.spec.ts index 4c0c21e646..4355cdb1b2 100644 --- a/packages/block/test/eip7685block.spec.ts +++ b/packages/block/test/eip7685block.spec.ts @@ -25,9 +25,9 @@ function getRandomDepositRequest(): CLRequest { const depositRequestData = { pubkey: randomBytes(48), withdrawalCredentials: randomBytes(32), - amount: bytesToBigInt(randomBytes(8)), + amount: randomBytes(8), signature: randomBytes(96), - index: bytesToBigInt(randomBytes(8)), + index: randomBytes(8), } return createDepositRequest(depositRequestData) as CLRequest } diff --git a/packages/util/src/request.ts b/packages/util/src/request.ts index 66e359e435..957083e3e0 100644 --- a/packages/util/src/request.ts +++ b/packages/util/src/request.ts @@ -1,15 +1,7 @@ import { RLP } from '@ethereumjs/rlp' import { concatBytes } from 'ethereum-cryptography/utils' -import { - bigIntToBytes, - bigIntToHex, - bytesToBigInt, - bytesToHex, - hexToBigInt, - hexToBytes, -} from './bytes.js' -import { BIGINT_0 } from './constants.js' +import { bytesToHex, hexToBytes } from './bytes.js' import type { PrefixedHexString } from './types.js' @@ -50,15 +42,18 @@ export interface RequestJSON { export type DepositRequestData = { pubkey: Uint8Array withdrawalCredentials: Uint8Array - amount: bigint + // 8 bytes uint64 LE + amount: Uint8Array signature: Uint8Array - index: bigint + // 8 bytes uint64 LE + index: Uint8Array } export type WithdrawalRequestData = { sourceAddress: Uint8Array validatorPubkey: Uint8Array - amount: bigint + // 8 bytes uint64 LE + amount: Uint8Array } export type ConsolidationRequestData = { @@ -94,27 +89,23 @@ export class DepositRequest extends CLRequest { constructor( public readonly pubkey: Uint8Array, public readonly withdrawalCredentials: Uint8Array, - public readonly amount: bigint, + // 8 bytes uint64 LE + public readonly amount: Uint8Array, public readonly signature: Uint8Array, - public readonly index: bigint, + // 8 bytes uint64 LE + public readonly index: Uint8Array, ) { super(CLRequestType.Deposit) } serialize() { - const indexBytes = this.index === BIGINT_0 ? new Uint8Array() : bigIntToBytes(this.index) - - const amountBytes = this.amount === BIGINT_0 ? new Uint8Array() : bigIntToBytes(this.amount) - return concatBytes( Uint8Array.from([this.type]), - RLP.encode([ - this.pubkey, - this.withdrawalCredentials, - amountBytes, - this.signature, - indexBytes, - ]), + this.pubkey, + this.withdrawalCredentials, + this.amount, + this.signature, + this.index, ) } @@ -122,9 +113,9 @@ export class DepositRequest extends CLRequest { return { pubkey: bytesToHex(this.pubkey), withdrawalCredentials: bytesToHex(this.withdrawalCredentials), - amount: bigIntToHex(this.amount), + amount: bytesToHex(this.amount), signature: bytesToHex(this.signature), - index: bigIntToHex(this.index), + index: bytesToHex(this.index), } } } @@ -133,17 +124,18 @@ export class WithdrawalRequest extends CLRequest { constructor( public readonly sourceAddress: Uint8Array, public readonly validatorPubkey: Uint8Array, - public readonly amount: bigint, + // 8 bytes uint64 LE + public readonly amount: Uint8Array, ) { super(CLRequestType.Withdrawal) } serialize() { - const amountBytes = this.amount === BIGINT_0 ? new Uint8Array() : bigIntToBytes(this.amount) - return concatBytes( Uint8Array.from([this.type]), - RLP.encode([this.sourceAddress, this.validatorPubkey, amountBytes]), + this.sourceAddress, + this.validatorPubkey, + this.amount, ) } @@ -151,7 +143,7 @@ export class WithdrawalRequest extends CLRequest { return { sourceAddress: bytesToHex(this.sourceAddress), validatorPubkey: bytesToHex(this.validatorPubkey), - amount: bigIntToHex(this.amount), + amount: bytesToHex(this.amount), } } } @@ -168,7 +160,9 @@ export class ConsolidationRequest extends CLRequest serialize() { return concatBytes( Uint8Array.from([this.type]), - RLP.encode([this.sourceAddress, this.sourcePubkey, this.targetPubkey]), + this.sourceAddress, + this.sourcePubkey, + this.targetPubkey, ) } @@ -191,26 +185,23 @@ export function createDepositRequestFromJSON(jsonData: DepositRequestV1): Deposi return createDepositRequest({ pubkey: hexToBytes(pubkey), withdrawalCredentials: hexToBytes(withdrawalCredentials), - amount: hexToBigInt(amount), + amount: hexToBytes(amount), signature: hexToBytes(signature), - index: hexToBigInt(index), + index: hexToBytes(index), }) } -export function createDepositRequestFromRLP(bytes: Uint8Array): DepositRequest { - const [pubkey, withdrawalCredentials, amount, signature, index] = RLP.decode(bytes) as [ - Uint8Array, - Uint8Array, - Uint8Array, - Uint8Array, - Uint8Array, - ] +export function createDepositRequestFromFlatData(bytes: Uint8Array): DepositRequest { + if (bytes.length !== 48 + 32 + 8 + 96) { + throw Error(`Invalid bytes=${bytes.length} for deposit request data`) + } + return createDepositRequest({ - pubkey, - withdrawalCredentials, - amount: bytesToBigInt(amount), - signature, - index: bytesToBigInt(index), + pubkey: bytes.subarray(0, 48), + withdrawalCredentials: bytes.subarray(48, 48 + 32), + amount: bytes.subarray(48 + 32, 48 + 32 + 8), + signature: bytes.subarray(48 + 32 + 8, 48 + 32 + 8 + 96), + index: bytes.subarray(48 + 32 + 8 + 96), }) } @@ -224,20 +215,18 @@ export function createWithdrawalRequestFromJSON(jsonData: WithdrawalRequestV1): return createWithdrawalRequest({ sourceAddress: hexToBytes(sourceAddress), validatorPubkey: hexToBytes(validatorPubkey), - amount: hexToBigInt(amount), + amount: hexToBytes(amount), }) } -export function createWithdrawalRequestFromRLP(bytes: Uint8Array): WithdrawalRequest { - const [sourceAddress, validatorPubkey, amount] = RLP.decode(bytes) as [ - Uint8Array, - Uint8Array, - Uint8Array, - ] +export function createWithdrawalRequestFromFlatData(bytes: Uint8Array): WithdrawalRequest { + if (bytes.length !== 20 + 48 + 8) { + throw Error(`Invalid bytes=${bytes.length} for withdrawal request data`) + } return createWithdrawalRequest({ - sourceAddress, - validatorPubkey, - amount: bytesToBigInt(amount), + sourceAddress: bytes.subarray(0, 20), + validatorPubkey: bytes.subarray(20, 20 + 48), + amount: bytes.subarray(20 + 48), }) } @@ -259,7 +248,7 @@ export function createConsolidationRequestFromJSON( }) } -export function createConsolidationRequestFromRLP(bytes: Uint8Array): ConsolidationRequest { +export function createConsolidationRequestFromFlatData(bytes: Uint8Array): ConsolidationRequest { const [sourceAddress, sourcePubkey, targetPubkey] = RLP.decode(bytes) as [ Uint8Array, Uint8Array, @@ -276,11 +265,11 @@ export class CLRequestFactory { public static fromSerializedRequest(bytes: Uint8Array): CLRequest { switch (bytes[0]) { case CLRequestType.Deposit: - return createDepositRequestFromRLP(bytes.subarray(1)) + return createDepositRequestFromFlatData(bytes.subarray(1)) case CLRequestType.Withdrawal: - return createWithdrawalRequestFromRLP(bytes.subarray(1)) + return createWithdrawalRequestFromFlatData(bytes.subarray(1)) case CLRequestType.Consolidation: - return createConsolidationRequestFromRLP(bytes.subarray(1)) + return createConsolidationRequestFromFlatData(bytes.subarray(1)) default: throw Error(`Invalid request type=${bytes[0]}`) } diff --git a/packages/vm/src/requests.ts b/packages/vm/src/requests.ts index 54230e18bc..9b135fdef3 100644 --- a/packages/vm/src/requests.ts +++ b/packages/vm/src/requests.ts @@ -2,7 +2,6 @@ import { Mainnet } from '@ethereumjs/common' import { bigIntToAddressBytes, bigIntToBytes, - bytesToBigInt, bytesToHex, bytesToInt, createAddressFromString, @@ -10,7 +9,6 @@ import { createDepositRequest, createWithdrawalRequest, setLengthLeft, - unpadBytes, } from '@ethereumjs/util' import type { RunTxResult } from './types.js' @@ -95,7 +93,7 @@ const accumulateEIP7002Requests = async ( const slicedBytes = resultsBytes.slice(startByte, startByte + 76) const sourceAddress = slicedBytes.slice(0, 20) // 20 Bytes const validatorPubkey = slicedBytes.slice(20, 68) // 48 Bytes - const amount = bytesToBigInt(unpadBytes(slicedBytes.slice(68, 76))) // 8 Bytes / Uint64 + const amount = slicedBytes.slice(68, 76) // 8 Bytes / Uint64 LE requests.push(createWithdrawalRequest({ sourceAddress, validatorPubkey, amount })) } } @@ -176,40 +174,16 @@ const accumulateDeposits = async ( const sigSize = bytesToInt(log[2].slice(sigIdx, sigIdx + 32)) const indexIdx = bytesToInt(log[2].slice(128, 160)) const indexSize = bytesToInt(log[2].slice(indexIdx, indexIdx + 32)) + const pubkey = log[2].slice(pubKeyIdx + 32, pubKeyIdx + 32 + pubKeySize) const withdrawalCredentials = log[2].slice( withdrawalCreditsIdx + 32, withdrawalCreditsIdx + 32 + withdrawalCreditsSize, ) - const amountBytes = log[2].slice(amountIdx + 32, amountIdx + 32 + amountSize) - const amountBytesBigEndian = new Uint8Array([ - amountBytes[7], - amountBytes[6], - amountBytes[5], - amountBytes[4], - amountBytes[3], - amountBytes[2], - amountBytes[1], - amountBytes[0], - ]) - const amount = bytesToBigInt(amountBytesBigEndian) - + const amount = log[2].slice(amountIdx + 32, amountIdx + 32 + amountSize) const signature = log[2].slice(sigIdx + 32, sigIdx + 32 + sigSize) + const index = log[2].slice(indexIdx + 32, indexIdx + 32 + indexSize) - const indexBytes = log[2].slice(indexIdx + 32, indexIdx + 32 + indexSize) - - // Convert the little-endian array to big-endian array - const indexBytesBigEndian = new Uint8Array([ - indexBytes[7], - indexBytes[6], - indexBytes[5], - indexBytes[4], - indexBytes[3], - indexBytes[2], - indexBytes[1], - indexBytes[0], - ]) - const index = bytesToBigInt(indexBytesBigEndian) requests.push( createDepositRequest({ pubkey, diff --git a/packages/vm/test/api/EIPs/eip-7685.spec.ts b/packages/vm/test/api/EIPs/eip-7685.spec.ts index d330f55533..27003c7381 100644 --- a/packages/vm/test/api/EIPs/eip-7685.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7685.spec.ts @@ -1,13 +1,7 @@ import { createBlock, genRequestsRoot } from '@ethereumjs/block' import { createBlockchain } from '@ethereumjs/blockchain' import { Common, Hardfork, Mainnet } from '@ethereumjs/common' -import { - KECCAK256_RLP, - bytesToBigInt, - createDepositRequest, - hexToBytes, - randomBytes, -} from '@ethereumjs/util' +import { KECCAK256_RLP, createDepositRequest, hexToBytes, randomBytes } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak.js' import { assert, describe, expect, it } from 'vitest' @@ -23,9 +17,9 @@ function getRandomDepositRequest(): CLRequest { const depositRequestData = { pubkey: randomBytes(48), withdrawalCredentials: randomBytes(32), - amount: bytesToBigInt(randomBytes(8)), + amount: randomBytes(8), signature: randomBytes(96), - index: bytesToBigInt(randomBytes(8)), + index: randomBytes(8), } return createDepositRequest(depositRequestData) as CLRequest } From 7cd91a261df372536eb6b19a70be8151fa998140 Mon Sep 17 00:00:00 2001 From: harkamal Date: Sun, 29 Sep 2024 19:27:52 +0530 Subject: [PATCH 04/49] bundle execution requests separately from execution payload --- packages/client/src/rpc/modules/engine/engine.ts | 8 +++++--- .../src/rpc/modules/engine/util/getPayload.ts | 13 ++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/client/src/rpc/modules/engine/engine.ts b/packages/client/src/rpc/modules/engine/engine.ts index 47d9abe161..ed6d09f109 100644 --- a/packages/client/src/rpc/modules/engine/engine.ts +++ b/packages/client/src/rpc/modules/engine/engine.ts @@ -212,8 +212,9 @@ export class Engine { [validators.object(executionPayloadV4FieldValidators)], [validators.array(validators.bytes32)], [validators.bytes32], + [validators.array(validators.hex)], ], - ['executionPayload', 'blobVersionedHashes', 'parentBeaconBlockRoot'], + ['executionPayload', 'blobVersionedHashes', 'parentBeaconBlockRoot', 'executionRequests'], ), ([payload], response) => this.connectionManager.lastNewPayload({ payload, response }), ) @@ -337,9 +338,9 @@ export class Engine { * 3. validationError: String|null - validation error message */ private async newPayload( - params: [ExecutionPayload, (Bytes32[] | null)?, (Bytes32 | null)?], + params: [ExecutionPayload, (Bytes32[] | null)?, (Bytes32 | null)?, (PrefixedHexString[] | null)?], ): Promise { - const [payload, blobVersionedHashes, parentBeaconBlockRoot] = params + const [payload, blobVersionedHashes, parentBeaconBlockRoot, executionRequests] = params if (this.config.synchronized) { this.connectionManager.newPayloadLog() } @@ -361,6 +362,7 @@ export class Engine { ...payload, // ExecutionPayload only handles undefined parentBeaconBlockRoot: parentBeaconBlockRoot ?? undefined, + executionRequests: executionRequests ?? undefined, }, this.chain, this.chainCache, diff --git a/packages/client/src/rpc/modules/engine/util/getPayload.ts b/packages/client/src/rpc/modules/engine/util/getPayload.ts index cc7b674af8..cae5de8694 100644 --- a/packages/client/src/rpc/modules/engine/util/getPayload.ts +++ b/packages/client/src/rpc/modules/engine/util/getPayload.ts @@ -13,10 +13,21 @@ export const blockToExecutionPayload = (block: Block, value: bigint, bundle?: Bl if (executionPayload.parentBeaconBlockRoot !== undefined) { delete executionPayload.parentBeaconBlockRoot } + const { executionRequests } = executionPayload + if (executionPayload.executionRequests !== undefined) { + delete executionPayload.executionRequests + } + const blobsBundle: BlobsBundleV1 | undefined = bundle ? bundle : undefined // ethereumjs does not provide any transaction censoring detection (yet) to suggest // overriding builder/mev-boost blocks const shouldOverrideBuilder = false - return { executionPayload, blockValue: bigIntToHex(value), blobsBundle, shouldOverrideBuilder } + return { + executionPayload, + executionRequests, + blockValue: bigIntToHex(value), + blobsBundle, + shouldOverrideBuilder, + } } From 420a7aaf0b97acc084761fe2868383f5af3c8783 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 10 Oct 2024 04:47:54 +0200 Subject: [PATCH 05/49] t8ntool: update to devnet-4 interface support --- packages/vm/test/t8n/t8ntool.ts | 33 ++++----------------------------- packages/vm/test/t8n/types.ts | 12 +++--------- 2 files changed, 7 insertions(+), 38 deletions(-) diff --git a/packages/vm/test/t8n/t8ntool.ts b/packages/vm/test/t8n/t8ntool.ts index e322d9a241..50209a235a 100644 --- a/packages/vm/test/t8n/t8ntool.ts +++ b/packages/vm/test/t8n/t8ntool.ts @@ -2,7 +2,7 @@ import { createBlock } from '@ethereumjs/block' import { EVMMockBlockchain, NobleBLS } from '@ethereumjs/evm' import { RLP } from '@ethereumjs/rlp' import { createTx } from '@ethereumjs/tx' -import { CLRequestType, bigIntToHex, bytesToHex, hexToBytes, toBytes } from '@ethereumjs/util' +import { bigIntToHex, bytesToHex, hexToBytes, toBytes } from '@ethereumjs/util' import { trustedSetup } from '@paulmillr/trusted-setups/fast.js' import { keccak256 } from 'ethereum-cryptography/keccak' import { readFileSync, writeFileSync } from 'fs' @@ -25,12 +25,7 @@ import type { Block } from '@ethereumjs/block' import type { Common } from '@ethereumjs/common' import type { Log } from '@ethereumjs/evm' import type { TypedTxData } from '@ethereumjs/tx' -import type { - ConsolidationRequestV1, - DepositRequestV1, - PrefixedHexString, - WithdrawalRequestV1, -} from '@ethereumjs/util' +import type { PrefixedHexString } from '@ethereumjs/util' const kzg = new microEthKZG(trustedSetup) /** @@ -226,31 +221,11 @@ export class TransitionTool { } if (block.header.requestsRoot !== undefined) { - output.requestsRoot = bytesToHex(block.header.requestsRoot) + output.requestsHash = bytesToHex(block.header.requestsRoot) } if (block.requests !== undefined) { - if (this.common.isActivatedEIP(6110)) { - output.depositRequests = [] - } - - if (this.common.isActivatedEIP(7002)) { - output.withdrawalRequests = [] - } - - if (this.common.isActivatedEIP(7251)) { - output.consolidationRequests = [] - } - - for (const request of block.requests) { - if (request.type === CLRequestType.Deposit) { - output.depositRequests!.push(request.toJSON()) - } else if (request.type === CLRequestType.Withdrawal) { - output.withdrawalRequests!.push(request.toJSON()) - } else if (request.type === CLRequestType.Consolidation) { - output.consolidationRequests!.push(request.toJSON()) - } - } + output.requests = block.requests.map((request) => request.toJSON()) } if (this.rejected.length > 0) { diff --git a/packages/vm/test/t8n/types.ts b/packages/vm/test/t8n/types.ts index 527be50049..75ba7bba82 100644 --- a/packages/vm/test/t8n/types.ts +++ b/packages/vm/test/t8n/types.ts @@ -1,8 +1,4 @@ -import type { - ConsolidationRequestV1, - DepositRequestV1, - WithdrawalRequestV1, -} from '@ethereumjs/util' +import type { CLRequestType, RequestJSON } from '@ethereumjs/util' export type T8NOptions = { state: { @@ -73,10 +69,8 @@ export type T8NOutput = { withdrawalsRoot?: string blobGasUsed?: string currentExcessBlobGas?: string - requestsRoot?: string - depositRequests?: DepositRequestV1[] - withdrawalRequests?: WithdrawalRequestV1[] - consolidationRequests?: ConsolidationRequestV1[] + requestsHash?: string + requests?: RequestJSON[CLRequestType][] rejected?: T8NRejectedTx[] } From 1b65e0ceedf34866c1d75e8a6ce38981eaca9dec Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 10 Oct 2024 05:59:13 +0200 Subject: [PATCH 06/49] update devnet-4 to EIP PRs 8924, 8394 --- packages/block/src/block/block.ts | 7 +++++-- packages/block/src/block/constructors.ts | 4 ++-- packages/block/src/helpers.ts | 9 +++++---- packages/vm/src/buildBlock.ts | 6 +++--- packages/vm/src/params.ts | 6 ++++-- packages/vm/src/runBlock.ts | 5 +++-- 6 files changed, 22 insertions(+), 15 deletions(-) diff --git a/packages/block/src/block/block.ts b/packages/block/src/block/block.ts index 3f1be8d212..e1738f85e5 100644 --- a/packages/block/src/block/block.ts +++ b/packages/block/src/block/block.ts @@ -10,6 +10,7 @@ import { equalsBytes, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak.js' +import { sha256 } from 'ethereum-cryptography/sha256' /* eslint-disable */ // This is to allow for a proper and linked collection of constructors for the class header. @@ -60,6 +61,7 @@ export class Block { public readonly requests?: CLRequest[] public readonly common: Common protected keccakFunction: (msg: Uint8Array) => Uint8Array + protected sha256Function: (msg: Uint8Array) => Uint8Array /** * EIP-6800: Verkle Proof Data (experimental) @@ -92,6 +94,7 @@ export class Block { this.header = header ?? new BlockHeader({}, opts) this.common = this.header.common this.keccakFunction = this.common.customCrypto.keccak256 ?? keccak256 + this.sha256Function = this.common.customCrypto.sha256 ?? sha256 this.transactions = transactions this.withdrawals = withdrawals ?? (this.common.isActivatedEIP(4895) ? [] : undefined) @@ -256,11 +259,11 @@ export class Block { if (requestsInput === undefined) { if (this.cache.requestsRoot === undefined) { - this.cache.requestsRoot = await genRequestsRoot(this.requests!, this.keccakFunction) + this.cache.requestsRoot = await genRequestsRoot(this.requests!, this.sha256Function) } return equalsBytes(this.cache.requestsRoot, this.header.requestsRoot!) } else { - const reportedRoot = await genRequestsRoot(requests, this.keccakFunction) + const reportedRoot = await genRequestsRoot(requests, this.sha256Function) return equalsBytes(reportedRoot, this.header.requestsRoot!) } } diff --git a/packages/block/src/block/constructors.ts b/packages/block/src/block/constructors.ts index ebe377d7af..5c5f6f3849 100644 --- a/packages/block/src/block/constructors.ts +++ b/packages/block/src/block/constructors.ts @@ -407,8 +407,8 @@ export async function createBlockFromExecutionPayload( const requests = executionRequests?.map((req) => CLRequestFactory.fromSerializedRequest(hexToBytes(req)), ) - const keccakFunction = opts?.common?.customCrypto.keccak256 ?? keccak256 - const requestsRoot = requests ? await genRequestsRoot(requests, keccakFunction) : undefined + const sha256Function = opts?.common?.customCrypto.sha256 ?? keccak256 + const requestsRoot = requests ? await genRequestsRoot(requests, sha256Function) : undefined const header: HeaderData = { ...payload, diff --git a/packages/block/src/helpers.ts b/packages/block/src/helpers.ts index e4c8388ed0..5c86dbcc72 100644 --- a/packages/block/src/helpers.ts +++ b/packages/block/src/helpers.ts @@ -2,6 +2,7 @@ import { MerklePatriciaTrie } from '@ethereumjs/mpt' import { RLP } from '@ethereumjs/rlp' import { Blob4844Tx } from '@ethereumjs/tx' import { BIGINT_0, BIGINT_1, TypeOutput, isHexString, toType } from '@ethereumjs/util' +import { concatBytes } from 'ethereum-cryptography/utils' import type { BlockHeaderBytes, HeaderData } from './types.js' import type { TypedTransaction } from '@ethereumjs/tx' @@ -156,7 +157,7 @@ export async function genTransactionsTrieRoot( */ export async function genRequestsRoot( requests: CLRequest[], - keccakFunction: (msg: Uint8Array) => Uint8Array, + sha256Function: (msg: Uint8Array) => Uint8Array, ) { // Requests should be sorted in monotonically ascending order based on type // and whatever internal sorting logic is defined by each request type @@ -170,10 +171,10 @@ export async function genRequestsRoot( // def compute_requests_hash(list): // return keccak256(rlp.encode([rlp.encode(req) for req in list])) - const encodedRequests = [] + let flatRequests = new Uint8Array() for (const req of requests) { - encodedRequests.push(RLP.encode(req.serialize())) + flatRequests = concatBytes(flatRequests, sha256Function(req.serialize())) } - return keccakFunction(RLP.encode(encodedRequests)) + return sha256Function(flatRequests) } diff --git a/packages/vm/src/buildBlock.ts b/packages/vm/src/buildBlock.ts index 5197403a95..51dd9d6401 100644 --- a/packages/vm/src/buildBlock.ts +++ b/packages/vm/src/buildBlock.ts @@ -22,7 +22,7 @@ import { toBytes, toType, } from '@ethereumjs/util' -import { keccak256 } from 'ethereum-cryptography/keccak.js' +import { sha256 } from 'ethereum-cryptography/sha256' import { Bloom } from './bloom/index.js' import { accumulateRequests } from './requests.js' @@ -345,9 +345,9 @@ export class BlockBuilder { let requests let requestsRoot if (this.vm.common.isActivatedEIP(7685)) { - const keccakFunction = this.vm.common.customCrypto.keccak256 ?? keccak256 + const sha256Function = this.vm.common.customCrypto.sha256 ?? sha256 requests = await accumulateRequests(this.vm, this.transactionResults) - requestsRoot = await genRequestsRoot(requests, keccakFunction) + requestsRoot = await genRequestsRoot(requests, sha256Function) // Do other validations per request type } diff --git a/packages/vm/src/params.ts b/packages/vm/src/params.ts index 0450a75af8..dfaa021cda 100644 --- a/packages/vm/src/params.ts +++ b/packages/vm/src/params.ts @@ -78,7 +78,8 @@ export const paramsVM: ParamsDict = { 7002: { // config systemAddress: '0xfffffffffffffffffffffffffffffffffffffffe', // The system address to perform operations on the withdrawal requests predeploy address - withdrawalRequestPredeployAddress: '0x05F27129610CB42103b665629CB5c8C00296AaAa', // Address of the validator excess address + // See: https://github.com/ethereum/EIPs/pull/8934/files + withdrawalRequestPredeployAddress: '0x09Fc772D0857550724b07B850a4323f39112aAaA', // Address of the validator excess address }, /** @@ -87,6 +88,7 @@ export const paramsVM: ParamsDict = { 7251: { // config systemAddress: '0xfffffffffffffffffffffffffffffffffffffffe', // The system address to perform operations on the consolidation requests predeploy address - consolidationRequestPredeployAddress: '0x00706203067988Ab3E2A2ab626EdCD6f28bDBbbb', // Address of the consolidations contract + // See: https://github.com/ethereum/EIPs/pull/8934/files + consolidationRequestPredeployAddress: '0x01aBEa29659e5e97C95107F20bb753cD3e09bBBb', // Address of the consolidations contract }, } diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index ea0b73a810..33c2f57a13 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -27,6 +27,7 @@ import { } from '@ethereumjs/util' import debugDefault from 'debug' import { keccak256 } from 'ethereum-cryptography/keccak.js' +import { sha256 } from 'ethereum-cryptography/sha256' import { Bloom } from './bloom/index.js' import { emitEVMProfile } from './emitEVMProfile.js' @@ -216,9 +217,9 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise[] | undefined if (block.common.isActivatedEIP(7685)) { - const keccakFunction = vm.common.customCrypto.keccak256 ?? keccak256 + const sha256Function = vm.common.customCrypto.sha256 ?? sha256 requests = await accumulateRequests(vm, result.results) - requestsRoot = await genRequestsRoot(requests, keccakFunction) + requestsRoot = await genRequestsRoot(requests, sha256Function) } // Persist state From 6d39f31d5e12fe49578621541be3e1041a9e57f2 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 10 Oct 2024 06:15:35 +0200 Subject: [PATCH 07/49] t8ntool hotfix to fix state tests --- packages/vm/test/t8n/t8ntool.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/vm/test/t8n/t8ntool.ts b/packages/vm/test/t8n/t8ntool.ts index 50209a235a..05283326d2 100644 --- a/packages/vm/test/t8n/t8ntool.ts +++ b/packages/vm/test/t8n/t8ntool.ts @@ -74,7 +74,9 @@ export class TransitionTool { private async run(args: T8NOptions) { await this.setup(args) - + // HACK: fix me! + this.inputEnv.parentUncleHash = + '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347' const block = makeBlockFromEnv(this.inputEnv, { common: this.common }) const parentBlockHeader = makeParentBlockHeader(this.inputEnv, { common: this.common }) const parentBlock = createBlock({ header: parentBlockHeader }, { common: this.common }) From 7d023c477f1d8e9b6a20040bb959af5efb455a04 Mon Sep 17 00:00:00 2001 From: harkamal Date: Fri, 11 Oct 2024 20:38:10 +0530 Subject: [PATCH 08/49] refactor cl requests to the new simplified version --- packages/util/src/request.ts | 219 +++++------------------------------ 1 file changed, 26 insertions(+), 193 deletions(-) diff --git a/packages/util/src/request.ts b/packages/util/src/request.ts index 957083e3e0..f86ef2c9c8 100644 --- a/packages/util/src/request.ts +++ b/packages/util/src/request.ts @@ -1,8 +1,5 @@ -import { RLP } from '@ethereumjs/rlp' import { concatBytes } from 'ethereum-cryptography/utils' -import { bytesToHex, hexToBytes } from './bytes.js' - import type { PrefixedHexString } from './types.js' export type RequestBytes = Uint8Array @@ -34,9 +31,8 @@ export type ConsolidationRequestV1 = { } export interface RequestJSON { - [CLRequestType.Deposit]: DepositRequestV1 - [CLRequestType.Withdrawal]: WithdrawalRequestV1 - [CLRequestType.Consolidation]: ConsolidationRequestV1 + type: PrefixedHexString + data: PrefixedHexString } export type DepositRequestData = { @@ -70,208 +66,45 @@ export interface RequestData { export type TypedRequestData = RequestData[CLRequestType] -export interface CLRequestInterface { - readonly type: T - serialize(): Uint8Array - toJSON(): RequestJSON[T] -} - -export abstract class CLRequest implements CLRequestInterface { - readonly type: T - abstract serialize(): Uint8Array - abstract toJSON(): RequestJSON[T] - constructor(type: T) { - this.type = type - } -} +export class CLRequest { + // for easy use + public readonly bytes: Uint8Array -export class DepositRequest extends CLRequest { constructor( - public readonly pubkey: Uint8Array, - public readonly withdrawalCredentials: Uint8Array, - // 8 bytes uint64 LE - public readonly amount: Uint8Array, - public readonly signature: Uint8Array, - // 8 bytes uint64 LE - public readonly index: Uint8Array, + public readonly type: T, + public readonly data: Uint8Array, ) { - super(CLRequestType.Deposit) - } - - serialize() { - return concatBytes( - Uint8Array.from([this.type]), - this.pubkey, - this.withdrawalCredentials, - this.amount, - this.signature, - this.index, - ) + this.bytes = concatBytes(new Uint8Array([this.type]), data) } +} - toJSON(): DepositRequestV1 { - return { - pubkey: bytesToHex(this.pubkey), - withdrawalCredentials: bytesToHex(this.withdrawalCredentials), - amount: bytesToHex(this.amount), - signature: bytesToHex(this.signature), - index: bytesToHex(this.index), - } +export class DepositRequest extends CLRequest { + constructor(requestData: Uint8Array) { + super(CLRequestType.Deposit, requestData) } } export class WithdrawalRequest extends CLRequest { - constructor( - public readonly sourceAddress: Uint8Array, - public readonly validatorPubkey: Uint8Array, - // 8 bytes uint64 LE - public readonly amount: Uint8Array, - ) { - super(CLRequestType.Withdrawal) - } - - serialize() { - return concatBytes( - Uint8Array.from([this.type]), - this.sourceAddress, - this.validatorPubkey, - this.amount, - ) - } - - toJSON(): WithdrawalRequestV1 { - return { - sourceAddress: bytesToHex(this.sourceAddress), - validatorPubkey: bytesToHex(this.validatorPubkey), - amount: bytesToHex(this.amount), - } + constructor(requestData: Uint8Array) { + super(CLRequestType.Withdrawal, requestData) } } export class ConsolidationRequest extends CLRequest { - constructor( - public readonly sourceAddress: Uint8Array, - public readonly sourcePubkey: Uint8Array, - public readonly targetPubkey: Uint8Array, - ) { - super(CLRequestType.Consolidation) - } - - serialize() { - return concatBytes( - Uint8Array.from([this.type]), - this.sourceAddress, - this.sourcePubkey, - this.targetPubkey, - ) - } - - toJSON(): ConsolidationRequestV1 { - return { - sourceAddress: bytesToHex(this.sourceAddress), - sourcePubkey: bytesToHex(this.sourcePubkey), - targetPubkey: bytesToHex(this.targetPubkey), - } + constructor(requestData: Uint8Array) { + super(CLRequestType.Consolidation, requestData) } } -export function createDepositRequest(depositData: DepositRequestData): DepositRequest { - const { pubkey, withdrawalCredentials, amount, signature, index } = depositData - return new DepositRequest(pubkey, withdrawalCredentials, amount, signature, index) -} - -export function createDepositRequestFromJSON(jsonData: DepositRequestV1): DepositRequest { - const { pubkey, withdrawalCredentials, amount, signature, index } = jsonData - return createDepositRequest({ - pubkey: hexToBytes(pubkey), - withdrawalCredentials: hexToBytes(withdrawalCredentials), - amount: hexToBytes(amount), - signature: hexToBytes(signature), - index: hexToBytes(index), - }) -} - -export function createDepositRequestFromFlatData(bytes: Uint8Array): DepositRequest { - if (bytes.length !== 48 + 32 + 8 + 96) { - throw Error(`Invalid bytes=${bytes.length} for deposit request data`) - } - - return createDepositRequest({ - pubkey: bytes.subarray(0, 48), - withdrawalCredentials: bytes.subarray(48, 48 + 32), - amount: bytes.subarray(48 + 32, 48 + 32 + 8), - signature: bytes.subarray(48 + 32 + 8, 48 + 32 + 8 + 96), - index: bytes.subarray(48 + 32 + 8 + 96), - }) -} - -export function createWithdrawalRequest(withdrawalData: WithdrawalRequestData): WithdrawalRequest { - const { sourceAddress, validatorPubkey, amount } = withdrawalData - return new WithdrawalRequest(sourceAddress, validatorPubkey, amount) -} - -export function createWithdrawalRequestFromJSON(jsonData: WithdrawalRequestV1): WithdrawalRequest { - const { sourceAddress, validatorPubkey, amount } = jsonData - return createWithdrawalRequest({ - sourceAddress: hexToBytes(sourceAddress), - validatorPubkey: hexToBytes(validatorPubkey), - amount: hexToBytes(amount), - }) -} - -export function createWithdrawalRequestFromFlatData(bytes: Uint8Array): WithdrawalRequest { - if (bytes.length !== 20 + 48 + 8) { - throw Error(`Invalid bytes=${bytes.length} for withdrawal request data`) - } - return createWithdrawalRequest({ - sourceAddress: bytes.subarray(0, 20), - validatorPubkey: bytes.subarray(20, 20 + 48), - amount: bytes.subarray(20 + 48), - }) -} - -export function createConsolidationRequest( - consolidationData: ConsolidationRequestData, -): ConsolidationRequest { - const { sourceAddress, sourcePubkey, targetPubkey } = consolidationData - return new ConsolidationRequest(sourceAddress, sourcePubkey, targetPubkey) -} - -export function createConsolidationRequestFromJSON( - jsonData: ConsolidationRequestV1, -): ConsolidationRequest { - const { sourceAddress, sourcePubkey, targetPubkey } = jsonData - return createConsolidationRequest({ - sourceAddress: hexToBytes(sourceAddress), - sourcePubkey: hexToBytes(sourcePubkey), - targetPubkey: hexToBytes(targetPubkey), - }) -} - -export function createConsolidationRequestFromFlatData(bytes: Uint8Array): ConsolidationRequest { - const [sourceAddress, sourcePubkey, targetPubkey] = RLP.decode(bytes) as [ - Uint8Array, - Uint8Array, - Uint8Array, - ] - return createConsolidationRequest({ - sourceAddress, - sourcePubkey, - targetPubkey, - }) -} - -export class CLRequestFactory { - public static fromSerializedRequest(bytes: Uint8Array): CLRequest { - switch (bytes[0]) { - case CLRequestType.Deposit: - return createDepositRequestFromFlatData(bytes.subarray(1)) - case CLRequestType.Withdrawal: - return createWithdrawalRequestFromFlatData(bytes.subarray(1)) - case CLRequestType.Consolidation: - return createConsolidationRequestFromFlatData(bytes.subarray(1)) - default: - throw Error(`Invalid request type=${bytes[0]}`) - } +export function createCLRequest(bytes: Uint8Array): CLRequest { + switch (bytes[0]) { + case CLRequestType.Deposit: + return new DepositRequest(bytes.subarray(1)) as CLRequest + case CLRequestType.Withdrawal: + return new WithdrawalRequest(bytes.subarray(1)) as CLRequest + case CLRequestType.Consolidation: + return new ConsolidationRequest(bytes.subarray(1)) as CLRequest + default: + throw Error(`Invalid request type=${bytes[0]}`) } } From 2998547d48336692483d3c6ada44141ccf15f6b9 Mon Sep 17 00:00:00 2001 From: harkamal Date: Fri, 11 Oct 2024 20:39:31 +0530 Subject: [PATCH 09/49] remove requests from the block and modify associated code paths --- packages/block/examples/6110Requests.ts | 4 +- packages/block/examples/7002Requests.ts | 4 +- packages/block/examples/7251Requests.ts | 4 +- packages/block/src/block/block.ts | 51 +---------------- packages/block/src/block/constructors.ts | 70 ++--------------------- packages/block/src/from-beacon-payload.ts | 7 --- packages/block/src/helpers.ts | 4 +- packages/block/src/types.ts | 17 +----- packages/block/test/eip7685block.spec.ts | 10 ++-- 9 files changed, 21 insertions(+), 150 deletions(-) diff --git a/packages/block/examples/6110Requests.ts b/packages/block/examples/6110Requests.ts index 56b831d9d2..6ac386aa9b 100644 --- a/packages/block/examples/6110Requests.ts +++ b/packages/block/examples/6110Requests.ts @@ -7,7 +7,7 @@ import { createDepositRequest, randomBytes, } from '@ethereumjs/util' -import { keccak256 } from 'ethereum-cryptography/keccak.js' +import { sha256 } from 'ethereum-cryptography/keccak.js' const main = async () => { const common = new Common({ @@ -24,7 +24,7 @@ const main = async () => { } const request = createDepositRequest(depositRequestData) as CLRequest const requests = [request] - const requestsRoot = await genRequestsRoot(requests, keccak256) + const requestsRoot = genRequestsRoot(requests, sha256) const block = createBlock( { diff --git a/packages/block/examples/7002Requests.ts b/packages/block/examples/7002Requests.ts index d3f04aae91..883d97b052 100644 --- a/packages/block/examples/7002Requests.ts +++ b/packages/block/examples/7002Requests.ts @@ -7,7 +7,7 @@ import { createWithdrawalRequest, randomBytes, } from '@ethereumjs/util' -import { keccak256 } from 'ethereum-cryptography/keccak.js' +import { sha256 } from 'ethereum-cryptography/keccak.js' const main = async () => { const common = new Common({ @@ -22,7 +22,7 @@ const main = async () => { } const request = createWithdrawalRequest(withdrawalRequestData) as CLRequest const requests = [request] - const requestsRoot = await genRequestsRoot(requests, keccak256) + const requestsRoot = genRequestsRoot(requests, sha256) const block = createBlock( { diff --git a/packages/block/examples/7251Requests.ts b/packages/block/examples/7251Requests.ts index 846fc4e8e0..e6d9b3116b 100644 --- a/packages/block/examples/7251Requests.ts +++ b/packages/block/examples/7251Requests.ts @@ -6,7 +6,7 @@ import { createConsolidationRequest, randomBytes, } from '@ethereumjs/util' -import { keccak256 } from 'ethereum-cryptography/keccak.js' +import { sha256 } from 'ethereum-cryptography/keccak.js' const main = async () => { const common = new Common({ @@ -21,7 +21,7 @@ const main = async () => { } const request = createConsolidationRequest(consolidationRequestData) as CLRequest const requests = [request] - const requestsRoot = await genRequestsRoot(requests, keccak256) + const requestsRoot = genRequestsRoot(requests, sha256) const block = createBlock( { diff --git a/packages/block/src/block/block.ts b/packages/block/src/block/block.ts index e1738f85e5..9a9bbf52ea 100644 --- a/packages/block/src/block/block.ts +++ b/packages/block/src/block/block.ts @@ -19,7 +19,6 @@ import { sha256 } from 'ethereum-cryptography/sha256' // See: https://github.com/microsoft/TypeScript/issues/47558 // (situation will eventually improve on Typescript and/or Eslint update) import { - genRequestsRoot, genTransactionsTrieRoot, genWithdrawalsTrieRoot, BlockHeader, @@ -35,7 +34,7 @@ import { import type { BlockBytes, BlockOptions, ExecutionPayload, JSONBlock } from '../types.js' import type { Common } from '@ethereumjs/common' import type { FeeMarket1559Tx, LegacyTx, TypedTransaction } from '@ethereumjs/tx' -import type { CLRequest, CLRequestType, VerkleExecutionWitness, Withdrawal } from '@ethereumjs/util' +import type { VerkleExecutionWitness, Withdrawal } from '@ethereumjs/util' /** * Class representing a block in the Ethereum network. The {@link BlockHeader} has its own @@ -58,7 +57,6 @@ export class Block { public readonly transactions: TypedTransaction[] = [] public readonly uncleHeaders: BlockHeader[] = [] public readonly withdrawals?: Withdrawal[] - public readonly requests?: CLRequest[] public readonly common: Common protected keccakFunction: (msg: Uint8Array) => Uint8Array protected sha256Function: (msg: Uint8Array) => Uint8Array @@ -73,7 +71,6 @@ export class Block { protected cache: { txTrieRoot?: Uint8Array withdrawalsTrieRoot?: Uint8Array - requestsRoot?: Uint8Array } = {} /** @@ -88,7 +85,6 @@ export class Block { uncleHeaders: BlockHeader[] = [], withdrawals?: Withdrawal[], opts: BlockOptions = {}, - requests?: CLRequest[], executionWitness?: VerkleExecutionWitness | null, ) { this.header = header ?? new BlockHeader({}, opts) @@ -99,7 +95,6 @@ export class Block { this.transactions = transactions this.withdrawals = withdrawals ?? (this.common.isActivatedEIP(4895) ? [] : undefined) this.executionWitness = executionWitness - this.requests = requests ?? (this.common.isActivatedEIP(7685) ? [] : undefined) // null indicates an intentional absence of value or unavailability // undefined indicates that the executionWitness should be initialized with the default state if (this.common.isActivatedEIP(6800) && this.executionWitness === undefined) { @@ -150,18 +145,6 @@ export class Block { throw new Error(`Cannot have executionWitness field if EIP 6800 is not active `) } - if (!this.common.isActivatedEIP(7685) && requests !== undefined) { - throw new Error(`Cannot have requests field if EIP 7685 is not active`) - } - - // Requests should be sorted in monotonically ascending order based on type - // and whatever internal sorting logic is defined by each request type - if (requests !== undefined && 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 freeze = opts?.freeze ?? true if (freeze) { Object.freeze(this) @@ -184,11 +167,6 @@ export class Block { bytesArray.push(withdrawalsRaw) } - const requestsRaw = this.requests?.map((req) => req.serialize()) - if (requestsRaw) { - bytesArray.push(requestsRaw) - } - if (this.executionWitness !== undefined && this.executionWitness !== null) { const executionWitnessBytes = RLP.encode(JSON.stringify(this.executionWitness)) bytesArray.push(executionWitnessBytes as any) @@ -246,27 +224,6 @@ export class Block { return result } - async requestsTrieIsValid(requestsInput?: CLRequest[]): Promise { - if (!this.common.isActivatedEIP(7685)) { - throw new Error('EIP 7685 is not activated') - } - - const requests = requestsInput ?? this.requests! - - if (requests!.length === 0) { - return equalsBytes(this.header.requestsRoot!, KECCAK256_RLP) - } - - if (requestsInput === undefined) { - if (this.cache.requestsRoot === undefined) { - this.cache.requestsRoot = await genRequestsRoot(this.requests!, this.sha256Function) - } - return equalsBytes(this.cache.requestsRoot, this.header.requestsRoot!) - } else { - const reportedRoot = await genRequestsRoot(requests, this.sha256Function) - return equalsBytes(reportedRoot, this.header.requestsRoot!) - } - } /** * Validates transaction signatures and minimum gas requirements. * @returns {string[]} an array of error strings @@ -528,7 +485,6 @@ export class Block { transactions: this.transactions.map((tx) => tx.toJSON()), uncleHeaders: this.uncleHeaders.map((uh) => uh.toJSON()), ...withdrawalsAttr, - requests: this.requests?.map((req) => bytesToHex(req.serialize())), } } @@ -543,10 +499,6 @@ export class Block { const header = blockJSON.header! const transactions = this.transactions.map((tx) => bytesToHex(tx.serialize())) ?? [] const withdrawalsArr = blockJSON.withdrawals ? { withdrawals: blockJSON.withdrawals } : {} - const executionRequestsArr = - this.requests !== undefined - ? { executionRequests: this.requests.map((req) => bytesToHex(req.serialize())) } - : undefined const executionPayload: ExecutionPayload = { blockNumber: header.number!, @@ -568,7 +520,6 @@ export class Block { ...withdrawalsArr, parentBeaconBlockRoot: header.parentBeaconBlockRoot, executionWitness: this.executionWitness, - ...executionRequestsArr, } return executionPayload diff --git a/packages/block/src/block/constructors.ts b/packages/block/src/block/constructors.ts index 5c5f6f3849..86920243db 100644 --- a/packages/block/src/block/constructors.ts +++ b/packages/block/src/block/constructors.ts @@ -8,7 +8,6 @@ import { normalizeTxParams, } from '@ethereumjs/tx' import { - CLRequestFactory, bigIntToHex, bytesToHex, bytesToUtf8, @@ -20,10 +19,9 @@ import { intToHex, isHexString, } from '@ethereumjs/util' -import { keccak256 } from 'ethereum-cryptography/keccak.js' import { generateCliqueBlockExtraData } from '../consensus/clique.js' -import { genRequestsRoot, genTransactionsTrieRoot, genWithdrawalsTrieRoot } from '../helpers.js' +import { genTransactionsTrieRoot, genWithdrawalsTrieRoot } from '../helpers.js' import { Block, createBlockHeader, @@ -41,16 +39,10 @@ import type { ExecutionWitnessBytes, HeaderData, JSONRPCBlock, - RequestsBytes, WithdrawalsBytes, } from '../types.js' import type { TypedTransaction } from '@ethereumjs/tx' -import type { - EthersProvider, - PrefixedHexString, - RequestBytes, - WithdrawalBytes, -} from '@ethereumjs/util' +import type { EthersProvider, PrefixedHexString, WithdrawalBytes } from '@ethereumjs/util' /** * Static constructor to create a block from a block data dictionary @@ -65,7 +57,6 @@ export function createBlock(blockData: BlockData = {}, opts?: BlockOptions) { uncleHeaders: uhsData, withdrawals: withdrawalsData, executionWitness: executionWitnessData, - requests: clRequests, } = blockData const header = createBlockHeader(headerData, opts) @@ -104,15 +95,7 @@ export function createBlock(blockData: BlockData = {}, opts?: BlockOptions) { // stub till that time const executionWitness = executionWitnessData - return new Block( - header, - transactions, - uncleHeaders, - withdrawals, - opts, - clRequests, - executionWitness, - ) + return new Block(header, transactions, uncleHeaders, withdrawals, opts, executionWitness) } /** @@ -147,9 +130,6 @@ export function createBlockFromBytesArray(values: BlockBytes, opts?: BlockOption 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) @@ -165,15 +145,6 @@ export function createBlockFromBytesArray(values: BlockBytes, opts?: BlockOption ) } - 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', @@ -218,12 +189,6 @@ export function createBlockFromBytesArray(values: BlockBytes, opts?: BlockOption })) ?.map(createWithdrawal) - 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 @@ -239,15 +204,7 @@ export function createBlockFromBytesArray(values: BlockBytes, opts?: BlockOption } } - return new Block( - header, - transactions, - uncleHeaders, - withdrawals, - opts, - requests, - executionWitness, - ) + return new Block(header, transactions, uncleHeaders, withdrawals, opts, executionWitness) } /** @@ -290,12 +247,8 @@ export function createBlockFromRPC( const uncleHeaders = uncles.map((uh) => createBlockHeaderFromRPC(uh, options)) - const requests = blockParams.requests?.map((req) => { - const bytes = hexToBytes(req as PrefixedHexString) - return CLRequestFactory.fromSerializedRequest(bytes) - }) return createBlock( - { header, transactions, uncleHeaders, withdrawals: blockParams.withdrawals, requests }, + { header, transactions, uncleHeaders, withdrawals: blockParams.withdrawals }, options, ) } @@ -378,7 +331,6 @@ export async function createBlockFromExecutionPayload( feeRecipient: coinbase, transactions, withdrawals: withdrawalsData, - executionRequests, executionWitness, } = payload @@ -404,12 +356,6 @@ export async function createBlockFromExecutionPayload( ? await genWithdrawalsTrieRoot(withdrawals, new MerklePatriciaTrie({ common: opts?.common })) : undefined - const requests = executionRequests?.map((req) => - CLRequestFactory.fromSerializedRequest(hexToBytes(req)), - ) - const sha256Function = opts?.common?.customCrypto.sha256 ?? keccak256 - const requestsRoot = requests ? await genRequestsRoot(requests, sha256Function) : undefined - const header: HeaderData = { ...payload, number, @@ -418,14 +364,10 @@ export async function createBlockFromExecutionPayload( withdrawalsRoot, mixHash, coinbase, - requestsRoot, } // we are not setting setHardfork as common is already set to the correct hf - const block = createBlock( - { header, transactions: txs, withdrawals, executionWitness, requests }, - opts, - ) + const block = createBlock({ header, transactions: txs, withdrawals, executionWitness }, opts) if ( block.common.isActivatedEIP(6800) && (executionWitness === undefined || executionWitness === null) diff --git a/packages/block/src/from-beacon-payload.ts b/packages/block/src/from-beacon-payload.ts index a6ff816b8f..b084b803bd 100644 --- a/packages/block/src/from-beacon-payload.ts +++ b/packages/block/src/from-beacon-payload.ts @@ -31,8 +31,6 @@ export type BeaconPayloadJSON = { blob_gas_used?: NumericString excess_blob_gas?: NumericString parent_beacon_block_root?: PrefixedHexString - // requests data - execution_requests?: PrefixedHexString[] // the casing of VerkleExecutionWitness remains same camel case for now execution_witness?: VerkleExecutionWitness } @@ -134,11 +132,6 @@ export function executionPayloadFromBeaconPayload(payload: BeaconPayloadJSON): E executionPayload.parentBeaconBlockRoot = payload.parent_beacon_block_root } - // requests - if (payload.execution_requests !== undefined && payload.execution_requests !== null) { - executionPayload.executionRequests = payload.execution_requests - } - if (payload.execution_witness !== undefined && payload.execution_witness !== null) { // the casing structure in payload could be camel case or snake depending upon the CL executionPayload.executionWitness = diff --git a/packages/block/src/helpers.ts b/packages/block/src/helpers.ts index 5c86dbcc72..7a4243bb71 100644 --- a/packages/block/src/helpers.ts +++ b/packages/block/src/helpers.ts @@ -155,7 +155,7 @@ export async function genTransactionsTrieRoot( * @param emptyTrie optional empty trie used to generate the root * @returns a 32 byte Uint8Array representing the requests trie root */ -export async function genRequestsRoot( +export function genRequestsRoot( requests: CLRequest[], sha256Function: (msg: Uint8Array) => Uint8Array, ) { @@ -173,7 +173,7 @@ export async function genRequestsRoot( let flatRequests = new Uint8Array() for (const req of requests) { - flatRequests = concatBytes(flatRequests, sha256Function(req.serialize())) + flatRequests = concatBytes(flatRequests, sha256Function(req.bytes)) } return sha256Function(flatRequests) diff --git a/packages/block/src/types.ts b/packages/block/src/types.ts index 50db8f09f2..0e45f40b15 100644 --- a/packages/block/src/types.ts +++ b/packages/block/src/types.ts @@ -5,12 +5,9 @@ import type { AddressLike, BigIntLike, BytesLike, - CLRequest, - CLRequestType, JSONRPCWithdrawal, NumericString, PrefixedHexString, - RequestBytes, VerkleExecutionWitness, WithdrawalBytes, WithdrawalData, @@ -125,7 +122,6 @@ export interface BlockData { transactions?: Array uncleHeaders?: Array withdrawals?: Array - requests?: Array> /** * EIP-6800: Verkle Proof Data (experimental) */ @@ -133,19 +129,16 @@ export interface BlockData { } export type WithdrawalsBytes = WithdrawalBytes[] -export type RequestsBytes = RequestBytes[] export type ExecutionWitnessBytes = Uint8Array export type BlockBytes = | [BlockHeaderBytes, TransactionsBytes, UncleHeadersBytes] | [BlockHeaderBytes, TransactionsBytes, UncleHeadersBytes, WithdrawalsBytes] - | [BlockHeaderBytes, TransactionsBytes, UncleHeadersBytes, WithdrawalsBytes, RequestsBytes] | [ BlockHeaderBytes, TransactionsBytes, UncleHeadersBytes, WithdrawalsBytes, - RequestsBytes, ExecutionWitnessBytes, ] @@ -153,12 +146,7 @@ export type BlockBytes = * BlockHeaderBuffer is a Buffer array, except for the Verkle PreState which is an array of prestate arrays. */ export type BlockHeaderBytes = Uint8Array[] -export type BlockBodyBytes = [ - TransactionsBytes, - UncleHeadersBytes, - WithdrawalsBytes?, - RequestsBytes?, -] +export type BlockBodyBytes = [TransactionsBytes, UncleHeadersBytes, WithdrawalsBytes?] /** * TransactionsBytes can be an array of serialized txs for Typed Transactions or an array of Uint8Array Arrays for legacy transactions. */ @@ -176,7 +164,6 @@ export interface JSONBlock { transactions?: JSONTx[] uncleHeaders?: JSONHeader[] withdrawals?: JSONRPCWithdrawal[] - requests?: PrefixedHexString[] | null executionWitness?: VerkleExecutionWitness | null } @@ -239,7 +226,6 @@ export interface JSONRPCBlock { parentBeaconBlockRoot?: PrefixedHexString // If EIP-4788 is enabled for this block, returns parent beacon block root executionWitness?: VerkleExecutionWitness | null // If Verkle is enabled for this block requestsRoot?: PrefixedHexString // If EIP-7685 is enabled for this block, returns the requests root - requests?: Array // If EIP-7685 is enabled for this block, array of serialized CL requests } export type WithdrawalV1 = { @@ -271,5 +257,4 @@ export type ExecutionPayload = { parentBeaconBlockRoot?: PrefixedHexString // QUANTITY, 64 Bits // VerkleExecutionWitness is already a hex serialized object executionWitness?: VerkleExecutionWitness | null // QUANTITY, 64 Bits, null implies not available - executionRequests?: PrefixedHexString[] | null // null implies not available } diff --git a/packages/block/test/eip7685block.spec.ts b/packages/block/test/eip7685block.spec.ts index 4355cdb1b2..c4114e5931 100644 --- a/packages/block/test/eip7685block.spec.ts +++ b/packages/block/test/eip7685block.spec.ts @@ -6,7 +6,7 @@ import { createWithdrawalRequest, randomBytes, } from '@ethereumjs/util' -import { keccak256 } from 'ethereum-cryptography/keccak.js' +import { sha256 } from 'ethereum-cryptography/keccak.js' import { assert, describe, expect, it } from 'vitest' import { genRequestsRoot } from '../src/helpers.js' @@ -56,7 +56,7 @@ describe('7685 tests', () => { }) it('should instantiate a block with requests', async () => { const request = getRandomDepositRequest() - const requestsRoot = await genRequestsRoot([request], keccak256) + const requestsRoot = genRequestsRoot([request], sha256) const block = createBlock( { requests: [request], @@ -84,7 +84,7 @@ describe('7685 tests', () => { const request2 = getRandomDepositRequest() const request3 = getRandomWithdrawalRequest() const requests = [request1, request2, request3] - const requestsRoot = await genRequestsRoot(requests, keccak256) + const requestsRoot = genRequestsRoot(requests, sha256) // Construct block with requests in correct order @@ -126,7 +126,7 @@ describe('createWithdrawalFromBytesArray tests', () => { const request2 = getRandomWithdrawalRequest() const request3 = getRandomWithdrawalRequest() const requests = [request1, request2, request3] - const requestsRoot = await genRequestsRoot(requests, keccak256) + const requestsRoot = genRequestsRoot(requests, sha256) const serializedRequests = [request1.serialize(), request2.serialize(), request3.serialize()] const block = createBlockFromBytesArray( @@ -146,7 +146,7 @@ describe('fromRPC tests', () => { const request2 = getRandomDepositRequest() const request3 = getRandomWithdrawalRequest() const requests = [request1, request2, request3] - const requestsRoot = await genRequestsRoot(requests, keccak256) + const requestsRoot = genRequestsRoot(requests, sha256) const serializedRequests = [request1.serialize(), request2.serialize(), request3.serialize()] const block = createBlockFromBytesArray( From 5d2ede23fd1997d63240dfdf89b259b20c66686b Mon Sep 17 00:00:00 2001 From: harkamal Date: Fri, 11 Oct 2024 20:40:05 +0530 Subject: [PATCH 10/49] remove storing and retriving of requests from blockchain --- packages/blockchain/src/blockchain.ts | 6 ------ packages/blockchain/src/db/manager.ts | 9 --------- 2 files changed, 15 deletions(-) diff --git a/packages/blockchain/src/blockchain.ts b/packages/blockchain/src/blockchain.ts index 8e1e7c4f70..3935a1526c 100644 --- a/packages/blockchain/src/blockchain.ts +++ b/packages/blockchain/src/blockchain.ts @@ -600,12 +600,6 @@ export class Blockchain implements BlockchainInterface { // (one for each uncle header and then for validateBlobTxs). const parentBlock = await this.getBlock(block.header.parentHash) block.validateBlobTransactions(parentBlock.header) - if (block.common.isActivatedEIP(7685)) { - const valid = await block.requestsTrieIsValid() - if (!valid) { - throw new Error('invalid requestsRoot') - } - } } /** * The following rules are checked in this method: diff --git a/packages/blockchain/src/db/manager.ts b/packages/blockchain/src/db/manager.ts index 42f826c464..82338a1885 100644 --- a/packages/blockchain/src/db/manager.ts +++ b/packages/blockchain/src/db/manager.ts @@ -128,15 +128,6 @@ export class DBManager { body.push([]) } } - - // If requests root exists, validate that requests array exists or insert it - if (header.requestsRoot !== undefined) { - if (!equalsBytes(header.requestsRoot, KECCAK256_RLP)) { - throw new Error('requestsRoot should be equal to hash of null when no requests') - } else { - body.push([]) - } - } } const blockData = [header.raw(), ...body] as BlockBytes From 5fe681b5183ab0b2cd3b4b1b642f6f9f230f0bae Mon Sep 17 00:00:00 2001 From: harkamal Date: Fri, 11 Oct 2024 20:41:29 +0530 Subject: [PATCH 11/49] modify the deposit, withdrawal and consolidation requests accumulation for buildblock and runblock and corresponding requestsroot calcs --- packages/vm/src/buildBlock.ts | 6 +- packages/vm/src/requests.ts | 107 ++++++++-------------------------- packages/vm/src/runBlock.ts | 18 +++--- 3 files changed, 35 insertions(+), 96 deletions(-) diff --git a/packages/vm/src/buildBlock.ts b/packages/vm/src/buildBlock.ts index 51dd9d6401..e96b7dfcff 100644 --- a/packages/vm/src/buildBlock.ts +++ b/packages/vm/src/buildBlock.ts @@ -347,8 +347,7 @@ export class BlockBuilder { if (this.vm.common.isActivatedEIP(7685)) { const sha256Function = this.vm.common.customCrypto.sha256 ?? sha256 requests = await accumulateRequests(this.vm, this.transactionResults) - requestsRoot = await genRequestsRoot(requests, sha256Function) - // Do other validations per request type + requestsRoot = genRequestsRoot(requests, sha256Function) } // get stateRoot after all the accumulateRequests etc have been done @@ -376,7 +375,6 @@ export class BlockBuilder { header: headerData, transactions: this.transactions, withdrawals: this.withdrawals, - requests, } let block @@ -397,7 +395,7 @@ export class BlockBuilder { this.checkpointed = false } - return block + return { block, requests } } async initState() { diff --git a/packages/vm/src/requests.ts b/packages/vm/src/requests.ts index 9b135fdef3..6a7338d838 100644 --- a/packages/vm/src/requests.ts +++ b/packages/vm/src/requests.ts @@ -3,14 +3,13 @@ import { bigIntToAddressBytes, bigIntToBytes, bytesToHex, - bytesToInt, createAddressFromString, - createConsolidationRequest, - createDepositRequest, - createWithdrawalRequest, setLengthLeft, } from '@ethereumjs/util' +import { concatBytes } from '../../util/src/bytes.js' +import { ConsolidationRequest, DepositRequest, WithdrawalRequest } from '../../util/src/request.js' + import type { RunTxResult } from './types.js' import type { VM } from './vm.js' import type { CLRequest, CLRequestType } from '@ethereumjs/util' @@ -33,30 +32,27 @@ export const accumulateRequests = async ( vm.common['_chainParams'].depositContractAddress ?? Mainnet.depositContractAddress if (depositContractAddress === undefined) throw new Error('deposit contract address required with EIP 6110') - await accumulateDeposits(depositContractAddress, txResults, requests) + const depositsRequest = accumulateDepositsRequest(depositContractAddress, txResults) + requests.push(depositsRequest) } if (common.isActivatedEIP(7002)) { - await accumulateEIP7002Requests(vm, requests) + const withdrawalsRequest = await accumulateWithdrawalsRequest(vm) + requests.push(withdrawalsRequest) } if (common.isActivatedEIP(7251)) { - await accumulateEIP7251Requests(vm, requests) + const consolidationsRequest = await accumulateConsolidationsRequest(vm) + requests.push(consolidationsRequest) } - 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 in ascending order') - } - } + // requests are already type byte ordered by construction return requests } -const accumulateEIP7002Requests = async ( +const accumulateWithdrawalsRequest = async ( vm: VM, - requests: CLRequest[], -): Promise => { +): Promise> => { // Partial withdrawals logic const addressBytes = setLengthLeft( bigIntToBytes(vm.common.param('withdrawalRequestPredeployAddress')), @@ -71,7 +67,7 @@ const accumulateEIP7002Requests = async ( const originalAccount = await vm.stateManager.getAccount(withdrawalsAddress) if (originalAccount === undefined) { - return + return new WithdrawalRequest(new Uint8Array()) } const results = await vm.evm.runCall({ @@ -87,22 +83,12 @@ const accumulateEIP7002Requests = async ( } const resultsBytes = results.execResult.returnValue - if (resultsBytes.length > 0) { - // Each request is 76 bytes - for (let startByte = 0; startByte < resultsBytes.length; startByte += 76) { - const slicedBytes = resultsBytes.slice(startByte, startByte + 76) - const sourceAddress = slicedBytes.slice(0, 20) // 20 Bytes - const validatorPubkey = slicedBytes.slice(20, 68) // 48 Bytes - const amount = slicedBytes.slice(68, 76) // 8 Bytes / Uint64 LE - requests.push(createWithdrawalRequest({ sourceAddress, validatorPubkey, amount })) - } - } + return new WithdrawalRequest(resultsBytes) } -const accumulateEIP7251Requests = async ( +const accumulateConsolidationsRequest = async ( vm: VM, - requests: CLRequest[], -): Promise => { +): Promise> => { // Partial withdrawals logic const addressBytes = setLengthLeft( bigIntToBytes(vm.common.param('consolidationRequestPredeployAddress')), @@ -117,7 +103,7 @@ const accumulateEIP7251Requests = async ( const originalAccount = await vm.stateManager.getAccount(consolidationsAddress) if (originalAccount === undefined) { - return + return new ConsolidationRequest(new Uint8Array(0)) } const results = await vm.evm.runCall({ @@ -133,67 +119,22 @@ const accumulateEIP7251Requests = async ( } const resultsBytes = results.execResult.returnValue - if (resultsBytes.length > 0) { - // Each request is 116 bytes - for (let startByte = 0; startByte < resultsBytes.length; startByte += 116) { - const slicedBytes = resultsBytes.slice(startByte, startByte + 116) - const sourceAddress = slicedBytes.slice(0, 20) // 20 Bytes - const sourcePubkey = slicedBytes.slice(20, 68) // 48 Bytes - const targetPubkey = slicedBytes.slice(68, 116) // 48 bytes - requests.push(createConsolidationRequest({ sourceAddress, sourcePubkey, targetPubkey })) - } - } + return new ConsolidationRequest(resultsBytes) } -const accumulateDeposits = async ( +const accumulateDepositsRequest = ( depositContractAddress: string, txResults: RunTxResult[], - requests: CLRequest[], -) => { +): CLRequest => { + let resultsBytes = new Uint8Array(0) for (const [_, tx] of txResults.entries()) { for (let i = 0; i < tx.receipt.logs.length; i++) { const log = tx.receipt.logs[i] if (bytesToHex(log[0]).toLowerCase() === depositContractAddress.toLowerCase()) { - // Extracts validator pubkey, withdrawal credential, deposit amount, signature, - // and validator index from Deposit Event log. - // The event fields are non-indexed so contained in one byte array (log[2]) so parsing is as follows: - // 1. Read the first 32 bytes to get the starting position of the first field. - // 2. Continue reading the byte array in 32 byte increments to get all the field starting positions - // 3. Read 32 bytes starting with the first field position to get the size of the first field - // 4. Read the bytes from first field position + 32 + the size of the first field to get the first field value - // 5. Repeat steps 3-4 for each field - const pubKeyIdx = bytesToInt(log[2].slice(0, 32)) - const pubKeySize = bytesToInt(log[2].slice(pubKeyIdx, pubKeyIdx + 32)) - const withdrawalCreditsIdx = bytesToInt(log[2].slice(32, 64)) - const withdrawalCreditsSize = bytesToInt( - log[2].slice(withdrawalCreditsIdx, withdrawalCreditsIdx + 32), - ) - const amountIdx = bytesToInt(log[2].slice(64, 96)) - const amountSize = bytesToInt(log[2].slice(amountIdx, amountIdx + 32)) - const sigIdx = bytesToInt(log[2].slice(96, 128)) - const sigSize = bytesToInt(log[2].slice(sigIdx, sigIdx + 32)) - const indexIdx = bytesToInt(log[2].slice(128, 160)) - const indexSize = bytesToInt(log[2].slice(indexIdx, indexIdx + 32)) - - const pubkey = log[2].slice(pubKeyIdx + 32, pubKeyIdx + 32 + pubKeySize) - const withdrawalCredentials = log[2].slice( - withdrawalCreditsIdx + 32, - withdrawalCreditsIdx + 32 + withdrawalCreditsSize, - ) - const amount = log[2].slice(amountIdx + 32, amountIdx + 32 + amountSize) - const signature = log[2].slice(sigIdx + 32, sigIdx + 32 + sigSize) - const index = log[2].slice(indexIdx + 32, indexIdx + 32 + indexSize) - - requests.push( - createDepositRequest({ - pubkey, - withdrawalCredentials, - amount, - signature, - index, - }), - ) + resultsBytes = concatBytes(resultsBytes, log[2]) } } } + + return new DepositRequest(resultsBytes) } diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index 33c2f57a13..7c51c32972 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -26,7 +26,6 @@ import { unprefixedHexToBytes, } from '@ethereumjs/util' import debugDefault from 'debug' -import { keccak256 } from 'ethereum-cryptography/keccak.js' import { sha256 } from 'ethereum-cryptography/sha256' import { Bloom } from './bloom/index.js' @@ -219,7 +218,7 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise Date: Fri, 11 Oct 2024 20:42:58 +0530 Subject: [PATCH 12/49] modfiy the 7002 eip spec along with the new contract and debug and fix the test including fixing a logs bloom bug in the generate fields block generation --- packages/vm/test/api/EIPs/eip-7002.spec.ts | 45 +++++++++++++--------- packages/vm/test/api/EIPs/eip-7685.spec.ts | 4 +- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/packages/vm/test/api/EIPs/eip-7002.spec.ts b/packages/vm/test/api/EIPs/eip-7002.spec.ts index c116e657d0..eaa7ed463f 100644 --- a/packages/vm/test/api/EIPs/eip-7002.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7002.spec.ts @@ -1,6 +1,5 @@ import { createBlock } from '@ethereumjs/block' import { Common, Hardfork, Mainnet } from '@ethereumjs/common' -import { RLP } from '@ethereumjs/rlp' import { createLegacyTx } from '@ethereumjs/tx' import { Account, @@ -15,7 +14,7 @@ import { } from '@ethereumjs/util' import { assert, describe, it } from 'vitest' -import { bytesToBigInt } from '../../../../util/src/bytes.js' +import { CLRequestType } from '../../../../util/src/request.js' import { runBlock } from '../../../src/index.js' import { setupVM } from '../utils.js' @@ -33,11 +32,11 @@ const deploymentTxData = { gasLimit: BigInt('0x3d090'), gasPrice: BigInt('0xe8d4a51000'), data: hexToBytes( - '0x61049d5f556101af80600f5f395ff33373fffffffffffffffffffffffffffffffffffffffe1460a0573615156028575f545f5260205ff35b36603814156101ab5760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b9093900434106101ab57600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160b4575060105b5f5b8181146101585780604c02838201600302600401805490600101805490600101549160601b83528260140152807fffffffffffffffffffffffffffffffff0000000000000000000000000000000016826034015260401c906044018160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160b6565b910180921461016a5790600255610175565b90505f6002555f6003555b5f548061049d141561018457505f5b6001546002828201116101995750505f61019f565b01600290035b5f555f600155604c025ff35b5f5ffd', + '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5f556101f480602d5f395ff33373fffffffffffffffffffffffffffffffffffffffe1460c7573615156028575f545f5260205ff35b36603814156101f05760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101f057600182026001905f5b5f821115608057810190830284830290049160010191906065565b9093900434106101f057600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160db575060105b5f5b81811461017f5780604c02838201600302600401805490600101805490600101549160601b83528260140152807fffffffffffffffffffffffffffffffff0000000000000000000000000000000016826034015260401c906044018160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160dd565b9101809214610191579060025561019c565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14156101c957505f5b6001546002828201116101de5750505f6101e4565b01600290035b5f555f600155604c025ff35b5f5ffd', ), v: BigInt('0x1b'), r: BigInt('0x539'), - s: BigInt('0x48655fec580f6877'), + s: BigInt('0x10e740537d4d36b9'), } const deploymentTx = createLegacyTx(deploymentTxData) @@ -111,7 +110,7 @@ describe('EIP-7002 tests', () => { generatedBlock = e.block }) - await runBlock(vm, { + let runBlockResults = await runBlock(vm, { block: block2, skipHeaderValidation: true, skipBlockValidation: true, @@ -119,21 +118,26 @@ describe('EIP-7002 tests', () => { }) // Ensure the request is generated - assert.ok(generatedBlock!.requests!.length === 1) - - const requestDecoded = RLP.decode(generatedBlock!.requests![0].serialize().slice(1)) + assert.ok(runBlockResults.requests!.length === 1) + assert.equal( + generatedBlock!.transactions.length, + 1, + 'withdrawal transaction should be included', + ) - const sourceAddressRequest = requestDecoded[0] as Uint8Array - const validatorPubkeyRequest = requestDecoded[1] as Uint8Array - const amountRequest = requestDecoded[2] as Uint8Array + const withdrawalRequest = runBlockResults.requests![0] + assert(withdrawalRequest.type, CLRequestType.Withdrawal, 'make sure its withdrawal request') + // amount is in le when contract pack it in requests + const expectedRequestData = concatBytes( + tx.getSenderAddress().bytes, + validatorPubkey, + amountBytes.reverse(), + ) // Ensure the requests are correct - assert.ok(equalsBytes(sourceAddressRequest, tx.getSenderAddress().bytes)) - assert.ok(equalsBytes(validatorPubkey, validatorPubkeyRequest)) - // the direct byte comparison fails because leading zeros have been stripped - // off the amountBytes because it was serialized in request from bigint - assert.equal(bytesToBigInt(amountBytes), bytesToBigInt(amountRequest)) + assert.ok(equalsBytes(expectedRequestData, withdrawalRequest.data)) + // generated block should be valid await runBlock(vm, { block: generatedBlock!, skipHeaderValidation: true, root }) // Run block with 2 requests @@ -152,7 +156,7 @@ describe('EIP-7002 tests', () => { { common }, ) - await runBlock(vm, { + runBlockResults = await runBlock(vm, { block: block3, skipHeaderValidation: true, skipBlockValidation: true, @@ -161,7 +165,12 @@ describe('EIP-7002 tests', () => { // Note: generatedBlock is now overridden with the new generated block (this is thus block number 3) // Ensure there are 2 requests - assert.ok(generatedBlock!.requests!.length === 2) + assert.ok(runBlockResults.requests!.length === 1) + assert.equal( + generatedBlock!.transactions.length, + 2, + 'withdrawal transactions should be included', + ) }) it('should throw when contract is not deployed', async () => { diff --git a/packages/vm/test/api/EIPs/eip-7685.spec.ts b/packages/vm/test/api/EIPs/eip-7685.spec.ts index 27003c7381..0bce5371d2 100644 --- a/packages/vm/test/api/EIPs/eip-7685.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7685.spec.ts @@ -2,7 +2,7 @@ import { createBlock, genRequestsRoot } from '@ethereumjs/block' import { createBlockchain } from '@ethereumjs/blockchain' import { Common, Hardfork, Mainnet } from '@ethereumjs/common' import { KECCAK256_RLP, createDepositRequest, hexToBytes, randomBytes } from '@ethereumjs/util' -import { keccak256 } from 'ethereum-cryptography/keccak.js' +import { sha256 } from 'ethereum-cryptography/keccak.js' import { assert, describe, expect, it } from 'vitest' import { buildBlock, createVM, runBlock } from '../../../src/index.js' @@ -49,7 +49,7 @@ 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 genRequestsRoot([request], keccak256) + const requestsRoot = genRequestsRoot([request], sha256) const block = createBlock( { requests: [request], From 9b5caf2e7d3525be114388d2c03e5a7b5a9065cd Mon Sep 17 00:00:00 2001 From: harkamal Date: Fri, 11 Oct 2024 20:46:20 +0530 Subject: [PATCH 13/49] modify code to correctly patch generated requests on getpayload/build/pending block --- packages/client/src/miner/miner.ts | 2 +- packages/client/src/miner/pendingBlock.ts | 17 +++++++++++++---- .../src/rpc/modules/engine/util/getPayload.ts | 16 +++++++++------- packages/client/test/rpc/helpers.ts | 2 +- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/packages/client/src/miner/miner.ts b/packages/client/src/miner/miner.ts index 7e6e5124a2..f0a74141c5 100644 --- a/packages/client/src/miner/miner.ts +++ b/packages/client/src/miner/miner.ts @@ -332,7 +332,7 @@ export class Miner { } if (interrupt) return // Build block, sealing it - const block = await blockBuilder.build(this.nextSolution) + const { block } = await blockBuilder.build(this.nextSolution) if (this.config.saveReceipts) { await this.execution.receiptsManager?.saveReceipts(block, receipts) } diff --git a/packages/client/src/miner/pendingBlock.ts b/packages/client/src/miner/pendingBlock.ts index cc17cf8316..15ba431dc1 100644 --- a/packages/client/src/miner/pendingBlock.ts +++ b/packages/client/src/miner/pendingBlock.ts @@ -19,7 +19,7 @@ import type { Config } from '../config.js' import type { TxPool } from '../service/txpool.js' import type { Block, HeaderData } from '@ethereumjs/block' import type { TypedTransaction } from '@ethereumjs/tx' -import type { PrefixedHexString, WithdrawalData } from '@ethereumjs/util' +import type { CLRequest, CLRequestType, PrefixedHexString, WithdrawalData } from '@ethereumjs/util' import type { BlockBuilder, TxReceipt, VM } from '@ethereumjs/vm' interface PendingBlockOpts { @@ -239,7 +239,16 @@ export class PendingBlock { */ async build( payloadIdBytes: Uint8Array | string, - ): Promise { + ): Promise< + | void + | [ + block: Block, + receipts: TxReceipt[], + value: bigint, + blobs?: BlobsBundle, + requests?: CLRequest[], + ] + > { const payloadId = typeof payloadIdBytes !== 'string' ? bytesToHex(payloadIdBytes) : payloadIdBytes const builder = this.pendingPayloads.get(payloadId) @@ -283,7 +292,7 @@ export class PendingBlock { const { skippedByAddErrors, blobTxs } = await this.addTransactions(builder, txs) - const block = await builder.build() + const { block, requests } = await builder.build() // Construct blobs bundle const blobs = block.common.isActivatedEIP(4844) @@ -301,7 +310,7 @@ export class PendingBlock { )}`, ) - return [block, builder.transactionReceipts, builder.minerValue, blobs] + return [block, builder.transactionReceipts, builder.minerValue, blobs, requests] } private async addTransactions(builder: BlockBuilder, txs: TypedTransaction[]) { diff --git a/packages/client/src/rpc/modules/engine/util/getPayload.ts b/packages/client/src/rpc/modules/engine/util/getPayload.ts index cae5de8694..ad281f4707 100644 --- a/packages/client/src/rpc/modules/engine/util/getPayload.ts +++ b/packages/client/src/rpc/modules/engine/util/getPayload.ts @@ -1,22 +1,24 @@ -import { bigIntToHex } from '@ethereumjs/util' +import { bigIntToHex, bytesToHex } from '@ethereumjs/util' import type { BlobsBundle } from '../../../../miner/index.js' import type { BlobsBundleV1 } from '../types.js' import type { Block, ExecutionPayload } from '@ethereumjs/block' +import type { CLRequest, CLRequestType } from '@ethereumjs/util' /** * Formats a block to {@link ExecutionPayloadV1}. */ -export const blockToExecutionPayload = (block: Block, value: bigint, bundle?: BlobsBundle) => { +export const blockToExecutionPayload = ( + block: Block, + value: bigint, + bundle?: BlobsBundle, + requests?: CLRequest[], +) => { const executionPayload: ExecutionPayload = block.toExecutionPayload() // parentBeaconBlockRoot is not part of the CL payload if (executionPayload.parentBeaconBlockRoot !== undefined) { delete executionPayload.parentBeaconBlockRoot } - const { executionRequests } = executionPayload - if (executionPayload.executionRequests !== undefined) { - delete executionPayload.executionRequests - } const blobsBundle: BlobsBundleV1 | undefined = bundle ? bundle : undefined @@ -25,7 +27,7 @@ export const blockToExecutionPayload = (block: Block, value: bigint, bundle?: Bl const shouldOverrideBuilder = false return { executionPayload, - executionRequests, + executionRequests: requests?.map((req) => bytesToHex(req.bytes)), blockValue: bigIntToHex(value), blobsBundle, shouldOverrideBuilder, diff --git a/packages/client/test/rpc/helpers.ts b/packages/client/test/rpc/helpers.ts index dda35b4b3d..06e0c1d7f8 100644 --- a/packages/client/test/rpc/helpers.ts +++ b/packages/client/test/rpc/helpers.ts @@ -308,7 +308,7 @@ export async function runBlockWithTxs( for (const tx of txs) { await blockBuilder.addTransaction(tx, { skipHardForkValidation: true }) } - const block = await blockBuilder.build() + const { block } = await blockBuilder.build() // put block into chain and run execution await chain.putBlocks([block], fromEngine) From a62ed492720a7cf4bfc0d22650dbd54fafc0dcba Mon Sep 17 00:00:00 2001 From: harkamal Date: Fri, 11 Oct 2024 20:48:12 +0530 Subject: [PATCH 14/49] fix the newpayload engine codeflow to validate the cl requests --- .../client/src/rpc/modules/engine/engine.ts | 43 ++--------- .../src/rpc/modules/engine/util/newPayload.ts | 71 ++++++++++++++++++- 2 files changed, 74 insertions(+), 40 deletions(-) diff --git a/packages/client/src/rpc/modules/engine/engine.ts b/packages/client/src/rpc/modules/engine/engine.ts index dad9e86748..140f8d9ba8 100644 --- a/packages/client/src/rpc/modules/engine/engine.ts +++ b/packages/client/src/rpc/modules/engine/engine.ts @@ -32,7 +32,6 @@ import { recursivelyFindParents, validExecutedChainBlock, validHash, - validate4844BlobVersionedHashes, validateHardforkRange, } from './util/index.js' import { @@ -374,6 +373,9 @@ export class Engine { ...payload, // ExecutionPayload only handles undefined parentBeaconBlockRoot: parentBeaconBlockRoot ?? undefined, + }, + { + blobVersionedHashes: blobVersionedHashes ?? undefined, executionRequests: executionRequests ?? undefined, }, this.chain, @@ -395,41 +397,6 @@ export class Engine { return response } - /** - * Validate blob versioned hashes in the context of EIP-4844 blob transactions - */ - if (headBlock.common.isActivatedEIP(4844)) { - let validationError: string | null = null - if (blobVersionedHashes === undefined || blobVersionedHashes === null) { - validationError = `Error verifying blobVersionedHashes: received none` - } else { - validationError = validate4844BlobVersionedHashes(headBlock, blobVersionedHashes) - } - - // if there was a validation error return invalid - if (validationError !== null) { - this.config.logger.debug(validationError) - const latestValidHash = await validHash( - hexToBytes(parentHash as PrefixedHexString), - this.chain, - this.chainCache, - ) - const response = { status: Status.INVALID, latestValidHash, validationError } - // skip marking the block invalid as this is more of a data issue from CL - return response - } - } else if (blobVersionedHashes !== undefined && blobVersionedHashes !== null) { - const validationError = `Invalid blobVersionedHashes before EIP-4844 is activated` - const latestValidHash = await validHash( - hexToBytes(parentHash as PrefixedHexString), - this.chain, - this.chainCache, - ) - const response = { status: Status.INVALID, latestValidHash, validationError } - // skip marking the block invalid as this is more of a data issue from CL - return response - } - /** * Stats and hardfork updates */ @@ -1341,7 +1308,7 @@ export class Engine { } // The third arg returned is the minerValue which we will use to // value the block - const [block, receipts, value, blobs] = built + const [block, receipts, value, blobs, requests] = built // do a blocking call even if execution might be busy for the moment and skip putting // it into chain till CL confirms with full data via new payload like versioned hashes @@ -1355,7 +1322,7 @@ export class Engine { /** * Creates the payload in ExecutionPayloadV1 format to be returned */ - const executionPayload = blockToExecutionPayload(block, value, blobs) + const executionPayload = blockToExecutionPayload(block, value, blobs, requests) let checkNotBeforeHf: Hardfork | null let checkNotAfterHf: Hardfork | null diff --git a/packages/client/src/rpc/modules/engine/util/newPayload.ts b/packages/client/src/rpc/modules/engine/util/newPayload.ts index 594f24b85f..5bce5b35f7 100644 --- a/packages/client/src/rpc/modules/engine/util/newPayload.ts +++ b/packages/client/src/rpc/modules/engine/util/newPayload.ts @@ -1,6 +1,7 @@ -import { createBlockFromExecutionPayload } from '@ethereumjs/block' +import { createBlockFromExecutionPayload, genRequestsRoot } from '@ethereumjs/block' import { Blob4844Tx } from '@ethereumjs/tx' -import { hexToBytes } from '@ethereumjs/util' +import { bytesToHex, createCLRequest, equalsBytes, hexToBytes } from '@ethereumjs/util' +import { sha256 } from 'ethereum-cryptography/sha256' import { short } from '../../../../util/index.js' import { Status } from '../types.js' @@ -12,12 +13,18 @@ import type { ChainCache, PayloadStatusV1 } from '../types.js' import type { Block, ExecutionPayload } from '@ethereumjs/block' import type { PrefixedHexString } from '@ethereumjs/util' +type CLValidationData = { + blobVersionedHashes?: PrefixedHexString[] + executionRequests?: PrefixedHexString[] +} + /** * Returns a block from a payload. * If errors, returns {@link PayloadStatusV1} */ export const assembleBlock = async ( payload: ExecutionPayload, + clValidationData: CLValidationData, chain: Chain, chainCache: ChainCache, ): Promise<{ block?: Block; error?: PayloadStatusV1 }> => { @@ -32,6 +39,47 @@ export const assembleBlock = async ( // 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() + + // Validate CL data to see if it matches with the assembled block + const { blobVersionedHashes, executionRequests } = clValidationData + + /** + * Validate blob versioned hashes in the context of EIP-4844 blob transactions + */ + if (block.common.isActivatedEIP(4844)) { + let validationError: string | null = null + if (blobVersionedHashes === undefined) { + validationError = `Error verifying blobVersionedHashes: received none` + } else { + validationError = validate4844BlobVersionedHashes(block, blobVersionedHashes) + } + + // if there was a validation error return invalid + if (validationError !== null) { + throw validationError + } + } else if (blobVersionedHashes !== undefined) { + const validationError = `Invalid blobVersionedHashes before EIP-4844 is activated` + throw validationError + } + + if (block.common.isActivatedEIP(7685)) { + let validationError: string | null = null + if (executionRequests === undefined) { + validationError = `Error verifying executionRequests: received none` + } else { + validationError = validate7685ExecutionRequests(block, executionRequests) + } + + // if there was a validation error return invalid + if (validationError !== null) { + throw validationError + } + } else if (executionRequests !== undefined) { + const validationError = `Invalid executionRequests before EIP-7685 is activated` + throw validationError + } + return { block } } catch (error) { const validationError = `Error assembling block from payload: ${error}` @@ -82,3 +130,22 @@ export const validate4844BlobVersionedHashes = ( } return validationError } + +export const validate7685ExecutionRequests = ( + headBlock: Block, + executionRequests: PrefixedHexString[], +): string | null => { + let validationError: string | null = null + + // Collect versioned hashes in the flat array `txVersionedHashes` to match with received + const requests = executionRequests.map((req) => createCLRequest(hexToBytes(req))) + const sha256Function = headBlock.common.customCrypto.sha256 ?? sha256 + const requestsRoot = genRequestsRoot(requests, sha256Function) + + if (!equalsBytes(requestsRoot, headBlock.header.requestsRoot!)) { + validationError = `Invalid requestsRoot received=${bytesToHex( + headBlock.header.requestsRoot!, + )} expected=${bytesToHex(requestsRoot)}` + } + return validationError +} From 7e898d4d6247dfa935d764612358ddff468e587b Mon Sep 17 00:00:00 2001 From: harkamal Date: Fri, 11 Oct 2024 20:48:44 +0530 Subject: [PATCH 15/49] remove the requests from eth rpc and blockfetcher p2p --- packages/client/src/rpc/modules/eth.ts | 1 - packages/client/src/sync/fetcher/blockfetcher.ts | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/client/src/rpc/modules/eth.ts b/packages/client/src/rpc/modules/eth.ts index d34c4434f8..0e2944b18c 100644 --- a/packages/client/src/rpc/modules/eth.ts +++ b/packages/client/src/rpc/modules/eth.ts @@ -153,7 +153,6 @@ const toJSONRPCBlock = async ( excessBlobGas: header.excessBlobGas, parentBeaconBlockRoot: header.parentBeaconBlockRoot, requestsRoot: header.requestsRoot, - requests: block.requests?.map((req) => bytesToHex(req.serialize())), } } diff --git a/packages/client/src/sync/fetcher/blockfetcher.ts b/packages/client/src/sync/fetcher/blockfetcher.ts index 7aa6b1091b..892536438d 100644 --- a/packages/client/src/sync/fetcher/blockfetcher.ts +++ b/packages/client/src/sync/fetcher/blockfetcher.ts @@ -73,7 +73,7 @@ export class BlockFetcher extends BlockFetcherBase { `Requested blocks=${blocksRange} from ${peerInfo} (received: ${headers.length} headers / ${bodies.length} bodies)`, ) const blocks: Block[] = [] - for (const [i, [txsData, unclesData, withdrawalsData, requestsData]] of bodies.entries()) { + for (const [i, [txsData, unclesData, withdrawalsData]] of bodies.entries()) { const header = headers[i] if ( (!equalsBytes(header.transactionsTrie, KECCAK256_RLP) && txsData.length === 0) || @@ -92,9 +92,6 @@ export class BlockFetcher extends BlockFetcherBase { if (withdrawalsData !== undefined) { values.push(withdrawalsData) } - if (requestsData !== undefined) { - values.push(requestsData) - } // Supply the common from the corresponding block header already set on correct fork const block = createBlockFromBytesArray(values, { common: headers[i].common }) // Only validate the data integrity From a5aa2db175971bf2989c7f1a73d2aeedc7ea4b13 Mon Sep 17 00:00:00 2001 From: harkamal Date: Fri, 11 Oct 2024 22:28:30 +0530 Subject: [PATCH 16/49] modify debug and fix 6110 deposit spec test --- packages/vm/test/api/EIPs/eip-6110.spec.ts | 82 ++++++++++++++++++++-- 1 file changed, 77 insertions(+), 5 deletions(-) diff --git a/packages/vm/test/api/EIPs/eip-6110.spec.ts b/packages/vm/test/api/EIPs/eip-6110.spec.ts index 787c85e83d..b57f556b28 100644 --- a/packages/vm/test/api/EIPs/eip-6110.spec.ts +++ b/packages/vm/test/api/EIPs/eip-6110.spec.ts @@ -2,7 +2,9 @@ import { createBlock } from '@ethereumjs/block' import { Common, Hardfork, Mainnet, getPresetChainConfig } from '@ethereumjs/common' import { createTx } from '@ethereumjs/tx' import { + bytesToBigInt, bytesToHex, + bytesToInt, createAccount, createAddressFromPrivateKey, createAddressFromString, @@ -12,10 +14,10 @@ import { import { keccak256 } from 'ethereum-cryptography/keccak.js' import { assert, describe, it } from 'vitest' +import { CLRequestType } from '../../../../util/src/request.js' import { buildBlock, runBlock } from '../../../src/index.js' import { setupVM } from '../utils.js' -import type { DepositRequest } from '../../../../util/src/request.js' import type { PrefixedHexString } from '@ethereumjs/util' const depositContractByteCode = hexToBytes( @@ -64,8 +66,10 @@ describe('EIP-6110 runBlock tests', () => { ) const res = await runBlock(vm, { block, generate: true, skipBlockValidation: true }) assert.equal(res.requests?.length, 1) - const reqPubkey = (res.requests![0] as DepositRequest).pubkey - assert.equal(bytesToHex(reqPubkey), pubkey) + const depositRequest = res.requests![0] + assert.equal(depositRequest.type, CLRequestType.Deposit) + const parsedRequest = parseDepositRequest(depositRequest.data) + assert.equal(bytesToHex(parsedRequest.pubkey), pubkey) }) }) @@ -95,7 +99,75 @@ describe('EIP-7685 buildBlock tests', () => { await blockBuilder.addTransaction(depositTx) const res = await blockBuilder.build() assert.equal(res.requests?.length, 1) - const reqPubkey = (res.requests![0] as DepositRequest).pubkey - assert.equal(bytesToHex(reqPubkey), pubkey) + + const depositRequest = res.requests![0] + assert.equal(depositRequest.type, CLRequestType.Deposit) + const parsedRequest = parseDepositRequest(depositRequest.data) + assert.equal(bytesToHex(parsedRequest.pubkey), pubkey) }) }) + +function parseDepositRequest(requestData: Uint8Array) { + // Extracts validator pubkey, withdrawal credential, deposit amount, signature, + // and validator index from Deposit Event log. + // The event fields are non-indexed so contained in one byte array (log[2]) so parsing is as follows: + // 1. Read the first 32 bytes to get the starting position of the first field. + // 2. Continue reading the byte array in 32 byte increments to get all the field starting positions + // 3. Read 32 bytes starting with the first field position to get the size of the first field + // 4. Read the bytes from first field position + 32 + the size of the first field to get the first field value + // 5. Repeat steps 3-4 for each field + const pubKeyIdx = bytesToInt(requestData.slice(0, 32)) + const pubKeySize = bytesToInt(requestData.slice(pubKeyIdx, pubKeyIdx + 32)) + const withdrawalCreditsIdx = bytesToInt(requestData.slice(32, 64)) + const withdrawalCreditsSize = bytesToInt( + requestData.slice(withdrawalCreditsIdx, withdrawalCreditsIdx + 32), + ) + const amountIdx = bytesToInt(requestData.slice(64, 96)) + const amountSize = bytesToInt(requestData.slice(amountIdx, amountIdx + 32)) + const sigIdx = bytesToInt(requestData.slice(96, 128)) + const sigSize = bytesToInt(requestData.slice(sigIdx, sigIdx + 32)) + const indexIdx = bytesToInt(requestData.slice(128, 160)) + const indexSize = bytesToInt(requestData.slice(indexIdx, indexIdx + 32)) + const pubkey = requestData.slice(pubKeyIdx + 32, pubKeyIdx + 32 + pubKeySize) + const withdrawalCredentials = requestData.slice( + withdrawalCreditsIdx + 32, + withdrawalCreditsIdx + 32 + withdrawalCreditsSize, + ) + const amountBytes = requestData.slice(amountIdx + 32, amountIdx + 32 + amountSize) + const amountBytesBigEndian = new Uint8Array([ + amountBytes[7], + amountBytes[6], + amountBytes[5], + amountBytes[4], + amountBytes[3], + amountBytes[2], + amountBytes[1], + amountBytes[0], + ]) + const amount = bytesToBigInt(amountBytesBigEndian) + + const signature = requestData.slice(sigIdx + 32, sigIdx + 32 + sigSize) + + const indexBytes = requestData.slice(indexIdx + 32, indexIdx + 32 + indexSize) + + // Convert the little-endian array to big-endian array + const indexBytesBigEndian = new Uint8Array([ + indexBytes[7], + indexBytes[6], + indexBytes[5], + indexBytes[4], + indexBytes[3], + indexBytes[2], + indexBytes[1], + indexBytes[0], + ]) + const index = bytesToBigInt(indexBytesBigEndian) + + return { + pubkey, + withdrawalCredentials, + amount, + signature, + index, + } +} From 3eafb13dc59786f0dea0dfab15ba79b3cb3a7513 Mon Sep 17 00:00:00 2001 From: harkamal Date: Fri, 11 Oct 2024 22:59:14 +0530 Subject: [PATCH 17/49] update the vm 7685 spec and add todos for later consideration --- packages/block/src/header/header.ts | 2 ++ packages/vm/test/api/EIPs/eip-7685.spec.ts | 29 +++++++++++----------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/block/src/header/header.ts b/packages/block/src/header/header.ts index 79675dafed..b065bcd0db 100644 --- a/packages/block/src/header/header.ts +++ b/packages/block/src/header/header.ts @@ -161,6 +161,8 @@ export class BlockHeader { blobGasUsed: this.common.isActivatedEIP(4844) ? BIGINT_0 : undefined, excessBlobGas: this.common.isActivatedEIP(4844) ? BIGINT_0 : undefined, parentBeaconBlockRoot: this.common.isActivatedEIP(4788) ? new Uint8Array(32) : undefined, + // TODO: not sure what the default should be here becuase it would depends on activated EIPs + // as even empty requests will produce data to sha hash requestsRoot: this.common.isActivatedEIP(7685) ? KECCAK256_RLP : undefined, } diff --git a/packages/vm/test/api/EIPs/eip-7685.spec.ts b/packages/vm/test/api/EIPs/eip-7685.spec.ts index 0bce5371d2..0f39a095f6 100644 --- a/packages/vm/test/api/EIPs/eip-7685.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7685.spec.ts @@ -1,8 +1,8 @@ import { createBlock, genRequestsRoot } from '@ethereumjs/block' import { createBlockchain } from '@ethereumjs/blockchain' import { Common, Hardfork, Mainnet } from '@ethereumjs/common' -import { KECCAK256_RLP, createDepositRequest, hexToBytes, randomBytes } from '@ethereumjs/util' -import { sha256 } from 'ethereum-cryptography/keccak.js' +import { createCLRequest, hexToBytes } from '@ethereumjs/util' +import { sha256 } from 'ethereum-cryptography/sha256' import { assert, describe, expect, it } from 'vitest' import { buildBlock, createVM, runBlock } from '../../../src/index.js' @@ -14,14 +14,10 @@ const invalidRequestsRoot = hexToBytes( '0xc98048d6605eb79ecc08d90b8817f44911ec474acd8d11688453d2c6ef743bc5', ) function getRandomDepositRequest(): CLRequest { - const depositRequestData = { - pubkey: randomBytes(48), - withdrawalCredentials: randomBytes(32), - amount: randomBytes(8), - signature: randomBytes(96), - index: randomBytes(8), - } - return createDepositRequest(depositRequestData) as CLRequest + const sampleDepositRequest = hexToBytes( + '0x0000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030ac842878bb70009552a4cfcad801d6e659c50bd50d7d03306790cb455ce7363c5b6972f0159d170f625a99b2064dbefc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020010000000000000000000000818ccb1c4eda80270b04d6df822b1e72dd83c303000000000000000000000000000000000000000000000000000000000000000800405973070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060a747f75c72d0cf0d2b52504c7385b516f0523e2f0842416399f42b4aee5c6384a5674f6426b1cc3d0827886fa9b909e616f5c9f61f986013ed2b9bf37071cbae951136265b549f44e3c8e26233c0433e9124b7fd0dc86e82f9fedfc0a179d76900000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000', + ) + return createCLRequest(sampleDepositRequest) } const common = new Common({ chain: Mainnet, hardfork: Hardfork.Cancun, eips: [7685] }) @@ -52,19 +48,19 @@ describe('EIP-7685 runBlock tests', () => { const requestsRoot = genRequestsRoot([request], sha256) const block = createBlock( { - requests: [request], header: { requestsRoot }, }, { common }, ) await expect(async () => runBlock(vm, { block })).rejects.toThrow(/invalid requestsRoot/) }) + + // TODO: no way to test this without running block, why check why does this test pass + // as it should not throw on some random request root it('should error when requestsRoot does not match requests provided', async () => { const vm = await setupVM({ common }) - const request = getRandomDepositRequest() const block = createBlock( { - requests: [request], header: { requestsRoot: invalidRequestsRoot }, }, { common }, @@ -91,8 +87,11 @@ describe('EIP 7685 buildBlock tests', () => { blockOpts: { calcDifficultyFromHeader: genesisBlock.header, freeze: false }, }) - const block = await blockBuilder.build() + const { block } = await blockBuilder.build() - assert.deepEqual(block.header.requestsRoot, KECCAK256_RLP) + assert.deepEqual( + block.header.requestsRoot, + hexToBytes('0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'), + ) }) }) From 638acc382afceb6ac032cd18636acabf8ad58bb4 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 11 Oct 2024 23:22:06 +0200 Subject: [PATCH 18/49] fix t8ntool rq output --- packages/vm/test/t8n/t8ntool.ts | 11 ++++++----- packages/vm/test/t8n/types.ts | 4 +--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/vm/test/t8n/t8ntool.ts b/packages/vm/test/t8n/t8ntool.ts index 05283326d2..3b6dc596ca 100644 --- a/packages/vm/test/t8n/t8ntool.ts +++ b/packages/vm/test/t8n/t8ntool.ts @@ -25,7 +25,7 @@ import type { Block } from '@ethereumjs/block' import type { Common } from '@ethereumjs/common' import type { Log } from '@ethereumjs/evm' import type { TypedTxData } from '@ethereumjs/tx' -import type { PrefixedHexString } from '@ethereumjs/util' +import type { CLRequest, CLRequestType, PrefixedHexString } from '@ethereumjs/util' const kzg = new microEthKZG(trustedSetup) /** @@ -118,7 +118,7 @@ export class TransitionTool { const result = await builder.build() - const convertedOutput = this.getOutput(result) + const convertedOutput = this.getOutput(result.block, result.requests) const alloc = await this.stateTracker.dumpAlloc() this.writeOutput(args, convertedOutput, alloc) @@ -195,7 +195,7 @@ export class TransitionTool { }) } - private getOutput(block: Block): T8NOutput { + private getOutput(block: Block, requests?: CLRequest[]): T8NOutput { const output: T8NOutput = { stateRoot: bytesToHex(block.header.stateRoot), txRoot: bytesToHex(block.header.transactionsTrie), @@ -226,8 +226,9 @@ export class TransitionTool { output.requestsHash = bytesToHex(block.header.requestsRoot) } - if (block.requests !== undefined) { - output.requests = block.requests.map((request) => request.toJSON()) + if (requests !== undefined) { + // NOTE: EEST currently wants the raw request bytes, **excluding** the type + output.requests = requests.map((request) => bytesToHex(request.bytes.slice(1))) } if (this.rejected.length > 0) { diff --git a/packages/vm/test/t8n/types.ts b/packages/vm/test/t8n/types.ts index 75ba7bba82..f4891dca8f 100644 --- a/packages/vm/test/t8n/types.ts +++ b/packages/vm/test/t8n/types.ts @@ -1,5 +1,3 @@ -import type { CLRequestType, RequestJSON } from '@ethereumjs/util' - export type T8NOptions = { state: { fork: string @@ -70,7 +68,7 @@ export type T8NOutput = { blobGasUsed?: string currentExcessBlobGas?: string requestsHash?: string - requests?: RequestJSON[CLRequestType][] + requests?: string[] rejected?: T8NRejectedTx[] } From 965eb866c6fa9ca9ad0354433ef130fea04032ea Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sat, 12 Oct 2024 00:44:22 +0200 Subject: [PATCH 19/49] vm: fix 6110 requests --- packages/vm/src/requests.ts | 53 +++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/packages/vm/src/requests.ts b/packages/vm/src/requests.ts index 6a7338d838..51fb79344e 100644 --- a/packages/vm/src/requests.ts +++ b/packages/vm/src/requests.ts @@ -3,6 +3,7 @@ import { bigIntToAddressBytes, bigIntToBytes, bytesToHex, + bytesToInt, createAddressFromString, setLengthLeft, } from '@ethereumjs/util' @@ -127,11 +128,59 @@ const accumulateDepositsRequest = ( txResults: RunTxResult[], ): CLRequest => { let resultsBytes = new Uint8Array(0) + const depositContractAddressLowerCase = depositContractAddress.toLowerCase() for (const [_, tx] of txResults.entries()) { for (let i = 0; i < tx.receipt.logs.length; i++) { const log = tx.receipt.logs[i] - if (bytesToHex(log[0]).toLowerCase() === depositContractAddress.toLowerCase()) { - resultsBytes = concatBytes(resultsBytes, log[2]) + if (bytesToHex(log[0]).toLowerCase() === depositContractAddressLowerCase) { + // Extracts validator pubkey, withdrawal credential, deposit amount, signature, + // and validator index from Deposit Event log. + // The event fields are non-indexed so contained in one byte array (log[2]) so parsing is as follows: + // 1. Read the first 32 bytes to get the starting position of the first field. + // 2. Continue reading the byte array in 32 byte increments to get all the field starting positions + // 3. Read 32 bytes starting with the first field position to get the size of the first field + // 4. Read the bytes from first field position + 32 + the size of the first field to get the first field value + // 5. Repeat steps 3-4 for each field + // This is equivalent to ABI-decoding the event: + // event DepositEvent( + // bytes pubkey, + // bytes withdrawal_credentials, + // bytes amount, + // bytes signature, + // bytes index + //); + + const pubKeyIdx = bytesToInt(log[2].slice(0, 32)) + const pubKeySize = bytesToInt(log[2].slice(pubKeyIdx, pubKeyIdx + 32)) + const withdrawalCreditsIdx = bytesToInt(log[2].slice(32, 64)) + const withdrawalCreditsSize = bytesToInt( + log[2].slice(withdrawalCreditsIdx, withdrawalCreditsIdx + 32), + ) + const amountIdx = bytesToInt(log[2].slice(64, 96)) + const amountSize = bytesToInt(log[2].slice(amountIdx, amountIdx + 32)) + const sigIdx = bytesToInt(log[2].slice(96, 128)) + const sigSize = bytesToInt(log[2].slice(sigIdx, sigIdx + 32)) + const indexIdx = bytesToInt(log[2].slice(128, 160)) + const indexSize = bytesToInt(log[2].slice(indexIdx, indexIdx + 32)) + + const pubkey = log[2].slice(pubKeyIdx + 32, pubKeyIdx + 32 + pubKeySize) + const withdrawalCredentials = log[2].slice( + withdrawalCreditsIdx + 32, + withdrawalCreditsIdx + 32 + withdrawalCreditsSize, + ) + const amount = log[2].slice(amountIdx + 32, amountIdx + 32 + amountSize) + const signature = log[2].slice(sigIdx + 32, sigIdx + 32 + sigSize) + const index = log[2].slice(indexIdx + 32, indexIdx + 32 + indexSize) + + const depositRequestBytes = concatBytes( + pubkey, + withdrawalCredentials, + amount, + signature, + index, + ) + + resultsBytes = concatBytes(resultsBytes, depositRequestBytes) } } } From 2fea7ae9060f057de824785944da46dccf4d131e Mon Sep 17 00:00:00 2001 From: harkamal Date: Sat, 12 Oct 2024 13:24:16 +0530 Subject: [PATCH 20/49] update request to just store bytes and expose getters for data and type and fix the 6110 and 7685 spec --- packages/util/src/request.ts | 15 ++-- packages/vm/src/requests.ts | 80 +++++++++++----------- packages/vm/test/api/EIPs/eip-6110.spec.ts | 61 ++--------------- packages/vm/test/api/EIPs/eip-7685.spec.ts | 2 +- 4 files changed, 57 insertions(+), 101 deletions(-) diff --git a/packages/util/src/request.ts b/packages/util/src/request.ts index f86ef2c9c8..adc30cd1f8 100644 --- a/packages/util/src/request.ts +++ b/packages/util/src/request.ts @@ -70,11 +70,16 @@ export class CLRequest { // for easy use public readonly bytes: Uint8Array - constructor( - public readonly type: T, - public readonly data: Uint8Array, - ) { - this.bytes = concatBytes(new Uint8Array([this.type]), data) + get type() { + return this.bytes[0] as T + } + + get data() { + return this.bytes.subarray(1) + } + + constructor(requestType: T, requestData: Uint8Array) { + this.bytes = concatBytes(new Uint8Array([requestType]), requestData) } } diff --git a/packages/vm/src/requests.ts b/packages/vm/src/requests.ts index 51fb79344e..cebc8b4b70 100644 --- a/packages/vm/src/requests.ts +++ b/packages/vm/src/requests.ts @@ -133,45 +133,7 @@ const accumulateDepositsRequest = ( for (let i = 0; i < tx.receipt.logs.length; i++) { const log = tx.receipt.logs[i] if (bytesToHex(log[0]).toLowerCase() === depositContractAddressLowerCase) { - // Extracts validator pubkey, withdrawal credential, deposit amount, signature, - // and validator index from Deposit Event log. - // The event fields are non-indexed so contained in one byte array (log[2]) so parsing is as follows: - // 1. Read the first 32 bytes to get the starting position of the first field. - // 2. Continue reading the byte array in 32 byte increments to get all the field starting positions - // 3. Read 32 bytes starting with the first field position to get the size of the first field - // 4. Read the bytes from first field position + 32 + the size of the first field to get the first field value - // 5. Repeat steps 3-4 for each field - // This is equivalent to ABI-decoding the event: - // event DepositEvent( - // bytes pubkey, - // bytes withdrawal_credentials, - // bytes amount, - // bytes signature, - // bytes index - //); - - const pubKeyIdx = bytesToInt(log[2].slice(0, 32)) - const pubKeySize = bytesToInt(log[2].slice(pubKeyIdx, pubKeyIdx + 32)) - const withdrawalCreditsIdx = bytesToInt(log[2].slice(32, 64)) - const withdrawalCreditsSize = bytesToInt( - log[2].slice(withdrawalCreditsIdx, withdrawalCreditsIdx + 32), - ) - const amountIdx = bytesToInt(log[2].slice(64, 96)) - const amountSize = bytesToInt(log[2].slice(amountIdx, amountIdx + 32)) - const sigIdx = bytesToInt(log[2].slice(96, 128)) - const sigSize = bytesToInt(log[2].slice(sigIdx, sigIdx + 32)) - const indexIdx = bytesToInt(log[2].slice(128, 160)) - const indexSize = bytesToInt(log[2].slice(indexIdx, indexIdx + 32)) - - const pubkey = log[2].slice(pubKeyIdx + 32, pubKeyIdx + 32 + pubKeySize) - const withdrawalCredentials = log[2].slice( - withdrawalCreditsIdx + 32, - withdrawalCreditsIdx + 32 + withdrawalCreditsSize, - ) - const amount = log[2].slice(amountIdx + 32, amountIdx + 32 + amountSize) - const signature = log[2].slice(sigIdx + 32, sigIdx + 32 + sigSize) - const index = log[2].slice(indexIdx + 32, indexIdx + 32 + indexSize) - + const { pubkey, withdrawalCredentials, amount, signature, index } = parseDepositLog(log[2]) const depositRequestBytes = concatBytes( pubkey, withdrawalCredentials, @@ -187,3 +149,43 @@ const accumulateDepositsRequest = ( return new DepositRequest(resultsBytes) } + +function parseDepositLog(requestData: Uint8Array) { + // Extracts validator pubkey, withdrawal credential, deposit amount, signature, + // and validator index from Deposit Event log. + // The event fields are non-indexed so contained in one byte array (log[2]) so parsing is as follows: + // 1. Read the first 32 bytes to get the starting position of the first field. + // 2. Continue reading the byte array in 32 byte increments to get all the field starting positions + // 3. Read 32 bytes starting with the first field position to get the size of the first field + // 4. Read the bytes from first field position + 32 + the size of the first field to get the first field value + // 5. Repeat steps 3-4 for each field + const pubKeyIdx = bytesToInt(requestData.slice(0, 32)) + const pubKeySize = bytesToInt(requestData.slice(pubKeyIdx, pubKeyIdx + 32)) + const withdrawalCreditsIdx = bytesToInt(requestData.slice(32, 64)) + const withdrawalCreditsSize = bytesToInt( + requestData.slice(withdrawalCreditsIdx, withdrawalCreditsIdx + 32), + ) + const amountIdx = bytesToInt(requestData.slice(64, 96)) + const amountSize = bytesToInt(requestData.slice(amountIdx, amountIdx + 32)) + const sigIdx = bytesToInt(requestData.slice(96, 128)) + const sigSize = bytesToInt(requestData.slice(sigIdx, sigIdx + 32)) + const indexIdx = bytesToInt(requestData.slice(128, 160)) + const indexSize = bytesToInt(requestData.slice(indexIdx, indexIdx + 32)) + + const pubkey = requestData.slice(pubKeyIdx + 32, pubKeyIdx + 32 + pubKeySize) + const withdrawalCredentials = requestData.slice( + withdrawalCreditsIdx + 32, + withdrawalCreditsIdx + 32 + withdrawalCreditsSize, + ) + const amount = requestData.slice(amountIdx + 32, amountIdx + 32 + amountSize) + const signature = requestData.slice(sigIdx + 32, sigIdx + 32 + sigSize) + const index = requestData.slice(indexIdx + 32, indexIdx + 32 + indexSize) + + return { + pubkey, + withdrawalCredentials, + amount, + signature, + index, + } +} diff --git a/packages/vm/test/api/EIPs/eip-6110.spec.ts b/packages/vm/test/api/EIPs/eip-6110.spec.ts index b57f556b28..0d97f7f9d1 100644 --- a/packages/vm/test/api/EIPs/eip-6110.spec.ts +++ b/packages/vm/test/api/EIPs/eip-6110.spec.ts @@ -2,9 +2,7 @@ import { createBlock } from '@ethereumjs/block' import { Common, Hardfork, Mainnet, getPresetChainConfig } from '@ethereumjs/common' import { createTx } from '@ethereumjs/tx' import { - bytesToBigInt, bytesToHex, - bytesToInt, createAccount, createAddressFromPrivateKey, createAddressFromString, @@ -108,60 +106,11 @@ describe('EIP-7685 buildBlock tests', () => { }) function parseDepositRequest(requestData: Uint8Array) { - // Extracts validator pubkey, withdrawal credential, deposit amount, signature, - // and validator index from Deposit Event log. - // The event fields are non-indexed so contained in one byte array (log[2]) so parsing is as follows: - // 1. Read the first 32 bytes to get the starting position of the first field. - // 2. Continue reading the byte array in 32 byte increments to get all the field starting positions - // 3. Read 32 bytes starting with the first field position to get the size of the first field - // 4. Read the bytes from first field position + 32 + the size of the first field to get the first field value - // 5. Repeat steps 3-4 for each field - const pubKeyIdx = bytesToInt(requestData.slice(0, 32)) - const pubKeySize = bytesToInt(requestData.slice(pubKeyIdx, pubKeyIdx + 32)) - const withdrawalCreditsIdx = bytesToInt(requestData.slice(32, 64)) - const withdrawalCreditsSize = bytesToInt( - requestData.slice(withdrawalCreditsIdx, withdrawalCreditsIdx + 32), - ) - const amountIdx = bytesToInt(requestData.slice(64, 96)) - const amountSize = bytesToInt(requestData.slice(amountIdx, amountIdx + 32)) - const sigIdx = bytesToInt(requestData.slice(96, 128)) - const sigSize = bytesToInt(requestData.slice(sigIdx, sigIdx + 32)) - const indexIdx = bytesToInt(requestData.slice(128, 160)) - const indexSize = bytesToInt(requestData.slice(indexIdx, indexIdx + 32)) - const pubkey = requestData.slice(pubKeyIdx + 32, pubKeyIdx + 32 + pubKeySize) - const withdrawalCredentials = requestData.slice( - withdrawalCreditsIdx + 32, - withdrawalCreditsIdx + 32 + withdrawalCreditsSize, - ) - const amountBytes = requestData.slice(amountIdx + 32, amountIdx + 32 + amountSize) - const amountBytesBigEndian = new Uint8Array([ - amountBytes[7], - amountBytes[6], - amountBytes[5], - amountBytes[4], - amountBytes[3], - amountBytes[2], - amountBytes[1], - amountBytes[0], - ]) - const amount = bytesToBigInt(amountBytesBigEndian) - - const signature = requestData.slice(sigIdx + 32, sigIdx + 32 + sigSize) - - const indexBytes = requestData.slice(indexIdx + 32, indexIdx + 32 + indexSize) - - // Convert the little-endian array to big-endian array - const indexBytesBigEndian = new Uint8Array([ - indexBytes[7], - indexBytes[6], - indexBytes[5], - indexBytes[4], - indexBytes[3], - indexBytes[2], - indexBytes[1], - indexBytes[0], - ]) - const index = bytesToBigInt(indexBytesBigEndian) + const pubkey = requestData.subarray(0, 48) + const withdrawalCredentials = requestData.subarray(48, 48 + 32) + const amount = requestData.subarray(48 + 32, 48 + 32 + 8) + const signature = requestData.subarray(48 + 32 + 8, 48 + 32 + 8 + 96) + const index = requestData.subarray(48 + 32 + 8 + 96, 48 + 32 + 8 + 96 + 8) return { pubkey, diff --git a/packages/vm/test/api/EIPs/eip-7685.spec.ts b/packages/vm/test/api/EIPs/eip-7685.spec.ts index 0f39a095f6..86acfc04aa 100644 --- a/packages/vm/test/api/EIPs/eip-7685.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7685.spec.ts @@ -15,7 +15,7 @@ const invalidRequestsRoot = hexToBytes( ) function getRandomDepositRequest(): CLRequest { const sampleDepositRequest = hexToBytes( - '0x0000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030ac842878bb70009552a4cfcad801d6e659c50bd50d7d03306790cb455ce7363c5b6972f0159d170f625a99b2064dbefc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020010000000000000000000000818ccb1c4eda80270b04d6df822b1e72dd83c303000000000000000000000000000000000000000000000000000000000000000800405973070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060a747f75c72d0cf0d2b52504c7385b516f0523e2f0842416399f42b4aee5c6384a5674f6426b1cc3d0827886fa9b909e616f5c9f61f986013ed2b9bf37071cbae951136265b549f44e3c8e26233c0433e9124b7fd0dc86e82f9fedfc0a179d76900000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000', + '0x00ac842878bb70009552a4cfcad801d6e659c50bd50d7d03306790cb455ce7363c5b6972f0159d170f625a99b2064dbefc010000000000000000000000818ccb1c4eda80270b04d6df822b1e72dd83c3030040597307000000a747f75c72d0cf0d2b52504c7385b516f0523e2f0842416399f42b4aee5c6384a5674f6426b1cc3d0827886fa9b909e616f5c9f61f986013ed2b9bf37071cbae951136265b549f44e3c8e26233c0433e9124b7fd0dc86e82f9fedfc0a179d7690000000000000000', ) return createCLRequest(sampleDepositRequest) } From 8b641dece9dc5b34be36dea1d83f1bef278fb12e Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 13 Oct 2024 19:06:23 +0200 Subject: [PATCH 21/49] repo: rename requestsRoot -> requestsHash --- packages/block/src/header/constructors.ts | 10 +++--- packages/block/src/header/header.ts | 22 ++++++------- packages/block/src/helpers.ts | 4 +-- packages/block/src/types.ts | 6 ++-- packages/block/test/eip7685block.spec.ts | 32 +++++++++---------- packages/blockchain/src/blockchain.ts | 4 +-- .../blockchain/test/blockValidation.spec.ts | 10 +++--- .../src/rpc/modules/engine/util/newPayload.ts | 10 +++--- packages/client/src/rpc/modules/eth.ts | 2 +- packages/vm/src/buildBlock.ts | 6 ++-- packages/vm/src/runBlock.ts | 20 ++++++------ packages/vm/src/types.ts | 4 +-- packages/vm/test/api/EIPs/eip-7685.spec.ts | 26 +++++++-------- packages/vm/test/t8n/t8ntool.ts | 4 +-- 14 files changed, 80 insertions(+), 80 deletions(-) diff --git a/packages/block/src/header/constructors.ts b/packages/block/src/header/constructors.ts index 97222b970d..2cf6891e53 100644 --- a/packages/block/src/header/constructors.ts +++ b/packages/block/src/header/constructors.ts @@ -25,7 +25,7 @@ export function createBlockHeader(headerData: HeaderData = {}, opts: BlockOption */ export function createBlockHeaderFromBytesArray(values: BlockHeaderBytes, opts: BlockOptions = {}) { const headerData = valuesArrayToHeaderData(values) - const { number, baseFeePerGas, excessBlobGas, blobGasUsed, parentBeaconBlockRoot, requestsRoot } = + const { number, baseFeePerGas, excessBlobGas, blobGasUsed, parentBeaconBlockRoot, requestsHash } = headerData const header = createBlockHeader(headerData, opts) if (header.common.isActivatedEIP(1559) && baseFeePerGas === undefined) { @@ -48,8 +48,8 @@ export function createBlockHeaderFromBytesArray(values: BlockHeaderBytes, opts: throw new Error('invalid header. parentBeaconBlockRoot should be provided') } - if (header.common.isActivatedEIP(7685) && requestsRoot === undefined) { - throw new Error('invalid header. requestsRoot should be provided') + if (header.common.isActivatedEIP(7685) && requestsHash === undefined) { + throw new Error('invalid header. requestsHash should be provided') } return header } @@ -118,7 +118,7 @@ export function createBlockHeaderFromRPC(blockParams: JSONRPCBlock, options?: Bl blobGasUsed, excessBlobGas, parentBeaconBlockRoot, - requestsRoot, + requestsHash, } = blockParams const blockHeader = new BlockHeader( @@ -143,7 +143,7 @@ export function createBlockHeaderFromRPC(blockParams: JSONRPCBlock, options?: Bl blobGasUsed, excessBlobGas, parentBeaconBlockRoot, - requestsRoot, + requestsHash, }, options, ) diff --git a/packages/block/src/header/header.ts b/packages/block/src/header/header.ts index b065bcd0db..d00e5eb668 100644 --- a/packages/block/src/header/header.ts +++ b/packages/block/src/header/header.ts @@ -60,7 +60,7 @@ export class BlockHeader { public readonly blobGasUsed?: bigint public readonly excessBlobGas?: bigint public readonly parentBeaconBlockRoot?: Uint8Array - public readonly requestsRoot?: Uint8Array + public readonly requestsHash?: Uint8Array public readonly common: Common @@ -163,7 +163,7 @@ export class BlockHeader { parentBeaconBlockRoot: this.common.isActivatedEIP(4788) ? new Uint8Array(32) : undefined, // TODO: not sure what the default should be here becuase it would depends on activated EIPs // as even empty requests will produce data to sha hash - requestsRoot: this.common.isActivatedEIP(7685) ? KECCAK256_RLP : undefined, + requestsHash: this.common.isActivatedEIP(7685) ? KECCAK256_RLP : undefined, } const baseFeePerGas = @@ -177,8 +177,8 @@ export class BlockHeader { const parentBeaconBlockRoot = toType(headerData.parentBeaconBlockRoot, TypeOutput.Uint8Array) ?? hardforkDefaults.parentBeaconBlockRoot - const requestsRoot = - toType(headerData.requestsRoot, TypeOutput.Uint8Array) ?? hardforkDefaults.requestsRoot + const requestsHash = + toType(headerData.requestsHash, TypeOutput.Uint8Array) ?? hardforkDefaults.requestsHash if (!this.common.isActivatedEIP(1559) && baseFeePerGas !== undefined) { throw new Error('A base fee for a block can only be set with EIP1559 being activated') @@ -206,8 +206,8 @@ export class BlockHeader { ) } - if (!this.common.isActivatedEIP(7685) && requestsRoot !== undefined) { - throw new Error('requestsRoot can only be provided with EIP 7685 activated') + if (!this.common.isActivatedEIP(7685) && requestsHash !== undefined) { + throw new Error('requestsHash can only be provided with EIP 7685 activated') } this.parentHash = parentHash @@ -230,7 +230,7 @@ export class BlockHeader { this.blobGasUsed = blobGasUsed this.excessBlobGas = excessBlobGas this.parentBeaconBlockRoot = parentBeaconBlockRoot - this.requestsRoot = requestsRoot + this.requestsHash = requestsHash this._genericFormatValidation() this._validateDAOExtraData() @@ -346,8 +346,8 @@ export class BlockHeader { } if (this.common.isActivatedEIP(7685)) { - if (this.requestsRoot === undefined) { - const msg = this._errorMsg('EIP7685 block has no requestsRoot field') + if (this.requestsHash === undefined) { + const msg = this._errorMsg('EIP7685 block has no requestsHash field') throw new Error(msg) } } @@ -628,7 +628,7 @@ export class BlockHeader { rawItems.push(this.parentBeaconBlockRoot!) } if (this.common.isActivatedEIP(7685)) { - rawItems.push(this.requestsRoot!) + rawItems.push(this.requestsHash!) } return rawItems @@ -770,7 +770,7 @@ export class BlockHeader { JSONDict.parentBeaconBlockRoot = bytesToHex(this.parentBeaconBlockRoot!) } if (this.common.isActivatedEIP(7685)) { - JSONDict.requestsRoot = bytesToHex(this.requestsRoot!) + JSONDict.requestsHash = bytesToHex(this.requestsHash!) } return JSONDict } diff --git a/packages/block/src/helpers.ts b/packages/block/src/helpers.ts index 7a4243bb71..3f0b498796 100644 --- a/packages/block/src/helpers.ts +++ b/packages/block/src/helpers.ts @@ -47,7 +47,7 @@ export function valuesArrayToHeaderData(values: BlockHeaderBytes): HeaderData { blobGasUsed, excessBlobGas, parentBeaconBlockRoot, - requestsRoot, + requestsHash, ] = values if (values.length > 21) { @@ -82,7 +82,7 @@ export function valuesArrayToHeaderData(values: BlockHeaderBytes): HeaderData { blobGasUsed, excessBlobGas, parentBeaconBlockRoot, - requestsRoot, + requestsHash, } } diff --git a/packages/block/src/types.ts b/packages/block/src/types.ts index 0e45f40b15..1b7df7c60e 100644 --- a/packages/block/src/types.ts +++ b/packages/block/src/types.ts @@ -108,7 +108,7 @@ export interface HeaderData { blobGasUsed?: BigIntLike excessBlobGas?: BigIntLike parentBeaconBlockRoot?: BytesLike - requestsRoot?: BytesLike + requestsHash?: BytesLike } /** @@ -191,7 +191,7 @@ export interface JSONHeader { blobGasUsed?: PrefixedHexString excessBlobGas?: PrefixedHexString parentBeaconBlockRoot?: PrefixedHexString - requestsRoot?: PrefixedHexString + requestsHash?: PrefixedHexString } /* @@ -225,7 +225,7 @@ export interface JSONRPCBlock { excessBlobGas?: PrefixedHexString // If EIP-4844 is enabled for this block, returns the excess blob gas for the block parentBeaconBlockRoot?: PrefixedHexString // If EIP-4788 is enabled for this block, returns parent beacon block root executionWitness?: VerkleExecutionWitness | null // If Verkle is enabled for this block - requestsRoot?: PrefixedHexString // If EIP-7685 is enabled for this block, returns the requests root + requestsHash?: PrefixedHexString // If EIP-7685 is enabled for this block, returns the requests root } export type WithdrawalV1 = { diff --git a/packages/block/test/eip7685block.spec.ts b/packages/block/test/eip7685block.spec.ts index c4114e5931..5f26df9ac0 100644 --- a/packages/block/test/eip7685block.spec.ts +++ b/packages/block/test/eip7685block.spec.ts @@ -49,30 +49,30 @@ const common = new Common({ describe('7685 tests', () => { it('should instantiate block with defaults', () => { const block = createBlock({}, { common }) - assert.deepEqual(block.header.requestsRoot, KECCAK256_RLP) + assert.deepEqual(block.header.requestsHash, KECCAK256_RLP) const block2 = new Block(undefined, undefined, undefined, undefined, { common }) - assert.deepEqual(block.header.requestsRoot, KECCAK256_RLP) + assert.deepEqual(block.header.requestsHash, KECCAK256_RLP) assert.equal(block2.requests?.length, 0) }) it('should instantiate a block with requests', async () => { const request = getRandomDepositRequest() - const requestsRoot = genRequestsRoot([request], sha256) + const requestsHash = genRequestsRoot([request], sha256) const block = createBlock( { requests: [request], - header: { requestsRoot }, + header: { requestsHash }, }, { common }, ) assert.equal(block.requests?.length, 1) - assert.deepEqual(block.header.requestsRoot, requestsRoot) + assert.deepEqual(block.header.requestsHash, requestsHash) }) - it('RequestsRootIsValid should return false when requestsRoot is invalid', async () => { + it('RequestsRootIsValid should return false when requestsHash is invalid', async () => { const request = getRandomDepositRequest() const block = createBlock( { requests: [request], - header: { requestsRoot: randomBytes(32) }, + header: { requestsHash: randomBytes(32) }, }, { common }, ) @@ -84,14 +84,14 @@ describe('7685 tests', () => { const request2 = getRandomDepositRequest() const request3 = getRandomWithdrawalRequest() const requests = [request1, request2, request3] - const requestsRoot = genRequestsRoot(requests, sha256) + const requestsHash = genRequestsRoot(requests, sha256) // Construct block with requests in correct order const block = createBlock( { requests, - header: { requestsRoot }, + header: { requestsHash }, }, { common }, ) @@ -103,7 +103,7 @@ describe('7685 tests', () => { createBlock( { requests: [request1, request3, request2], - header: { requestsRoot }, + header: { requestsHash }, }, { common }, ), @@ -119,23 +119,23 @@ describe('createWithdrawalFromBytesArray tests', () => { common, }, ) - assert.deepEqual(block.header.requestsRoot, KECCAK256_RLP) + assert.deepEqual(block.header.requestsHash, KECCAK256_RLP) }) it('should construct a block with a valid requests array', async () => { const request1 = getRandomDepositRequest() const request2 = getRandomWithdrawalRequest() const request3 = getRandomWithdrawalRequest() const requests = [request1, request2, request3] - const requestsRoot = genRequestsRoot(requests, sha256) + const requestsHash = genRequestsRoot(requests, sha256) const serializedRequests = [request1.serialize(), request2.serialize(), request3.serialize()] const block = createBlockFromBytesArray( - [createBlockHeader({ requestsRoot }, { common }).raw(), [], [], [], serializedRequests], + [createBlockHeader({ requestsHash }, { common }).raw(), [], [], [], serializedRequests], { common, }, ) - assert.deepEqual(block.header.requestsRoot, requestsRoot) + assert.deepEqual(block.header.requestsHash, requestsHash) assert.equal(block.requests?.length, 3) }) }) @@ -146,11 +146,11 @@ describe('fromRPC tests', () => { const request2 = getRandomDepositRequest() const request3 = getRandomWithdrawalRequest() const requests = [request1, request2, request3] - const requestsRoot = genRequestsRoot(requests, sha256) + const requestsHash = genRequestsRoot(requests, sha256) const serializedRequests = [request1.serialize(), request2.serialize(), request3.serialize()] const block = createBlockFromBytesArray( - [createBlockHeader({ requestsRoot }, { common }).raw(), [], [], [], serializedRequests], + [createBlockHeader({ requestsHash }, { common }).raw(), [], [], [], serializedRequests], { common, }, diff --git a/packages/blockchain/src/blockchain.ts b/packages/blockchain/src/blockchain.ts index 3935a1526c..cf84c1091d 100644 --- a/packages/blockchain/src/blockchain.ts +++ b/packages/blockchain/src/blockchain.ts @@ -581,8 +581,8 @@ export class Blockchain implements BlockchainInterface { } if (header.common.isActivatedEIP(7685)) { - if (header.requestsRoot === undefined) { - throw new Error(`requestsRoot must be provided when EIP-7685 is active`) + if (header.requestsHash === undefined) { + throw new Error(`requestsHash must be provided when EIP-7685 is active`) } } } diff --git a/packages/blockchain/test/blockValidation.spec.ts b/packages/blockchain/test/blockValidation.spec.ts index 0a9a57732e..f0b050360b 100644 --- a/packages/blockchain/test/blockValidation.spec.ts +++ b/packages/blockchain/test/blockValidation.spec.ts @@ -390,7 +390,7 @@ describe('[Blockchain]: Block validation tests', () => { }) }) describe('EIP 7685: requests field validation tests', () => { - it('should throw when putting a block with an invalid requestsRoot', async () => { + it('should throw when putting a block with an invalid requestsHash', async () => { const common = new Common({ chain: Mainnet, hardfork: Hardfork.Cancun, @@ -403,7 +403,7 @@ describe('EIP 7685: requests field validation tests', () => { { header: { number: 1n, - requestsRoot: randomBytes(32), + requestsHash: randomBytes(32), withdrawalsRoot: KECCAK256_RLP, parentHash: blockchain.genesisBlock.hash(), timestamp: blockchain.genesisBlock.header.timestamp + 1n, @@ -413,13 +413,13 @@ describe('EIP 7685: requests field validation tests', () => { { common }, ) - await expect(async () => blockchain.putBlock(block)).rejects.toThrow('invalid requestsRoot') + await expect(async () => blockchain.putBlock(block)).rejects.toThrow('invalid requestsHash') const blockWithRequest = createBlock( { header: { number: 1n, - requestsRoot: randomBytes(32), + requestsHash: randomBytes(32), withdrawalsRoot: KECCAK256_RLP, parentHash: blockchain.genesisBlock.hash(), timestamp: blockchain.genesisBlock.header.timestamp + 1n, @@ -430,7 +430,7 @@ describe('EIP 7685: requests field validation tests', () => { { common }, ) await expect(async () => blockchain.putBlock(blockWithRequest)).rejects.toThrow( - 'invalid requestsRoot', + 'invalid requestsHash', ) }) }) diff --git a/packages/client/src/rpc/modules/engine/util/newPayload.ts b/packages/client/src/rpc/modules/engine/util/newPayload.ts index 5bce5b35f7..175e42d662 100644 --- a/packages/client/src/rpc/modules/engine/util/newPayload.ts +++ b/packages/client/src/rpc/modules/engine/util/newPayload.ts @@ -140,12 +140,12 @@ export const validate7685ExecutionRequests = ( // Collect versioned hashes in the flat array `txVersionedHashes` to match with received const requests = executionRequests.map((req) => createCLRequest(hexToBytes(req))) const sha256Function = headBlock.common.customCrypto.sha256 ?? sha256 - const requestsRoot = genRequestsRoot(requests, sha256Function) + const requestsHash = genRequestsRoot(requests, sha256Function) - if (!equalsBytes(requestsRoot, headBlock.header.requestsRoot!)) { - validationError = `Invalid requestsRoot received=${bytesToHex( - headBlock.header.requestsRoot!, - )} expected=${bytesToHex(requestsRoot)}` + if (!equalsBytes(requestsHash, headBlock.header.requestsHash!)) { + validationError = `Invalid requestsHash received=${bytesToHex( + headBlock.header.requestsHash!, + )} expected=${bytesToHex(requestsHash)}` } return validationError } diff --git a/packages/client/src/rpc/modules/eth.ts b/packages/client/src/rpc/modules/eth.ts index 0e2944b18c..111a3d2b00 100644 --- a/packages/client/src/rpc/modules/eth.ts +++ b/packages/client/src/rpc/modules/eth.ts @@ -152,7 +152,7 @@ const toJSONRPCBlock = async ( blobGasUsed: header.blobGasUsed, excessBlobGas: header.excessBlobGas, parentBeaconBlockRoot: header.parentBeaconBlockRoot, - requestsRoot: header.requestsRoot, + requestsHash: header.requestsHash, } } diff --git a/packages/vm/src/buildBlock.ts b/packages/vm/src/buildBlock.ts index e96b7dfcff..60535a28c6 100644 --- a/packages/vm/src/buildBlock.ts +++ b/packages/vm/src/buildBlock.ts @@ -343,11 +343,11 @@ export class BlockBuilder { } let requests - let requestsRoot + let requestsHash if (this.vm.common.isActivatedEIP(7685)) { const sha256Function = this.vm.common.customCrypto.sha256 ?? sha256 requests = await accumulateRequests(this.vm, this.transactionResults) - requestsRoot = genRequestsRoot(requests, sha256Function) + requestsHash = genRequestsRoot(requests, sha256Function) } // get stateRoot after all the accumulateRequests etc have been done @@ -363,7 +363,7 @@ export class BlockBuilder { timestamp, // correct excessBlobGas should already be part of headerData used above blobGasUsed, - requestsRoot, + requestsHash, } if (consensusType === ConsensusType.ProofOfWork) { diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index 7c51c32972..d3c7f8ace5 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -213,12 +213,12 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise[] | undefined if (block.common.isActivatedEIP(7685)) { const sha256Function = vm.common.customCrypto.sha256 ?? sha256 requests = await accumulateRequests(vm, result.results) - requestsRoot = genRequestsRoot(requests, sha256Function) + requestsHash = genRequestsRoot(requests, sha256Function) } // Persist state @@ -243,7 +243,7 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise { logsBloom: Uint8Array /** - * The requestsRoot for any CL requests in the block + * The requestsHash for any CL requests in the block */ - requestsRoot?: Uint8Array + requestsHash?: Uint8Array /** * Any CL requests that were processed in the course of this block */ diff --git a/packages/vm/test/api/EIPs/eip-7685.spec.ts b/packages/vm/test/api/EIPs/eip-7685.spec.ts index 86acfc04aa..99b0f4c2d2 100644 --- a/packages/vm/test/api/EIPs/eip-7685.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7685.spec.ts @@ -23,7 +23,7 @@ function getRandomDepositRequest(): CLRequest { const common = new Common({ chain: Mainnet, hardfork: Hardfork.Cancun, eips: [7685] }) describe('EIP-7685 runBlock tests', () => { - it('should not error when a valid requestsRoot is provided', async () => { + it('should not error when a valid requestsHash is provided', async () => { const vm = await setupVM({ common }) const emptyBlock = createBlock({}, { common }) const res = await runBlock(vm, { @@ -32,45 +32,45 @@ describe('EIP-7685 runBlock tests', () => { }) assert.equal(res.gasUsed, 0n) }) - it('should error when an invalid requestsRoot is provided', async () => { + it('should error when an invalid requestsHash is provided', async () => { const vm = await setupVM({ common }) - const emptyBlock = createBlock({ header: { requestsRoot: invalidRequestsRoot } }, { common }) + const emptyBlock = createBlock({ header: { requestsHash: invalidRequestsRoot } }, { common }) await expect(async () => runBlock(vm, { block: emptyBlock, }), - ).rejects.toThrow('invalid requestsRoot') + ).rejects.toThrow('invalid requestsHash') }) - it('should not throw invalid requestsRoot error when valid requests are provided', async () => { + it('should not throw invalid requestsHash error when valid requests are provided', async () => { const vm = await setupVM({ common }) const request = getRandomDepositRequest() - const requestsRoot = genRequestsRoot([request], sha256) + const requestsHash = genRequestsRoot([request], sha256) const block = createBlock( { - header: { requestsRoot }, + header: { requestsHash }, }, { common }, ) - await expect(async () => runBlock(vm, { block })).rejects.toThrow(/invalid requestsRoot/) + await expect(async () => runBlock(vm, { block })).rejects.toThrow(/invalid requestsHash/) }) // TODO: no way to test this without running block, why check why does this test pass // as it should not throw on some random request root - it('should error when requestsRoot does not match requests provided', async () => { + it('should error when requestsHash does not match requests provided', async () => { const vm = await setupVM({ common }) const block = createBlock( { - header: { requestsRoot: invalidRequestsRoot }, + header: { requestsHash: invalidRequestsRoot }, }, { common }, ) - await expect(() => runBlock(vm, { block })).rejects.toThrow('invalid requestsRoot') + await expect(() => runBlock(vm, { block })).rejects.toThrow('invalid requestsHash') }) }) describe('EIP 7685 buildBlock tests', () => { - it('should build a block without a request and a valid requestsRoot', async () => { + it('should build a block without a request and a valid requestsHash', async () => { const common = new Common({ chain: Mainnet, hardfork: Hardfork.Cancun, @@ -90,7 +90,7 @@ describe('EIP 7685 buildBlock tests', () => { const { block } = await blockBuilder.build() assert.deepEqual( - block.header.requestsRoot, + block.header.requestsHash, hexToBytes('0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'), ) }) diff --git a/packages/vm/test/t8n/t8ntool.ts b/packages/vm/test/t8n/t8ntool.ts index 3b6dc596ca..89b1d27bf4 100644 --- a/packages/vm/test/t8n/t8ntool.ts +++ b/packages/vm/test/t8n/t8ntool.ts @@ -222,8 +222,8 @@ export class TransitionTool { output.currentExcessBlobGas = bigIntToHex(block.header.excessBlobGas) } - if (block.header.requestsRoot !== undefined) { - output.requestsHash = bytesToHex(block.header.requestsRoot) + if (block.header.requestsHash !== undefined) { + output.requestsHash = bytesToHex(block.header.requestsHash) } if (requests !== undefined) { From dae997ed45f03064c8b695fba8759189c9f95e56 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 14 Oct 2024 08:58:27 +0200 Subject: [PATCH 22/49] vm: fix import (fix docker build) --- packages/vm/src/requests.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/vm/src/requests.ts b/packages/vm/src/requests.ts index cebc8b4b70..fad182cde3 100644 --- a/packages/vm/src/requests.ts +++ b/packages/vm/src/requests.ts @@ -1,16 +1,17 @@ import { Mainnet } from '@ethereumjs/common' import { + ConsolidationRequest, + DepositRequest, + WithdrawalRequest, bigIntToAddressBytes, bigIntToBytes, bytesToHex, bytesToInt, + concatBytes, createAddressFromString, setLengthLeft, } from '@ethereumjs/util' -import { concatBytes } from '../../util/src/bytes.js' -import { ConsolidationRequest, DepositRequest, WithdrawalRequest } from '../../util/src/request.js' - import type { RunTxResult } from './types.js' import type { VM } from './vm.js' import type { CLRequest, CLRequestType } from '@ethereumjs/util' From bef2f34a8366782b1e369f33a542689ed6042691 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 14 Oct 2024 09:18:55 +0200 Subject: [PATCH 23/49] client: correctly return request data (not including type) --- packages/client/src/rpc/modules/engine/util/getPayload.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/rpc/modules/engine/util/getPayload.ts b/packages/client/src/rpc/modules/engine/util/getPayload.ts index ad281f4707..fec0814f2d 100644 --- a/packages/client/src/rpc/modules/engine/util/getPayload.ts +++ b/packages/client/src/rpc/modules/engine/util/getPayload.ts @@ -27,7 +27,7 @@ export const blockToExecutionPayload = ( const shouldOverrideBuilder = false return { executionPayload, - executionRequests: requests?.map((req) => bytesToHex(req.bytes)), + executionRequests: requests?.map((req) => bytesToHex(req.data)), blockValue: bigIntToHex(value), blobsBundle, shouldOverrideBuilder, From 860c6216fd76e2285fc398dbd884c213322e9529 Mon Sep 17 00:00:00 2001 From: harkamal Date: Mon, 14 Oct 2024 17:34:41 +0530 Subject: [PATCH 24/49] fix the ingress, generation and propagation of execution requests/requestsroot data from the engine api and debug and fix the newpayloadv4 spec --- packages/block/src/block/block.ts | 1 + packages/block/src/from-beacon-payload.ts | 4 + packages/block/src/types.ts | 1 + .../client/src/rpc/modules/engine/engine.ts | 15 ++- .../client/src/rpc/modules/engine/types.ts | 12 +-- .../src/rpc/modules/engine/util/newPayload.ts | 91 +++++++++++-------- .../src/rpc/modules/engine/validators.ts | 7 -- .../test/rpc/engine/newPayloadV4.spec.ts | 42 ++++----- packages/util/src/request.ts | 51 ----------- 9 files changed, 83 insertions(+), 141 deletions(-) diff --git a/packages/block/src/block/block.ts b/packages/block/src/block/block.ts index 9a9bbf52ea..f86913174b 100644 --- a/packages/block/src/block/block.ts +++ b/packages/block/src/block/block.ts @@ -519,6 +519,7 @@ export class Block { transactions, ...withdrawalsArr, parentBeaconBlockRoot: header.parentBeaconBlockRoot, + requestsHash: header.requestsHash, executionWitness: this.executionWitness, } diff --git a/packages/block/src/from-beacon-payload.ts b/packages/block/src/from-beacon-payload.ts index b084b803bd..e83ea9f780 100644 --- a/packages/block/src/from-beacon-payload.ts +++ b/packages/block/src/from-beacon-payload.ts @@ -31,6 +31,7 @@ export type BeaconPayloadJSON = { blob_gas_used?: NumericString excess_blob_gas?: NumericString parent_beacon_block_root?: PrefixedHexString + requests_hash?: PrefixedHexString // the casing of VerkleExecutionWitness remains same camel case for now execution_witness?: VerkleExecutionWitness } @@ -131,6 +132,9 @@ export function executionPayloadFromBeaconPayload(payload: BeaconPayloadJSON): E if (payload.parent_beacon_block_root !== undefined && payload.parent_beacon_block_root !== null) { executionPayload.parentBeaconBlockRoot = payload.parent_beacon_block_root } + if (payload.requests_hash !== undefined && payload.requests_hash !== null) { + executionPayload.requestsHash = payload.requests_hash + } if (payload.execution_witness !== undefined && payload.execution_witness !== null) { // the casing structure in payload could be camel case or snake depending upon the CL diff --git a/packages/block/src/types.ts b/packages/block/src/types.ts index 1b7df7c60e..5efee2bedf 100644 --- a/packages/block/src/types.ts +++ b/packages/block/src/types.ts @@ -255,6 +255,7 @@ export type ExecutionPayload = { blobGasUsed?: PrefixedHexString // QUANTITY, 64 Bits excessBlobGas?: PrefixedHexString // QUANTITY, 64 Bits parentBeaconBlockRoot?: PrefixedHexString // QUANTITY, 64 Bits + requestsHash?: PrefixedHexString // VerkleExecutionWitness is already a hex serialized object executionWitness?: VerkleExecutionWitness | null // QUANTITY, 64 Bits, null implies not available } diff --git a/packages/client/src/rpc/modules/engine/engine.ts b/packages/client/src/rpc/modules/engine/engine.ts index 140f8d9ba8..cab8134dcd 100644 --- a/packages/client/src/rpc/modules/engine/engine.ts +++ b/packages/client/src/rpc/modules/engine/engine.ts @@ -38,7 +38,6 @@ import { executionPayloadV1FieldValidators, executionPayloadV2FieldValidators, executionPayloadV3FieldValidators, - executionPayloadV4FieldValidators, forkchoiceFieldValidators, payloadAttributesFieldValidatorsV1, payloadAttributesFieldValidatorsV2, @@ -58,7 +57,6 @@ import type { ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3, - ExecutionPayloadV4, ForkchoiceResponseV1, ForkchoiceStateV1, PayloadAttributes, @@ -206,9 +204,9 @@ export class Engine { this.newPayloadV4 = cmMiddleware( middleware( callWithStackTrace(this.newPayloadV4.bind(this), this._rpcDebug), - 3, + 4, [ - [validators.object(executionPayloadV4FieldValidators)], + [validators.object(executionPayloadV3FieldValidators)], [validators.array(validators.bytes32)], [validators.bytes32], [validators.array(validators.hex)], @@ -369,12 +367,9 @@ export class Engine { */ // newpayloadv3 comes with parentBeaconBlockRoot out of the payload const { block: headBlock, error } = await assembleBlock( + payload, { - ...payload, - // ExecutionPayload only handles undefined parentBeaconBlockRoot: parentBeaconBlockRoot ?? undefined, - }, - { blobVersionedHashes: blobVersionedHashes ?? undefined, executionRequests: executionRequests ?? undefined, }, @@ -826,7 +821,9 @@ export class Engine { return newPayloadRes } - async newPayloadV4(params: [ExecutionPayloadV4, Bytes32[], Bytes32]): Promise { + async newPayloadV4( + params: [ExecutionPayloadV3, Bytes32[], Bytes32, Bytes32[]], + ): Promise { const pragueTimestamp = this.chain.config.chainCommon.hardforkTimestamp(Hardfork.Prague) const ts = parseInt(params[0].timestamp) if (pragueTimestamp === null || ts < pragueTimestamp) { diff --git a/packages/client/src/rpc/modules/engine/types.ts b/packages/client/src/rpc/modules/engine/types.ts index 811633b26d..8563a7c59a 100644 --- a/packages/client/src/rpc/modules/engine/types.ts +++ b/packages/client/src/rpc/modules/engine/types.ts @@ -2,12 +2,7 @@ import { UNKNOWN_PAYLOAD } from '../../error-code.js' import type { Skeleton } from '../../../service/index.js' import type { Block, ExecutionPayload } from '@ethereumjs/block' -import type { - ConsolidationRequestV1, - DepositRequestV1, - PrefixedHexString, - WithdrawalRequestV1, -} from '@ethereumjs/util' +import type { PrefixedHexString } from '@ethereumjs/util' export enum Status { ACCEPTED = 'ACCEPTED', @@ -32,11 +27,6 @@ export type ExecutionPayloadV1 = ExecutionPayload export type ExecutionPayloadV2 = ExecutionPayloadV1 & { withdrawals: WithdrawalV1[] } // parentBeaconBlockRoot comes separate in new payloads and needs to be added to payload data export type ExecutionPayloadV3 = ExecutionPayloadV2 & { excessBlobGas: Uint64; blobGasUsed: Uint64 } -export type ExecutionPayloadV4 = ExecutionPayloadV3 & { - depositRequests: DepositRequestV1[] - withdrawalRequests: WithdrawalRequestV1[] - consolidationRequests: ConsolidationRequestV1[] -} export type ForkchoiceStateV1 = { headBlockHash: Bytes32 diff --git a/packages/client/src/rpc/modules/engine/util/newPayload.ts b/packages/client/src/rpc/modules/engine/util/newPayload.ts index 175e42d662..8b3218c80a 100644 --- a/packages/client/src/rpc/modules/engine/util/newPayload.ts +++ b/packages/client/src/rpc/modules/engine/util/newPayload.ts @@ -1,6 +1,12 @@ import { createBlockFromExecutionPayload, genRequestsRoot } from '@ethereumjs/block' import { Blob4844Tx } from '@ethereumjs/tx' -import { bytesToHex, createCLRequest, equalsBytes, hexToBytes } from '@ethereumjs/util' +import { + ConsolidationRequest, + DepositRequest, + WithdrawalRequest, + bytesToHex, + hexToBytes, +} from '@ethereumjs/util' import { sha256 } from 'ethereum-cryptography/sha256' import { short } from '../../../../util/index.js' @@ -11,9 +17,11 @@ import { validHash } from './generic.js' import type { Chain } from '../../../../blockchain/index.js' import type { ChainCache, PayloadStatusV1 } from '../types.js' import type { Block, ExecutionPayload } from '@ethereumjs/block' -import type { PrefixedHexString } from '@ethereumjs/util' +import type { Common } from '@ethereumjs/common' +import type { CLRequest, CLRequestType, PrefixedHexString } from '@ethereumjs/util' -type CLValidationData = { +type CLData = { + parentBeaconBlockRoot?: PrefixedHexString blobVersionedHashes?: PrefixedHexString[] executionRequests?: PrefixedHexString[] } @@ -23,26 +31,35 @@ type CLValidationData = { * If errors, returns {@link PayloadStatusV1} */ export const assembleBlock = async ( - payload: ExecutionPayload, - clValidationData: CLValidationData, + payload: Omit, + clValidationData: CLData, chain: Chain, chainCache: ChainCache, ): Promise<{ block?: Block; error?: PayloadStatusV1 }> => { const { blockNumber, timestamp } = payload const { config } = chain const common = config.chainCommon.copy() - common.setHardforkBy({ blockNumber, timestamp }) try { - const block = await createBlockFromExecutionPayload(payload, { common }) + // Validate CL data to see if it matches with the assembled block + const { blobVersionedHashes, executionRequests, parentBeaconBlockRoot } = clValidationData + + let requestsHash + if (executionRequests !== undefined) { + requestsHash = validateAndGen7685RequestsHash(common, executionRequests) + } else if (common.isActivatedEIP(7685)) { + throw `Invalid executionRequests=undefined for EIP-7685 activated block` + } + + const block = await createBlockFromExecutionPayload( + { ...payload, parentBeaconBlockRoot, requestsHash }, + { 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() - // Validate CL data to see if it matches with the assembled block - const { blobVersionedHashes, executionRequests } = clValidationData - /** * Validate blob versioned hashes in the context of EIP-4844 blob transactions */ @@ -63,23 +80,6 @@ export const assembleBlock = async ( throw validationError } - if (block.common.isActivatedEIP(7685)) { - let validationError: string | null = null - if (executionRequests === undefined) { - validationError = `Error verifying executionRequests: received none` - } else { - validationError = validate7685ExecutionRequests(block, executionRequests) - } - - // if there was a validation error return invalid - if (validationError !== null) { - throw validationError - } - } else if (executionRequests !== undefined) { - const validationError = `Invalid executionRequests before EIP-7685 is activated` - throw validationError - } - return { block } } catch (error) { const validationError = `Error assembling block from payload: ${error}` @@ -131,21 +131,34 @@ export const validate4844BlobVersionedHashes = ( return validationError } -export const validate7685ExecutionRequests = ( - headBlock: Block, +export const validateAndGen7685RequestsHash = ( + common: Common, executionRequests: PrefixedHexString[], -): string | null => { +): PrefixedHexString => { let validationError: string | null = null - // Collect versioned hashes in the flat array `txVersionedHashes` to match with received - const requests = executionRequests.map((req) => createCLRequest(hexToBytes(req))) - const sha256Function = headBlock.common.customCrypto.sha256 ?? sha256 - const requestsHash = genRequestsRoot(requests, sha256Function) + const requests: CLRequest[] = [] + let requestIndex = 0 + if (common.isActivatedEIP(6110)) { + requests.push(new DepositRequest(hexToBytes(executionRequests[requestIndex]))) + requestIndex++ + } + if (common.isActivatedEIP(7002)) { + requests.push(new WithdrawalRequest(hexToBytes(executionRequests[requestIndex]))) + requestIndex++ + } + if (common.isActivatedEIP(7251)) { + requests.push(new ConsolidationRequest(hexToBytes(executionRequests[requestIndex]))) + requestIndex++ + } - if (!equalsBytes(requestsHash, headBlock.header.requestsHash!)) { - validationError = `Invalid requestsHash received=${bytesToHex( - headBlock.header.requestsHash!, - )} expected=${bytesToHex(requestsHash)}` + if (requestIndex !== executionRequests.length) { + validationError = `Invalid executionRequests=${executionRequests.length} expected=${requestIndex}` + throw validationError } - return validationError + + const sha256Function = common.customCrypto.sha256 ?? sha256 + const requestsHash = genRequestsRoot(requests, sha256Function) + + return bytesToHex(requestsHash) } diff --git a/packages/client/src/rpc/modules/engine/validators.ts b/packages/client/src/rpc/modules/engine/validators.ts index a90704cb5d..9aae69fe2a 100644 --- a/packages/client/src/rpc/modules/engine/validators.ts +++ b/packages/client/src/rpc/modules/engine/validators.ts @@ -26,13 +26,6 @@ export const executionPayloadV3FieldValidators = { excessBlobGas: validators.uint64, } -export const executionPayloadV4FieldValidators = { - ...executionPayloadV3FieldValidators, - depositRequests: validators.array(validators.depositRequest()), - withdrawalRequests: validators.array(validators.withdrawalRequest()), - consolidationRequests: validators.array(validators.consolidationRequest()), -} - export const forkchoiceFieldValidators = { headBlockHash: validators.blockHash, safeBlockHash: validators.blockHash, diff --git a/packages/client/test/rpc/engine/newPayloadV4.spec.ts b/packages/client/test/rpc/engine/newPayloadV4.spec.ts index 8de2af52a9..3039e8db13 100644 --- a/packages/client/test/rpc/engine/newPayloadV4.spec.ts +++ b/packages/client/test/rpc/engine/newPayloadV4.spec.ts @@ -12,9 +12,9 @@ const [blockData] = beaconData const parentBeaconBlockRoot = '0x42942949c4ed512cd85c2cb54ca88591338cbb0564d3a2bea7961a639ef29d64' const validForkChoiceState = { - headBlockHash: '0xdc1727ee420fead1d6a8dfe548758e5a5a48eed888b162278534b105b9909cf9', - safeBlockHash: '0xdc1727ee420fead1d6a8dfe548758e5a5a48eed888b162278534b105b9909cf9', - finalizedBlockHash: '0xdc1727ee420fead1d6a8dfe548758e5a5a48eed888b162278534b105b9909cf9', + headBlockHash: '0x6abe4c2777a6a1e994d83920cfb95229b071174b95c89343f54b926f733789f2', + safeBlockHash: '0x6abe4c2777a6a1e994d83920cfb95229b071174b95c89343f54b926f733789f2', + finalizedBlockHash: '0x6abe4c2777a6a1e994d83920cfb95229b071174b95c89343f54b926f733789f2', } const validPayloadAttributes = { timestamp: '0x64ba84fd', @@ -44,13 +44,13 @@ const electraGenesisContracts = { '0x00706203067988Ab3E2A2ab626EdCD6f28bDBbbb': { balance: '0', nonce: '1', - code: '0x3373fffffffffffffffffffffffffffffffffffffffe1460a8573615156028575f545f5260205ff35b36606014156101555760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061015557600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060011160bc575060015b5f5b8181146101025780607402838201600402600401805490600101805490600101805490600101549260601b84529083601401528260340152906054015260010160be565b9101809214610114579060025561011f565b90505f6002555f6003555b5f548061049d141561012e57505f5b6001546001828201116101435750505f610149565b01600190035b5f555f6001556074025ff35b5f5ffd', + code: '0x3373fffffffffffffffffffffffffffffffffffffffe1460cf573615156028575f545f5260205ff35b366060141561019a5760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461019a57600182026001905f5b5f821115608057810190830284830290049160010191906065565b90939004341061019a57600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060011160e3575060015b5f5b8181146101295780607402838201600402600401805490600101805490600101805490600101549260601b84529083601401528260340152906054015260010160e5565b910180921461013b5790600255610146565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561017357505f5b6001546001828201116101885750505f61018e565b01600190035b5f555f6001556074025ff35b5f5ffd', }, // withdrawals request contract '0x05F27129610CB42103b665629CB5c8C00296AaAa': { balance: '0', nonce: '1', - code: '0x3373fffffffffffffffffffffffffffffffffffffffe1460a0573615156028575f545f5260205ff35b366038141561013f5760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061013f57600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160b4575060105b5f5b81811460ed5780604c02838201600302600401805490600101805490600101549160601b83528260140152906034015260010160b6565b910180921460fe5790600255610109565b90505f6002555f6003555b5f548061049d141561011857505f5b60015460028282011161012d5750505f610133565b01600290035b5f555f600155604c025ff35b5f5ffd', + code: '0x3373fffffffffffffffffffffffffffffffffffffffe1460c7573615156028575f545f5260205ff35b36603814156101f05760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101f057600182026001905f5b5f821115608057810190830284830290049160010191906065565b9093900434106101f057600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160db575060105b5f5b81811461017f5780604c02838201600302600401805490600101805490600101549160601b83528260140152807fffffffffffffffffffffffffffffffff0000000000000000000000000000000016826034015260401c906044018160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160dd565b9101809214610191579060025561019c565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14156101c957505f5b6001546002828201116101de5750505f6101e4565b01600290035b5f555f600155604c025ff35b5f5ffd', storage: { '0x0000000000000000000000000000000000000000000000000000000000000000': '0x000000000000000000000000000000000000000000000000000000000000049d', @@ -160,12 +160,9 @@ describe(`${method}: call with executionPayloadV4`, () => { withdrawals: [], blobGasUsed: '0x0', excessBlobGas: '0x0', - depositRequests: [], - withdrawalRequests: [], - consolidationRequests: [], - parentHash: '0xdc1727ee420fead1d6a8dfe548758e5a5a48eed888b162278534b105b9909cf9', - stateRoot: '0x42a2ea499b9805a99881ebb8057b7b336082a4890aac29f346064ca7a4777b82', - blockHash: '0xb8dd5003e1390c64eda06c87b931ede724905d4a33b77aba58c865bd2055c142', + parentHash: '0x6abe4c2777a6a1e994d83920cfb95229b071174b95c89343f54b926f733789f2', + stateRoot: '0x7aa6e46df1f78988a3141b5e7da8abee78d1daca175f43fe8866b2d1bf8d8ef8', + blockHash: '0x9a5903d803e6e7c3631cd76cb7279f93d7facc995c53eaffadf4e225504b18eb', } const oldMethods = ['engine_newPayloadV1', 'engine_newPayloadV2', 'engine_newPayloadV3'] @@ -183,7 +180,7 @@ describe(`${method}: call with executionPayloadV4`, () => { assert.ok(res.error.message.includes(expectedError)) } - res = await rpc.request(method, [validBlock, [], parentBeaconBlockRoot]) + res = await rpc.request(method, [validBlock, [], parentBeaconBlockRoot, ['0x', '0x', '0x']]) assert.equal(res.result.status, 'VALID') res = await rpc.request('engine_forkchoiceUpdatedV3', validPayload) @@ -203,21 +200,18 @@ describe(`${method}: call with executionPayloadV4`, () => { await service.txPool.add(depositTx, true) res = await rpc.request('engine_getPayloadV4', [payloadId]) - const { executionPayload } = res.result + const { executionPayload, executionRequests } = res.result assert.ok( - executionPayload.depositRequests?.length === 1, - 'depositRequests should have 1 deposit request', - ) - assert.ok( - executionPayload.withdrawalRequests !== undefined, - 'depositRequests field should be received', - ) - assert.ok( - executionPayload.consolidationRequests !== undefined, - 'consolidationRequests field should be received', + executionRequests?.length === 3, + 'executionRequests should have 3 entries for each request type', ) - res = await rpc.request(method, [executionPayload, [], parentBeaconBlockRoot]) + res = await rpc.request(method, [ + executionPayload, + [], + parentBeaconBlockRoot, + executionRequests, + ]) assert.equal(res.result.status, 'VALID') const newBlockHashHex = executionPayload.blockHash diff --git a/packages/util/src/request.ts b/packages/util/src/request.ts index adc30cd1f8..6de6ca3d38 100644 --- a/packages/util/src/request.ts +++ b/packages/util/src/request.ts @@ -10,62 +10,11 @@ export enum CLRequestType { Consolidation = 0x02, } -export type DepositRequestV1 = { - pubkey: PrefixedHexString // DATA 48 bytes - withdrawalCredentials: PrefixedHexString // DATA 32 bytes - amount: PrefixedHexString // QUANTITY 8 bytes in gwei - signature: PrefixedHexString // DATA 96 bytes - index: PrefixedHexString // QUANTITY 8 bytes -} - -export type WithdrawalRequestV1 = { - sourceAddress: PrefixedHexString // DATA 20 bytes - validatorPubkey: PrefixedHexString // DATA 48 bytes - amount: PrefixedHexString // QUANTITY 8 bytes in gwei -} - -export type ConsolidationRequestV1 = { - sourceAddress: PrefixedHexString // DATA 20 bytes - sourcePubkey: PrefixedHexString // DATA 48 bytes - targetPubkey: PrefixedHexString // DATA 48 bytes -} - export interface RequestJSON { type: PrefixedHexString data: PrefixedHexString } -export type DepositRequestData = { - pubkey: Uint8Array - withdrawalCredentials: Uint8Array - // 8 bytes uint64 LE - amount: Uint8Array - signature: Uint8Array - // 8 bytes uint64 LE - index: Uint8Array -} - -export type WithdrawalRequestData = { - sourceAddress: Uint8Array - validatorPubkey: Uint8Array - // 8 bytes uint64 LE - amount: Uint8Array -} - -export type ConsolidationRequestData = { - sourceAddress: Uint8Array - sourcePubkey: Uint8Array - targetPubkey: Uint8Array -} - -export interface RequestData { - [CLRequestType.Deposit]: DepositRequestData - [CLRequestType.Withdrawal]: WithdrawalRequestData - [CLRequestType.Consolidation]: ConsolidationRequestData -} - -export type TypedRequestData = RequestData[CLRequestType] - export class CLRequest { // for easy use public readonly bytes: Uint8Array From a6dde76e68bb7452643c3b3f9b9e91c2481a4640 Mon Sep 17 00:00:00 2001 From: Amir Date: Tue, 22 Oct 2024 14:41:38 -0700 Subject: [PATCH 25/49] Update 6110 example --- packages/block/examples/6110Requests.ts | 27 ++++++++++++++----------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/packages/block/examples/6110Requests.ts b/packages/block/examples/6110Requests.ts index 6ac386aa9b..1c67c249f3 100644 --- a/packages/block/examples/6110Requests.ts +++ b/packages/block/examples/6110Requests.ts @@ -2,12 +2,12 @@ import { createBlock, genRequestsRoot } from '@ethereumjs/block' import { Common, Hardfork, Mainnet } from '@ethereumjs/common' import { type CLRequest, - type CLRequestType, - bytesToBigInt, - createDepositRequest, + CLRequestType, + bytesToHex, + createCLRequest, randomBytes, } from '@ethereumjs/util' -import { sha256 } from 'ethereum-cryptography/keccak.js' +import { sha256 } from 'ethereum-cryptography/sha256.js' const main = async () => { const common = new Common({ @@ -22,22 +22,25 @@ const main = async () => { signature: randomBytes(96), index: randomBytes(8), } - const request = createDepositRequest(depositRequestData) as CLRequest + // flatten request bytes as per EIP-7685 + const depositRequestBytes = new Uint8Array( + Object.values(depositRequestData) + .map((arr) => Array.from(arr)) // Convert Uint8Arrays to regular arrays + .reduce((acc, curr) => acc.concat(curr), []), // Concatenate arrays + ) + const request = createCLRequest( + new Uint8Array([CLRequestType.Deposit, ...depositRequestBytes]), + ) as CLRequest const requests = [request] const requestsRoot = genRequestsRoot(requests, sha256) const block = createBlock( { - requests, - header: { requestsRoot }, + header: { requestsHash: requestsRoot }, }, { common }, ) - console.log( - `Instantiated block with ${ - block.requests?.length - } deposit request, requestTrieValid=${await block.requestsTrieIsValid()}`, - ) + console.log(`Instantiated block ${block}, requestsHash=${bytesToHex(block.header.requestsHash!)}`) } void main() From e7ba8b9426c4a24f3fdc0dbed8fcded0132144e5 Mon Sep 17 00:00:00 2001 From: Amir Date: Tue, 22 Oct 2024 17:08:21 -0700 Subject: [PATCH 26/49] Use sha256 constant for default --- packages/block/src/header/header.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/block/src/header/header.ts b/packages/block/src/header/header.ts index d00e5eb668..c9353f396e 100644 --- a/packages/block/src/header/header.ts +++ b/packages/block/src/header/header.ts @@ -8,6 +8,7 @@ import { BIGINT_7, KECCAK256_RLP, KECCAK256_RLP_ARRAY, + SHA256_NULL, TypeOutput, bigIntToHex, bigIntToUnpaddedBytes, @@ -163,7 +164,7 @@ export class BlockHeader { parentBeaconBlockRoot: this.common.isActivatedEIP(4788) ? new Uint8Array(32) : undefined, // TODO: not sure what the default should be here becuase it would depends on activated EIPs // as even empty requests will produce data to sha hash - requestsHash: this.common.isActivatedEIP(7685) ? KECCAK256_RLP : undefined, + requestsHash: this.common.isActivatedEIP(7685) ? SHA256_NULL : undefined, } const baseFeePerGas = From ee9a33d3153ecf46efcc75de18a36d5f74e5404a Mon Sep 17 00:00:00 2001 From: Amir Date: Tue, 22 Oct 2024 17:08:37 -0700 Subject: [PATCH 27/49] Fix asserts --- packages/block/test/eip7685block.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/block/test/eip7685block.spec.ts b/packages/block/test/eip7685block.spec.ts index 5f26df9ac0..0ca22de271 100644 --- a/packages/block/test/eip7685block.spec.ts +++ b/packages/block/test/eip7685block.spec.ts @@ -1,6 +1,7 @@ import { Common, Hardfork, Mainnet } from '@ethereumjs/common' import { KECCAK256_RLP, + SHA256_NULL, bytesToBigInt, createDepositRequest, createWithdrawalRequest, @@ -49,10 +50,9 @@ const common = new Common({ describe('7685 tests', () => { it('should instantiate block with defaults', () => { const block = createBlock({}, { common }) - assert.deepEqual(block.header.requestsHash, KECCAK256_RLP) + assert.deepEqual(block.header.requestsHash, SHA256_NULL) const block2 = new Block(undefined, undefined, undefined, undefined, { common }) - assert.deepEqual(block.header.requestsHash, KECCAK256_RLP) - assert.equal(block2.requests?.length, 0) + assert.deepEqual(block2.header.requestsHash, SHA256_NULL) }) it('should instantiate a block with requests', async () => { const request = getRandomDepositRequest() From 239a63d2009a29d245d4dd4059d19dcfcf226b05 Mon Sep 17 00:00:00 2001 From: Amir Date: Tue, 22 Oct 2024 17:09:11 -0700 Subject: [PATCH 28/49] Add sha256 empty string constant --- packages/util/src/constants.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/util/src/constants.ts b/packages/util/src/constants.ts index b1587c9bc7..25eb3f1ee1 100644 --- a/packages/util/src/constants.ts +++ b/packages/util/src/constants.ts @@ -1,4 +1,5 @@ import { secp256k1 } from 'ethereum-cryptography/secp256k1.js' +import { sha256 } from 'ethereum-cryptography/sha256.js' import { hexToBytes } from './bytes.js' @@ -64,6 +65,8 @@ export const KECCAK256_RLP_S = '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cad */ export const KECCAK256_RLP = hexToBytes(KECCAK256_RLP_S) +export const SHA256_NULL = sha256(new Uint8Array()) + /** * RLP encoded empty string */ From 4f9ae46de21fd4e6ae57de3127a21a826bf1b92d Mon Sep 17 00:00:00 2001 From: Amir Date: Tue, 22 Oct 2024 17:09:41 -0700 Subject: [PATCH 29/49] Update block REAME examples --- packages/block/README.md | 50 ++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/packages/block/README.md b/packages/block/README.md index aec0e2f64e..b303ca27fa 100644 --- a/packages/block/README.md +++ b/packages/block/README.md @@ -82,7 +82,7 @@ This library supports the creation of [EIP-1559](https://eips.ethereum.org/EIPS/ import { createBlock } from '@ethereumjs/block' import { Common, Hardfork, Mainnet } from '@ethereumjs/common' -import { createTxFromTxData } from '@ethereumjs/tx' +import { createTx } from '@ethereumjs/tx' const common = new Common({ chain: Mainnet, hardfork: Hardfork.London }) const block = createBlock( @@ -119,7 +119,7 @@ console.log(Number(blockWithMatchingBaseFee.header.baseFeePerGas)) // 11 await blockWithMatchingBaseFee.validateData() // failed validation throws error -const tx = createTxFromTxData( +const tx = createTx( { type: 2, maxFeePerGas: BigInt(20) }, { common: new Common({ chain: Mainnet, hardfork: Hardfork.London }) }, ) @@ -190,11 +190,12 @@ import { createBlock } from '@ethereumjs/block' import { Common, Hardfork, Mainnet } from '@ethereumjs/common' import { createBlob4844Tx } from '@ethereumjs/tx' import { createAddressFromPrivateKey } from '@ethereumjs/util' +import { trustedSetup } from '@paulmillr/trusted-setups/fast.js' import { randomBytes } from 'crypto' -import { loadKZG } from 'kzg-wasm' +import { KZG as microEthKZG } from 'micro-eth-signer/kzg' const main = async () => { - const kzg = await loadKZG() + const kzg = new microEthKZG(trustedSetup) const common = new Common({ chain: Mainnet, @@ -248,12 +249,12 @@ import { createBlock, genRequestsRoot } from '@ethereumjs/block' import { Common, Hardfork, Mainnet } from '@ethereumjs/common' import { type CLRequest, - type CLRequestType, - bytesToBigInt, - createDepositRequest, + CLRequestType, + bytesToHex, + createCLRequest, randomBytes, } from '@ethereumjs/util' -import { keccak256 } from 'ethereum-cryptography/keccak.js' +import { sha256 } from 'ethereum-cryptography/sha256.js' const main = async () => { const common = new Common({ @@ -264,26 +265,29 @@ const main = async () => { const depositRequestData = { pubkey: randomBytes(48), withdrawalCredentials: randomBytes(32), - amount: (randomBytes(8)), + amount: randomBytes(8), signature: randomBytes(96), - index: (randomBytes(8)), + index: randomBytes(8), } - const request = createDepositRequest(depositRequestData) as CLRequest + // flatten request bytes as per EIP-7685 + const depositRequestBytes = new Uint8Array( + Object.values(depositRequestData) + .map((arr) => Array.from(arr)) // Convert Uint8Arrays to regular arrays + .reduce((acc, curr) => acc.concat(curr), []), // Concatenate arrays + ) + const request = createCLRequest( + new Uint8Array([CLRequestType.Deposit, ...depositRequestBytes]), + ) as CLRequest const requests = [request] - const requestsRoot = await genRequestsRoot(requests, keccak256) + const requestsRoot = genRequestsRoot(requests, sha256) const block = createBlock( { - requests, - header: { requestsRoot }, + header: { requestsHash: requestsRoot }, }, { common }, ) - console.log( - `Instantiated block with ${ - block.requests?.length - } deposit request, requestTrieValid=${await block.requestsTrieIsValid()}`, - ) + console.log(`Instantiated block ${block}, requestsHash=${bytesToHex(block.header.requestsHash!)}`) } void main() @@ -307,7 +311,7 @@ import { createWithdrawalRequest, randomBytes, } from '@ethereumjs/util' -import { keccak256 } from 'ethereum-cryptography/keccak.js' +import { sha256 } from 'ethereum-cryptography/keccak.js' const main = async () => { const common = new Common({ @@ -322,7 +326,7 @@ const main = async () => { } const request = createWithdrawalRequest(withdrawalRequestData) as CLRequest const requests = [request] - const requestsRoot = await genRequestsRoot(requests, keecak256) + const requestsRoot = genRequestsRoot(requests, sha256) const block = createBlock( { @@ -358,7 +362,7 @@ import { createConsolidationRequest, randomBytes, } from '@ethereumjs/util' -import { keccak256 } from 'ethereum-cryptography/keccak.js' +import { sha256 } from 'ethereum-cryptography/keccak.js' const main = async () => { const common = new Common({ @@ -373,7 +377,7 @@ const main = async () => { } const request = createConsolidationRequest(consolidationRequestData) as CLRequest const requests = [request] - const requestsRoot = await genRequestsRoot(requests, keccak256) + const requestsRoot = genRequestsRoot(requests, sha256) const block = createBlock( { From f0b5accd0a6ee11b4ad3674d23594a3b5597323c Mon Sep 17 00:00:00 2001 From: Amir Date: Tue, 22 Oct 2024 17:13:49 -0700 Subject: [PATCH 30/49] Reuse already computed hash --- packages/vm/src/runBlock.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index d3c7f8ace5..6c05f2f50e 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -253,15 +253,12 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise Date: Wed, 23 Oct 2024 11:07:04 -0700 Subject: [PATCH 31/49] Fix buildBlock tests --- packages/vm/test/api/buildBlock.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/vm/test/api/buildBlock.spec.ts b/packages/vm/test/api/buildBlock.spec.ts index 5405a1a860..6bd2f8af2f 100644 --- a/packages/vm/test/api/buildBlock.spec.ts +++ b/packages/vm/test/api/buildBlock.spec.ts @@ -57,7 +57,7 @@ describe('BlockBuilder', () => { ).sign(privateKey) await blockBuilder.addTransaction(tx) - const block = await blockBuilder.build() + const { block } = await blockBuilder.build() assert.equal( blockBuilder.transactionReceipts.length, 1, @@ -132,7 +132,7 @@ describe('BlockBuilder', () => { mixHash: new Uint8Array(32), nonce: new Uint8Array(8), } - const block = await blockBuilder.build(sealOpts) + const { block } = await blockBuilder.build(sealOpts) assert.deepEqual(block.header.mixHash, sealOpts.mixHash) assert.deepEqual(block.header.nonce, sealOpts.nonce) @@ -230,7 +230,7 @@ describe('BlockBuilder', () => { await blockBuilder.addTransaction(tx) - const block = await blockBuilder.build() + const { block } = await blockBuilder.build() assert.ok(cliqueVerifySignature(block.header, [signer.address]), 'should verify signature') assert.deepEqual( @@ -306,7 +306,7 @@ describe('BlockBuilder', () => { blockOpts: { calcDifficultyFromHeader: genesisBlock.header, freeze: false }, }) - const block = await blockBuilder.build() + const { block } = await blockBuilder.build() // block should successfully execute with VM.runBlock and have same outputs const result = await runBlock(vmCopy, { block }) @@ -374,7 +374,7 @@ describe('BlockBuilder', () => { assert.ok('should pass') } - const block = await blockBuilder.build() + const { block } = await blockBuilder.build() assert.equal( blockBuilder.transactionReceipts.length, 2, From b1bae1706302e5d92f782dd396a3433d0cb2eb9f Mon Sep 17 00:00:00 2001 From: Amir Date: Wed, 23 Oct 2024 12:08:09 -0700 Subject: [PATCH 32/49] Fix vm api tests --- packages/block/test/eip7685block.spec.ts | 19 +++++++++++++++---- .../eip-2935-historical-block-hashes.spec.ts | 5 +++-- .../vm/test/api/EIPs/eip-4844-blobs.spec.ts | 2 +- .../api/EIPs/eip-4895-withdrawals.spec.ts | 2 +- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/block/test/eip7685block.spec.ts b/packages/block/test/eip7685block.spec.ts index 0ca22de271..5ea8ba90a6 100644 --- a/packages/block/test/eip7685block.spec.ts +++ b/packages/block/test/eip7685block.spec.ts @@ -1,13 +1,14 @@ import { Common, Hardfork, Mainnet } from '@ethereumjs/common' import { + CLRequestType, KECCAK256_RLP, SHA256_NULL, bytesToBigInt, - createDepositRequest, + createCLRequest, createWithdrawalRequest, randomBytes, } from '@ethereumjs/util' -import { sha256 } from 'ethereum-cryptography/keccak.js' +import { sha256 } from 'ethereum-cryptography/sha256.js' import { assert, describe, expect, it } from 'vitest' import { genRequestsRoot } from '../src/helpers.js' @@ -20,7 +21,7 @@ import { } from '../src/index.js' import type { JSONRPCBlock } from '../src/index.js' -import type { CLRequest, CLRequestType } from '@ethereumjs/util' +import type { CLRequest } from '@ethereumjs/util' function getRandomDepositRequest(): CLRequest { const depositRequestData = { @@ -30,7 +31,17 @@ function getRandomDepositRequest(): CLRequest { signature: randomBytes(96), index: randomBytes(8), } - return createDepositRequest(depositRequestData) as CLRequest + // return createDepositRequest(depositRequestData) as CLRequest + + // flatten request bytes as per EIP-7685 + const depositRequestBytes = new Uint8Array( + Object.values(depositRequestData) + .map((arr) => Array.from(arr)) // Convert Uint8Arrays to regular arrays + .reduce((acc, curr) => acc.concat(curr), []), // Concatenate arrays + ) + return createCLRequest( + new Uint8Array([CLRequestType.Deposit, ...depositRequestBytes]), + ) as CLRequest } function getRandomWithdrawalRequest(): CLRequest { 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 73aeffbef6..b5eae454e2 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 @@ -182,7 +182,7 @@ describe('EIP 2935: historical block hashes', () => { timestamp: 1, }) const genesis = (await vm.blockchain.getBlock(0)) as Block - const block = await ( + const { block } = await ( await buildBlock(vm, { parentBlock: genesis, blockOpts: { @@ -220,7 +220,7 @@ describe('EIP 2935: historical block hashes', () => { await vm.stateManager.putCode(historyAddress, contract2935Code) let lastBlock = (await vm.blockchain.getBlock(0)) as Block for (let i = 1; i <= blocksToBuild; i++) { - lastBlock = await ( + const buildResult = await ( await buildBlock(vm, { parentBlock: lastBlock, blockOpts: { @@ -232,6 +232,7 @@ describe('EIP 2935: historical block hashes', () => { }, }) ).build() + lastBlock = buildResult.block await vm.blockchain.putBlock(lastBlock) await runBlock(vm, { block: lastBlock, 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 01189ed9f3..bd0e8cdf1c 100644 --- a/packages/vm/test/api/EIPs/eip-4844-blobs.spec.ts +++ b/packages/vm/test/api/EIPs/eip-4844-blobs.spec.ts @@ -82,7 +82,7 @@ describe('EIP4844 tests', () => { await blockBuilder.addTransaction(signedTx) - const block = await blockBuilder.build() + const { block } = await blockBuilder.build() assert.equal(block.transactions.length, 1, 'blob transaction should be included') assert.equal( bytesToHex(block.transactions[0].hash()), 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 6686a10350..86684b2f1f 100644 --- a/packages/vm/test/api/EIPs/eip-4895-withdrawals.spec.ts +++ b/packages/vm/test/api/EIPs/eip-4895-withdrawals.spec.ts @@ -226,7 +226,7 @@ describe('EIP4895 tests', () => { }, }) - const block = await blockBuilder.build() + const { block } = await blockBuilder.build() assert.equal( bytesToHex(block.header.stateRoot), From 9d3652fea2116f6b14e5a2c4e1a3bc73c9b0846a Mon Sep 17 00:00:00 2001 From: Amir Date: Wed, 23 Oct 2024 12:31:51 -0700 Subject: [PATCH 33/49] Fix client tests --- packages/client/test/rpc/debug/storageRangeAt.spec.ts | 2 +- .../test/rpc/engine/newPayloadV3VersionedHashes.spec.ts | 7 ++++--- packages/client/test/rpc/eth/blobBaseFee.spec.ts | 2 +- packages/client/test/rpc/eth/getFeeHistory.spec.ts | 6 +++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/client/test/rpc/debug/storageRangeAt.spec.ts b/packages/client/test/rpc/debug/storageRangeAt.spec.ts index 102c751e97..39ed821131 100644 --- a/packages/client/test/rpc/debug/storageRangeAt.spec.ts +++ b/packages/client/test/rpc/debug/storageRangeAt.spec.ts @@ -154,7 +154,7 @@ describe(method, () => { const thirdResult = await blockBuilder.addTransaction(thirdTx, { skipHardForkValidation: true }) - const block = await blockBuilder.build() + const { block } = await blockBuilder.build() await chain.putBlocks([block], true) context.rpc = rpc diff --git a/packages/client/test/rpc/engine/newPayloadV3VersionedHashes.spec.ts b/packages/client/test/rpc/engine/newPayloadV3VersionedHashes.spec.ts index 0e324b19b7..982f686dd4 100644 --- a/packages/client/test/rpc/engine/newPayloadV3VersionedHashes.spec.ts +++ b/packages/client/test/rpc/engine/newPayloadV3VersionedHashes.spec.ts @@ -40,9 +40,10 @@ describe(`${method}: Cancun validations`, () => { let res = await rpc.request(method, blockDataExtraVersionedHashes) assert.equal(res.result.status, 'INVALID') + console.log(res.result.validationError) assert.equal( res.result.validationError, - 'Error verifying blobVersionedHashes: expected=0 received=2', + 'Error assembling block from payload: Error verifying blobVersionedHashes: expected=0 received=2', ) const txString = @@ -105,7 +106,7 @@ describe(`${method}: Cancun validations`, () => { assert.equal(res.result.status, 'INVALID') assert.equal( res.result.validationError, - 'Error verifying blobVersionedHashes: expected=2 received=1', + 'Error assembling block from payload: Error verifying blobVersionedHashes: expected=2 received=1', ) const blockDataExtraMisMatchingHashes1 = [ @@ -127,7 +128,7 @@ describe(`${method}: Cancun validations`, () => { assert.equal(res.result.status, 'INVALID') assert.equal( res.result.validationError, - 'Error verifying blobVersionedHashes: mismatch at index=1 expected=0x0131…52c5 received=0x3456…', + 'Error assembling block from payload: Error verifying blobVersionedHashes: mismatch at index=1 expected=0x0131…52c5 received=0x3456…', ) const blockDataMatchingVersionedHashes = [ diff --git a/packages/client/test/rpc/eth/blobBaseFee.spec.ts b/packages/client/test/rpc/eth/blobBaseFee.spec.ts index fe2bb51dcd..6d7b00afcb 100644 --- a/packages/client/test/rpc/eth/blobBaseFee.spec.ts +++ b/packages/client/test/rpc/eth/blobBaseFee.spec.ts @@ -84,7 +84,7 @@ const produceBlockWith4844Tx = async ( nonce++ } - const block = await blockBuilder.build() + const { block } = await blockBuilder.build() await chain.putBlocks([block], true) await execution.run() } diff --git a/packages/client/test/rpc/eth/getFeeHistory.spec.ts b/packages/client/test/rpc/eth/getFeeHistory.spec.ts index 6d59877687..65598557a8 100644 --- a/packages/client/test/rpc/eth/getFeeHistory.spec.ts +++ b/packages/client/test/rpc/eth/getFeeHistory.spec.ts @@ -55,7 +55,7 @@ const produceFakeGasUsedBlock = async (execution: VMExecution, chain: Chain, gas }) blockBuilder.gasUsed = gasUsed - const block = await blockBuilder.build() + const { block } = await blockBuilder.build() await chain.putBlocks([block], false) //await execution.run() } @@ -110,7 +110,7 @@ const produceBlockWithTx = async ( nonce++ } - const block = await blockBuilder.build() + const { block } = await blockBuilder.build() await chain.putBlocks([block], false) await execution.run() } @@ -179,7 +179,7 @@ const produceBlockWith4844Tx = async ( nonce++ } - const block = await blockBuilder.build() + const { block } = await blockBuilder.build() await chain.putBlocks([block], true) await execution.run() } From ea935e722499f9f95b4b6d2c67d0a2bbc8d94c62 Mon Sep 17 00:00:00 2001 From: Amir Date: Wed, 23 Oct 2024 14:27:18 -0700 Subject: [PATCH 34/49] Fix tests --- packages/util/test/requests.spec.ts | 56 ++++++++++------------------- 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/packages/util/test/requests.spec.ts b/packages/util/test/requests.spec.ts index 45832866cd..b24737a4da 100644 --- a/packages/util/test/requests.spec.ts +++ b/packages/util/test/requests.spec.ts @@ -1,49 +1,31 @@ import { assert, describe, it } from 'vitest' -import { bytesToBigInt, randomBytes } from '../src/bytes.js' -import { - CLRequestFactory, - CLRequestType, - createConsolidationRequest, - createDepositRequest, - createWithdrawalRequest, -} from '../src/request.js' +import { concatBytes, randomBytes } from '../src/bytes.js' +import { CLRequestType, createCLRequest } from '../src/request.js' -import type { - CLRequest, - ConsolidationRequest, - DepositRequest, - WithdrawalRequest, -} from '../src/request.js' +import type { CLRequest } from '../src/request.js' describe('Requests', () => { - const testCases: [ - string, - any, - CLRequestType, - (...args: any) => ConsolidationRequest | DepositRequest | WithdrawalRequest, - ][] = [ + const testCases: [string, any, CLRequestType][] = [ [ 'DepositRequest', { pubkey: randomBytes(48), withdrawalCredentials: randomBytes(32), - amount: bytesToBigInt(randomBytes(8)), + amount: randomBytes(8), signature: randomBytes(96), - index: bytesToBigInt(randomBytes(8)), + index: randomBytes(8), }, CLRequestType.Deposit, - createDepositRequest, ], [ 'WithdrawalRequest', { sourceAddress: randomBytes(20), validatorPubkey: randomBytes(48), - amount: bytesToBigInt(randomBytes(8)), + amount: randomBytes(8), }, CLRequestType.Withdrawal, - createWithdrawalRequest, ], [ 'ConsolidationRequest', @@ -53,22 +35,22 @@ describe('Requests', () => { targetPubkey: randomBytes(48), }, CLRequestType.Consolidation, - createConsolidationRequest, ], ] - for (const [requestName, requestData, requestType, requestInstanceConstructor] of testCases) { + for (const [requestName, requestData, requestType] of testCases) { it(`${requestName}`, () => { - const requestObject = requestInstanceConstructor(requestData) as CLRequest - const requestJSON = requestObject.toJSON() - const serialized = requestObject.serialize() - assert.equal(serialized[0], requestType) + // flatten request bytes as per EIP-7685 + const depositRequestBytes = new Uint8Array( + Object.values(requestData) + .map((arr) => Array.from(arr)) // Convert Uint8Arrays to regular arrays + .reduce((acc, curr) => acc.concat(curr), []), // Concatenate arrays + ) + const requestObject = createCLRequest( + concatBytes(new Uint8Array([requestType]), depositRequestBytes), + ) as CLRequest - const deserialized = CLRequestFactory.fromSerializedRequest(serialized) - const deserializedJSON = deserialized.toJSON() - assert.deepEqual(deserializedJSON, requestJSON) - - const reserialized = deserialized.serialize() - assert.deepEqual(serialized, reserialized) + assert.equal(requestObject.type, requestType) + assert.deepEqual(requestObject.data, depositRequestBytes) }) } }) From 7e1c382d6f6085dc4a1af4d84b91ef44740b7cb2 Mon Sep 17 00:00:00 2001 From: spencer Date: Fri, 25 Oct 2024 23:02:42 +0700 Subject: [PATCH 35/49] packages: add requests hash to genesis block (#3771) * packages: add requests hash to genesis block. * Update packages/util/src/constants.ts Co-authored-by: Jochem Brouwer * Update packages/util/src/constants.ts Co-authored-by: Jochem Brouwer * packages: integrate suggestions. * Update packages/util/src/constants.ts --------- Co-authored-by: Jochem Brouwer --- packages/blockchain/src/blockchain.ts | 2 ++ packages/common/src/types.ts | 1 + packages/common/src/utils.ts | 3 +++ packages/util/src/constants.ts | 5 +++++ 4 files changed, 11 insertions(+) diff --git a/packages/blockchain/src/blockchain.ts b/packages/blockchain/src/blockchain.ts index a40ce99f67..54fe8a61c4 100644 --- a/packages/blockchain/src/blockchain.ts +++ b/packages/blockchain/src/blockchain.ts @@ -5,6 +5,7 @@ import { BIGINT_1, BIGINT_8, KECCAK256_RLP, + SHA256_EMPTY_RH, Lock, MapDB, bigIntToHex, @@ -1313,6 +1314,7 @@ export class Blockchain implements BlockchainInterface { number: 0, stateRoot, withdrawalsRoot: common.isActivatedEIP(4895) ? KECCAK256_RLP : undefined, + requestsHash: common.isActivatedEIP(7685) ? SHA256_EMPTY_RH : undefined, } if (common.consensusType() === 'poa') { if (common.genesis().extraData) { diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index 6176fb1b0e..7737ca4996 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -52,6 +52,7 @@ export interface GenesisBlockConfig { extraData: PrefixedHexString baseFeePerGas?: PrefixedHexString excessBlobGas?: PrefixedHexString + requestsHash?: PrefixedHexString } export interface HardforkTransitionConfig { diff --git a/packages/common/src/utils.ts b/packages/common/src/utils.ts index 6fcbc92f44..fc8ce1c23f 100644 --- a/packages/common/src/utils.ts +++ b/packages/common/src/utils.ts @@ -38,6 +38,7 @@ function parseGethParams(json: any) { coinbase, baseFeePerGas, excessBlobGas, + requestsHash, extraData: unparsedExtraData, nonce: unparsedNonce, timestamp: unparsedTimestamp, @@ -50,6 +51,7 @@ function parseGethParams(json: any) { coinbase: PrefixedHexString baseFeePerGas: PrefixedHexString excessBlobGas: PrefixedHexString + requestsHash: PrefixedHexString extraData: string nonce: string timestamp: string @@ -105,6 +107,7 @@ function parseGethParams(json: any) { coinbase, baseFeePerGas, excessBlobGas, + requestsHash, }, hardfork: undefined as string | undefined, hardforks: [] as ConfigHardfork[], diff --git a/packages/util/src/constants.ts b/packages/util/src/constants.ts index 25eb3f1ee1..d2798e90bd 100644 --- a/packages/util/src/constants.ts +++ b/packages/util/src/constants.ts @@ -67,6 +67,11 @@ export const KECCAK256_RLP = hexToBytes(KECCAK256_RLP_S) export const SHA256_NULL = sha256(new Uint8Array()) +/** + * SHA-256 hash of the RLP of an empty requests hash + */ +export const SHA256_EMPTY_RH = sha256(new Uint8Array([0, 1, 2])) + /** * RLP encoded empty string */ From 9d5822fa588315b7e61baed1ae93d09a4320324e Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sat, 26 Oct 2024 02:15:25 +0200 Subject: [PATCH 36/49] util: correctly report empty rq hash --- packages/util/src/constants.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/util/src/constants.ts b/packages/util/src/constants.ts index d2798e90bd..d7ba02138c 100644 --- a/packages/util/src/constants.ts +++ b/packages/util/src/constants.ts @@ -1,7 +1,7 @@ import { secp256k1 } from 'ethereum-cryptography/secp256k1.js' import { sha256 } from 'ethereum-cryptography/sha256.js' -import { hexToBytes } from './bytes.js' +import { concatBytes, hexToBytes } from './bytes.js' /** * 2^64-1 @@ -67,10 +67,15 @@ export const KECCAK256_RLP = hexToBytes(KECCAK256_RLP_S) export const SHA256_NULL = sha256(new Uint8Array()) +// Used to calculate the empty requests hash +const z0 = sha256(new Uint8Array([0])) +const z1 = sha256(new Uint8Array([1])) +const z2 = sha256(new Uint8Array([2])) + /** * SHA-256 hash of the RLP of an empty requests hash */ -export const SHA256_EMPTY_RH = sha256(new Uint8Array([0, 1, 2])) +export const SHA256_EMPTY_RH = sha256(concatBytes(z0, z1, z2)) /** * RLP encoded empty string From dc93f8f0cec8b0b025d3c393f1b336ee08349e73 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 28 Oct 2024 18:36:59 +0100 Subject: [PATCH 37/49] blockchain/util: remove sha256_empty_rh from exported util constants --- packages/blockchain/src/blockchain.ts | 2 +- packages/blockchain/src/helpers.ts | 20 +++++++++++++++++++- packages/util/src/constants.ts | 12 +----------- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/packages/blockchain/src/blockchain.ts b/packages/blockchain/src/blockchain.ts index 54fe8a61c4..381c571042 100644 --- a/packages/blockchain/src/blockchain.ts +++ b/packages/blockchain/src/blockchain.ts @@ -5,7 +5,6 @@ import { BIGINT_1, BIGINT_8, KECCAK256_RLP, - SHA256_EMPTY_RH, Lock, MapDB, bigIntToHex, @@ -27,6 +26,7 @@ import { } from './db/helpers.js' import { DBManager } from './db/manager.js' import { DBTarget } from './db/operation.js' +import { SHA256_EMPTY_RH } from './helpers.js' import type { BlockchainEvent, diff --git a/packages/blockchain/src/helpers.ts b/packages/blockchain/src/helpers.ts index b6529c26d2..d3c0272b03 100644 --- a/packages/blockchain/src/helpers.ts +++ b/packages/blockchain/src/helpers.ts @@ -1,8 +1,8 @@ import { ChainGenesis } from '@ethereumjs/common' import { genesisMPTStateRoot } from '@ethereumjs/mpt' +import { type GenesisState, hexToBytes } from '@ethereumjs/util' import type { Chain, Common } from '@ethereumjs/common' -import type { GenesisState } from '@ethereumjs/util' /** * Safe creation of a new Blockchain object awaiting the initialization function, @@ -40,3 +40,21 @@ export async function getGenesisStateRoot(chainId: Chain, common: Common): Promi const chainGenesis = ChainGenesis[chainId] return chainGenesis !== undefined ? chainGenesis.stateRoot : genGenesisStateRoot({}, common) } + +/* +The code below calculates the empty requests hash as of devnet-4 for EIP 7685 +Note: it is not possible to calculate this directly in the blockchain package, +this introduces the `ethereum-cryptography` dependency. + +// Used to calculate the empty requests hash +const z0 = sha256(new Uint8Array([0])) +const z1 = sha256(new Uint8Array([1])) +const z2 = sha256(new Uint8Array([2])) + +export const SHA256_EMPTY_RH = sha256(concatBytes(z0, z1, z2)) + +*/ + +export const SHA256_EMPTY_RH = hexToBytes( + '0x6036c41849da9c076ed79654d434017387a88fb833c2856b32e18218b3341c5f', +) diff --git a/packages/util/src/constants.ts b/packages/util/src/constants.ts index d7ba02138c..25eb3f1ee1 100644 --- a/packages/util/src/constants.ts +++ b/packages/util/src/constants.ts @@ -1,7 +1,7 @@ import { secp256k1 } from 'ethereum-cryptography/secp256k1.js' import { sha256 } from 'ethereum-cryptography/sha256.js' -import { concatBytes, hexToBytes } from './bytes.js' +import { hexToBytes } from './bytes.js' /** * 2^64-1 @@ -67,16 +67,6 @@ export const KECCAK256_RLP = hexToBytes(KECCAK256_RLP_S) export const SHA256_NULL = sha256(new Uint8Array()) -// Used to calculate the empty requests hash -const z0 = sha256(new Uint8Array([0])) -const z1 = sha256(new Uint8Array([1])) -const z2 = sha256(new Uint8Array([2])) - -/** - * SHA-256 hash of the RLP of an empty requests hash - */ -export const SHA256_EMPTY_RH = sha256(concatBytes(z0, z1, z2)) - /** * RLP encoded empty string */ From 328cd53ec73245f3fe85e2a0b0a05401b927e40f Mon Sep 17 00:00:00 2001 From: Amir Date: Mon, 28 Oct 2024 12:03:50 -0700 Subject: [PATCH 38/49] Remove requests from being passed in as blockData --- packages/vm/src/runBlock.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index b7c416f3ac..ad129c1617 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -255,7 +255,6 @@ export async function runBlock(vm: VM, opts: RunBlockOpts): Promise Date: Mon, 28 Oct 2024 16:44:15 -0700 Subject: [PATCH 39/49] Remove old tests that do not conform to new devnet4 specs --- packages/block/test/eip7685block.spec.ts | 174 ----------------------- 1 file changed, 174 deletions(-) delete mode 100644 packages/block/test/eip7685block.spec.ts diff --git a/packages/block/test/eip7685block.spec.ts b/packages/block/test/eip7685block.spec.ts deleted file mode 100644 index 5ea8ba90a6..0000000000 --- a/packages/block/test/eip7685block.spec.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { Common, Hardfork, Mainnet } from '@ethereumjs/common' -import { - CLRequestType, - KECCAK256_RLP, - SHA256_NULL, - bytesToBigInt, - createCLRequest, - createWithdrawalRequest, - randomBytes, -} from '@ethereumjs/util' -import { sha256 } from 'ethereum-cryptography/sha256.js' -import { assert, describe, expect, it } from 'vitest' - -import { genRequestsRoot } from '../src/helpers.js' -import { - Block, - createBlock, - createBlockFromBytesArray, - createBlockFromRPC, - createBlockHeader, -} from '../src/index.js' - -import type { JSONRPCBlock } from '../src/index.js' -import type { CLRequest } from '@ethereumjs/util' - -function getRandomDepositRequest(): CLRequest { - const depositRequestData = { - pubkey: randomBytes(48), - withdrawalCredentials: randomBytes(32), - amount: randomBytes(8), - signature: randomBytes(96), - index: randomBytes(8), - } - // return createDepositRequest(depositRequestData) as CLRequest - - // flatten request bytes as per EIP-7685 - const depositRequestBytes = new Uint8Array( - Object.values(depositRequestData) - .map((arr) => Array.from(arr)) // Convert Uint8Arrays to regular arrays - .reduce((acc, curr) => acc.concat(curr), []), // Concatenate arrays - ) - return createCLRequest( - new Uint8Array([CLRequestType.Deposit, ...depositRequestBytes]), - ) as CLRequest -} - -function getRandomWithdrawalRequest(): CLRequest { - const withdrawalRequestData = { - sourceAddress: randomBytes(20), - validatorPubkey: randomBytes(48), - amount: bytesToBigInt(randomBytes(8)), - } - return createWithdrawalRequest(withdrawalRequestData) as CLRequest -} - -const common = new Common({ - chain: Mainnet, - hardfork: Hardfork.Cancun, - eips: [7685, 4844, 4788], -}) -describe('7685 tests', () => { - it('should instantiate block with defaults', () => { - const block = createBlock({}, { common }) - assert.deepEqual(block.header.requestsHash, SHA256_NULL) - const block2 = new Block(undefined, undefined, undefined, undefined, { common }) - assert.deepEqual(block2.header.requestsHash, SHA256_NULL) - }) - it('should instantiate a block with requests', async () => { - const request = getRandomDepositRequest() - const requestsHash = genRequestsRoot([request], sha256) - const block = createBlock( - { - requests: [request], - header: { requestsHash }, - }, - { common }, - ) - assert.equal(block.requests?.length, 1) - assert.deepEqual(block.header.requestsHash, requestsHash) - }) - it('RequestsRootIsValid should return false when requestsHash is invalid', async () => { - const request = getRandomDepositRequest() - const block = createBlock( - { - requests: [request], - header: { requestsHash: randomBytes(32) }, - }, - { common }, - ) - - assert.equal(await block.requestsTrieIsValid(), false) - }) - it('should validate requests order', async () => { - const request1 = getRandomDepositRequest() - const request2 = getRandomDepositRequest() - const request3 = getRandomWithdrawalRequest() - const requests = [request1, request2, request3] - const requestsHash = genRequestsRoot(requests, sha256) - - // Construct block with requests in correct order - - const block = createBlock( - { - requests, - header: { requestsHash }, - }, - { common }, - ) - - assert.ok(await block.requestsTrieIsValid()) - - // Throws when requests are not ordered correctly - await expect(async () => - createBlock( - { - requests: [request1, request3, request2], - header: { requestsHash }, - }, - { common }, - ), - ).rejects.toThrow('ascending order') - }) -}) - -describe('createWithdrawalFromBytesArray tests', () => { - it('should construct a block with empty requests root', () => { - const block = createBlockFromBytesArray( - [createBlockHeader({}, { common }).raw(), [], [], [], []], - { - common, - }, - ) - assert.deepEqual(block.header.requestsHash, KECCAK256_RLP) - }) - it('should construct a block with a valid requests array', async () => { - const request1 = getRandomDepositRequest() - const request2 = getRandomWithdrawalRequest() - const request3 = getRandomWithdrawalRequest() - const requests = [request1, request2, request3] - const requestsHash = genRequestsRoot(requests, sha256) - const serializedRequests = [request1.serialize(), request2.serialize(), request3.serialize()] - - const block = createBlockFromBytesArray( - [createBlockHeader({ requestsHash }, { common }).raw(), [], [], [], serializedRequests], - { - common, - }, - ) - assert.deepEqual(block.header.requestsHash, requestsHash) - assert.equal(block.requests?.length, 3) - }) -}) - -describe('fromRPC tests', () => { - it('should construct a block from a JSON object', async () => { - const request1 = getRandomDepositRequest() - const request2 = getRandomDepositRequest() - const request3 = getRandomWithdrawalRequest() - const requests = [request1, request2, request3] - const requestsHash = genRequestsRoot(requests, sha256) - const serializedRequests = [request1.serialize(), request2.serialize(), request3.serialize()] - - const block = createBlockFromBytesArray( - [createBlockHeader({ requestsHash }, { common }).raw(), [], [], [], serializedRequests], - { - common, - }, - ) - const JSONBlock = block.toJSON() - const RPCBlock = { ...JSONBlock.header, requests: JSONBlock.requests } - const createBlockFromJSON = createBlockFromRPC(RPCBlock as JSONRPCBlock, undefined, { common }) - assert.deepEqual(block.hash(), createBlockFromJSON.hash()) - }) -}) From a973b4318ffb0968c5e8a2c25af4c79b3fd88771 Mon Sep 17 00:00:00 2001 From: Amir Date: Mon, 28 Oct 2024 16:44:25 -0700 Subject: [PATCH 40/49] Remove old test that does not conform to new devnet4 specs --- .../blockchain/test/blockValidation.spec.ts | 45 ------------------- 1 file changed, 45 deletions(-) diff --git a/packages/blockchain/test/blockValidation.spec.ts b/packages/blockchain/test/blockValidation.spec.ts index f0b050360b..a315342fa1 100644 --- a/packages/blockchain/test/blockValidation.spec.ts +++ b/packages/blockchain/test/blockValidation.spec.ts @@ -389,48 +389,3 @@ describe('[Blockchain]: Block validation tests', () => { assert.equal(common.hardfork(), Hardfork.London, 'validation did not change common hardfork') }) }) -describe('EIP 7685: requests field validation tests', () => { - it('should throw when putting a block with an invalid requestsHash', async () => { - const common = new Common({ - chain: Mainnet, - hardfork: Hardfork.Cancun, - eips: [7685, 1559, 4895, 4844, 4788], - }) - const blockchain = await createBlockchain({ - common, - }) - const block = createBlock( - { - header: { - number: 1n, - requestsHash: randomBytes(32), - withdrawalsRoot: KECCAK256_RLP, - parentHash: blockchain.genesisBlock.hash(), - timestamp: blockchain.genesisBlock.header.timestamp + 1n, - gasLimit: 5000, - }, - }, - { common }, - ) - - await expect(async () => blockchain.putBlock(block)).rejects.toThrow('invalid requestsHash') - - const blockWithRequest = createBlock( - { - header: { - number: 1n, - requestsHash: randomBytes(32), - withdrawalsRoot: KECCAK256_RLP, - parentHash: blockchain.genesisBlock.hash(), - timestamp: blockchain.genesisBlock.header.timestamp + 1n, - gasLimit: 5000, - }, - requests: [{ type: 0x1, bytes: randomBytes(12), serialize: () => randomBytes(32) } as any], - }, - { common }, - ) - await expect(async () => blockchain.putBlock(blockWithRequest)).rejects.toThrow( - 'invalid requestsHash', - ) - }) -}) From 000839d4fd7cd64a47004b8e8d27407c13fe136e Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 31 Oct 2024 19:21:47 +0100 Subject: [PATCH 41/49] make linter happy --- packages/client/src/ext/qheap.ts | 1 + .../src/rpc/modules/engine/util/newPayload.ts | 130 +++++++++--------- .../newPayloadV3VersionedHashes.spec.ts | 1 - 3 files changed, 66 insertions(+), 66 deletions(-) diff --git a/packages/client/src/ext/qheap.ts b/packages/client/src/ext/qheap.ts index dc83e56f23..55d5d109ae 100644 --- a/packages/client/src/ext/qheap.ts +++ b/packages/client/src/ext/qheap.ts @@ -210,6 +210,7 @@ export class Heap { fail = i } } + // eslint-disable-next-line no-console if (fail) console.log('failed at', fail >>> 1, fail) return !fail } diff --git a/packages/client/src/rpc/modules/engine/util/newPayload.ts b/packages/client/src/rpc/modules/engine/util/newPayload.ts index 8b3218c80a..e86b8bbbb6 100644 --- a/packages/client/src/rpc/modules/engine/util/newPayload.ts +++ b/packages/client/src/rpc/modules/engine/util/newPayload.ts @@ -26,6 +26,71 @@ type CLData = { executionRequests?: PrefixedHexString[] } +export const validate4844BlobVersionedHashes = ( + headBlock: Block, + blobVersionedHashes: PrefixedHexString[], +): string | null => { + let validationError: string | null = null + + // Collect versioned hashes in the flat array `txVersionedHashes` to match with received + const txVersionedHashes = [] + for (const tx of headBlock.transactions) { + if (tx instanceof Blob4844Tx) { + for (const vHash of tx.blobVersionedHashes) { + txVersionedHashes.push(vHash) + } + } + } + + if (blobVersionedHashes.length !== txVersionedHashes.length) { + validationError = `Error verifying blobVersionedHashes: expected=${txVersionedHashes.length} received=${blobVersionedHashes.length}` + } else { + // match individual hashes + for (let vIndex = 0; vIndex < blobVersionedHashes.length; vIndex++) { + // if mismatch, record error and break + if (blobVersionedHashes[vIndex] !== txVersionedHashes[vIndex]) { + validationError = `Error verifying blobVersionedHashes: mismatch at index=${vIndex} expected=${short( + txVersionedHashes[vIndex], + )} received=${short(blobVersionedHashes[vIndex])}` + break + } + } + } + return validationError +} + +export const validateAndGen7685RequestsHash = ( + common: Common, + executionRequests: PrefixedHexString[], +): PrefixedHexString => { + let validationError: string | null = null + + const requests: CLRequest[] = [] + let requestIndex = 0 + if (common.isActivatedEIP(6110)) { + requests.push(new DepositRequest(hexToBytes(executionRequests[requestIndex]))) + requestIndex++ + } + if (common.isActivatedEIP(7002)) { + requests.push(new WithdrawalRequest(hexToBytes(executionRequests[requestIndex]))) + requestIndex++ + } + if (common.isActivatedEIP(7251)) { + requests.push(new ConsolidationRequest(hexToBytes(executionRequests[requestIndex]))) + requestIndex++ + } + + if (requestIndex !== executionRequests.length) { + validationError = `Invalid executionRequests=${executionRequests.length} expected=${requestIndex}` + throw validationError + } + + const sha256Function = common.customCrypto.sha256 ?? sha256 + const requestsHash = genRequestsRoot(requests, sha256Function) + + return bytesToHex(requestsHash) +} + /** * Returns a block from a payload. * If errors, returns {@link PayloadStatusV1} @@ -97,68 +162,3 @@ export const assembleBlock = async ( return { error: response } } } - -export const validate4844BlobVersionedHashes = ( - headBlock: Block, - blobVersionedHashes: PrefixedHexString[], -): string | null => { - let validationError: string | null = null - - // Collect versioned hashes in the flat array `txVersionedHashes` to match with received - const txVersionedHashes = [] - for (const tx of headBlock.transactions) { - if (tx instanceof Blob4844Tx) { - for (const vHash of tx.blobVersionedHashes) { - txVersionedHashes.push(vHash) - } - } - } - - if (blobVersionedHashes.length !== txVersionedHashes.length) { - validationError = `Error verifying blobVersionedHashes: expected=${txVersionedHashes.length} received=${blobVersionedHashes.length}` - } else { - // match individual hashes - for (let vIndex = 0; vIndex < blobVersionedHashes.length; vIndex++) { - // if mismatch, record error and break - if (blobVersionedHashes[vIndex] !== txVersionedHashes[vIndex]) { - validationError = `Error verifying blobVersionedHashes: mismatch at index=${vIndex} expected=${short( - txVersionedHashes[vIndex], - )} received=${short(blobVersionedHashes[vIndex])}` - break - } - } - } - return validationError -} - -export const validateAndGen7685RequestsHash = ( - common: Common, - executionRequests: PrefixedHexString[], -): PrefixedHexString => { - let validationError: string | null = null - - const requests: CLRequest[] = [] - let requestIndex = 0 - if (common.isActivatedEIP(6110)) { - requests.push(new DepositRequest(hexToBytes(executionRequests[requestIndex]))) - requestIndex++ - } - if (common.isActivatedEIP(7002)) { - requests.push(new WithdrawalRequest(hexToBytes(executionRequests[requestIndex]))) - requestIndex++ - } - if (common.isActivatedEIP(7251)) { - requests.push(new ConsolidationRequest(hexToBytes(executionRequests[requestIndex]))) - requestIndex++ - } - - if (requestIndex !== executionRequests.length) { - validationError = `Invalid executionRequests=${executionRequests.length} expected=${requestIndex}` - throw validationError - } - - const sha256Function = common.customCrypto.sha256 ?? sha256 - const requestsHash = genRequestsRoot(requests, sha256Function) - - return bytesToHex(requestsHash) -} diff --git a/packages/client/test/rpc/engine/newPayloadV3VersionedHashes.spec.ts b/packages/client/test/rpc/engine/newPayloadV3VersionedHashes.spec.ts index 982f686dd4..1d337524a7 100644 --- a/packages/client/test/rpc/engine/newPayloadV3VersionedHashes.spec.ts +++ b/packages/client/test/rpc/engine/newPayloadV3VersionedHashes.spec.ts @@ -40,7 +40,6 @@ describe(`${method}: Cancun validations`, () => { let res = await rpc.request(method, blockDataExtraVersionedHashes) assert.equal(res.result.status, 'INVALID') - console.log(res.result.validationError) assert.equal( res.result.validationError, 'Error assembling block from payload: Error verifying blobVersionedHashes: expected=0 received=2', From d91b6664196ba102652a9f800783ab5d37f5d795 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 31 Oct 2024 19:30:39 +0100 Subject: [PATCH 42/49] block: make tsc happy --- packages/block/test/eip4844block.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/block/test/eip4844block.spec.ts b/packages/block/test/eip4844block.spec.ts index d7632c90f3..9cd8288015 100644 --- a/packages/block/test/eip4844block.spec.ts +++ b/packages/block/test/eip4844block.spec.ts @@ -228,7 +228,6 @@ describe('transaction validation tests', () => { ) const blockJSON = blockWithValidTx.toJSON() blockJSON.header!.blobGasUsed = '0x0' - // @ts-expect-error const blockWithInvalidHeader = createBlock(blockJSON, { common }) assert.throws( () => blockWithInvalidHeader.validateBlobTransactions(parentHeader), From 84f40e350bd0e8603348242269f7ff73cc4ecdc7 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 31 Oct 2024 19:30:59 +0100 Subject: [PATCH 43/49] block: remove obsolete examples --- packages/block/examples/6110Requests.ts | 46 ------------------------- packages/block/examples/7002Requests.ts | 41 ---------------------- packages/block/examples/7251Requests.ts | 40 --------------------- 3 files changed, 127 deletions(-) delete mode 100644 packages/block/examples/6110Requests.ts delete mode 100644 packages/block/examples/7002Requests.ts delete mode 100644 packages/block/examples/7251Requests.ts diff --git a/packages/block/examples/6110Requests.ts b/packages/block/examples/6110Requests.ts deleted file mode 100644 index 1c67c249f3..0000000000 --- a/packages/block/examples/6110Requests.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { createBlock, genRequestsRoot } from '@ethereumjs/block' -import { Common, Hardfork, Mainnet } from '@ethereumjs/common' -import { - type CLRequest, - CLRequestType, - bytesToHex, - createCLRequest, - randomBytes, -} from '@ethereumjs/util' -import { sha256 } from 'ethereum-cryptography/sha256.js' - -const main = async () => { - const common = new Common({ - chain: Mainnet, - hardfork: Hardfork.Prague, - }) - - const depositRequestData = { - pubkey: randomBytes(48), - withdrawalCredentials: randomBytes(32), - amount: randomBytes(8), - signature: randomBytes(96), - index: randomBytes(8), - } - // flatten request bytes as per EIP-7685 - const depositRequestBytes = new Uint8Array( - Object.values(depositRequestData) - .map((arr) => Array.from(arr)) // Convert Uint8Arrays to regular arrays - .reduce((acc, curr) => acc.concat(curr), []), // Concatenate arrays - ) - const request = createCLRequest( - new Uint8Array([CLRequestType.Deposit, ...depositRequestBytes]), - ) as CLRequest - const requests = [request] - const requestsRoot = genRequestsRoot(requests, sha256) - - const block = createBlock( - { - header: { requestsHash: requestsRoot }, - }, - { common }, - ) - console.log(`Instantiated block ${block}, requestsHash=${bytesToHex(block.header.requestsHash!)}`) -} - -void main() diff --git a/packages/block/examples/7002Requests.ts b/packages/block/examples/7002Requests.ts deleted file mode 100644 index 883d97b052..0000000000 --- a/packages/block/examples/7002Requests.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { createBlock, genRequestsRoot } from '@ethereumjs/block' -import { Common, Hardfork, Mainnet } from '@ethereumjs/common' -import { - type CLRequest, - type CLRequestType, - bytesToBigInt, - createWithdrawalRequest, - randomBytes, -} from '@ethereumjs/util' -import { sha256 } from 'ethereum-cryptography/keccak.js' - -const main = async () => { - const common = new Common({ - chain: Mainnet, - hardfork: Hardfork.Prague, - }) - - const withdrawalRequestData = { - sourceAddress: randomBytes(20), - validatorPubkey: randomBytes(48), - amount: bytesToBigInt(randomBytes(8)), - } - const request = createWithdrawalRequest(withdrawalRequestData) as CLRequest - const requests = [request] - const requestsRoot = genRequestsRoot(requests, sha256) - - const block = createBlock( - { - requests, - header: { requestsRoot }, - }, - { common }, - ) - console.log( - `Instantiated block with ${ - block.requests?.length - } withdrawal request, requestTrieValid=${await block.requestsTrieIsValid()}`, - ) -} - -void main() diff --git a/packages/block/examples/7251Requests.ts b/packages/block/examples/7251Requests.ts deleted file mode 100644 index e6d9b3116b..0000000000 --- a/packages/block/examples/7251Requests.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { createBlock, genRequestsRoot } from '@ethereumjs/block' -import { Common, Hardfork, Mainnet } from '@ethereumjs/common' -import { - type CLRequest, - type CLRequestType, - createConsolidationRequest, - randomBytes, -} from '@ethereumjs/util' -import { sha256 } from 'ethereum-cryptography/keccak.js' - -const main = async () => { - const common = new Common({ - chain: Mainnet, - hardfork: Hardfork.Prague, - }) - - const consolidationRequestData = { - sourceAddress: randomBytes(20), - sourcePubkey: randomBytes(48), - targetPubkey: randomBytes(48), - } - const request = createConsolidationRequest(consolidationRequestData) as CLRequest - const requests = [request] - const requestsRoot = genRequestsRoot(requests, sha256) - - const block = createBlock( - { - requests, - header: { requestsRoot }, - }, - { common }, - ) - console.log( - `Instantiated block with ${ - block.requests?.length - } consolidation request, requestTrieValid=${await block.requestsTrieIsValid()}`, - ) -} - -void main() From d3e6cca99b25d375efe6d8c044c7ea243db4a177 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 31 Oct 2024 19:39:39 +0100 Subject: [PATCH 44/49] util: make tsc happy --- packages/util/test/requests.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/util/test/requests.spec.ts b/packages/util/test/requests.spec.ts index b24737a4da..9253ec925e 100644 --- a/packages/util/test/requests.spec.ts +++ b/packages/util/test/requests.spec.ts @@ -6,7 +6,7 @@ import { CLRequestType, createCLRequest } from '../src/request.js' import type { CLRequest } from '../src/request.js' describe('Requests', () => { - const testCases: [string, any, CLRequestType][] = [ + const testCases: [string, { [key: string]: Uint8Array }, CLRequestType][] = [ [ 'DepositRequest', { From 802145d1590e34f58fd584432a638d6c1526fbfa Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 31 Oct 2024 19:47:23 +0100 Subject: [PATCH 45/49] vm: make linter and tsc happy --- packages/vm/test/api/EIPs/eip-7002.spec.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/vm/test/api/EIPs/eip-7002.spec.ts b/packages/vm/test/api/EIPs/eip-7002.spec.ts index eaa7ed463f..35d064686d 100644 --- a/packages/vm/test/api/EIPs/eip-7002.spec.ts +++ b/packages/vm/test/api/EIPs/eip-7002.spec.ts @@ -126,7 +126,11 @@ describe('EIP-7002 tests', () => { ) const withdrawalRequest = runBlockResults.requests![0] - assert(withdrawalRequest.type, CLRequestType.Withdrawal, 'make sure its withdrawal request') + assert.equal( + withdrawalRequest.type, + CLRequestType.Withdrawal, + 'make sure its withdrawal request', + ) // amount is in le when contract pack it in requests const expectedRequestData = concatBytes( From 88eb2001fb42364e8d10bf90a69bf7d48be0f57d Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 31 Oct 2024 20:01:02 +0100 Subject: [PATCH 46/49] blockchain: make linter happy --- packages/blockchain/test/blockValidation.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/blockchain/test/blockValidation.spec.ts b/packages/blockchain/test/blockValidation.spec.ts index a315342fa1..8e6402d8b3 100644 --- a/packages/blockchain/test/blockValidation.spec.ts +++ b/packages/blockchain/test/blockValidation.spec.ts @@ -2,9 +2,9 @@ import { createBlock, createBlockHeader } from '@ethereumjs/block' import { Common, ConsensusAlgorithm, Hardfork, Mainnet } from '@ethereumjs/common' import { Ethash } from '@ethereumjs/ethash' import { RLP } from '@ethereumjs/rlp' -import { KECCAK256_RLP, bytesToHex, randomBytes } from '@ethereumjs/util' +import { bytesToHex } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak.js' -import { assert, describe, expect, it } from 'vitest' +import { assert, describe, it } from 'vitest' import { EthashConsensus, createBlockchain } from '../src/index.js' From adff539242c35bbe90e4e72f5e9d8675a63c3d1e Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 31 Oct 2024 20:03:41 +0100 Subject: [PATCH 47/49] vm: fix example --- packages/vm/examples/buildBlock.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/examples/buildBlock.ts b/packages/vm/examples/buildBlock.ts index 1aee92e9d2..7f2d07963e 100644 --- a/packages/vm/examples/buildBlock.ts +++ b/packages/vm/examples/buildBlock.ts @@ -35,7 +35,7 @@ const main = async () => { // Add more transactions - const block = await blockBuilder.build() + const { block } = await blockBuilder.build() console.log(`Built a block with hash ${bytesToHex(block.hash())}`) } From 46ec57d5440b8dee0e281fc639c79652d412eeb1 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 31 Oct 2024 20:11:46 +0100 Subject: [PATCH 48/49] make cspell happy --- config/cspell-ts.json | 1 + packages/block/src/header/header.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/config/cspell-ts.json b/config/cspell-ts.json index 454adfe0b5..f50bc6c720 100644 --- a/config/cspell-ts.json +++ b/config/cspell-ts.json @@ -12,6 +12,7 @@ } ], "words": [ + "EEST", "paulmillr", "t8ntool", "!Json", diff --git a/packages/block/src/header/header.ts b/packages/block/src/header/header.ts index c9353f396e..ccd7af2a6c 100644 --- a/packages/block/src/header/header.ts +++ b/packages/block/src/header/header.ts @@ -162,8 +162,8 @@ export class BlockHeader { blobGasUsed: this.common.isActivatedEIP(4844) ? BIGINT_0 : undefined, excessBlobGas: this.common.isActivatedEIP(4844) ? BIGINT_0 : undefined, parentBeaconBlockRoot: this.common.isActivatedEIP(4788) ? new Uint8Array(32) : undefined, - // TODO: not sure what the default should be here becuase it would depends on activated EIPs - // as even empty requests will produce data to sha hash + // Note: as of devnet-4 we stub the null SHA256 hash, but for devnet5 this will actually + // be the correct hash for empty requests. requestsHash: this.common.isActivatedEIP(7685) ? SHA256_NULL : undefined, } From 5446fb8a5dcd1e14ad8ab9c2610b98f8661b10c4 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 31 Oct 2024 20:59:03 +0100 Subject: [PATCH 49/49] client/util/vm: simplify CLRequest --- .../src/rpc/modules/engine/util/newPayload.ts | 20 +++++++------- packages/util/src/request.ts | 26 +++---------------- packages/vm/src/requests.ts | 16 +++++------- 3 files changed, 20 insertions(+), 42 deletions(-) diff --git a/packages/client/src/rpc/modules/engine/util/newPayload.ts b/packages/client/src/rpc/modules/engine/util/newPayload.ts index e86b8bbbb6..5155ddfc5a 100644 --- a/packages/client/src/rpc/modules/engine/util/newPayload.ts +++ b/packages/client/src/rpc/modules/engine/util/newPayload.ts @@ -1,12 +1,6 @@ import { createBlockFromExecutionPayload, genRequestsRoot } from '@ethereumjs/block' import { Blob4844Tx } from '@ethereumjs/tx' -import { - ConsolidationRequest, - DepositRequest, - WithdrawalRequest, - bytesToHex, - hexToBytes, -} from '@ethereumjs/util' +import { CLRequest, CLRequestType, bytesToHex, hexToBytes } from '@ethereumjs/util' import { sha256 } from 'ethereum-cryptography/sha256' import { short } from '../../../../util/index.js' @@ -18,7 +12,7 @@ import type { Chain } from '../../../../blockchain/index.js' import type { ChainCache, PayloadStatusV1 } from '../types.js' import type { Block, ExecutionPayload } from '@ethereumjs/block' import type { Common } from '@ethereumjs/common' -import type { CLRequest, CLRequestType, PrefixedHexString } from '@ethereumjs/util' +import type { PrefixedHexString } from '@ethereumjs/util' type CLData = { parentBeaconBlockRoot?: PrefixedHexString @@ -68,15 +62,19 @@ export const validateAndGen7685RequestsHash = ( const requests: CLRequest[] = [] let requestIndex = 0 if (common.isActivatedEIP(6110)) { - requests.push(new DepositRequest(hexToBytes(executionRequests[requestIndex]))) + requests.push(new CLRequest(CLRequestType.Deposit, hexToBytes(executionRequests[requestIndex]))) requestIndex++ } if (common.isActivatedEIP(7002)) { - requests.push(new WithdrawalRequest(hexToBytes(executionRequests[requestIndex]))) + requests.push( + new CLRequest(CLRequestType.Withdrawal, hexToBytes(executionRequests[requestIndex])), + ) requestIndex++ } if (common.isActivatedEIP(7251)) { - requests.push(new ConsolidationRequest(hexToBytes(executionRequests[requestIndex]))) + requests.push( + new CLRequest(CLRequestType.Consolidation, hexToBytes(executionRequests[requestIndex])), + ) requestIndex++ } diff --git a/packages/util/src/request.ts b/packages/util/src/request.ts index 6de6ca3d38..86ff768dd6 100644 --- a/packages/util/src/request.ts +++ b/packages/util/src/request.ts @@ -32,32 +32,14 @@ export class CLRequest { } } -export class DepositRequest extends CLRequest { - constructor(requestData: Uint8Array) { - super(CLRequestType.Deposit, requestData) - } -} - -export class WithdrawalRequest extends CLRequest { - constructor(requestData: Uint8Array) { - super(CLRequestType.Withdrawal, requestData) - } -} - -export class ConsolidationRequest extends CLRequest { - constructor(requestData: Uint8Array) { - super(CLRequestType.Consolidation, requestData) - } -} - -export function createCLRequest(bytes: Uint8Array): CLRequest { +export function createCLRequest(bytes: Uint8Array): CLRequest { switch (bytes[0]) { case CLRequestType.Deposit: - return new DepositRequest(bytes.subarray(1)) as CLRequest + return new CLRequest(CLRequestType.Deposit, bytes.subarray(1)) case CLRequestType.Withdrawal: - return new WithdrawalRequest(bytes.subarray(1)) as CLRequest + return new CLRequest(CLRequestType.Withdrawal, bytes.subarray(1)) case CLRequestType.Consolidation: - return new ConsolidationRequest(bytes.subarray(1)) as CLRequest + return new CLRequest(CLRequestType.Consolidation, bytes.subarray(1)) default: throw Error(`Invalid request type=${bytes[0]}`) } diff --git a/packages/vm/src/requests.ts b/packages/vm/src/requests.ts index fad182cde3..136e6a43b0 100644 --- a/packages/vm/src/requests.ts +++ b/packages/vm/src/requests.ts @@ -1,8 +1,7 @@ import { Mainnet } from '@ethereumjs/common' import { - ConsolidationRequest, - DepositRequest, - WithdrawalRequest, + CLRequest, + CLRequestType, bigIntToAddressBytes, bigIntToBytes, bytesToHex, @@ -14,7 +13,6 @@ import { import type { RunTxResult } from './types.js' import type { VM } from './vm.js' -import type { CLRequest, CLRequestType } from '@ethereumjs/util' /** * This helper method generates a list of all CL requests that can be included in a pending block @@ -69,7 +67,7 @@ const accumulateWithdrawalsRequest = async ( const originalAccount = await vm.stateManager.getAccount(withdrawalsAddress) if (originalAccount === undefined) { - return new WithdrawalRequest(new Uint8Array()) + return new CLRequest(CLRequestType.Withdrawal, new Uint8Array()) } const results = await vm.evm.runCall({ @@ -85,7 +83,7 @@ const accumulateWithdrawalsRequest = async ( } const resultsBytes = results.execResult.returnValue - return new WithdrawalRequest(resultsBytes) + return new CLRequest(CLRequestType.Withdrawal, resultsBytes) } const accumulateConsolidationsRequest = async ( @@ -105,7 +103,7 @@ const accumulateConsolidationsRequest = async ( const originalAccount = await vm.stateManager.getAccount(consolidationsAddress) if (originalAccount === undefined) { - return new ConsolidationRequest(new Uint8Array(0)) + return new CLRequest(CLRequestType.Consolidation, new Uint8Array(0)) } const results = await vm.evm.runCall({ @@ -121,7 +119,7 @@ const accumulateConsolidationsRequest = async ( } const resultsBytes = results.execResult.returnValue - return new ConsolidationRequest(resultsBytes) + return new CLRequest(CLRequestType.Consolidation, resultsBytes) } const accumulateDepositsRequest = ( @@ -148,7 +146,7 @@ const accumulateDepositsRequest = ( } } - return new DepositRequest(resultsBytes) + return new CLRequest(CLRequestType.Deposit, resultsBytes) } function parseDepositLog(requestData: Uint8Array) {