11import { createBlockFromExecutionPayload , genRequestsRoot } from '@ethereumjs/block'
22import { Blob4844Tx } from '@ethereumjs/tx'
3- import { bytesToHex , createCLRequest , equalsBytes , hexToBytes } from '@ethereumjs/util'
3+ import {
4+ ConsolidationRequest ,
5+ DepositRequest ,
6+ WithdrawalRequest ,
7+ bytesToHex ,
8+ hexToBytes ,
9+ } from '@ethereumjs/util'
410import { sha256 } from 'ethereum-cryptography/sha256'
511
612import { short } from '../../../../util/index.js'
@@ -11,9 +17,11 @@ import { validHash } from './generic.js'
1117import type { Chain } from '../../../../blockchain/index.js'
1218import type { ChainCache , PayloadStatusV1 } from '../types.js'
1319import type { Block , ExecutionPayload } from '@ethereumjs/block'
14- import type { PrefixedHexString } from '@ethereumjs/util'
20+ import type { Common } from '@ethereumjs/common'
21+ import type { CLRequest , CLRequestType , PrefixedHexString } from '@ethereumjs/util'
1522
16- type CLValidationData = {
23+ type CLData = {
24+ parentBeaconBlockRoot ?: PrefixedHexString
1725 blobVersionedHashes ?: PrefixedHexString [ ]
1826 executionRequests ?: PrefixedHexString [ ]
1927}
@@ -23,26 +31,35 @@ type CLValidationData = {
2331 * If errors, returns {@link PayloadStatusV1}
2432 */
2533export const assembleBlock = async (
26- payload : ExecutionPayload ,
27- clValidationData : CLValidationData ,
34+ payload : Omit < ExecutionPayload , 'requestsHash' | 'parentBeaconBlockRoot' > ,
35+ clValidationData : CLData ,
2836 chain : Chain ,
2937 chainCache : ChainCache ,
3038) : Promise < { block ?: Block ; error ?: PayloadStatusV1 } > => {
3139 const { blockNumber, timestamp } = payload
3240 const { config } = chain
3341 const common = config . chainCommon . copy ( )
34-
3542 common . setHardforkBy ( { blockNumber, timestamp } )
3643
3744 try {
38- const block = await createBlockFromExecutionPayload ( payload , { common } )
45+ // Validate CL data to see if it matches with the assembled block
46+ const { blobVersionedHashes, executionRequests, parentBeaconBlockRoot } = clValidationData
47+
48+ let requestsHash
49+ if ( executionRequests !== undefined ) {
50+ requestsHash = validateAndGen7685RequestsHash ( common , executionRequests )
51+ } else if ( common . isActivatedEIP ( 7685 ) ) {
52+ throw `Invalid executionRequests=undefined for EIP-7685 activated block`
53+ }
54+
55+ const block = await createBlockFromExecutionPayload (
56+ { ...payload , parentBeaconBlockRoot, requestsHash } ,
57+ { common } ,
58+ )
3959 // TODO: validateData is also called in applyBlock while runBlock, may be it can be optimized
4060 // by removing/skipping block data validation from there
4161 await block . validateData ( )
4262
43- // Validate CL data to see if it matches with the assembled block
44- const { blobVersionedHashes, executionRequests } = clValidationData
45-
4663 /**
4764 * Validate blob versioned hashes in the context of EIP-4844 blob transactions
4865 */
@@ -63,23 +80,6 @@ export const assembleBlock = async (
6380 throw validationError
6481 }
6582
66- if ( block . common . isActivatedEIP ( 7685 ) ) {
67- let validationError : string | null = null
68- if ( executionRequests === undefined ) {
69- validationError = `Error verifying executionRequests: received none`
70- } else {
71- validationError = validate7685ExecutionRequests ( block , executionRequests )
72- }
73-
74- // if there was a validation error return invalid
75- if ( validationError !== null ) {
76- throw validationError
77- }
78- } else if ( executionRequests !== undefined ) {
79- const validationError = `Invalid executionRequests before EIP-7685 is activated`
80- throw validationError
81- }
82-
8383 return { block }
8484 } catch ( error ) {
8585 const validationError = `Error assembling block from payload: ${ error } `
@@ -131,21 +131,34 @@ export const validate4844BlobVersionedHashes = (
131131 return validationError
132132}
133133
134- export const validate7685ExecutionRequests = (
135- headBlock : Block ,
134+ export const validateAndGen7685RequestsHash = (
135+ common : Common ,
136136 executionRequests : PrefixedHexString [ ] ,
137- ) : string | null => {
137+ ) : PrefixedHexString => {
138138 let validationError : string | null = null
139139
140- // Collect versioned hashes in the flat array `txVersionedHashes` to match with received
141- const requests = executionRequests . map ( ( req ) => createCLRequest ( hexToBytes ( req ) ) )
142- const sha256Function = headBlock . common . customCrypto . sha256 ?? sha256
143- const requestsHash = genRequestsRoot ( requests , sha256Function )
140+ const requests : CLRequest < CLRequestType > [ ] = [ ]
141+ let requestIndex = 0
142+ if ( common . isActivatedEIP ( 6110 ) ) {
143+ requests . push ( new DepositRequest ( hexToBytes ( executionRequests [ requestIndex ] ) ) )
144+ requestIndex ++
145+ }
146+ if ( common . isActivatedEIP ( 7002 ) ) {
147+ requests . push ( new WithdrawalRequest ( hexToBytes ( executionRequests [ requestIndex ] ) ) )
148+ requestIndex ++
149+ }
150+ if ( common . isActivatedEIP ( 7251 ) ) {
151+ requests . push ( new ConsolidationRequest ( hexToBytes ( executionRequests [ requestIndex ] ) ) )
152+ requestIndex ++
153+ }
144154
145- if ( ! equalsBytes ( requestsHash , headBlock . header . requestsHash ! ) ) {
146- validationError = `Invalid requestsHash received=${ bytesToHex (
147- headBlock . header . requestsHash ! ,
148- ) } expected=${ bytesToHex ( requestsHash ) } `
155+ if ( requestIndex !== executionRequests . length ) {
156+ validationError = `Invalid executionRequests=${ executionRequests . length } expected=${ requestIndex } `
157+ throw validationError
149158 }
150- return validationError
159+
160+ const sha256Function = common . customCrypto . sha256 ?? sha256
161+ const requestsHash = genRequestsRoot ( requests , sha256Function )
162+
163+ return bytesToHex ( requestsHash )
151164}
0 commit comments