Skip to content

Commit 3f3a3cc

Browse files
Merge branch 'devnet4-contracts' into 7702-devnet-4-plus-t8ntool
2 parents 5b87ca5 + 860c621 commit 3f3a3cc

File tree

9 files changed

+83
-141
lines changed

9 files changed

+83
-141
lines changed

packages/block/src/block/block.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,7 @@ export class Block {
519519
transactions,
520520
...withdrawalsArr,
521521
parentBeaconBlockRoot: header.parentBeaconBlockRoot,
522+
requestsHash: header.requestsHash,
522523
executionWitness: this.executionWitness,
523524
}
524525

packages/block/src/from-beacon-payload.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export type BeaconPayloadJSON = {
3131
blob_gas_used?: NumericString
3232
excess_blob_gas?: NumericString
3333
parent_beacon_block_root?: PrefixedHexString
34+
requests_hash?: PrefixedHexString
3435
// the casing of VerkleExecutionWitness remains same camel case for now
3536
execution_witness?: VerkleExecutionWitness
3637
}
@@ -131,6 +132,9 @@ export function executionPayloadFromBeaconPayload(payload: BeaconPayloadJSON): E
131132
if (payload.parent_beacon_block_root !== undefined && payload.parent_beacon_block_root !== null) {
132133
executionPayload.parentBeaconBlockRoot = payload.parent_beacon_block_root
133134
}
135+
if (payload.requests_hash !== undefined && payload.requests_hash !== null) {
136+
executionPayload.requestsHash = payload.requests_hash
137+
}
134138

