lighthouse/consensus/state_processing/src/per_block_processing/verify_exit.rs
Paul Hauner 2b3084f578 Use head state for exit verification (#4183)
## Issue Addressed

NA

## Proposed Changes

Similar to #4181 but without the version bump and a more nuanced fix.

Patches the high CPU usage seen after the Capella fork which was caused by processing exits when there are skip slots.

## Additional Info

~~This is an imperfect solution that will cause us to drop some exits at the fork boundary. This is tracked at #4184.~~
2023-04-14 01:11:46 +00:00

84 lines
2.3 KiB
Rust

use super::errors::{BlockOperationError, ExitInvalid};
use crate::per_block_processing::{
signature_sets::{exit_signature_set, get_pubkey_from_state},
VerifySignatures,
};
use safe_arith::SafeArith;
use types::*;
type Result<T> = std::result::Result<T, BlockOperationError<ExitInvalid>>;
fn error(reason: ExitInvalid) -> BlockOperationError<ExitInvalid> {
BlockOperationError::invalid(reason)
}
/// Indicates if an `Exit` is valid to be included in a block in the current epoch of the given
/// state.
///
/// Returns `Ok(())` if the `Exit` is valid, otherwise indicates the reason for invalidity.
///
/// Spec v0.12.1
pub fn verify_exit<T: EthSpec>(
state: &BeaconState<T>,
current_epoch: Option<Epoch>,
signed_exit: &SignedVoluntaryExit,
verify_signatures: VerifySignatures,
spec: &ChainSpec,
) -> Result<()> {
let current_epoch = current_epoch.unwrap_or(state.current_epoch());
let exit = &signed_exit.message;
let validator = state
.validators()
.get(exit.validator_index as usize)
.ok_or_else(|| error(ExitInvalid::ValidatorUnknown(exit.validator_index)))?;
// Verify the validator is active.
verify!(
validator.is_active_at(current_epoch),
ExitInvalid::NotActive(exit.validator_index)
);
// Verify that the validator has not yet exited.
verify!(
validator.exit_epoch == spec.far_future_epoch,
ExitInvalid::AlreadyExited(exit.validator_index)
);
// Exits must specify an epoch when they become valid; they are not valid before then.
verify!(
current_epoch >= exit.epoch,
ExitInvalid::FutureEpoch {
state: current_epoch,
exit: exit.epoch
}
);
// Verify the validator has been active long enough.
let earliest_exit_epoch = validator
.activation_epoch
.safe_add(spec.shard_committee_period)?;
verify!(
current_epoch >= earliest_exit_epoch,
ExitInvalid::TooYoungToExit {
current_epoch,
earliest_exit_epoch,
}
);
if verify_signatures.is_true() {
verify!(
exit_signature_set(
state,
|i| get_pubkey_from_state(state, i),
signed_exit,
spec
)?
.verify(),
ExitInvalid::BadSignature
);
}
Ok(())
}