diff --git a/packages/beacon-node/src/execution/engine/payloadIdCache.ts b/packages/beacon-node/src/execution/engine/payloadIdCache.ts index 960b061f12da..b5fe3d33e267 100644 --- a/packages/beacon-node/src/execution/engine/payloadIdCache.ts +++ b/packages/beacon-node/src/execution/engine/payloadIdCache.ts @@ -26,9 +26,10 @@ export type DepositReceiptV1 = { index: QUANTITY; }; -export type ExecutionLayerExitV1 = { +export type ExecutionLayerWithdrawalRequestV1 = { sourceAddress: DATA; validatorPubkey: DATA; + amount: QUANTITY; }; type FcuAttributes = {headBlockHash: DATA; finalizedBlockHash: DATA} & Omit; diff --git a/packages/beacon-node/src/execution/engine/types.ts b/packages/beacon-node/src/execution/engine/types.ts index f12f98e1a0f7..54b3a415ec98 100644 --- a/packages/beacon-node/src/execution/engine/types.ts +++ b/packages/beacon-node/src/execution/engine/types.ts @@ -17,7 +17,7 @@ import { quantityToBigint, } from "../../eth1/provider/utils.js"; import {ExecutionPayloadStatus, BlobsBundle, PayloadAttributes, VersionedHashes} from "./interface.js"; -import {WithdrawalV1, DepositReceiptV1, ExecutionLayerExitV1} from "./payloadIdCache.js"; +import {WithdrawalV1, DepositReceiptV1, ExecutionLayerWithdrawalRequestV1} from "./payloadIdCache.js"; /* eslint-disable @typescript-eslint/naming-convention */ @@ -126,14 +126,14 @@ export type ExecutionPayloadBodyRpc = { transactions: DATA[]; withdrawals: WithdrawalV1[] | null | undefined; depositReceipts: DepositReceiptV1[] | null | undefined; - exits: ExecutionLayerExitV1[] | null | undefined; + withdrawalRequests: ExecutionLayerWithdrawalRequestV1[] | null | undefined; }; export type ExecutionPayloadBody = { transactions: bellatrix.Transaction[]; withdrawals: capella.Withdrawals | null; depositReceipts: electra.DepositReceipts | null; - exits: electra.ExecutionLayerExits | null; + withdrawalRequests: electra.ExecutionLayerWithdrawalRequests | null; }; export type ExecutionPayloadRpc = { @@ -156,7 +156,7 @@ export type ExecutionPayloadRpc = { excessBlobGas?: QUANTITY; // DENEB parentBeaconBlockRoot?: QUANTITY; // DENEB depositReceipts?: DepositReceiptRpc[]; // ELECTRA - exits?: ExecutionLayerExitRpc[]; // ELECTRA + withdrawalRequests?: ExecutionLayerWithdrawalRequestRpc[]; // ELECTRA }; export type WithdrawalRpc = { @@ -167,7 +167,7 @@ export type WithdrawalRpc = { }; export type DepositReceiptRpc = DepositReceiptV1; -export type ExecutionLayerExitRpc = ExecutionLayerExitV1; +export type ExecutionLayerWithdrawalRequestRpc = ExecutionLayerWithdrawalRequestV1; export type VersionedHashesRpc = DATA[]; @@ -233,9 +233,9 @@ export function serializeExecutionPayload(fork: ForkName, data: ExecutionPayload // ELECTRA adds depositReceipts to the ExecutionPayload if (ForkSeq[fork] >= ForkSeq.electra) { - const {depositReceipts, exits} = data as electra.ExecutionPayload; + const {depositReceipts, withdrawalRequests} = data as electra.ExecutionPayload; payload.depositReceipts = depositReceipts.map(serializeDepositReceipt); - payload.exits = exits.map(serializeExecutionLayerExit); + payload.withdrawalRequests = withdrawalRequests.map(serializeExecutionLayerWithdrawalRequest); } return payload; @@ -324,7 +324,7 @@ export function parseExecutionPayload( } if (ForkSeq[fork] >= ForkSeq.electra) { - const {depositReceipts, exits} = data; + const {depositReceipts, withdrawalRequests} = data; // Geth can also reply with null if (depositReceipts == null) { throw Error( @@ -333,12 +333,14 @@ export function parseExecutionPayload( } (executionPayload as electra.ExecutionPayload).depositReceipts = depositReceipts.map(deserializeDepositReceipt); - if (exits == null) { + if (withdrawalRequests == null) { throw Error( - `exits missing for ${fork} >= electra executionPayload number=${executionPayload.blockNumber} hash=${data.blockHash}` + `withdrawalRequests missing for ${fork} >= electra executionPayload number=${executionPayload.blockNumber} hash=${data.blockHash}` ); } - (executionPayload as electra.ExecutionPayload).exits = exits.map(deserializeExecutionLayerExit); + (executionPayload as electra.ExecutionPayload).withdrawalRequests = withdrawalRequests.map( + deserializeExecutionLayerWithdrawalRequest + ); } return {executionPayload, executionPayloadValue, blobsBundle, shouldOverrideBuilder}; @@ -427,17 +429,23 @@ export function deserializeDepositReceipt(serialized: DepositReceiptRpc): electr } as electra.DepositReceipt; } -export function serializeExecutionLayerExit(exit: electra.ExecutionLayerExit): ExecutionLayerExitRpc { +export function serializeExecutionLayerWithdrawalRequest( + withdrawalRequest: electra.ExecutionLayerWithdrawalRequest +): ExecutionLayerWithdrawalRequestRpc { return { - sourceAddress: bytesToData(exit.sourceAddress), - validatorPubkey: bytesToData(exit.validatorPubkey), + sourceAddress: bytesToData(withdrawalRequest.sourceAddress), + validatorPubkey: bytesToData(withdrawalRequest.validatorPubkey), + amount: numToQuantity(withdrawalRequest.amount), }; } -export function deserializeExecutionLayerExit(exit: ExecutionLayerExitRpc): electra.ExecutionLayerExit { +export function deserializeExecutionLayerWithdrawalRequest( + withdrawalRequest: ExecutionLayerWithdrawalRequestRpc +): electra.ExecutionLayerWithdrawalRequest { return { - sourceAddress: dataToBytes(exit.sourceAddress, 20), - validatorPubkey: dataToBytes(exit.validatorPubkey, 48), + sourceAddress: dataToBytes(withdrawalRequest.sourceAddress, 20), + validatorPubkey: dataToBytes(withdrawalRequest.validatorPubkey, 48), + amount: quantityToNum(withdrawalRequest.amount), }; } @@ -447,7 +455,9 @@ export function deserializeExecutionPayloadBody(data: ExecutionPayloadBodyRpc | transactions: data.transactions.map((tran) => dataToBytes(tran, null)), withdrawals: data.withdrawals ? data.withdrawals.map(deserializeWithdrawal) : null, depositReceipts: data.depositReceipts ? data.depositReceipts.map(deserializeDepositReceipt) : null, - exits: data.exits ? data.exits.map(deserializeExecutionLayerExit) : null, + withdrawalRequests: data.withdrawalRequests + ? data.withdrawalRequests.map(deserializeExecutionLayerWithdrawalRequest) + : null, } : null; } @@ -458,7 +468,9 @@ export function serializeExecutionPayloadBody(data: ExecutionPayloadBody | null) transactions: data.transactions.map((tran) => bytesToData(tran)), withdrawals: data.withdrawals ? data.withdrawals.map(serializeWithdrawal) : null, depositReceipts: data.depositReceipts ? data.depositReceipts.map(serializeDepositReceipt) : null, - exits: data.exits ? data.exits.map(serializeExecutionLayerExit) : null, + withdrawalRequests: data.withdrawalRequests + ? data.withdrawalRequests.map(serializeExecutionLayerWithdrawalRequest) + : null, } : null; } diff --git a/packages/light-client/src/spec/utils.ts b/packages/light-client/src/spec/utils.ts index 8e6b3456a600..1b460dd39cf2 100644 --- a/packages/light-client/src/spec/utils.ts +++ b/packages/light-client/src/spec/utils.ts @@ -117,8 +117,8 @@ export function upgradeLightClientHeader( case ForkName.electra: (upgradedHeader as LightClientHeader).execution.depositReceiptsRoot = ssz.electra.LightClientHeader.fields.execution.fields.depositReceiptsRoot.defaultValue(); - (upgradedHeader as electra.LightClientHeader).execution.exitsRoot = - ssz.electra.LightClientHeader.fields.execution.fields.exitsRoot.defaultValue(); + (upgradedHeader as electra.LightClientHeader).execution.withdrawalRequestsRoot = + ssz.electra.LightClientHeader.fields.execution.fields.withdrawalRequestsRoot.defaultValue(); // Break if no further upgrades is required else fall through if (ForkSeq[targetFork] <= ForkSeq.electra) break; @@ -158,7 +158,7 @@ export function isValidLightClientHeader(config: ChainForkConfig, header: LightC if (epoch < config.ELECTRA_FORK_EPOCH) { if ( (header as LightClientHeader).execution.depositReceiptsRoot !== undefined || - (header as LightClientHeader).execution.exitsRoot !== undefined + (header as LightClientHeader).execution.withdrawalRequestsRoot !== undefined ) { return false; } diff --git a/packages/params/src/index.ts b/packages/params/src/index.ts index 3e56effc4138..b261e07959d0 100644 --- a/packages/params/src/index.ts +++ b/packages/params/src/index.ts @@ -95,7 +95,7 @@ export const { KZG_COMMITMENT_INCLUSION_PROOF_DEPTH, MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD, - MAX_EXECUTION_LAYER_EXITS, + MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD, MAX_ATTESTER_SLASHINGS_ELECTRA, MAX_ATTESTATIONS_ELECTRA, } = activePreset; diff --git a/packages/params/src/presets/mainnet.ts b/packages/params/src/presets/mainnet.ts index 27cb7640b2dd..5343966a43f4 100644 --- a/packages/params/src/presets/mainnet.ts +++ b/packages/params/src/presets/mainnet.ts @@ -121,7 +121,7 @@ export const mainnetPreset: BeaconPreset = { // ELECTRA MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 8192, - MAX_EXECUTION_LAYER_EXITS: 16, + MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 16, MAX_ATTESTER_SLASHINGS_ELECTRA: 1, MAX_ATTESTATIONS_ELECTRA: 8, }; diff --git a/packages/params/src/presets/minimal.ts b/packages/params/src/presets/minimal.ts index 022532a49e6f..e4938d501a51 100644 --- a/packages/params/src/presets/minimal.ts +++ b/packages/params/src/presets/minimal.ts @@ -122,7 +122,7 @@ export const minimalPreset: BeaconPreset = { // ELECTRA MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 4, - MAX_EXECUTION_LAYER_EXITS: 16, + MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: 16, MAX_ATTESTER_SLASHINGS_ELECTRA: 1, MAX_ATTESTATIONS_ELECTRA: 8, }; diff --git a/packages/params/src/types.ts b/packages/params/src/types.ts index 34f40a66707e..e5b85a9e2224 100644 --- a/packages/params/src/types.ts +++ b/packages/params/src/types.ts @@ -85,7 +85,7 @@ export type BeaconPreset = { // ELECTRA MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: number; - MAX_EXECUTION_LAYER_EXITS: number; + MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: number; MAX_ATTESTER_SLASHINGS_ELECTRA: number; MAX_ATTESTATIONS_ELECTRA: number; }; @@ -176,7 +176,7 @@ export const beaconPresetTypes: BeaconPresetTypes = { // ELECTRA MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: "number", - MAX_EXECUTION_LAYER_EXITS: "number", + MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: "number", MAX_ATTESTER_SLASHINGS_ELECTRA: "number", MAX_ATTESTATIONS_ELECTRA: "number", }; diff --git a/packages/state-transition/src/block/processExecutionLayerExit.ts b/packages/state-transition/src/block/processExecutionLayerWithdrawalRequest.ts similarity index 71% rename from packages/state-transition/src/block/processExecutionLayerExit.ts rename to packages/state-transition/src/block/processExecutionLayerWithdrawalRequest.ts index 5068d9af8667..cfe719bf57f3 100644 --- a/packages/state-transition/src/block/processExecutionLayerExit.ts +++ b/packages/state-transition/src/block/processExecutionLayerWithdrawalRequest.ts @@ -6,22 +6,33 @@ import {isActiveValidator} from "../util/index.js"; import {CachedBeaconStateElectra} from "../types.js"; import {initiateValidatorExit} from "./index.js"; +const FULL_EXIT_REQUEST_AMOUNT = 0; /** * Process execution layer exit messages and initiate exit incase they belong to a valid active validator * otherwise silent ignore. */ -export function processExecutionLayerExit(state: CachedBeaconStateElectra, exit: electra.ExecutionLayerExit): void { - const validator = isValidExecutionLayerExit(state, exit); - if (validator === null) { - return; - } +export function processExecutionLayerWithdrawalRequest( + state: CachedBeaconStateElectra, + withdrawalRequest: electra.ExecutionLayerWithdrawalRequest +): void { + const isFullExitRequest = withdrawalRequest.amount === FULL_EXIT_REQUEST_AMOUNT; + + if (isFullExitRequest) { + const validator = isValidExecutionLayerExit(state, withdrawalRequest); + if (validator === null) { + return; + } - initiateValidatorExit(state, validator); + initiateValidatorExit(state, validator); + } else { + // partial withdral request add codeblock + } } +// TODO electra : add pending withdrawal check before exit export function isValidExecutionLayerExit( state: CachedBeaconStateElectra, - exit: electra.ExecutionLayerExit + exit: electra.ExecutionLayerWithdrawalRequest ): CompositeViewDU | null { const {config, epochCtx} = state; const validatorIndex = epochCtx.getValidatorIndex(exit.validatorPubkey); diff --git a/packages/state-transition/src/block/processOperations.ts b/packages/state-transition/src/block/processOperations.ts index c4879da4aa71..228b4eef6fd3 100644 --- a/packages/state-transition/src/block/processOperations.ts +++ b/packages/state-transition/src/block/processOperations.ts @@ -8,7 +8,7 @@ import {processProposerSlashing} from "./processProposerSlashing.js"; import {processAttesterSlashing} from "./processAttesterSlashing.js"; import {processDeposit} from "./processDeposit.js"; import {processVoluntaryExit} from "./processVoluntaryExit.js"; -import {processExecutionLayerExit} from "./processExecutionLayerExit.js"; +import {processExecutionLayerWithdrawalRequest} from "./processExecutionLayerWithdrawalRequest.js"; import {processBlsToExecutionChange} from "./processBlsToExecutionChange.js"; import {processDepositReceipt} from "./processDepositReceipt.js"; import {ProcessBlockOpts} from "./types.js"; @@ -19,7 +19,7 @@ export { processAttestations, processDeposit, processVoluntaryExit, - processExecutionLayerExit, + processExecutionLayerWithdrawalRequest, processBlsToExecutionChange, processDepositReceipt, }; @@ -55,8 +55,8 @@ export function processOperations( processVoluntaryExit(state, voluntaryExit, opts.verifySignatures); } if (fork >= ForkSeq.electra) { - for (const elExit of (body as electra.BeaconBlockBody).executionPayload.exits) { - processExecutionLayerExit(state as CachedBeaconStateElectra, elExit); + for (const elWithdrawalRequest of (body as electra.BeaconBlockBody).executionPayload.withdrawalRequests) { + processExecutionLayerWithdrawalRequest(state as CachedBeaconStateElectra, elWithdrawalRequest); } } diff --git a/packages/state-transition/src/slot/upgradeStateToElectra.ts b/packages/state-transition/src/slot/upgradeStateToElectra.ts index 369ab19c447b..f41c37af94aa 100644 --- a/packages/state-transition/src/slot/upgradeStateToElectra.ts +++ b/packages/state-transition/src/slot/upgradeStateToElectra.ts @@ -20,7 +20,7 @@ export function upgradeStateToElectra(stateDeneb: CachedBeaconStateDeneb): Cache epoch: stateDeneb.epochCtx.epoch, }); - // latestExecutionPayloadHeader's depositReceiptsRoot and exitsRoot set to zeros by default + // latestExecutionPayloadHeader's depositReceiptsRoot and withdrawalRequestsRoot set to zeros by default // default value of depositReceiptsStartIndex is UNSET_DEPOSIT_RECEIPTS_START_INDEX stateElectra.depositReceiptsStartIndex = UNSET_DEPOSIT_RECEIPTS_START_INDEX; diff --git a/packages/state-transition/src/util/execution.ts b/packages/state-transition/src/util/execution.ts index 2975b84bf747..c7f0ec2f395c 100644 --- a/packages/state-transition/src/util/execution.ts +++ b/packages/state-transition/src/util/execution.ts @@ -174,9 +174,10 @@ export function executionPayloadToPayloadHeader(fork: ForkSeq, payload: Executio if (fork >= ForkSeq.electra) { (bellatrixPayloadFields as electra.ExecutionPayloadHeader).depositReceiptsRoot = ssz.electra.DepositReceipts.hashTreeRoot((payload as electra.ExecutionPayload).depositReceipts); - (bellatrixPayloadFields as electra.ExecutionPayloadHeader).exitsRoot = ssz.electra.ExecutionLayerExits.hashTreeRoot( - (payload as electra.ExecutionPayload).exits - ); + (bellatrixPayloadFields as electra.ExecutionPayloadHeader).withdrawalRequestsRoot = + ssz.electra.ExecutionLayerWithdrawalRequests.hashTreeRoot( + (payload as electra.ExecutionPayload).withdrawalRequests + ); } return bellatrixPayloadFields; diff --git a/packages/types/src/electra/sszTypes.ts b/packages/types/src/electra/sszTypes.ts index 3404baf04110..53195f30de11 100644 --- a/packages/types/src/electra/sszTypes.ts +++ b/packages/types/src/electra/sszTypes.ts @@ -16,7 +16,7 @@ import { MAX_COMMITTEES_PER_SLOT, MAX_ATTESTATIONS_ELECTRA, MAX_ATTESTER_SLASHINGS_ELECTRA, - MAX_EXECUTION_LAYER_EXITS, + MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD, } from "@lodestar/params"; import {ssz as primitiveSsz} from "../primitive/index.js"; import {ssz as phase0Ssz} from "../phase0/index.js"; @@ -117,20 +117,24 @@ export const DepositReceipt = new ContainerType( export const DepositReceipts = new ListCompositeType(DepositReceipt, MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD); -export const ExecutionLayerExit = new ContainerType( +export const ExecutionLayerWithdrawalRequest = new ContainerType( { sourceAddress: ExecutionAddress, validatorPubkey: BLSPubkey, + amount: UintNum64, }, - {typeName: "ExecutionLayerExit", jsonCase: "eth2"} + {typeName: "ExecutionLayerWithdrawalRequest", jsonCase: "eth2"} +); +export const ExecutionLayerWithdrawalRequests = new ListCompositeType( + ExecutionLayerWithdrawalRequest, + MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD ); -export const ExecutionLayerExits = new ListCompositeType(ExecutionLayerExit, MAX_EXECUTION_LAYER_EXITS); export const ExecutionPayload = new ContainerType( { ...denebSsz.ExecutionPayload.fields, depositReceipts: DepositReceipts, // New in ELECTRA - exits: ExecutionLayerExits, // New in ELECTRA + withdrawalRequests: ExecutionLayerWithdrawalRequests, // New in ELECTRA }, {typeName: "ExecutionPayload", jsonCase: "eth2"} ); @@ -139,7 +143,7 @@ export const ExecutionPayloadHeader = new ContainerType( { ...denebSsz.ExecutionPayloadHeader.fields, depositReceiptsRoot: Root, // New in ELECTRA - exitsRoot: Root, // New in ELECTRA + withdrawalRequestsRoot: Root, // New in ELECTRA }, {typeName: "ExecutionPayloadHeader", jsonCase: "eth2"} ); diff --git a/packages/types/src/electra/types.ts b/packages/types/src/electra/types.ts index 2925885b3af3..d1d0109e6ae6 100644 --- a/packages/types/src/electra/types.ts +++ b/packages/types/src/electra/types.ts @@ -12,8 +12,8 @@ export type SignedAggregateAndProof = ValueOf; export type DepositReceipts = ValueOf; -export type ExecutionLayerExit = ValueOf; -export type ExecutionLayerExits = ValueOf; +export type ExecutionLayerWithdrawalRequest = ValueOf; +export type ExecutionLayerWithdrawalRequests = ValueOf; export type ExecutionPayload = ValueOf; export type ExecutionPayloadHeader = ValueOf; diff --git a/packages/validator/src/util/params.ts b/packages/validator/src/util/params.ts index 1eda005b70c3..ff1c8c0fdc25 100644 --- a/packages/validator/src/util/params.ts +++ b/packages/validator/src/util/params.ts @@ -224,7 +224,7 @@ function getSpecCriticalParams(localConfig: ChainConfig): Record