From d584aed680aafedec56e19a77162a847bbd21a33 Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Mon, 20 Jan 2025 21:30:44 +0000 Subject: [PATCH] fix: consistently check no pending withdrawals when processing voluntary exit (#7379) * fix: consistently check no pending withdrawals when processing voluntary exit * Lint --- .../beacon-node/src/chain/opPools/opPool.ts | 2 +- .../src/chain/validation/voluntaryExit.ts | 2 +- .../src/block/processVoluntaryExit.ts | 27 ++++++------------- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/packages/beacon-node/src/chain/opPools/opPool.ts b/packages/beacon-node/src/chain/opPools/opPool.ts index 5eec1597a24e..819873d2e835 100644 --- a/packages/beacon-node/src/chain/opPools/opPool.ts +++ b/packages/beacon-node/src/chain/opPools/opPool.ts @@ -247,7 +247,7 @@ export class OpPool { for (const voluntaryExit of this.voluntaryExits.values()) { if ( !toBeSlashedIndices.has(voluntaryExit.message.validatorIndex) && - isValidVoluntaryExit(state, voluntaryExit, false) && + isValidVoluntaryExit(stateFork, state, voluntaryExit, false) && // Signature validation is skipped in `isValidVoluntaryExit(,,false)` since it was already validated in gossip // However we must make sure that the signature fork is the same, or it will become invalid if included through // a future fork. diff --git a/packages/beacon-node/src/chain/validation/voluntaryExit.ts b/packages/beacon-node/src/chain/validation/voluntaryExit.ts index 79da31084a36..60ee133e4b69 100644 --- a/packages/beacon-node/src/chain/validation/voluntaryExit.ts +++ b/packages/beacon-node/src/chain/validation/voluntaryExit.ts @@ -43,7 +43,7 @@ async function validateVoluntaryExit( // [REJECT] All of the conditions within process_voluntary_exit pass validation. // verifySignature = false, verified in batch below - if (!isValidVoluntaryExit(state, voluntaryExit, false)) { + if (!isValidVoluntaryExit(chain.config.getForkSeq(state.slot), state, voluntaryExit, false)) { throw new VoluntaryExitError(GossipAction.REJECT, { code: VoluntaryExitErrorCode.INVALID, }); diff --git a/packages/state-transition/src/block/processVoluntaryExit.ts b/packages/state-transition/src/block/processVoluntaryExit.ts index a0a0271a0d1e..3cc6afb958f8 100644 --- a/packages/state-transition/src/block/processVoluntaryExit.ts +++ b/packages/state-transition/src/block/processVoluntaryExit.ts @@ -1,5 +1,5 @@ import {FAR_FUTURE_EPOCH, ForkSeq} from "@lodestar/params"; -import {phase0} from "@lodestar/types"; +import {ValidatorIndex, phase0} from "@lodestar/types"; import {verifyVoluntaryExitSignature} from "../signatureSets/index.js"; import {CachedBeaconStateAllForks, CachedBeaconStateElectra} from "../types.js"; import {getPendingBalanceToWithdraw, isActiveValidator} from "../util/index.js"; @@ -16,11 +16,7 @@ export function processVoluntaryExit( signedVoluntaryExit: phase0.SignedVoluntaryExit, verifySignature = true ): void { - const isValidExit = - fork >= ForkSeq.electra - ? isValidVoluntaryExitElectra(state as CachedBeaconStateElectra, signedVoluntaryExit, verifySignature) - : isValidVoluntaryExit(state, signedVoluntaryExit, verifySignature); - if (!isValidExit) { + if (!isValidVoluntaryExit(fork, state, signedVoluntaryExit, verifySignature)) { throw Error(`Invalid voluntary exit at forkSeq=${fork}`); } @@ -29,6 +25,7 @@ export function processVoluntaryExit( } export function isValidVoluntaryExit( + fork: ForkSeq, state: CachedBeaconStateAllForks, signedVoluntaryExit: phase0.SignedVoluntaryExit, verifySignature = true @@ -47,20 +44,12 @@ export function isValidVoluntaryExit( currentEpoch >= voluntaryExit.epoch && // verify the validator had been active long enough currentEpoch >= validator.activationEpoch + config.SHARD_COMMITTEE_PERIOD && + (fork >= ForkSeq.electra + ? // only exit validator if it has no pending withdrawals in the queue + getPendingBalanceToWithdraw(state as CachedBeaconStateElectra, voluntaryExit.validatorIndex) === 0 + : // there are no pending withdrawals in previous forks + true) && // verify signature (!verifySignature || verifyVoluntaryExitSignature(state, signedVoluntaryExit)) ); } - -function isValidVoluntaryExitElectra( - state: CachedBeaconStateElectra, - signedVoluntaryExit: phase0.SignedVoluntaryExit, - verifySignature = true -): boolean { - // only exit validator if it has no pending withdrawals in the queue (post-Electra only) - if (getPendingBalanceToWithdraw(state, signedVoluntaryExit.message.validatorIndex) === 0) { - return isValidVoluntaryExit(state, signedVoluntaryExit, verifySignature); - } - - return false; -}