1- import { createBlockFromExecutionPayload } from '@ethereumjs/block'
1+ import { createBlockFromExecutionPayload , genRequestsRoot } from '@ethereumjs/block'
22import { Blob4844Tx } from '@ethereumjs/tx'
3- import { hexToBytes } from '@ethereumjs/util'
3+ import { bytesToHex , createCLRequest , equalsBytes , hexToBytes } from '@ethereumjs/util'
4+ import { sha256 } from 'ethereum-cryptography/sha256'
45
56import { short } from '../../../../util/index.js'
67import { Status } from '../types.js'
@@ -12,12 +13,18 @@ import type { ChainCache, PayloadStatusV1 } from '../types.js'
1213import type { Block , ExecutionPayload } from '@ethereumjs/block'
1314import type { PrefixedHexString } from '@ethereumjs/util'
1415
16+ type CLValidationData = {
17+ blobVersionedHashes ?: PrefixedHexString [ ]
18+ executionRequests ?: PrefixedHexString [ ]
19+ }
20+
1521/**
1622 * Returns a block from a payload.
1723 * If errors, returns {@link PayloadStatusV1}
1824 */
1925export const assembleBlock = async (
2026 payload : ExecutionPayload ,
27+ clValidationData : CLValidationData ,
2128 chain : Chain ,
2229 chainCache : ChainCache ,
2330) : Promise < { block ?: Block ; error ?: PayloadStatusV1 } > => {
@@ -32,6 +39,47 @@ export const assembleBlock = async (
3239 // TODO: validateData is also called in applyBlock while runBlock, may be it can be optimized
3340 // by removing/skipping block data validation from there
3441 await block . validateData ( )
42+
43+ // Validate CL data to see if it matches with the assembled block
44+ const { blobVersionedHashes, executionRequests } = clValidationData
45+
46+ /**
47+ * Validate blob versioned hashes in the context of EIP-4844 blob transactions
48+ */
49+ if ( block . common . isActivatedEIP ( 4844 ) ) {
50+ let validationError : string | null = null
51+ if ( blobVersionedHashes === undefined ) {
52+ validationError = `Error verifying blobVersionedHashes: received none`
53+ } else {
54+ validationError = validate4844BlobVersionedHashes ( block , blobVersionedHashes )
55+ }
56+
57+ // if there was a validation error return invalid
58+ if ( validationError !== null ) {
59+ throw validationError
60+ }
61+ } else if ( blobVersionedHashes !== undefined ) {
62+ const validationError = `Invalid blobVersionedHashes before EIP-4844 is activated`
63+ throw validationError
64+ }
65+
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+
3583 return { block }
3684 } catch ( error ) {
3785 const validationError = `Error assembling block from payload: ${ error } `
@@ -82,3 +130,22 @@ export const validate4844BlobVersionedHashes = (
82130 }
83131 return validationError
84132}
133+
134+ export const validate7685ExecutionRequests = (
135+ headBlock : Block ,
136+ executionRequests : PrefixedHexString [ ] ,
137+ ) : string | null => {
138+ let validationError : string | null = null
139+
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 requestsRoot = genRequestsRoot ( requests , sha256Function )
144+
145+ if ( ! equalsBytes ( requestsRoot , headBlock . header . requestsRoot ! ) ) {
146+ validationError = `Invalid requestsRoot received=${ bytesToHex (
147+ headBlock . header . requestsRoot ! ,
148+ ) } expected=${ bytesToHex ( requestsRoot ) } `
149+ }
150+ return validationError
151+ }
0 commit comments