Skip to content

Commit b082410

Browse files
authored
feat: devnet-4 support (#7154)
2 parents de0d6ab + 223e051 commit b082410

33 files changed

+424
-376
lines changed

packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,14 @@ export async function produceBlockBody<T extends BlockType>(
220220
} else {
221221
blobsResult = {type: BlobsResultType.preDeneb};
222222
}
223+
224+
if (ForkSeq[fork] >= ForkSeq.electra) {
225+
const {executionRequests} = builderRes;
226+
if (executionRequests === undefined) {
227+
throw Error(`Invalid builder getHeader response for fork=${fork}, missing executionRequests`);
228+
}
229+
(blockBody as electra.BlindedBeaconBlockBody).executionRequests = executionRequests;
230+
}
223231
}
224232

225233
// blockType === BlockType.Full
@@ -285,7 +293,6 @@ export async function produceBlockBody<T extends BlockType>(
285293
throw Error(`Missing blobsBundle response from getPayload at fork=${fork}`);
286294
}
287295

288-
// validate blindedBlobsBundle
289296
if (this.opts.sanityCheckExecutionEngineBlobs) {
290297
validateBlobsAndKzgCommitments(executionPayload, blobsBundle);
291298
}
@@ -455,6 +462,7 @@ async function prepareExecutionPayloadHeader(
455462
header: ExecutionPayloadHeader;
456463
executionPayloadValue: Wei;
457464
blobKzgCommitments?: deneb.BlobKzgCommitments;
465+
executionRequests?: electra.ExecutionRequests;
458466
}> {
459467
if (!chain.executionBuilder) {
460468
throw Error("executionBuilder required");

packages/beacon-node/src/execution/builder/http.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
SignedBeaconBlockOrContents,
99
SignedBlindedBeaconBlock,
1010
ExecutionPayloadHeader,
11+
electra,
1112
} from "@lodestar/types";
1213
import {parseExecutionPayloadAndBlobsBundle, reconstructFullBlockOrContents} from "@lodestar/state-transition";
1314
import {ChainForkConfig} from "@lodestar/config";
@@ -120,6 +121,7 @@ export class ExecutionBuilderHttp implements IExecutionBuilder {
120121
header: ExecutionPayloadHeader;
121122
executionPayloadValue: Wei;
122123
blobKzgCommitments?: deneb.BlobKzgCommitments;
124+
executionRequests?: electra.ExecutionRequests;
123125
}> {
124126
const signedBuilderBid = (
125127
await this.api.getHeader({slot, parentHash, proposerPubkey}, {timeoutMs: BUILDER_PROPOSAL_DELAY_TOLERANCE})
@@ -131,7 +133,8 @@ export class ExecutionBuilderHttp implements IExecutionBuilder {
131133

132134
const {header, value: executionPayloadValue} = signedBuilderBid.message;
133135
const {blobKzgCommitments} = signedBuilderBid.message as deneb.BuilderBid;
134-
return {header, executionPayloadValue, blobKzgCommitments};
136+
const {executionRequests} = signedBuilderBid.message as electra.BuilderBid;
137+
return {header, executionPayloadValue, blobKzgCommitments, executionRequests};
135138
}
136139

137140
async submitBlindedBlock(signedBlindedBlock: SignedBlindedBeaconBlock): Promise<SignedBeaconBlockOrContents> {

packages/beacon-node/src/execution/builder/interface.ts

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
SignedBeaconBlockOrContents,
99
ExecutionPayloadHeader,
1010
SignedBlindedBeaconBlock,
11+
electra,
1112
} from "@lodestar/types";
1213
import {ForkExecution} from "@lodestar/params";
1314

@@ -36,6 +37,7 @@ export interface IExecutionBuilder {
3637
header: ExecutionPayloadHeader;
3738
executionPayloadValue: Wei;
3839
blobKzgCommitments?: deneb.BlobKzgCommitments;
40+
executionRequests?: electra.ExecutionRequests;
3941
}>;
4042
submitBlindedBlock(signedBlock: SignedBlindedBeaconBlock): Promise<SignedBeaconBlockOrContents>;
4143
}

packages/beacon-node/src/execution/engine/types.ts

+45-75
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {capella, deneb, electra, Wei, bellatrix, Root, ExecutionPayload, ExecutionRequests} from "@lodestar/types";
1+
import {capella, deneb, electra, Wei, bellatrix, Root, ExecutionPayload, ExecutionRequests, ssz} from "@lodestar/types";
22
import {
33
BYTES_PER_LOGS_BLOOM,
44
FIELD_ELEMENTS_PER_BLOB,
@@ -116,7 +116,7 @@ type ExecutionPayloadRpcWithValue = {
116116
// even though CL tracks this as executionPayloadValue, EL returns this as blockValue
117117
blockValue: QUANTITY;
118118
blobsBundle?: BlobsBundleRpc;
119-
requests?: ExecutionRequestsRpc;
119+
executionRequests?: ExecutionRequestsRpc;
120120
shouldOverrideBuilder?: boolean;
121121
};
122122
type ExecutionPayloadResponse = ExecutionPayloadRpc | ExecutionPayloadRpcWithValue;
@@ -159,29 +159,17 @@ export type WithdrawalRpc = {
159159
amount: QUANTITY;
160160
};
161161

162-
export type ExecutionRequestsRpc = {
163-
deposits: DepositRequestRpc[];
164-
withdrawals: WithdrawalRequestRpc[];
165-
consolidations: ConsolidationRequestRpc[];
166-
};
162+
/**
163+
* ExecutionRequestsRpc only holds 3 elements in the following order:
164+
* - ssz'ed DepositRequests
165+
* - ssz'ed WithdrawalRequests
166+
* - ssz'ed ConsolidationRequests
167+
*/
168+
export type ExecutionRequestsRpc = [DepositRequestsRpc, WithdrawalRequestsRpc, ConsolidationRequestsRpc];
167169

168-
export type DepositRequestRpc = {
169-
pubkey: DATA;
170-
withdrawalCredentials: DATA;
171-
amount: QUANTITY;
172-
signature: DATA;
173-
index: QUANTITY;
174-
};
175-
export type WithdrawalRequestRpc = {
176-
sourceAddress: DATA;
177-
validatorPubkey: DATA;
178-
amount: QUANTITY;
179-
};
180-
export type ConsolidationRequestRpc = {
181-
sourceAddress: DATA;
182-
sourcePubkey: DATA;
183-
targetPubkey: DATA;
184-
};
170+
export type DepositRequestsRpc = DATA;
171+
export type WithdrawalRequestsRpc = DATA;
172+
export type ConsolidationRequestsRpc = DATA;
185173

186174
export type VersionedHashesRpc = DATA[];
187175

@@ -278,7 +266,9 @@ export function parseExecutionPayload(
278266
executionPayloadValue = quantityToBigint(response.blockValue);
279267
data = response.executionPayload;
280268
blobsBundle = response.blobsBundle ? parseBlobsBundle(response.blobsBundle) : undefined;
281-
executionRequests = response.requests ? deserializeExecutionRequests(response.requests) : undefined;
269+
executionRequests = response.executionRequests
270+
? deserializeExecutionRequests(response.executionRequests)
271+
: undefined;
282272
shouldOverrideBuilder = response.shouldOverrideBuilder ?? false;
283273
} else {
284274
data = response;
@@ -404,73 +394,53 @@ export function deserializeWithdrawal(serialized: WithdrawalRpc): capella.Withdr
404394
} as capella.Withdrawal;
405395
}
406396

407-
function serializeDepositRequest(depositRequest: electra.DepositRequest): DepositRequestRpc {
408-
return {
409-
pubkey: bytesToData(depositRequest.pubkey),
410-
withdrawalCredentials: bytesToData(depositRequest.withdrawalCredentials),
411-
amount: numToQuantity(depositRequest.amount),
412-
signature: bytesToData(depositRequest.signature),
413-
index: numToQuantity(depositRequest.index),
414-
};
397+
function serializeDepositRequests(depositRequests: electra.DepositRequests): DepositRequestsRpc {
398+
return bytesToData(ssz.electra.DepositRequests.serialize(depositRequests));
415399
}
416400

417-
function deserializeDepositRequest(serialized: DepositRequestRpc): electra.DepositRequest {
418-
return {
419-
pubkey: dataToBytes(serialized.pubkey, 48),
420-
withdrawalCredentials: dataToBytes(serialized.withdrawalCredentials, 32),
421-
amount: quantityToNum(serialized.amount),
422-
signature: dataToBytes(serialized.signature, 96),
423-
index: quantityToNum(serialized.index),
424-
} as electra.DepositRequest;
401+
function deserializeDepositRequests(serialized: DepositRequestsRpc): electra.DepositRequests {
402+
return ssz.electra.DepositRequests.deserialize(dataToBytes(serialized, null));
425403
}
426404

427-
function serializeWithdrawalRequest(withdrawalRequest: electra.WithdrawalRequest): WithdrawalRequestRpc {
428-
return {
429-
sourceAddress: bytesToData(withdrawalRequest.sourceAddress),
430-
validatorPubkey: bytesToData(withdrawalRequest.validatorPubkey),
431-
amount: numToQuantity(withdrawalRequest.amount),
432-
};
405+
function serializeWithdrawalRequests(withdrawalRequests: electra.WithdrawalRequests): WithdrawalRequestsRpc {
406+
return bytesToData(ssz.electra.WithdrawalRequests.serialize(withdrawalRequests));
433407
}
434408

435-
function deserializeWithdrawalRequest(withdrawalRequest: WithdrawalRequestRpc): electra.WithdrawalRequest {
436-
return {
437-
sourceAddress: dataToBytes(withdrawalRequest.sourceAddress, 20),
438-
validatorPubkey: dataToBytes(withdrawalRequest.validatorPubkey, 48),
439-
amount: quantityToBigint(withdrawalRequest.amount),
440-
};
409+
function deserializeWithdrawalRequest(serialized: WithdrawalRequestsRpc): electra.WithdrawalRequests {
410+
return ssz.electra.WithdrawalRequests.deserialize(dataToBytes(serialized, null));
441411
}
442412

443-
function serializeConsolidationRequest(consolidationRequest: electra.ConsolidationRequest): ConsolidationRequestRpc {
444-
return {
445-
sourceAddress: bytesToData(consolidationRequest.sourceAddress),
446-
sourcePubkey: bytesToData(consolidationRequest.sourcePubkey),
447-
targetPubkey: bytesToData(consolidationRequest.targetPubkey),
448-
};
413+
function serializeConsolidationRequests(
414+
consolidationRequests: electra.ConsolidationRequests
415+
): ConsolidationRequestsRpc {
416+
return bytesToData(ssz.electra.ConsolidationRequests.serialize(consolidationRequests));
449417
}
450418

451-
function deserializeConsolidationRequest(consolidationRequest: ConsolidationRequestRpc): electra.ConsolidationRequest {
452-
return {
453-
sourceAddress: dataToBytes(consolidationRequest.sourceAddress, 20),
454-
sourcePubkey: dataToBytes(consolidationRequest.sourcePubkey, 48),
455-
targetPubkey: dataToBytes(consolidationRequest.targetPubkey, 48),
456-
};
419+
function deserializeConsolidationRequests(serialized: ConsolidationRequestsRpc): electra.ConsolidationRequests {
420+
return ssz.electra.ConsolidationRequests.deserialize(dataToBytes(serialized, null));
457421
}
458422

423+
/**
424+
* This is identical to get_execution_requests_list in
425+
* https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/electra/beacon-chain.md#new-get_execution_requests_list
426+
*/
459427
export function serializeExecutionRequests(executionRequests: ExecutionRequests): ExecutionRequestsRpc {
460428
const {deposits, withdrawals, consolidations} = executionRequests;
461-
return {
462-
deposits: deposits.map(serializeDepositRequest),
463-
withdrawals: withdrawals.map(serializeWithdrawalRequest),
464-
consolidations: consolidations.map(serializeConsolidationRequest),
465-
};
429+
430+
return [
431+
serializeDepositRequests(deposits),
432+
serializeWithdrawalRequests(withdrawals),
433+
serializeConsolidationRequests(consolidations),
434+
];
466435
}
467436

468-
export function deserializeExecutionRequests(executionRequests: ExecutionRequestsRpc): ExecutionRequests {
469-
const {deposits, withdrawals, consolidations} = executionRequests;
437+
export function deserializeExecutionRequests(serialized: ExecutionRequestsRpc): ExecutionRequests {
438+
const [deposits, withdrawals, consolidations] = serialized;
439+
470440
return {
471-
deposits: deposits.map(deserializeDepositRequest),
472-
withdrawals: withdrawals.map(deserializeWithdrawalRequest),
473-
consolidations: consolidations.map(deserializeConsolidationRequest),
441+
deposits: deserializeDepositRequests(deposits),
442+
withdrawals: deserializeWithdrawalRequest(withdrawals),
443+
consolidations: deserializeConsolidationRequests(consolidations),
474444
};
475445
}
476446

packages/beacon-node/test/spec/presets/epoch_processing.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const epochTransitionFns: Record<string, EpochTransitionFn> = {
4444
epochFns.processSyncCommitteeUpdates(fork, state as CachedBeaconStateAltair);
4545
},
4646
historical_summaries_update: epochFns.processHistoricalSummariesUpdate as EpochTransitionFn,
47-
pending_balance_deposits: epochFns.processPendingBalanceDeposits as EpochTransitionFn,
47+
pending_deposits: epochFns.processPendingDeposits as EpochTransitionFn,
4848
pending_consolidations: epochFns.processPendingConsolidations as EpochTransitionFn,
4949
};
5050

packages/beacon-node/test/spec/presets/operations.test.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,7 @@ const operationFns: Record<string, BlockProcessFn<CachedBeaconStateAllForks>> =
9292
},
9393

9494
deposit_request: (state, testCase: {deposit_request: electra.DepositRequest}) => {
95-
const fork = state.config.getForkSeq(state.slot);
96-
blockFns.processDepositRequest(fork, state as CachedBeaconStateElectra, testCase.deposit_request);
95+
blockFns.processDepositRequest(state as CachedBeaconStateElectra, testCase.deposit_request);
9796
},
9897

9998
consolidation_request: (state, testCase: {consolidation_request: electra.ConsolidationRequest}) => {

packages/beacon-node/test/spec/specTestVersioning.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {DownloadTestsOptions} from "@lodestar/spec-test-util/downloadTests";
1414
const __dirname = path.dirname(fileURLToPath(import.meta.url));
1515

1616
export const ethereumConsensusSpecsTests: DownloadTestsOptions = {
17-
specVersion: "v1.5.0-alpha.6",
17+
specVersion: "v1.5.0-alpha.8",
1818
// Target directory is the host package root: 'packages/*/spec-tests'
1919
outputDir: path.join(__dirname, "../../spec-tests"),
2020
specTestsRepoUrl: "https://github.com/ethereum/consensus-spec-tests",

packages/params/src/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ export const {
9696

9797
MAX_EFFECTIVE_BALANCE_ELECTRA,
9898
MIN_ACTIVATION_BALANCE,
99-
PENDING_BALANCE_DEPOSITS_LIMIT,
99+
PENDING_DEPOSITS_LIMIT,
100100
PENDING_PARTIAL_WITHDRAWALS_LIMIT,
101101
PENDING_CONSOLIDATIONS_LIMIT,
102102
MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA,
@@ -107,6 +107,7 @@ export const {
107107
MAX_ATTESTER_SLASHINGS_ELECTRA,
108108
MAX_ATTESTATIONS_ELECTRA,
109109
MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP,
110+
MAX_PENDING_DEPOSITS_PER_EPOCH,
110111
WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA,
111112
} = activePreset;
112113

packages/params/src/presets/mainnet.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,12 @@ export const mainnetPreset: BeaconPreset = {
124124
MAX_ATTESTER_SLASHINGS_ELECTRA: 1,
125125
MAX_ATTESTATIONS_ELECTRA: 8,
126126
MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 8,
127+
MAX_PENDING_DEPOSITS_PER_EPOCH: 16,
127128
// 2**11 * 10**9 (= 2,048,000,000,000) Gwei
128129
MAX_EFFECTIVE_BALANCE_ELECTRA: 2048000000000,
129130
MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA: 4096,
130131
MIN_ACTIVATION_BALANCE: 32000000000,
131-
PENDING_BALANCE_DEPOSITS_LIMIT: 134217728,
132+
PENDING_DEPOSITS_LIMIT: 134217728,
132133
PENDING_PARTIAL_WITHDRAWALS_LIMIT: 134217728,
133134
PENDING_CONSOLIDATIONS_LIMIT: 262144,
134135
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: 1,

packages/params/src/presets/minimal.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,13 @@ export const minimalPreset: BeaconPreset = {
124124
MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 2,
125125
MAX_ATTESTER_SLASHINGS_ELECTRA: 1,
126126
MAX_ATTESTATIONS_ELECTRA: 8,
127-
MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 1,
127+
MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 2,
128+
MAX_PENDING_DEPOSITS_PER_EPOCH: 16,
128129
// 2**11 * 10**9 (= 2,048,000,000,000) Gwei
129130
MAX_EFFECTIVE_BALANCE_ELECTRA: 2048000000000,
130131
MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA: 4096,
131132
MIN_ACTIVATION_BALANCE: 32000000000,
132-
PENDING_BALANCE_DEPOSITS_LIMIT: 134217728,
133+
PENDING_DEPOSITS_LIMIT: 134217728,
133134
PENDING_PARTIAL_WITHDRAWALS_LIMIT: 64,
134135
PENDING_CONSOLIDATIONS_LIMIT: 64,
135136
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: 1,

packages/params/src/types.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,11 @@ export type BeaconPreset = {
8787
MAX_ATTESTER_SLASHINGS_ELECTRA: number;
8888
MAX_ATTESTATIONS_ELECTRA: number;
8989
MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: number;
90+
MAX_PENDING_DEPOSITS_PER_EPOCH: number;
9091
MAX_EFFECTIVE_BALANCE_ELECTRA: number;
9192
MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA: number;
9293
MIN_ACTIVATION_BALANCE: number;
93-
PENDING_BALANCE_DEPOSITS_LIMIT: number;
94+
PENDING_DEPOSITS_LIMIT: number;
9495
PENDING_PARTIAL_WITHDRAWALS_LIMIT: number;
9596
PENDING_CONSOLIDATIONS_LIMIT: number;
9697
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: number;
@@ -187,10 +188,11 @@ export const beaconPresetTypes: BeaconPresetTypes = {
187188
MAX_ATTESTER_SLASHINGS_ELECTRA: "number",
188189
MAX_ATTESTATIONS_ELECTRA: "number",
189190
MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: "number",
191+
MAX_PENDING_DEPOSITS_PER_EPOCH: "number",
190192
MAX_EFFECTIVE_BALANCE_ELECTRA: "number",
191193
MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA: "number",
192194
MIN_ACTIVATION_BALANCE: "number",
193-
PENDING_BALANCE_DEPOSITS_LIMIT: "number",
195+
PENDING_DEPOSITS_LIMIT: "number",
194196
PENDING_PARTIAL_WITHDRAWALS_LIMIT: "number",
195197
PENDING_CONSOLIDATIONS_LIMIT: "number",
196198
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: "number",

packages/params/test/e2e/ensure-config-is-synced.test.ts

+19-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ import {loadConfigYaml} from "../yaml.js";
88
// Not e2e, but slow. Run with e2e tests
99

1010
/** https://github.com/ethereum/consensus-specs/releases */
11-
const specConfigCommit = "v1.5.0-alpha.3";
11+
const specConfigCommit = "v1.5.0-alpha.8";
12+
/**
13+
* Fields that we filter from local config when doing comparison.
14+
* Ideally this should be empty as it is not spec compliant
15+
* For `MAX_BLOBS_PER_BLOCK`, see https://github.com/ChainSafe/lodestar/issues/7172
16+
*/
17+
const ignoredLocalPresetFields: (keyof BeaconPreset)[] = ["MAX_BLOBS_PER_BLOCK"];
1218

1319
describe("Ensure config is synced", () => {
1420
vi.setConfig({testTimeout: 60 * 1000});
@@ -25,12 +31,22 @@ describe("Ensure config is synced", () => {
2531
});
2632

2733
function assertCorrectPreset(localPreset: BeaconPreset, remotePreset: BeaconPreset): void {
34+
const filteredLocalPreset: Partial<BeaconPreset> = Object.keys(localPreset)
35+
.filter((key) => !ignoredLocalPresetFields.includes(key as keyof BeaconPreset))
36+
.reduce(
37+
(acc, key) => {
38+
acc[key as keyof BeaconPreset] = localPreset[key as keyof BeaconPreset];
39+
return acc;
40+
},
41+
{} as Partial<BeaconPreset>
42+
);
43+
2844
// Check each key for better debuggability
2945
for (const key of Object.keys(remotePreset) as (keyof BeaconPreset)[]) {
30-
expect(localPreset[key]).toBe(remotePreset[key]);
46+
expect(filteredLocalPreset[key]).toBe(remotePreset[key]);
3147
}
3248

33-
expect(localPreset).toEqual(remotePreset);
49+
expect(filteredLocalPreset).toEqual(remotePreset);
3450
}
3551

3652
async function downloadRemoteConfig(preset: "mainnet" | "minimal", commit: string): Promise<BeaconPreset> {

0 commit comments

Comments
 (0)