Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: consistently check no pending withdrawals when processing voluntary exit #7379

Merged
merged 2 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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 {ValidatorIndex, phase0} 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;
}
Loading