import {BeaconConfig} from "@lodestar/config"; import {ForkSeq} from "@lodestar/params"; import {AttesterSlashing, Slot} from "@lodestar/types"; import {Index2PubkeyCache} from "../cache/pubkeyCache.js"; import {CachedBeaconStateAllForks} from "../types.js"; import {getAttesterSlashableIndices, isSlashableAttestationData, isSlashableValidator} from "../util/index.js"; import {isValidIndexedAttestationBigint} from "./isValidIndexedAttestation.js"; import {slashValidator} from "./slashValidator.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: AttesterSlashing, verifySignatures = true ): void { const {epochCtx} = state; assertValidAttesterSlashing( state.config, epochCtx.index2pubkey, state.slot, state.validators.length, 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), epochCtx.epoch)) { slashValidator(fork, state, index); slashedAny = true; } } if (!slashedAny) { throw new Error("AttesterSlashing did not result in any slashings"); } } export function assertValidAttesterSlashing( config: BeaconConfig, index2pubkey: Index2PubkeyCache, stateSlot: Slot, validatorsLen: number, attesterSlashing: 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(config, index2pubkey, stateSlot, validatorsLen, attestation, verifySignatures) ) { throw new Error(`AttesterSlashing attestation${i} is invalid`); } } }