135139
if (payload.execution_witness !== undefined && payload.execution_witness !== null) {
136140
// the casing structure in payload could be camel case or snake depending upon the CL

packages/block/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ export type ExecutionPayload = {
255255
blobGasUsed?: PrefixedHexString // QUANTITY, 64 Bits
256256
excessBlobGas?: PrefixedHexString // QUANTITY, 64 Bits
257257
parentBeaconBlockRoot?: PrefixedHexString // QUANTITY, 64 Bits
258+
requestsHash?: PrefixedHexString
258259
// VerkleExecutionWitness is already a hex serialized object
259260
executionWitness?: VerkleExecutionWitness | null // QUANTITY, 64 Bits, null implies not available
260261
}

packages/client/src/rpc/modules/engine/engine.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import {
3838
executionPayloadV1FieldValidators,
3939
executionPayloadV2FieldValidators,
4040
executionPayloadV3FieldValidators,
41-
executionPayloadV4FieldValidators,
4241
forkchoiceFieldValidators,
4342
payloadAttributesFieldValidatorsV1,
4443
payloadAttributesFieldValidatorsV2,
@@ -58,7 +57,6 @@ import type {
5857
ExecutionPayloadV1,
5958
ExecutionPayloadV2,
6059
ExecutionPayloadV3,
61-
ExecutionPayloadV4,
6260
ForkchoiceResponseV1,
6361
ForkchoiceStateV1,
6462
PayloadAttributes,
@@ -206,9 +204,9 @@ export class Engine {
206204
this.newPayloadV4 = cmMiddleware(
207205
middleware(
208206
callWithStackTrace(this.newPayloadV4.bind(this), this._rpcDebug),
209-
3,
207+
4,
210208
[
211-
[validators.object(executionPayloadV4FieldValidators)],
209+
[validators.object(executionPayloadV3FieldValidators)],
212210
[validators.array(validators.bytes32)],
213211
[validators.bytes32],
214212
[validators.array(validators.hex)],
@@ -369,12 +367,9 @@ export class Engine {
369367
*/
370368
// newpayloadv3 comes with parentBeaconBlockRoot out of the payload
371369
const { block: headBlock, error } = await assembleBlock(
370+
payload,
372371
{
373-
...payload,
374-
// ExecutionPayload only handles undefined
375372
parentBeaconBlockRoot: parentBeaconBlockRoot ?? undefined,
376-
},
377-
{
378373
blobVersionedHashes: blobVersionedHashes ?? undefined,
379374
executionRequests: executionRequests ?? undefined,
380375
},
@@ -826,7 +821,9 @@ export class Engine {
826821
return newPayloadRes
827822
}
828823

829-
async newPayloadV4(params: [ExecutionPayloadV4, Bytes32[], Bytes32]): Promise<PayloadStatusV1> {
824+
async newPayloadV4(
825+
params: [ExecutionPayloadV3, Bytes32[], Bytes32, Bytes32[]],
826+
): Promise<PayloadStatusV1> {
830827
const pragueTimestamp = this.chain.config.chainCommon.hardforkTimestamp(Hardfork.Prague)
831828
const ts = parseInt(params[0].timestamp)
832829
if (pragueTimestamp === null || ts < pragueTimestamp) {

packages/client/src/rpc/modules/engine/types.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,7 @@ import { UNKNOWN_PAYLOAD } from '../../error-code.js'
22

33
import type { Skeleton } from '../../../service/index.js'
44
import type { Block, ExecutionPayload } from '@ethereumjs/block'
5-
import type {
6-
ConsolidationRequestV1,
7-
DepositRequestV1,
8-
PrefixedHexString,
9-
WithdrawalRequestV1,
10-
} from '@ethereumjs/util'
5+
import type { PrefixedHexString } from '@ethereumjs/util'
116

127
export enum Status {
138
ACCEPTED = 'ACCEPTED',
@@ -32,11 +27,6 @@ export type ExecutionPayloadV1 = ExecutionPayload
3227
export type ExecutionPayloadV2 = ExecutionPayloadV1 & { withdrawals: WithdrawalV1[] }
3328
// parentBeaconBlockRoot comes separate in new payloads and needs to be added to payload data
3429
export type ExecutionPayloadV3 = ExecutionPayloadV2 & { excessBlobGas: Uint64; blobGasUsed: Uint64 }
35-
export type ExecutionPayloadV4 = ExecutionPayloadV3 & {
36-
depositRequests: DepositRequestV1[]
37-
withdrawalRequests: WithdrawalRequestV1[]
38-
consolidationRequests: ConsolidationRequestV1[]
39-
}
4030

4131
export type ForkchoiceStateV1 = {
4232
headBlockHash: Bytes32

packages/client/src/rpc/modules/engine/util/newPayload.ts

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { createBlockFromExecutionPayload, genRequestsRoot } from '@ethereumjs/block'
22
import { 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'
410
import { sha256 } from 'ethereum-cryptography/sha256'
511

612
import { short } from '../../../../util/index.js'
@@ -11,9 +17,11 @@ import { validHash } from './generic.js'
1117
import type { Chain } from '../../../../blockchain/index.js'
1218
import type { ChainCache, PayloadStatusV1 } from '../types.js'
1319
import 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
*/
2533
export 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
}

packages/client/src/rpc/modules/engine/validators.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,6 @@ export const executionPayloadV3FieldValidators = {
2626
excessBlobGas: validators.uint64,
2727
}
2828

29-
export const executionPayloadV4FieldValidators = {
30-
...executionPayloadV3FieldValidators,
31-
depositRequests: validators.array(validators.depositRequest()),
32-
withdrawalRequests: validators.array(validators.withdrawalRequest()),
33-
consolidationRequests: validators.array(validators.consolidationRequest()),
34-
}
35-
3629
export const forkchoiceFieldValidators = {
3730
headBlockHash: validators.blockHash,
3831
safeBlockHash: validators.blockHash,

packages/client/test/rpc/engine/newPayloadV4.spec.ts

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ const [blockData] = beaconData
1212

1313
const parentBeaconBlockRoot = '0x42942949c4ed512cd85c2cb54ca88591338cbb0564d3a2bea7961a639ef29d64'
1414
const validForkChoiceState = {
15-
headBlockHash: '0xdc1727ee420fead1d6a8dfe548758e5a5a48eed888b162278534b105b9909cf9',
16-
safeBlockHash: '0xdc1727ee420fead1d6a8dfe548758e5a5a48eed888b162278534b105b9909cf9',
17-
finalizedBlockHash: '0xdc1727ee420fead1d6a8dfe548758e5a5a48eed888b162278534b105b9909cf9',
15+
headBlockHash: '0x6abe4c2777a6a1e994d83920cfb95229b071174b95c89343f54b926f733789f2',
16+
safeBlockHash: '0x6abe4c2777a6a1e994d83920cfb95229b071174b95c89343f54b926f733789f2',
17+
finalizedBlockHash: '0x6abe4c2777a6a1e994d83920cfb95229b071174b95c89343f54b926f733789f2',
1818
}
1919
const validPayloadAttributes = {
2020
timestamp: '0x64ba84fd',
@@ -44,13 +44,13 @@ const electraGenesisContracts = {
4444
'0x00706203067988Ab3E2A2ab626EdCD6f28bDBbbb': {
4545
balance: '0',
4646
nonce: '1',
47-
code: '0x3373fffffffffffffffffffffffffffffffffffffffe1460a8573615156028575f545f5260205ff35b36606014156101555760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061015557600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060011160bc575060015b5f5b8181146101025780607402838201600402600401805490600101805490600101805490600101549260601b84529083601401528260340152906054015260010160be565b9101809214610114579060025561011f565b90505f6002555f6003555b5f548061049d141561012e57505f5b6001546001828201116101435750505f610149565b01600190035b5f555f6001556074025ff35b5f5ffd',
47+
code: '0x3373fffffffffffffffffffffffffffffffffffffffe1460cf573615156028575f545f5260205ff35b366060141561019a5760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461019a57600182026001905f5b5f821115608057810190830284830290049160010191906065565b90939004341061019a57600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060011160e3575060015b5f5b8181146101295780607402838201600402600401805490600101805490600101805490600101549260601b84529083601401528260340152906054015260010160e5565b910180921461013b5790600255610146565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561017357505f5b6001546001828201116101885750505f61018e565b01600190035b5f555f6001556074025ff35b5f5ffd',
4848
},
4949
// withdrawals request contract
5050
'0x05F27129610CB42103b665629CB5c8C00296AaAa': {
5151
balance: '0',
5252
nonce: '1',
53-
code: '0x3373fffffffffffffffffffffffffffffffffffffffe1460a0573615156028575f545f5260205ff35b366038141561013f5760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061013f57600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160b4575060105b5f5b81811460ed5780604c02838201600302600401805490600101805490600101549160601b83528260140152906034015260010160b6565b910180921460fe5790600255610109565b90505f6002555f6003555b5f548061049d141561011857505f5b60015460028282011161012d5750505f610133565b01600290035b5f555f600155604c025ff35b5f5ffd',
53+
code: '0x3373fffffffffffffffffffffffffffffffffffffffe1460c7573615156028575f545f5260205ff35b36603814156101f05760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101f057600182026001905f5b5f821115608057810190830284830290049160010191906065565b9093900434106101f057600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160db575060105b5f5b81811461017f5780604c02838201600302600401805490600101805490600101549160601b83528260140152807fffffffffffffffffffffffffffffffff0000000000000000000000000000000016826034015260401c906044018160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160dd565b9101809214610191579060025561019c565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14156101c957505f5b6001546002828201116101de5750505f6101e4565b01600290035b5f555f600155604c025ff35b5f5ffd',
5454
storage: {
5555
'0x0000000000000000000000000000000000000000000000000000000000000000':
5656
'0x000000000000000000000000000000000000000000000000000000000000049d',
@@ -160,12 +160,9 @@ describe(`${method}: call with executionPayloadV4`, () => {
160160
withdrawals: [],
161161
blobGasUsed: '0x0',
162162
excessBlobGas: '0x0',
163-
depositRequests: [],
164-
withdrawalRequests: [],
165-
consolidationRequests: [],
166-
parentHash: '0xdc1727ee420fead1d6a8dfe548758e5a5a48eed888b162278534b105b9909cf9',
167-
stateRoot: '0x42a2ea499b9805a99881ebb8057b7b336082a4890aac29f346064ca7a4777b82',
168-
blockHash: '0xb8dd5003e1390c64eda06c87b931ede724905d4a33b77aba58c865bd2055c142',
163+
parentHash: '0x6abe4c2777a6a1e994d83920cfb95229b071174b95c89343f54b926f733789f2',
164+
stateRoot: '0x7aa6e46df1f78988a3141b5e7da8abee78d1daca175f43fe8866b2d1bf8d8ef8',
165+
blockHash: '0x9a5903d803e6e7c3631cd76cb7279f93d7facc995c53eaffadf4e225504b18eb',
169166
}
170167

171168
const oldMethods = ['engine_newPayloadV1', 'engine_newPayloadV2', 'engine_newPayloadV3']
@@ -183,7 +180,7 @@ describe(`${method}: call with executionPayloadV4`, () => {
183180
assert.ok(res.error.message.includes(expectedError))
184181
}
185182

186-
res = await rpc.request(method, [validBlock, [], parentBeaconBlockRoot])
183+
res = await rpc.request(method, [validBlock, [], parentBeaconBlockRoot, ['0x', '0x', '0x']])
187184
assert.equal(res.result.status, 'VALID')
188185

189186
res = await rpc.request('engine_forkchoiceUpdatedV3', validPayload)
@@ -203,21 +200,18 @@ describe(`${method}: call with executionPayloadV4`, () => {
203200
await service.txPool.add(depositTx, true)
204201

205202
res = await rpc.request('engine_getPayloadV4', [payloadId])
206-
const { executionPayload } = res.result
203+
const { executionPayload, executionRequests } = res.result
207204
assert.ok(
208-
executionPayload.depositRequests?.length === 1,
209-
'depositRequests should have 1 deposit request',
210-
)
211-
assert.ok(
212-
executionPayload.withdrawalRequests !== undefined,
213-
'depositRequests field should be received',
214-
)
215-
assert.ok(
216-
executionPayload.consolidationRequests !== undefined,
217-
'consolidationRequests field should be received',
205+
executionRequests?.length === 3,
206+
'executionRequests should have 3 entries for each request type',
218207
)
219208

220-
res = await rpc.request(method, [executionPayload, [], parentBeaconBlockRoot])
209+
res = await rpc.request(method, [
210+
executionPayload,
211+
[],
212+
parentBeaconBlockRoot,
213+
executionRequests,
214+
])
221215
assert.equal(res.result.status, 'VALID')
222216

223217
const newBlockHashHex = executionPayload.blockHash

0 commit comments

Comments
 (0)