From cfd97b921175108a9770565f6670c9996397b1ef Mon Sep 17 00:00:00 2001 From: NC Date: Fri, 21 Jun 2024 19:33:42 +0300 Subject: [PATCH] feat: add EL triggered consolidation and remove `ExecutionLayer` prefix (#6865) * init commit * Add consolidation request * fix lint * Remove ExecutionLayer prefix * Address comment * Fix verification logic * lint * Add todo --- .../chain/produceBlock/produceBlockBody.ts | 4 - .../src/execution/engine/payloadIdCache.ts | 2 +- .../beacon-node/src/execution/engine/types.ts | 33 ++---- .../test/spec/presets/operations.test.ts | 20 ++-- packages/params/src/index.ts | 2 +- packages/params/src/presets/mainnet.ts | 2 +- packages/params/src/presets/minimal.ts | 2 +- packages/params/src/types.ts | 4 +- .../src/block/processConsolidation.ts | 111 ------------------ .../src/block/processConsolidationRequest.ts | 74 ++++++++++++ .../src/block/processOperations.ts | 20 ++-- ...Request.ts => processWithdrawalRequest.ts} | 10 +- .../src/signatureSets/consolidation.ts | 51 -------- .../src/signatureSets/index.ts | 12 -- .../src/slot/upgradeStateToElectra.ts | 1 + .../state-transition/src/util/execution.ts | 4 +- packages/types/src/electra/sszTypes.ts | 44 +++---- packages/types/src/electra/types.ts | 10 +- packages/validator/src/util/params.ts | 2 +- .../test/unit/utils/interopConfigs.ts | 8 +- 20 files changed, 144 insertions(+), 272 deletions(-) delete mode 100644 packages/state-transition/src/block/processConsolidation.ts create mode 100644 packages/state-transition/src/block/processConsolidationRequest.ts rename packages/state-transition/src/block/{processExecutionLayerWithdrawalRequest.ts => processWithdrawalRequest.ts} (89%) delete mode 100644 packages/state-transition/src/signatureSets/consolidation.ts diff --git a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts index 05b6cb5e7d82..182220e11f1d 100644 --- a/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts +++ b/packages/beacon-node/src/chain/produceBlock/produceBlockBody.ts @@ -343,10 +343,6 @@ export async function produceBlockBody( } } - if (ForkSeq[fork] >= ForkSeq.electra) { - (blockBody as electra.BeaconBlockBody).consolidations = []; - } - Object.assign(logMeta, {executionPayloadValue}); this.logger.verbose("Produced beacon block body", logMeta); diff --git a/packages/beacon-node/src/execution/engine/payloadIdCache.ts b/packages/beacon-node/src/execution/engine/payloadIdCache.ts index f79c28582459..8cb5b4e4f8e9 100644 --- a/packages/beacon-node/src/execution/engine/payloadIdCache.ts +++ b/packages/beacon-node/src/execution/engine/payloadIdCache.ts @@ -26,7 +26,7 @@ export type DepositRequestV1 = { index: QUANTITY; }; -export type ExecutionLayerWithdrawalRequestV1 = { +export type WithdrawalRequestV1 = { sourceAddress: DATA; validatorPubkey: DATA; amount: QUANTITY; diff --git a/packages/beacon-node/src/execution/engine/types.ts b/packages/beacon-node/src/execution/engine/types.ts index c0f6058a72ee..b45c9fd9fa5c 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, DepositRequestV1, ExecutionLayerWithdrawalRequestV1} from "./payloadIdCache.js"; +import {WithdrawalV1, DepositRequestV1, WithdrawalRequestV1} from "./payloadIdCache.js"; /* eslint-disable @typescript-eslint/naming-convention */ @@ -125,14 +125,14 @@ export type ExecutionPayloadBodyRpc = { // currently there is a discepancy between EL and CL field name references for deposit requests // its likely CL receipt will be renamed to requests depositRequests: DepositRequestV1[] | null | undefined; - withdrawalRequests: ExecutionLayerWithdrawalRequestV1[] | null | undefined; + withdrawalRequests: WithdrawalRequestV1[] | null | undefined; }; export type ExecutionPayloadBody = { transactions: bellatrix.Transaction[]; withdrawals: capella.Withdrawals | null; depositRequests: electra.DepositRequests | null; - withdrawalRequests: electra.ExecutionLayerWithdrawalRequests | null; + withdrawalRequests: electra.WithdrawalRequests | null; }; export type ExecutionPayloadRpc = { @@ -155,7 +155,7 @@ export type ExecutionPayloadRpc = { excessBlobGas?: QUANTITY; // DENEB parentBeaconBlockRoot?: QUANTITY; // DENEB depositRequests?: DepositRequestRpc[]; // ELECTRA - withdrawalRequests?: ExecutionLayerWithdrawalRequestRpc[]; // ELECTRA + withdrawalRequests?: WithdrawalRequestRpc[]; // ELECTRA }; export type WithdrawalRpc = { @@ -166,7 +166,7 @@ export type WithdrawalRpc = { }; export type DepositRequestRpc = DepositRequestV1; -export type ExecutionLayerWithdrawalRequestRpc = ExecutionLayerWithdrawalRequestV1; +export type WithdrawalRequestRpc = WithdrawalRequestV1; export type VersionedHashesRpc = DATA[]; @@ -223,7 +223,7 @@ export function serializeExecutionPayload(fork: ForkName, data: ExecutionPayload if (ForkSeq[fork] >= ForkSeq.electra) { const {depositRequests, withdrawalRequests} = data as electra.ExecutionPayload; payload.depositRequests = depositRequests.map(serializeDepositRequest); - payload.withdrawalRequests = withdrawalRequests.map(serializeExecutionLayerWithdrawalRequest); + payload.withdrawalRequests = withdrawalRequests.map(serializeWithdrawalRequest); } return payload; @@ -327,9 +327,8 @@ export function parseExecutionPayload( `withdrawalRequests missing for ${fork} >= electra executionPayload number=${executionPayload.blockNumber} hash=${data.blockHash}` ); } - (executionPayload as electra.ExecutionPayload).withdrawalRequests = withdrawalRequests.map( - deserializeExecutionLayerWithdrawalRequest - ); + (executionPayload as electra.ExecutionPayload).withdrawalRequests = + withdrawalRequests.map(deserializeWithdrawalRequest); } return {executionPayload, executionPayloadValue, blobsBundle, shouldOverrideBuilder}; @@ -418,9 +417,7 @@ export function deserializeDepositRequest(serialized: DepositRequestRpc): electr } as electra.DepositRequest; } -export function serializeExecutionLayerWithdrawalRequest( - withdrawalRequest: electra.ExecutionLayerWithdrawalRequest -): ExecutionLayerWithdrawalRequestRpc { +export function serializeWithdrawalRequest(withdrawalRequest: electra.WithdrawalRequest): WithdrawalRequestRpc { return { sourceAddress: bytesToData(withdrawalRequest.sourceAddress), validatorPubkey: bytesToData(withdrawalRequest.validatorPubkey), @@ -428,9 +425,7 @@ export function serializeExecutionLayerWithdrawalRequest( }; } -export function deserializeExecutionLayerWithdrawalRequest( - withdrawalRequest: ExecutionLayerWithdrawalRequestRpc -): electra.ExecutionLayerWithdrawalRequest { +export function deserializeWithdrawalRequest(withdrawalRequest: WithdrawalRequestRpc): electra.WithdrawalRequest { return { sourceAddress: dataToBytes(withdrawalRequest.sourceAddress, 20), validatorPubkey: dataToBytes(withdrawalRequest.validatorPubkey, 48), @@ -444,9 +439,7 @@ export function deserializeExecutionPayloadBody(data: ExecutionPayloadBodyRpc | transactions: data.transactions.map((tran) => dataToBytes(tran, null)), withdrawals: data.withdrawals ? data.withdrawals.map(deserializeWithdrawal) : null, depositRequests: data.depositRequests ? data.depositRequests.map(deserializeDepositRequest) : null, - withdrawalRequests: data.withdrawalRequests - ? data.withdrawalRequests.map(deserializeExecutionLayerWithdrawalRequest) - : null, + withdrawalRequests: data.withdrawalRequests ? data.withdrawalRequests.map(deserializeWithdrawalRequest) : null, } : null; } @@ -457,9 +450,7 @@ export function serializeExecutionPayloadBody(data: ExecutionPayloadBody | null) transactions: data.transactions.map((tran) => bytesToData(tran)), withdrawals: data.withdrawals ? data.withdrawals.map(serializeWithdrawal) : null, depositRequests: data.depositRequests ? data.depositRequests.map(serializeDepositRequest) : null, - withdrawalRequests: data.withdrawalRequests - ? data.withdrawalRequests.map(serializeExecutionLayerWithdrawalRequest) - : null, + withdrawalRequests: data.withdrawalRequests ? data.withdrawalRequests.map(serializeWithdrawalRequest) : null, } : null; } diff --git a/packages/beacon-node/test/spec/presets/operations.test.ts b/packages/beacon-node/test/spec/presets/operations.test.ts index 552e0cfa44b6..47895540ac1a 100644 --- a/packages/beacon-node/test/spec/presets/operations.test.ts +++ b/packages/beacon-node/test/spec/presets/operations.test.ts @@ -92,20 +92,14 @@ const operationFns: Record> = blockFns.processWithdrawals(ForkSeq.capella, state as CachedBeaconStateCapella, testCase.execution_payload); }, - consolidation: (state, testCase: {consolidation: electra.SignedConsolidation}) => { - blockFns.processConsolidation(state as CachedBeaconStateElectra, testCase.consolidation); + withdrawal_request: (state, testCase: {withdrawal_request: electra.WithdrawalRequest}) => { + blockFns.processWithdrawalRequest(ForkSeq.electra, state as CachedBeaconStateElectra, testCase.withdrawal_request); }, - execution_layer_withdrawal_request: ( - state, - testCase: {execution_layer_withdrawal_request: electra.ExecutionLayerWithdrawalRequest} - ) => { - blockFns.processExecutionLayerWithdrawalRequest( - ForkSeq.electra, - state as CachedBeaconStateElectra, - testCase.execution_layer_withdrawal_request - ); + consolidation_request: (state, testCase: {consolidation_request: electra.ConsolidationRequest}) => { + blockFns.processConsolidationRequest(state as CachedBeaconStateElectra, testCase.consolidation_request); }, + }; export type BlockProcessFn = (state: T, testCase: any) => void; @@ -156,8 +150,8 @@ const operations: TestRunnerFn = (fork, // Capella address_change: ssz.capella.SignedBLSToExecutionChange, // Electra - consolidation: ssz.electra.SignedConsolidation, - execution_layer_withdrawal_request: ssz.electra.ExecutionLayerWithdrawalRequest, + withdrawal_request: ssz.electra.WithdrawalRequest, + consolidation_request: ssz.electra.ConsolidationRequest, }, shouldError: (testCase) => testCase.post === undefined, getExpected: (testCase) => testCase.post, diff --git a/packages/params/src/index.ts b/packages/params/src/index.ts index 0c2e3b34794b..a3dfb65da350 100644 --- a/packages/params/src/index.ts +++ b/packages/params/src/index.ts @@ -100,7 +100,7 @@ export const { PENDING_PARTIAL_WITHDRAWALS_LIMIT, PENDING_CONSOLIDATIONS_LIMIT, MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA, - MAX_CONSOLIDATIONS, + MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD, MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD, diff --git a/packages/params/src/presets/mainnet.ts b/packages/params/src/presets/mainnet.ts index 2495f7ef97a1..072936492bd3 100644 --- a/packages/params/src/presets/mainnet.ts +++ b/packages/params/src/presets/mainnet.ts @@ -133,6 +133,6 @@ export const mainnetPreset: BeaconPreset = { PENDING_BALANCE_DEPOSITS_LIMIT: 134217728, PENDING_PARTIAL_WITHDRAWALS_LIMIT: 134217728, PENDING_CONSOLIDATIONS_LIMIT: 262144, - MAX_CONSOLIDATIONS: 1, + MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: 1, WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA: 4096, }; diff --git a/packages/params/src/presets/minimal.ts b/packages/params/src/presets/minimal.ts index 8e71407965d7..4a1e4af50665 100644 --- a/packages/params/src/presets/minimal.ts +++ b/packages/params/src/presets/minimal.ts @@ -134,6 +134,6 @@ export const minimalPreset: BeaconPreset = { PENDING_BALANCE_DEPOSITS_LIMIT: 134217728, PENDING_PARTIAL_WITHDRAWALS_LIMIT: 64, PENDING_CONSOLIDATIONS_LIMIT: 64, - MAX_CONSOLIDATIONS: 1, + MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: 1, WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA: 4096, }; diff --git a/packages/params/src/types.ts b/packages/params/src/types.ts index dffd98518006..14e8f8c172d8 100644 --- a/packages/params/src/types.ts +++ b/packages/params/src/types.ts @@ -95,7 +95,7 @@ export type BeaconPreset = { PENDING_BALANCE_DEPOSITS_LIMIT: number; PENDING_PARTIAL_WITHDRAWALS_LIMIT: number; PENDING_CONSOLIDATIONS_LIMIT: number; - MAX_CONSOLIDATIONS: number; + MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: number; WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA: number; }; @@ -195,7 +195,7 @@ export const beaconPresetTypes: BeaconPresetTypes = { PENDING_BALANCE_DEPOSITS_LIMIT: "number", PENDING_PARTIAL_WITHDRAWALS_LIMIT: "number", PENDING_CONSOLIDATIONS_LIMIT: "number", - MAX_CONSOLIDATIONS: "number", + MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: "number", WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA: "number", }; diff --git a/packages/state-transition/src/block/processConsolidation.ts b/packages/state-transition/src/block/processConsolidation.ts deleted file mode 100644 index 846b0e2521b4..000000000000 --- a/packages/state-transition/src/block/processConsolidation.ts +++ /dev/null @@ -1,111 +0,0 @@ -import {toHexString} from "@chainsafe/ssz"; -import {electra, ssz} from "@lodestar/types"; -import {FAR_FUTURE_EPOCH, MIN_ACTIVATION_BALANCE, PENDING_CONSOLIDATIONS_LIMIT} from "@lodestar/params"; -import {verifyConsolidationSignature} from "../signatureSets/index.js"; - -import {CachedBeaconStateElectra} from "../types.js"; -import {getConsolidationChurnLimit, isActiveValidator} from "../util/validator.js"; -import {hasExecutionWithdrawalCredential} from "../util/electra.js"; -import {computeConsolidationEpochAndUpdateChurn} from "../util/epoch.js"; - -export function processConsolidation( - state: CachedBeaconStateElectra, - signedConsolidation: electra.SignedConsolidation -): void { - assertValidConsolidation(state, signedConsolidation); - - // Initiate source validator exit and append pending consolidation - const {sourceIndex, targetIndex} = signedConsolidation.message; - const sourceValidator = state.validators.get(sourceIndex); - - const exitEpoch = computeConsolidationEpochAndUpdateChurn(state, BigInt(sourceValidator.effectiveBalance)); - sourceValidator.exitEpoch = exitEpoch; - sourceValidator.withdrawableEpoch = exitEpoch + state.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY; - - const pendingConsolidation = ssz.electra.PendingConsolidation.toViewDU({ - sourceIndex, - targetIndex, - }); - state.pendingConsolidations.push(pendingConsolidation); -} - -function assertValidConsolidation( - state: CachedBeaconStateElectra, - signedConsolidation: electra.SignedConsolidation -): void { - // If the pending consolidations queue is full, no consolidations are allowed in the block - if (state.pendingConsolidations.length >= PENDING_CONSOLIDATIONS_LIMIT) { - throw new Error("Pending consolidation queue is full"); - } - - // If there is too little available consolidation churn limit, no consolidations are allowed in the block - // assert get_consolidation_churn_limit(state) > MIN_ACTIVATION_BALANCE - if (getConsolidationChurnLimit(state) <= MIN_ACTIVATION_BALANCE) { - throw new Error(`Consolidation churn limit too low. consolidationChurnLimit=${getConsolidationChurnLimit(state)}`); - } - - const consolidation = signedConsolidation.message; - const {sourceIndex, targetIndex} = consolidation; - - // Verify that source != target, so a consolidation cannot be used as an exit. - if (sourceIndex === targetIndex) { - throw new Error( - `Consolidation source and target index cannot be the same: sourceIndex=${sourceIndex} targetIndex=${targetIndex}` - ); - } - - const sourceValidator = state.validators.getReadonly(sourceIndex); - const targetValidator = state.validators.getReadonly(targetIndex); - const currentEpoch = state.epochCtx.epoch; - - // Verify the source and the target are active - if (!isActiveValidator(sourceValidator, currentEpoch)) { - throw new Error(`Consolidation source validator is not active: sourceIndex=${sourceIndex}`); - } - - if (!isActiveValidator(targetValidator, currentEpoch)) { - throw new Error(`Consolidation target validator is not active: targetIndex=${targetIndex}`); - } - - // Verify exits for source and target have not been initiated - if (sourceValidator.exitEpoch !== FAR_FUTURE_EPOCH) { - throw new Error(`Consolidation source validator has initialized exit: sourceIndex=${sourceIndex}`); - } - if (targetValidator.exitEpoch !== FAR_FUTURE_EPOCH) { - throw new Error(`Consolidation target validator has initialized exit: targetIndex=${targetIndex}`); - } - - // Consolidations must specify an epoch when they become valid; they are not valid before then - if (currentEpoch < consolidation.epoch) { - throw new Error( - `Consolidation epoch is after the current epoch: consolidationEpoch=${consolidation.epoch} currentEpoch=${currentEpoch}` - ); - } - - // Verify the source and the target have Execution layer withdrawal credentials - if (!hasExecutionWithdrawalCredential(sourceValidator.withdrawalCredentials)) { - throw new Error( - `Consolidation source validator does not have execution withdrawal credentials: sourceIndex=${sourceIndex}` - ); - } - if (!hasExecutionWithdrawalCredential(targetValidator.withdrawalCredentials)) { - throw new Error( - `Consolidation target validator does not have execution withdrawal credentials: targetIndex=${targetIndex}` - ); - } - - // Verify the same withdrawal address - const sourceWithdrawalAddress = toHexString(sourceValidator.withdrawalCredentials.subarray(12)); - const targetWithdrawalAddress = toHexString(targetValidator.withdrawalCredentials.subarray(12)); - - if (sourceWithdrawalAddress !== targetWithdrawalAddress) { - throw new Error( - `Consolidation source and target withdrawal address are different: source: ${sourceWithdrawalAddress} target: ${targetWithdrawalAddress}` - ); - } - - // Verify consolidation is signed by the source and the target - if (!verifyConsolidationSignature(state, signedConsolidation)) { - throw new Error("Consolidation not valid"); - } -} diff --git a/packages/state-transition/src/block/processConsolidationRequest.ts b/packages/state-transition/src/block/processConsolidationRequest.ts new file mode 100644 index 000000000000..4b6a65e036ad --- /dev/null +++ b/packages/state-transition/src/block/processConsolidationRequest.ts @@ -0,0 +1,74 @@ +import {electra, ssz} from "@lodestar/types"; +import {FAR_FUTURE_EPOCH, MIN_ACTIVATION_BALANCE, PENDING_CONSOLIDATIONS_LIMIT} from "@lodestar/params"; + +import {CachedBeaconStateElectra} from "../types.js"; +import {getConsolidationChurnLimit, isActiveValidator} from "../util/validator.js"; +import {hasExecutionWithdrawalCredential} from "../util/electra.js"; +import {computeConsolidationEpochAndUpdateChurn} from "../util/epoch.js"; + +export function processConsolidationRequest( + state: CachedBeaconStateElectra, + consolidationRequest: electra.ConsolidationRequest +): void { + + // If the pending consolidations queue is full, consolidation requests are ignored + if (state.pendingConsolidations.length >= PENDING_CONSOLIDATIONS_LIMIT) { + return; + } + + // If there is too little available consolidation churn limit, consolidation requests are ignored + if (getConsolidationChurnLimit(state) <= MIN_ACTIVATION_BALANCE) { + return; + } + + const {sourcePubkey, targetPubkey} = consolidationRequest; + const sourceIndex = state.epochCtx.getValidatorIndex(sourcePubkey); + const targetIndex = state.epochCtx.getValidatorIndex(targetPubkey); + + if (sourceIndex === undefined || targetIndex === undefined) { + return; + } + + // Verify that source != target, so a consolidation cannot be used as an exit. + if (sourceIndex === targetIndex){ + return; + } + + const sourceValidator = state.validators.getReadonly(sourceIndex); + const targetValidator = state.validators.getReadonly(targetIndex); + const sourceWithdrawalAddress = sourceValidator.withdrawalCredentials.subarray(12); + const currentEpoch = state.epochCtx.epoch; + + // Verify withdrawal credentials + if ( + !hasExecutionWithdrawalCredential(sourceValidator.withdrawalCredentials) || + !hasExecutionWithdrawalCredential(targetValidator.withdrawalCredentials) + ) { + return; + } + + if (Buffer.compare(sourceWithdrawalAddress, consolidationRequest.sourceAddress) !== 0) { + return; + } + + // Verify the source and the target are active + if (!isActiveValidator(sourceValidator, currentEpoch) || !isActiveValidator(targetValidator, currentEpoch)) { + return; + } + + // Verify exits for source and target have not been initiated + if (sourceValidator.exitEpoch !== FAR_FUTURE_EPOCH || targetValidator.exitEpoch !== FAR_FUTURE_EPOCH) { + return; + } + + // TODO Electra: See if we can get rid of big int + const exitEpoch = computeConsolidationEpochAndUpdateChurn(state, BigInt(sourceValidator.effectiveBalance)); + sourceValidator.exitEpoch = exitEpoch; + sourceValidator.withdrawableEpoch = exitEpoch + state.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY; + + const pendingConsolidation = ssz.electra.PendingConsolidation.toViewDU({ + sourceIndex, + targetIndex, + }); + state.pendingConsolidations.push(pendingConsolidation); +} \ No newline at end of file diff --git a/packages/state-transition/src/block/processOperations.ts b/packages/state-transition/src/block/processOperations.ts index a2bd691337ad..8290771d2d8e 100644 --- a/packages/state-transition/src/block/processOperations.ts +++ b/packages/state-transition/src/block/processOperations.ts @@ -9,10 +9,10 @@ import {processAttesterSlashing} from "./processAttesterSlashing.js"; import {processDeposit} from "./processDeposit.js"; import {processVoluntaryExit} from "./processVoluntaryExit.js"; import {processBlsToExecutionChange} from "./processBlsToExecutionChange.js"; -import {processExecutionLayerWithdrawalRequest} from "./processExecutionLayerWithdrawalRequest.js"; +import {processWithdrawalRequest} from "./processWithdrawalRequest.js"; import {processDepositRequest} from "./processDepositRequest.js"; import {ProcessBlockOpts} from "./types.js"; -import {processConsolidation} from "./processConsolidation.js"; +import {processConsolidationRequest} from "./processConsolidationRequest.js"; export { processProposerSlashing, @@ -20,10 +20,10 @@ export { processAttestations, processDeposit, processVoluntaryExit, - processExecutionLayerWithdrawalRequest, + processWithdrawalRequest, processBlsToExecutionChange, processDepositRequest, - processConsolidation, + processConsolidationRequest, }; export function processOperations( @@ -67,16 +67,16 @@ export function processOperations( const stateElectra = state as CachedBeaconStateElectra; const bodyElectra = body as electra.BeaconBlockBody; - for (const elWithdrawalRequest of bodyElectra.executionPayload.withdrawalRequests) { - processExecutionLayerWithdrawalRequest(fork, state as CachedBeaconStateElectra, elWithdrawalRequest); + for (const depositRequest of bodyElectra.executionPayload.depositReceipts) { + processDepositRequest(fork, stateElectra, depositRequest); } - for (const depositRequest of bodyElectra.executionPayload.depositRequests) { - processDepositRequest(fork, stateElectra, depositRequest); + for (const elWithdrawalRequest of bodyElectra.executionPayload.withdrawalRequests) { + processWithdrawalRequest(fork, state as CachedBeaconStateElectra, elWithdrawalRequest); } - for (const consolidation of bodyElectra.consolidations) { - processConsolidation(stateElectra, consolidation); + for (const elConsolidationRequest of bodyElectra.executionPayload.consolidationRequests) { + processConsolidationRequest(stateElectra, elConsolidationRequest); } } } diff --git a/packages/state-transition/src/block/processExecutionLayerWithdrawalRequest.ts b/packages/state-transition/src/block/processWithdrawalRequest.ts similarity index 89% rename from packages/state-transition/src/block/processExecutionLayerWithdrawalRequest.ts rename to packages/state-transition/src/block/processWithdrawalRequest.ts index e02129a09d05..cff1ea03bd80 100644 --- a/packages/state-transition/src/block/processExecutionLayerWithdrawalRequest.ts +++ b/packages/state-transition/src/block/processWithdrawalRequest.ts @@ -14,12 +14,12 @@ import {getPendingBalanceToWithdraw, isActiveValidator} from "../util/validator. import {computeExitEpochAndUpdateChurn} from "../util/epoch.js"; import {initiateValidatorExit} from "./initiateValidatorExit.js"; -export function processExecutionLayerWithdrawalRequest( +export function processWithdrawalRequest( fork: ForkSeq, state: CachedBeaconStateElectra, - executionLayerWithdrawalRequest: electra.ExecutionLayerWithdrawalRequest + withdrawalRequest: electra.WithdrawalRequest ): void { - const amount = Number(executionLayerWithdrawalRequest.amount); + const amount = Number(withdrawalRequest.amount); const {pendingPartialWithdrawals, validators, epochCtx} = state; // no need to use unfinalized pubkey cache from 6110 as validator won't be active anyway const {pubkey2index, config} = epochCtx; @@ -32,13 +32,13 @@ export function processExecutionLayerWithdrawalRequest( // bail out if validator is not in beacon state // note that we don't need to check for 6110 unfinalized vals as they won't be eligible for withdraw/exit anyway - const validatorIndex = pubkey2index.get(executionLayerWithdrawalRequest.validatorPubkey); + const validatorIndex = pubkey2index.get(withdrawalRequest.validatorPubkey); if (validatorIndex === undefined) { return; } const validator = validators.get(validatorIndex); - if (!isValidatorEligibleForWithdrawOrExit(validator, executionLayerWithdrawalRequest.sourceAddress, state)) { + if (!isValidatorEligibleForWithdrawOrExit(validator, withdrawalRequest.sourceAddress, state)) { return; } diff --git a/packages/state-transition/src/signatureSets/consolidation.ts b/packages/state-transition/src/signatureSets/consolidation.ts deleted file mode 100644 index 0aef47d417a5..000000000000 --- a/packages/state-transition/src/signatureSets/consolidation.ts +++ /dev/null @@ -1,51 +0,0 @@ -import {DOMAIN_CONSOLIDATION, ForkName} from "@lodestar/params"; -import {electra, ssz} from "@lodestar/types"; - -import { - computeSigningRoot, - createAggregateSignatureSetFromComponents, - ISignatureSet, - verifySignatureSet, -} from "../util/index.js"; -import {CachedBeaconStateElectra} from "../types.js"; - -export function verifyConsolidationSignature( - state: CachedBeaconStateElectra, - signedConsolidation: electra.SignedConsolidation -): boolean { - return verifySignatureSet(getConsolidationSignatureSet(state, signedConsolidation)); -} - -/** - * Extract signatures to allow validating all block signatures at once - */ -export function getConsolidationSignatureSet( - state: CachedBeaconStateElectra, - signedConsolidation: electra.SignedConsolidation -): ISignatureSet { - const {config} = state; - const {index2pubkey} = state.epochCtx; // TODO Electra: Use 6110 pubkey cache - const {sourceIndex, targetIndex} = signedConsolidation.message; - const sourcePubkey = index2pubkey[sourceIndex]; - const targetPubkey = index2pubkey[targetIndex]; - - // signatureFork for signing domain is fixed - const signatureFork = ForkName.phase0; - const domain = config.getDomainAtFork(signatureFork, DOMAIN_CONSOLIDATION); - const signingRoot = computeSigningRoot(ssz.electra.Consolidation, signedConsolidation.message, domain); - - return createAggregateSignatureSetFromComponents( - [sourcePubkey, targetPubkey], - signingRoot, - signedConsolidation.signature - ); -} - -export function getConsolidationSignatureSets( - state: CachedBeaconStateElectra, - signedBlock: electra.SignedBeaconBlock -): ISignatureSet[] { - return signedBlock.message.body.consolidations.map((consolidation) => - getConsolidationSignatureSet(state, consolidation) - ); -} diff --git a/packages/state-transition/src/signatureSets/index.ts b/packages/state-transition/src/signatureSets/index.ts index 5f063235735c..eae1d434a121 100644 --- a/packages/state-transition/src/signatureSets/index.ts +++ b/packages/state-transition/src/signatureSets/index.ts @@ -10,7 +10,6 @@ import {getBlockProposerSignatureSet} from "./proposer.js"; import {getRandaoRevealSignatureSet} from "./randao.js"; import {getVoluntaryExitsSignatureSets} from "./voluntaryExits.js"; import {getBlsToExecutionChangeSignatureSets} from "./blsToExecutionChange.js"; -import {getConsolidationSignatureSets} from "./consolidation.js"; export * from "./attesterSlashings.js"; export * from "./indexedAttestation.js"; @@ -19,7 +18,6 @@ export * from "./proposerSlashings.js"; export * from "./randao.js"; export * from "./voluntaryExits.js"; export * from "./blsToExecutionChange.js"; -export * from "./consolidation.js"; /** * Includes all signatures on the block (except the deposit signatures) for verification. @@ -71,15 +69,5 @@ export function getBlockSignatureSets( } } - if (fork >= ForkSeq.electra) { - const consolidationSignatureSets = getConsolidationSignatureSets( - state as CachedBeaconStateElectra, - signedBlock as electra.SignedBeaconBlock - ); - if (consolidationSignatureSets.length > 0) { - signatureSets.push(...consolidationSignatureSets); - } - } - return signatureSets; } diff --git a/packages/state-transition/src/slot/upgradeStateToElectra.ts b/packages/state-transition/src/slot/upgradeStateToElectra.ts index 3d77366cbe57..7031aaa76fce 100644 --- a/packages/state-transition/src/slot/upgradeStateToElectra.ts +++ b/packages/state-transition/src/slot/upgradeStateToElectra.ts @@ -52,6 +52,7 @@ export function upgradeStateToElectra(stateDeneb: CachedBeaconStateDeneb): Cache ...stateElectraCloned.latestExecutionPayloadHeader.toValue(), depositRequestsRoot: ssz.Root.defaultValue(), withdrawalRequestsRoot: ssz.Root.defaultValue(), + consolidationRequestsRoot: ssz.Root.defaultValue(), }); stateElectraView.nextWithdrawalIndex = stateDeneb.nextWithdrawalIndex; stateElectraView.nextWithdrawalValidatorIndex = stateDeneb.nextWithdrawalValidatorIndex; diff --git a/packages/state-transition/src/util/execution.ts b/packages/state-transition/src/util/execution.ts index 2c55dc61a502..2f9d5ae0f6d7 100644 --- a/packages/state-transition/src/util/execution.ts +++ b/packages/state-transition/src/util/execution.ts @@ -175,9 +175,7 @@ export function executionPayloadToPayloadHeader(fork: ForkSeq, payload: Executio (bellatrixPayloadFields as electra.ExecutionPayloadHeader).depositRequestsRoot = ssz.electra.DepositRequests.hashTreeRoot((payload as electra.ExecutionPayload).depositRequests); (bellatrixPayloadFields as electra.ExecutionPayloadHeader).withdrawalRequestsRoot = - ssz.electra.ExecutionLayerWithdrawalRequests.hashTreeRoot( - (payload as electra.ExecutionPayload).withdrawalRequests - ); + ssz.electra.WithdrawalRequests.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 ab9cbbaacd80..f02a1a27aefe 100644 --- a/packages/types/src/electra/sszTypes.ts +++ b/packages/types/src/electra/sszTypes.ts @@ -17,7 +17,7 @@ import { MAX_ATTESTATIONS_ELECTRA, MAX_ATTESTER_SLASHINGS_ELECTRA, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD, - MAX_CONSOLIDATIONS, + MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD, PENDING_BALANCE_DEPOSITS_LIMIT, PENDING_PARTIAL_WITHDRAWALS_LIMIT, PENDING_CONSOLIDATIONS_LIMIT, @@ -125,24 +125,34 @@ export const DepositRequest = new ContainerType( export const DepositRequests = new ListCompositeType(DepositRequest, MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD); -export const ExecutionLayerWithdrawalRequest = new ContainerType( +export const WithdrawalRequest = new ContainerType( { sourceAddress: ExecutionAddress, validatorPubkey: BLSPubkey, amount: UintNum64, }, - {typeName: "ExecutionLayerWithdrawalRequest", jsonCase: "eth2"} + {typeName: "WithdrawalRequest", jsonCase: "eth2"} ); -export const ExecutionLayerWithdrawalRequests = new ListCompositeType( - ExecutionLayerWithdrawalRequest, - MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD +export const WithdrawalRequests = new ListCompositeType(WithdrawalRequest, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD); +export const ConsolidationRequest = new ContainerType( + { + sourceAddress: ExecutionAddress, + sourcePubkey: BLSPubkey, + targetPubkey: BLSPubkey, + }, + {typeName: "ConsolidationRequest", jsonCase: "eth2"} +); +export const ConsolidationRequests = new ListCompositeType( + ConsolidationRequest, + MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD ); export const ExecutionPayload = new ContainerType( { ...denebSsz.ExecutionPayload.fields, depositRequests: DepositRequests, // New in ELECTRA - withdrawalRequests: ExecutionLayerWithdrawalRequests, // New in ELECTRA + withdrawalRequests: WithdrawalRequests, // New in ELECTRA + consolidationRequests: ConsolidationRequests, // [New in Electra] }, {typeName: "ExecutionPayload", jsonCase: "eth2"} ); @@ -152,27 +162,11 @@ export const ExecutionPayloadHeader = new ContainerType( ...denebSsz.ExecutionPayloadHeader.fields, depositRequestsRoot: Root, // New in ELECTRA withdrawalRequestsRoot: Root, // New in ELECTRA + consolidationRequestsRoot: Root, // New in ELECTRA }, {typeName: "ExecutionPayloadHeader", jsonCase: "eth2"} ); -export const Consolidation = new ContainerType( - { - sourceIndex: ValidatorIndex, - targetIndex: ValidatorIndex, - epoch: Epoch, - }, - {typeName: "Consolidation", jsonCase: "eth2"} -); - -export const SignedConsolidation = new ContainerType( - { - message: Consolidation, - signature: BLSSignature, - }, - {typeName: "SignedConsolidation", jsonCase: "eth2"} -); - // We have to preserve Fields ordering while changing the type of ExecutionPayload export const BeaconBlockBody = new ContainerType( { @@ -188,7 +182,6 @@ export const BeaconBlockBody = new ContainerType( executionPayload: ExecutionPayload, // Modified in ELECTRA blsToExecutionChanges: capellaSsz.BeaconBlockBody.fields.blsToExecutionChanges, blobKzgCommitments: denebSsz.BeaconBlockBody.fields.blobKzgCommitments, - consolidations: new ListCompositeType(SignedConsolidation, MAX_CONSOLIDATIONS), // [New in Electra] }, {typeName: "BeaconBlockBody", jsonCase: "eth2", cachePermanentRootStruct: true} ); @@ -223,7 +216,6 @@ export const BlindedBeaconBlockBody = new ContainerType( executionPayloadHeader: ExecutionPayloadHeader, // Modified in ELECTRA blsToExecutionChanges: capellaSsz.BeaconBlockBody.fields.blsToExecutionChanges, blobKzgCommitments: denebSsz.BeaconBlockBody.fields.blobKzgCommitments, - consolidations: new ListCompositeType(SignedConsolidation, MAX_CONSOLIDATIONS), // [New in Electra] }, {typeName: "BlindedBeaconBlockBody", jsonCase: "eth2", cachePermanentRootStruct: true} ); diff --git a/packages/types/src/electra/types.ts b/packages/types/src/electra/types.ts index 994e5a732243..728ffc56ac83 100644 --- a/packages/types/src/electra/types.ts +++ b/packages/types/src/electra/types.ts @@ -12,8 +12,11 @@ export type SignedAggregateAndProof = ValueOf; export type DepositRequests = ValueOf; -export type ExecutionLayerWithdrawalRequest = ValueOf; -export type ExecutionLayerWithdrawalRequests = ValueOf; +export type WithdrawalRequest = ValueOf; +export type WithdrawalRequests = ValueOf; + +export type ConsolidationRequest = ValueOf; +export type ConsolidationRequests = ValueOf; export type ExecutionPayload = ValueOf; export type ExecutionPayloadHeader = ValueOf; @@ -43,9 +46,6 @@ export type LightClientFinalityUpdate = ValueOf; export type LightClientStore = ValueOf; -export type Consolidation = ValueOf; -export type SignedConsolidation = ValueOf; - export type PendingBalanceDeposit = ValueOf; export type PendingPartialWithdrawal = ValueOf; export type PendingConsolidation = ValueOf; diff --git a/packages/validator/src/util/params.ts b/packages/validator/src/util/params.ts index 3a497555361a..d5369515d47b 100644 --- a/packages/validator/src/util/params.ts +++ b/packages/validator/src/util/params.ts @@ -234,7 +234,7 @@ function getSpecCriticalParams(localConfig: ChainConfig): Record