Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: devnet-4 support #7154

Merged
merged 11 commits into from
Oct 19, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,14 @@ export async function produceBlockBody<T extends BlockType>(
} else {
blobsResult = {type: BlobsResultType.preDeneb};
}

if (ForkSeq[fork] >= ForkSeq.electra) {
const {executionRequests} = builderRes;
if (executionRequests === undefined) {
throw Error(`Invalid builder getHeader response for fork=${fork}, missing executionRequests`);
}
(blockBody as electra.BlindedBeaconBlockBody).executionRequests = executionRequests;
}
}

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

// validate blindedBlobsBundle
if (this.opts.sanityCheckExecutionEngineBlobs) {
validateBlobsAndKzgCommitments(executionPayload, blobsBundle);
}
Expand Down Expand Up @@ -455,6 +462,7 @@ async function prepareExecutionPayloadHeader(
header: ExecutionPayloadHeader;
executionPayloadValue: Wei;
blobKzgCommitments?: deneb.BlobKzgCommitments;
executionRequests?: electra.ExecutionRequests;
}> {
if (!chain.executionBuilder) {
throw Error("executionBuilder required");
Expand Down
5 changes: 4 additions & 1 deletion packages/beacon-node/src/execution/builder/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
SignedBeaconBlockOrContents,
SignedBlindedBeaconBlock,
ExecutionPayloadHeader,
electra,
} from "@lodestar/types";
import {parseExecutionPayloadAndBlobsBundle, reconstructFullBlockOrContents} from "@lodestar/state-transition";
import {ChainForkConfig} from "@lodestar/config";
Expand Down Expand Up @@ -120,6 +121,7 @@ export class ExecutionBuilderHttp implements IExecutionBuilder {
header: ExecutionPayloadHeader;
executionPayloadValue: Wei;
blobKzgCommitments?: deneb.BlobKzgCommitments;
executionRequests?: electra.ExecutionRequests;
}> {
const signedBuilderBid = (
await this.api.getHeader({slot, parentHash, proposerPubkey}, {timeoutMs: BUILDER_PROPOSAL_DELAY_TOLERANCE})
Expand All @@ -131,7 +133,8 @@ export class ExecutionBuilderHttp implements IExecutionBuilder {

const {header, value: executionPayloadValue} = signedBuilderBid.message;
const {blobKzgCommitments} = signedBuilderBid.message as deneb.BuilderBid;
return {header, executionPayloadValue, blobKzgCommitments};
const {executionRequests} = signedBuilderBid.message as electra.BuilderBid;
return {header, executionPayloadValue, blobKzgCommitments, executionRequests};
}

async submitBlindedBlock(signedBlindedBlock: SignedBlindedBeaconBlock): Promise<SignedBeaconBlockOrContents> {
Expand Down
2 changes: 2 additions & 0 deletions packages/beacon-node/src/execution/builder/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
SignedBeaconBlockOrContents,
ExecutionPayloadHeader,
SignedBlindedBeaconBlock,
electra,
} from "@lodestar/types";
import {ForkExecution} from "@lodestar/params";

Expand Down Expand Up @@ -36,6 +37,7 @@ export interface IExecutionBuilder {
header: ExecutionPayloadHeader;
executionPayloadValue: Wei;
blobKzgCommitments?: deneb.BlobKzgCommitments;
executionRequests?: electra.ExecutionRequests;
}>;
submitBlindedBlock(signedBlock: SignedBlindedBeaconBlock): Promise<SignedBeaconBlockOrContents>;
}
120 changes: 45 additions & 75 deletions packages/beacon-node/src/execution/engine/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {capella, deneb, electra, Wei, bellatrix, Root, ExecutionPayload, ExecutionRequests} from "@lodestar/types";
import {capella, deneb, electra, Wei, bellatrix, Root, ExecutionPayload, ExecutionRequests, ssz} from "@lodestar/types";
import {
BYTES_PER_LOGS_BLOOM,
FIELD_ELEMENTS_PER_BLOB,
Expand Down Expand Up @@ -116,7 +116,7 @@ type ExecutionPayloadRpcWithValue = {
// even though CL tracks this as executionPayloadValue, EL returns this as blockValue
blockValue: QUANTITY;
blobsBundle?: BlobsBundleRpc;
requests?: ExecutionRequestsRpc;
executionRequests?: ExecutionRequestsRpc;
shouldOverrideBuilder?: boolean;
};
type ExecutionPayloadResponse = ExecutionPayloadRpc | ExecutionPayloadRpcWithValue;
Expand Down Expand Up @@ -159,29 +159,17 @@ export type WithdrawalRpc = {
amount: QUANTITY;
};

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

export type DepositRequestRpc = {
pubkey: DATA;
withdrawalCredentials: DATA;
amount: QUANTITY;
signature: DATA;
index: QUANTITY;
};
export type WithdrawalRequestRpc = {
sourceAddress: DATA;
validatorPubkey: DATA;
amount: QUANTITY;
};
export type ConsolidationRequestRpc = {
sourceAddress: DATA;
sourcePubkey: DATA;
targetPubkey: DATA;
};
export type DepositRequestsRpc = DATA;
export type WithdrawalRequestsRpc = DATA;
export type ConsolidationRequestsRpc = DATA;

export type VersionedHashesRpc = DATA[];

Expand Down Expand Up @@ -278,7 +266,9 @@ export function parseExecutionPayload(
executionPayloadValue = quantityToBigint(response.blockValue);
data = response.executionPayload;
blobsBundle = response.blobsBundle ? parseBlobsBundle(response.blobsBundle) : undefined;
executionRequests = response.requests ? deserializeExecutionRequests(response.requests) : undefined;
executionRequests = response.executionRequests
? deserializeExecutionRequests(response.executionRequests)
: undefined;
shouldOverrideBuilder = response.shouldOverrideBuilder ?? false;
} else {
data = response;
Expand Down Expand Up @@ -404,73 +394,53 @@ export function deserializeWithdrawal(serialized: WithdrawalRpc): capella.Withdr
} as capella.Withdrawal;
}

function serializeDepositRequest(depositRequest: electra.DepositRequest): DepositRequestRpc {
return {
pubkey: bytesToData(depositRequest.pubkey),
withdrawalCredentials: bytesToData(depositRequest.withdrawalCredentials),
amount: numToQuantity(depositRequest.amount),
signature: bytesToData(depositRequest.signature),
index: numToQuantity(depositRequest.index),
};
function serializeDepositRequests(depositRequests: electra.DepositRequests): DepositRequestsRpc {
return bytesToData(ssz.electra.DepositRequests.serialize(depositRequests));
}

function deserializeDepositRequest(serialized: DepositRequestRpc): electra.DepositRequest {
return {
pubkey: dataToBytes(serialized.pubkey, 48),
withdrawalCredentials: dataToBytes(serialized.withdrawalCredentials, 32),
amount: quantityToNum(serialized.amount),
signature: dataToBytes(serialized.signature, 96),
index: quantityToNum(serialized.index),
} as electra.DepositRequest;
function deserializeDepositRequests(serialized: DepositRequestsRpc): electra.DepositRequests {
return ssz.electra.DepositRequests.deserialize(dataToBytes(serialized, null));
}

function serializeWithdrawalRequest(withdrawalRequest: electra.WithdrawalRequest): WithdrawalRequestRpc {
return {
sourceAddress: bytesToData(withdrawalRequest.sourceAddress),
validatorPubkey: bytesToData(withdrawalRequest.validatorPubkey),
amount: numToQuantity(withdrawalRequest.amount),
};
function serializeWithdrawalRequests(withdrawalRequests: electra.WithdrawalRequests): WithdrawalRequestsRpc {
return bytesToData(ssz.electra.WithdrawalRequests.serialize(withdrawalRequests));
}

function deserializeWithdrawalRequest(withdrawalRequest: WithdrawalRequestRpc): electra.WithdrawalRequest {
return {
sourceAddress: dataToBytes(withdrawalRequest.sourceAddress, 20),
validatorPubkey: dataToBytes(withdrawalRequest.validatorPubkey, 48),
amount: quantityToBigint(withdrawalRequest.amount),
};
function deserializeWithdrawalRequest(serialized: WithdrawalRequestsRpc): electra.WithdrawalRequests {
return ssz.electra.WithdrawalRequests.deserialize(dataToBytes(serialized, null));
}

function serializeConsolidationRequest(consolidationRequest: electra.ConsolidationRequest): ConsolidationRequestRpc {
return {
sourceAddress: bytesToData(consolidationRequest.sourceAddress),
sourcePubkey: bytesToData(consolidationRequest.sourcePubkey),
targetPubkey: bytesToData(consolidationRequest.targetPubkey),
};
function serializeConsolidationRequests(
consolidationRequests: electra.ConsolidationRequests
): ConsolidationRequestsRpc {
return bytesToData(ssz.electra.ConsolidationRequests.serialize(consolidationRequests));
}

function deserializeConsolidationRequest(consolidationRequest: ConsolidationRequestRpc): electra.ConsolidationRequest {
return {
sourceAddress: dataToBytes(consolidationRequest.sourceAddress, 20),
sourcePubkey: dataToBytes(consolidationRequest.sourcePubkey, 48),
targetPubkey: dataToBytes(consolidationRequest.targetPubkey, 48),
};
function deserializeConsolidationRequests(serialized: ConsolidationRequestsRpc): electra.ConsolidationRequests {
return ssz.electra.ConsolidationRequests.deserialize(dataToBytes(serialized, null));
}

/**
* This is identical to get_execution_requests_list in
* https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/electra/beacon-chain.md#new-get_execution_requests_list
*/
export function serializeExecutionRequests(executionRequests: ExecutionRequests): ExecutionRequestsRpc {
const {deposits, withdrawals, consolidations} = executionRequests;
return {
deposits: deposits.map(serializeDepositRequest),
withdrawals: withdrawals.map(serializeWithdrawalRequest),
consolidations: consolidations.map(serializeConsolidationRequest),
};

return [
serializeDepositRequests(deposits),
serializeWithdrawalRequests(withdrawals),
serializeConsolidationRequests(consolidations),
];
}

export function deserializeExecutionRequests(executionRequests: ExecutionRequestsRpc): ExecutionRequests {
const {deposits, withdrawals, consolidations} = executionRequests;
export function deserializeExecutionRequests(serialized: ExecutionRequestsRpc): ExecutionRequests {
const [deposits, withdrawals, consolidations] = serialized;

return {
deposits: deposits.map(deserializeDepositRequest),
withdrawals: withdrawals.map(deserializeWithdrawalRequest),
consolidations: consolidations.map(deserializeConsolidationRequest),
deposits: deserializeDepositRequests(deposits),
withdrawals: deserializeWithdrawalRequest(withdrawals),
consolidations: deserializeConsolidationRequests(consolidations),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const epochTransitionFns: Record<string, EpochTransitionFn> = {
epochFns.processSyncCommitteeUpdates(fork, state as CachedBeaconStateAltair);
},
historical_summaries_update: epochFns.processHistoricalSummariesUpdate as EpochTransitionFn,
pending_balance_deposits: epochFns.processPendingBalanceDeposits as EpochTransitionFn,
pending_deposits: epochFns.processPendingDeposits as EpochTransitionFn,
pending_consolidations: epochFns.processPendingConsolidations as EpochTransitionFn,
};

Expand Down
3 changes: 1 addition & 2 deletions packages/beacon-node/test/spec/presets/operations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,7 @@ const operationFns: Record<string, BlockProcessFn<CachedBeaconStateAllForks>> =
},

deposit_request: (state, testCase: {deposit_request: electra.DepositRequest}) => {
const fork = state.config.getForkSeq(state.slot);
blockFns.processDepositRequest(fork, state as CachedBeaconStateElectra, testCase.deposit_request);
blockFns.processDepositRequest(state as CachedBeaconStateElectra, testCase.deposit_request);
},

consolidation_request: (state, testCase: {consolidation_request: electra.ConsolidationRequest}) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/beacon-node/test/spec/specTestVersioning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {DownloadTestsOptions} from "@lodestar/spec-test-util/downloadTests";
const __dirname = path.dirname(fileURLToPath(import.meta.url));

export const ethereumConsensusSpecsTests: DownloadTestsOptions = {
specVersion: "v1.5.0-alpha.6",
specVersion: "v1.5.0-alpha.8",
// Target directory is the host package root: 'packages/*/spec-tests'
outputDir: path.join(__dirname, "../../spec-tests"),
specTestsRepoUrl: "https://github.com/ethereum/consensus-spec-tests",
Expand Down
3 changes: 2 additions & 1 deletion packages/params/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export const {

MAX_EFFECTIVE_BALANCE_ELECTRA,
MIN_ACTIVATION_BALANCE,
PENDING_BALANCE_DEPOSITS_LIMIT,
PENDING_DEPOSITS_LIMIT,
PENDING_PARTIAL_WITHDRAWALS_LIMIT,
PENDING_CONSOLIDATIONS_LIMIT,
MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA,
Expand All @@ -107,6 +107,7 @@ export const {
MAX_ATTESTER_SLASHINGS_ELECTRA,
MAX_ATTESTATIONS_ELECTRA,
MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP,
MAX_PENDING_DEPOSITS_PER_EPOCH,
WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA,
} = activePreset;

Expand Down
3 changes: 2 additions & 1 deletion packages/params/src/presets/mainnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,12 @@ export const mainnetPreset: BeaconPreset = {
MAX_ATTESTER_SLASHINGS_ELECTRA: 1,
MAX_ATTESTATIONS_ELECTRA: 8,
MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 8,
MAX_PENDING_DEPOSITS_PER_EPOCH: 16,
// 2**11 * 10**9 (= 2,048,000,000,000) Gwei
MAX_EFFECTIVE_BALANCE_ELECTRA: 2048000000000,
MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA: 4096,
MIN_ACTIVATION_BALANCE: 32000000000,
PENDING_BALANCE_DEPOSITS_LIMIT: 134217728,
PENDING_DEPOSITS_LIMIT: 134217728,
PENDING_PARTIAL_WITHDRAWALS_LIMIT: 134217728,
PENDING_CONSOLIDATIONS_LIMIT: 262144,
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: 1,
Expand Down
5 changes: 3 additions & 2 deletions packages/params/src/presets/minimal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,13 @@ export const minimalPreset: BeaconPreset = {
MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 2,
MAX_ATTESTER_SLASHINGS_ELECTRA: 1,
MAX_ATTESTATIONS_ELECTRA: 8,
MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 1,
MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: 2,
MAX_PENDING_DEPOSITS_PER_EPOCH: 16,
// 2**11 * 10**9 (= 2,048,000,000,000) Gwei
MAX_EFFECTIVE_BALANCE_ELECTRA: 2048000000000,
MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA: 4096,
MIN_ACTIVATION_BALANCE: 32000000000,
PENDING_BALANCE_DEPOSITS_LIMIT: 134217728,
PENDING_DEPOSITS_LIMIT: 134217728,
PENDING_PARTIAL_WITHDRAWALS_LIMIT: 64,
PENDING_CONSOLIDATIONS_LIMIT: 64,
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: 1,
Expand Down
6 changes: 4 additions & 2 deletions packages/params/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,11 @@ export type BeaconPreset = {
MAX_ATTESTER_SLASHINGS_ELECTRA: number;
MAX_ATTESTATIONS_ELECTRA: number;
MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: number;
MAX_PENDING_DEPOSITS_PER_EPOCH: number;
MAX_EFFECTIVE_BALANCE_ELECTRA: number;
MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA: number;
MIN_ACTIVATION_BALANCE: number;
PENDING_BALANCE_DEPOSITS_LIMIT: number;
PENDING_DEPOSITS_LIMIT: number;
PENDING_PARTIAL_WITHDRAWALS_LIMIT: number;
PENDING_CONSOLIDATIONS_LIMIT: number;
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: number;
Expand Down Expand Up @@ -187,10 +188,11 @@ export const beaconPresetTypes: BeaconPresetTypes = {
MAX_ATTESTER_SLASHINGS_ELECTRA: "number",
MAX_ATTESTATIONS_ELECTRA: "number",
MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: "number",
MAX_PENDING_DEPOSITS_PER_EPOCH: "number",
MAX_EFFECTIVE_BALANCE_ELECTRA: "number",
MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA: "number",
MIN_ACTIVATION_BALANCE: "number",
PENDING_BALANCE_DEPOSITS_LIMIT: "number",
PENDING_DEPOSITS_LIMIT: "number",
PENDING_PARTIAL_WITHDRAWALS_LIMIT: "number",
PENDING_CONSOLIDATIONS_LIMIT: "number",
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: "number",
Expand Down
22 changes: 19 additions & 3 deletions packages/params/test/e2e/ensure-config-is-synced.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import {loadConfigYaml} from "../yaml.js";
// Not e2e, but slow. Run with e2e tests

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

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

function assertCorrectPreset(localPreset: BeaconPreset, remotePreset: BeaconPreset): void {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remote preset is missing MAX_BLOBS_PER_BLOCK since it has moved to config. But Lodestar has not made it configurable and it remains in local config.

Adding ignoredLocalPresetFields is a temporary workaround to make this test pass such that this PR is safe to be merged into unstable.

See #7172 for details

const filteredLocalPreset: Partial<BeaconPreset> = Object.keys(localPreset)
.filter((key) => !ignoredLocalPresetFields.includes(key as keyof BeaconPreset))
.reduce(
(acc, key) => {
acc[key as keyof BeaconPreset] = localPreset[key as keyof BeaconPreset];
return acc;
},
{} as Partial<BeaconPreset>
);

// Check each key for better debuggability
for (const key of Object.keys(remotePreset) as (keyof BeaconPreset)[]) {
expect(localPreset[key]).toBe(remotePreset[key]);
expect(filteredLocalPreset[key]).toBe(remotePreset[key]);
}

expect(localPreset).toEqual(remotePreset);
expect(filteredLocalPreset).toEqual(remotePreset);
}

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