import {phase0} from "@lodestar/types"; import {ForkSeq} from "@lodestar/params"; import {isSlashableValidator, isSlashableAttestationData, getAttesterSlashableIndices} from "../util/index.js"; import {CachedBeaconStateAllForks} from "../types.js"; import {slashValidator} from "./slashValidator.js"; import {isValidIndexedAttestationBigint} from "./isValidIndexedAttestation.js"; /** * Process an AttesterSlashing operation. Initiates the exit of a validator, decreases the balance of the slashed * validators and increases the block proposer balance. * * PERF: Work depends on number of AttesterSlashing per block. On regular networks the average is 0 / block. */ export function processAttesterSlashing( fork: ForkSeq, state: CachedBeaconStateAllForks, attesterSlashing: phase0.AttesterSlashing, verifySignatures = true ): void { assertValidAttesterSlashing(state, attesterSlashing, verifySignatures); const intersectingIndices = getAttesterSlashableIndices(attesterSlashing); let slashedAny = false; const validators = state.validators; // Get the validators sub tree once for all indices // Spec requires to sort indexes beforehand for (const index of intersectingIndices.sort((a, b) => a - b)) { if (isSlashableValidator(validators.getReadonly(index), state.epochCtx.epoch)) { slashValidator(fork, state, index); slashedAny = true; } } if (!slashedAny) { throw new Error("AttesterSlashing did not result in any slashings"); } } export function assertValidAttesterSlashing( state: CachedBeaconStateAllForks, attesterSlashing: phase0.AttesterSlashing, verifySignatures = true ): void { const attestation1 = attesterSlashing.attestation1; const attestation2 = attesterSlashing.attestation2; if (!isSlashableAttestationData(attestation1.data, attestation2.data)) { throw new Error("AttesterSlashing is not slashable"); } // In state transition, AttesterSlashing attestations are only partially validated. Their slot and epoch could // be higher than the clock and the slashing would still be valid. Same applies to attestation data index, which // can be any arbitrary value. Must use bigint variants to hash correctly to all possible values for (const [i, attestation] of [attestation1, attestation2].entries()) { if (!isValidIndexedAttestationBigint(state, attestation, verifySignatures)) { throw new Error(`AttesterSlashing attestation${i} is invalid`); } } }