import { Epoch, ValidatorIndex } from "@lodestar/types"; import { CachedBeaconStateAllForks } from "../index.js"; import { EpochShuffling } from "../util/epochShuffling.js"; export type EpochTransitionCacheOpts = { /** * Assert progressive balances the same to EpochTransitionCache */ assertCorrectProgressiveBalances?: boolean; }; /** * EpochTransitionCache is the parent object of: * - Any data-structures not part of the spec'ed BeaconState * - Necessary to only compute data once * - Only necessary for epoch processing, can be disposed immediately * - Not already part of `EpochCache` {@see} {@link EpochCache} * * EpochTransitionCache speeds up epoch processing as a whole at the cost of more memory temporarily. This is okay since * only epoch process is done at once, limiting the max total memory increase. In summary it helps: * - Only loop state.validators once for all `process_*` fns * - Only loop status array once */ export interface EpochTransitionCache { prevEpoch: Epoch; currentEpoch: Epoch; /** * This is sum of active validators' balance in eth. */ totalActiveStakeByIncrement: number; /** For altair */ baseRewardPerIncrement: number; prevEpochUnslashedStake: { sourceStakeByIncrement: number; targetStakeByIncrement: number; headStakeByIncrement: number; }; currEpochUnslashedTargetStakeByIncrement: number; /** * Indices which will receive the slashing penalty * ``` * v.withdrawableEpoch === currentEpoch + EPOCHS_PER_SLASHINGS_VECTOR / 2 * ``` * There's a practical limitation in number of possible validators slashed by epoch, which would share the same * withdrawableEpoch. Note that after some count exitChurn would advance the withdrawableEpoch. * ``` * maxSlashedPerSlot = SLOTS_PER_EPOCH * (MAX_PROPOSER_SLASHINGS + MAX_ATTESTER_SLASHINGS * bits) * ``` * For current mainnet conditions (bits = 128) that's `maxSlashedPerSlot = 8704`. * For less than 327680 validators, churnLimit = 4 (minimum possible) * For exitChurn to overtake the slashing delay, there should be * ``` * churnLimit * (EPOCHS_PER_SLASHINGS_VECTOR / 2 - 1 - MAX_SEED_LOOKAHEAD) * ``` * For mainnet conditions that's 16364 validators. So the limiting factor is the max operations on the block. Note * that on average indicesToSlash must contain churnLimit validators (4), but it can spike to a max of 8704 in a * single epoch if there haven't been exits in a while and there's a massive attester slashing at once of validators * that happen to be in the same committee, which is very unlikely. */ indicesToSlash: ValidatorIndex[]; /** * Indices of validators that just joined and will be eligible for the active queue. * ``` * v.activationEligibilityEpoch === FAR_FUTURE_EPOCH && v.effectiveBalance >= MAX_EFFECTIVE_BALANCE * ``` * All validators in indicesEligibleForActivationQueue get activationEligibilityEpoch set. So it can only include * validators that have just joined the registry through a valid full deposit(s). * ``` * max indicesEligibleForActivationQueue = SLOTS_PER_EPOCH * MAX_DEPOSITS * ``` * For mainnet spec = 512 */ indicesEligibleForActivationQueue: ValidatorIndex[]; /** * Indices of validators that may become active once churn and finaly allow. * ``` * v.activationEpoch === FAR_FUTURE_EPOCH && v.activationEligibilityEpoch <= currentEpoch * ``` * Many validators could be on indicesEligibleForActivation, but only up to churnLimit will be activated. * For less than 327680 validators, churnLimit = 4 (minimum possible), so max processed is 4. */ indicesEligibleForActivation: ValidatorIndex[]; /** * Indices of validators that will be ejected due to low balance. * ``` * status.active && v.exitEpoch === FAR_FUTURE_EPOCH && v.effectiveBalance <= config.EJECTION_BALANCE * ``` * Potentially the entire validator set could be added to indicesToEject, and all validators in the array will have * their validator object mutated. Exit queue churn delays exit, but the object is mutated immediately. */ indicesToEject: ValidatorIndex[]; /** * Pre-computes status flags for faster checking of statuses during epoch transition. * Spec requires some reward or penalty to apply to * - eligible validators * - un-slashed validators * - prev attester flag set * With a status flag to check this conditions at once we just have to mask with an OR of the conditions. * This is only for phase0 only. */ proposerIndices: number[]; /** * This is for phase0 only. */ inclusionDelays: number[]; flags: number[]; isCompoundingValidatorArr: boolean[]; /** * balances array will be populated by processRewardsAndPenalties() and consumed by processEffectiveBalanceUpdates(). * processRewardsAndPenalties() already has a regular Javascript array of balances. * Then processEffectiveBalanceUpdates() needs to iterate all balances so it can re-use the array pre-computed previously. */ balances?: number[]; /** * Active validator indices for currentEpoch + 2. * This is only used in `afterProcessEpoch` to compute epoch shuffling, it's not efficient to calculate it at that time * since it requires 1 loop through validator. * | epoch process fn | nextEpochTotalActiveBalance action | * | -------------------------------- | ---------------------------------- | * | beforeProcessEpoch | calculate during the validator loop| * | afterEpochTransitionCache | read it | */ nextShufflingActiveIndices: Uint32Array; /** * Pre-computed shuffling for epoch N+2, populated by processProposerLookahead (Fulu+). * Used by afterProcessEpoch to avoid recomputing the same shuffling. */ nextShuffling: EpochShuffling | null; /** * Altair specific, this is total active balances for the next epoch. * This is only used in `afterProcessEpoch` to compute base reward and sync participant reward. * It's not efficient to calculate it at that time since it requires looping through all active validators, * so we should calculate it during `processEffectiveBalancesUpdate` which gives us updated effective balance. * | epoch process fn | nextEpochTotalActiveBalance action | * | -------------------------------- | ---------------------------------- | * | beforeProcessEpoch | initialize as BigInt(0) | * | processEffectiveBalancesUpdate | calculate during the loop | * | afterEpochTransitionCache | read it | */ nextEpochTotalActiveBalanceByIncrement: number; /** * Track by validator index if it's active in the prev epoch. * Used in metrics */ isActivePrevEpoch: boolean[]; /** * Track by validator index if it's active in the current epoch. * Used in metrics */ isActiveCurrEpoch: boolean[]; /** * Track by validator index if it's active in the next epoch. * Used in `processEffectiveBalanceUpdates` to save one loop over validators after epoch process. */ isActiveNextEpoch: boolean[]; } export declare function beforeProcessEpoch(state: CachedBeaconStateAllForks, opts?: EpochTransitionCacheOpts): EpochTransitionCache; //# sourceMappingURL=epochTransitionCache.d.ts.map