import { RollupContract } from '@aztec/ethereum/contracts'; import { EpochNumber, SlotNumber } from '@aztec/foundation/branded-types'; import { EthAddress } from '@aztec/foundation/eth-address'; import { DateProvider } from '@aztec/foundation/timer'; import { type L1RollupConstants } from '@aztec/stdlib/epoch-helpers'; import { type EpochCacheConfig } from './config.js'; /** The proposer pipelines by building one slot ahead. */ export declare const PROPOSER_PIPELINING_SLOT_OFFSET = 1; /** Flat return type for compound epoch/slot getters. */ export type EpochAndSlot = { slot: SlotNumber; epoch: EpochNumber; ts: bigint; }; export type EpochCommitteeInfo = { committee: EthAddress[] | undefined; seed: bigint; epoch: EpochNumber; /** True if the epoch is within an open escape hatch window. */ isEscapeHatchOpen: boolean; }; export type SlotTag = 'now' | 'next' | SlotNumber; /** Resolved cache entry with L1 provenance metadata. */ type CachedEpochEntry = { data: EpochCommitteeInfo; /** L1 block number at which the committee data was originally queried. */ lastQueryL1BlockNumber: bigint; /** L1 block hash at which the committee data was originally queried. Used to detect reorgs. */ lastQueryL1BlockHash: `0x${string}`; /** Latest L1 block timestamp at the time of the most recent refresh (full fetch or lightweight check). */ lastRefreshL1Timestamp: bigint; /** Whether the epoch's sampling data falls within finalized L1 history. */ finalized: boolean; }; export interface EpochCacheInterface { getCommittee(slot: SlotTag | undefined): Promise; getSlotNow(): SlotNumber; getTargetSlot(): SlotNumber; getEpochNow(): EpochNumber; getTargetEpoch(): EpochNumber; getEpochAndSlotNow(): EpochAndSlot & { nowMs: bigint; }; getEpochAndSlotInNextL1Slot(): EpochAndSlot & { nowSeconds: bigint; }; /** Returns epoch/slot info for the next L1 slot with pipeline offset applied. */ getTargetEpochAndSlotInNextL1Slot(): EpochAndSlot & { nowSeconds: bigint; }; isEscapeHatchOpen(epoch: EpochNumber): Promise; isEscapeHatchOpenAtSlot(slot: SlotTag): Promise; getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}`; computeProposerIndex(slot: SlotNumber, epoch: EpochNumber, seed: bigint, size: bigint): bigint; getCurrentAndNextSlot(): { currentSlot: SlotNumber; nextSlot: SlotNumber; }; getTargetAndNextSlot(): { targetSlot: SlotNumber; nextSlot: SlotNumber; }; getProposerAttesterAddressInSlot(slot: SlotNumber): Promise; getRegisteredValidators(): Promise; isInCommittee(slot: SlotTag, validator: EthAddress): Promise; filterInCommittee(slot: SlotTag, validators: EthAddress[]): Promise; getL1Constants(): L1RollupConstants; } /** * Epoch cache * * This class is responsible for managing traffic to the l1 node, by caching the validator set. * Keeps the last N epochs in cache. * It also provides a method to get the current or next proposer, and to check who is in the current slot. * * Note: This class is very dependent on the system clock being in sync. */ export declare class EpochCache implements EpochCacheInterface { private rollup; private readonly l1constants; private readonly dateProvider; protected readonly config: { cacheSize: number; validatorRefreshIntervalSeconds: number; }; /** * Single map holding both resolved entries and in-flight promises. * A `Promise` value means a fetch is in progress; concurrent callers await it. */ protected cache: Map>; private allValidators; private lastValidatorRefresh; private readonly log; constructor(rollup: RollupContract, l1constants: L1RollupConstants & { lagInEpochsForValidatorSet: number; lagInEpochsForRandao: number; }, dateProvider?: DateProvider, config?: { cacheSize: number; validatorRefreshIntervalSeconds: number; }); static create(rollupOrAddress: EthAddress | RollupContract, config?: EpochCacheConfig, deps?: { dateProvider?: DateProvider; }): Promise; getL1Constants(): L1RollupConstants; getSlotNow(): SlotNumber; getTargetSlot(): SlotNumber; getEpochNow(): EpochNumber; getTargetEpoch(): EpochNumber; getEpochAndSlotNow(): EpochAndSlot & { nowMs: bigint; }; private getEpochAndSlotAtSlot; getEpochAndSlotInNextL1Slot(): EpochAndSlot & { nowSeconds: bigint; }; getTargetEpochAndSlotInNextL1Slot(): EpochAndSlot & { nowSeconds: bigint; }; private getEpochAndSlotAtTimestamp; getCommitteeForEpoch(epoch: EpochNumber): Promise; /** Returns whether the escape hatch is open for the given epoch. */ isEscapeHatchOpen(epoch: EpochNumber): Promise; /** * Returns whether the escape hatch is open for the epoch containing the given slot. * * This is a lightweight helper intended for callers that already have a slot number and only * need the escape hatch flag (without pulling full committee info). */ isEscapeHatchOpenAtSlot(slot?: SlotTag): Promise; /** * Get the current validator set. * * Returns cached data if the entry is finalized or still fresh (queried less than one * Ethereum slot ago). Stale non-finalized entries are re-queried, and concurrent callers * coalesce on the same in-flight promise so the L1 query happens only once. */ getCommittee(slot?: SlotTag): Promise; private getEpochAndTimestamp; /** Evicts oldest cache entries (resolved or in-flight) beyond cacheSize. */ private purgeCache; /** Returns true if a non-finalized cache entry is older than one Ethereum slot. */ private isStale; /** Whether a cached epoch entry has been marked as finalized. Returns undefined if not cached or still in-flight. */ isFinalized(epoch: EpochNumber): boolean | undefined; /** Returns the latest L1 timestamp stored in the cached entry. Undefined if not cached or in-flight. */ getCachedLastRefreshL1Timestamp(epoch: EpochNumber): bigint | undefined; /** Computes the sampling timestamp for an epoch's committee data. */ private getSamplingTimestamp; private refreshStaleEntry; private fetchAndCache; /** * Get the ABI encoding of the proposer index - see ValidatorSelectionLib.sol computeProposerIndex */ getProposerIndexEncoding(epoch: EpochNumber, slot: SlotNumber, seed: bigint): `0x${string}`; computeProposerIndex(slot: SlotNumber, epoch: EpochNumber, seed: bigint, size: bigint): bigint; /** Returns the current and next L2 slot in next eth L1 Slot. */ getCurrentAndNextSlot(): { currentSlot: SlotNumber; nextSlot: SlotNumber; }; /** Returns the target and next L2 slot in the next L1 slot. */ getTargetAndNextSlot(): { targetSlot: SlotNumber; nextSlot: SlotNumber; }; /** * Get the proposer attester address in the given L2 slot * @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError. * If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined. */ getProposerAttesterAddressInSlot(slot: SlotNumber): Promise; /** * Get the proposer attester address in the next slot * @returns The proposer attester address. If the committee does not exist, we throw a NoCommitteeError. * If the committee is empty (i.e. target committee size is 0, and anyone can propose), we return undefined. */ getProposerAttesterAddressInNextSlot(): Promise; private getProposerAttesterAddressAt; getProposerFromEpochCommittee(epochCommitteeInfo: EpochCommitteeInfo, slot: SlotNumber): EthAddress | undefined; /** Check if a validator is in the given slot's committee */ isInCommittee(slot: SlotTag, validator: EthAddress): Promise; /** From the set of given addresses, return all that are on the committee for the given slot */ filterInCommittee(slot: SlotTag, validators: EthAddress[]): Promise; getRegisteredValidators(): Promise; } export {}; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXBvY2hfY2FjaGUuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9lcG9jaF9jYWNoZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEVBQW9CLGNBQWMsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBRTdFLE9BQU8sRUFBRSxXQUFXLEVBQUUsVUFBVSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDMUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBRTNELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN2RCxPQUFPLEVBQ0wsS0FBSyxpQkFBaUIsRUFTdkIsTUFBTSw2QkFBNkIsQ0FBQztBQUlyQyxPQUFPLEVBQUUsS0FBSyxnQkFBZ0IsRUFBOEIsTUFBTSxhQUFhLENBQUM7QUFFaEYseURBQXlEO0FBQ3pELGVBQU8sTUFBTSwrQkFBK0IsSUFBSSxDQUFDO0FBRWpELHdEQUF3RDtBQUN4RCxNQUFNLE1BQU0sWUFBWSxHQUFHO0lBQ3pCLElBQUksRUFBRSxVQUFVLENBQUM7SUFDakIsS0FBSyxFQUFFLFdBQVcsQ0FBQztJQUNuQixFQUFFLEVBQUUsTUFBTSxDQUFDO0NBQ1osQ0FBQztBQUVGLE1BQU0sTUFBTSxrQkFBa0IsR0FBRztJQUMvQixTQUFTLEVBQUUsVUFBVSxFQUFFLEdBQUcsU0FBUyxDQUFDO0lBQ3BDLElBQUksRUFBRSxNQUFNLENBQUM7SUFDYixLQUFLLEVBQUUsV0FBVyxDQUFDO0lBQ25CLCtEQUErRDtJQUMvRCxpQkFBaUIsRUFBRSxPQUFPLENBQUM7Q0FDNUIsQ0FBQztBQUVGLE1BQU0sTUFBTSxPQUFPLEdBQUcsS0FBSyxHQUFHLE1BQU0sR0FBRyxVQUFVLENBQUM7QUFLbEQsd0RBQXdEO0FBQ3hELEtBQUssZ0JBQWdCLEdBQUc7SUFDdEIsSUFBSSxFQUFFLGtCQUFrQixDQUFDO0lBQ3pCLDBFQUEwRTtJQUMxRSxzQkFBc0IsRUFBRSxNQUFNLENBQUM7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