diff --git a/packages/beacon-node/src/chain/opPools/opPool.ts b/packages/beacon-node/src/chain/opPools/opPool.ts index 5eec1597a24..819873d2e83 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 79da31084a3..60ee133e4b6 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 a0a0271a0d1..3cc6afb958f 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; -}