Skip to content

Commit

Permalink
fix: consistently check no pending withdrawals when processing volunt…
Browse files Browse the repository at this point in the history
…ary exit
  • Loading branch information
nflaig committed Jan 20, 2025
1 parent 06831cf commit 88f994f
Show file tree
Hide file tree
Showing 3 changed files with 10 additions and 21 deletions.
2 changes: 1 addition & 1 deletion packages/beacon-node/src/chain/opPools/opPool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion packages/beacon-node/src/chain/validation/voluntaryExit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
Expand Down
27 changes: 8 additions & 19 deletions packages/state-transition/src/block/processVoluntaryExit.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {FAR_FUTURE_EPOCH, ForkSeq} from "@lodestar/params";
import {phase0} from "@lodestar/types";
import {phase0, ValidatorIndex} from "@lodestar/types";
import {verifyVoluntaryExitSignature} from "../signatureSets/index.js";
import {CachedBeaconStateAllForks, CachedBeaconStateElectra} from "../types.js";
import {getPendingBalanceToWithdraw, isActiveValidator} from "../util/index.js";
Expand All @@ -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}`);
}

Expand All @@ -29,6 +25,7 @@ export function processVoluntaryExit(
}

export function isValidVoluntaryExit(
fork: ForkSeq,
state: CachedBeaconStateAllForks,
signedVoluntaryExit: phase0.SignedVoluntaryExit,
verifySignature = true
Expand All @@ -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;
}

0 comments on commit 88f994f

Please sign in to comment.