import { BitArray } from "@chainsafe/ssz";
import { ChainForkConfig } from "@lodestar/config";
import { ForkName, ForkPostAltair } from "@lodestar/params";
import { CachedBeaconStateAltair } from "@lodestar/state-transition";
import { BeaconBlock, LightClientBootstrap, LightClientFinalityUpdate, LightClientHeader, LightClientOptimisticUpdate, LightClientUpdate, Slot, phase0 } from "@lodestar/types";
import { Logger } from "@lodestar/utils";
import { IBeaconDb } from "../../db/index.js";
import { Metrics } from "../../metrics/index.js";
import { ChainEventEmitter } from "../emitter.js";
export type LightClientServerOpts = {
    disableLightClientServerOnImportBlockHead?: boolean;
    disableLightClientServer?: boolean;
};
export type SyncAttestedData = {
    attestedHeader: LightClientHeader;
    /** Precomputed root to prevent re-hashing */
    blockRoot: Uint8Array;
} & ({
    isFinalized: true;
    finalityBranch: Uint8Array[];
    finalizedCheckpoint: phase0.Checkpoint;
} | {
    isFinalized: false;
});
type LightClientServerModules = {
    config: ChainForkConfig;
    db: IBeaconDb;
    metrics: Metrics | null;
    emitter: ChainEventEmitter;
    logger: Logger;
};
/**
 * Compute and cache "init" proofs as the chain advances.
 * Will compute proofs for:
 * - All finalized blocks
 * - All non-finalized checkpoint blocks
 *
 * Params:
 * - How many epochs ago do you consider a re-org can happen? 10
 * - How many consecutive slots in a epoch you consider can be skipped? 32
 *
 * ### What data to store?
 *
 * An altair beacon state has 24 fields, with a depth of 5.
 * | field                 | gindex | index |
 * | --------------------- | ------ | ----- |
 * | finalizedCheckpoint   | 52     | 20    |
 * | currentSyncCommittee  | 54     | 22    |
 * | nextSyncCommittee     | 55     | 23    |
 *
 * Fields `currentSyncCommittee` and `nextSyncCommittee` are contiguous fields. Since they change its
 * more optimal to only store the witnesses different blocks of interest.
 *
 * ```ts
 * SyncCommitteeWitness = Container({
 *   witness: Vector[Bytes32, 4],
 *   currentSyncCommitteeRoot: Bytes32,
 *   nextSyncCommitteeRoot: Bytes32,
 * })
 * ```
 *
 * To produce finalized light-client updates, need the FinalizedCheckpointWitness + the finalized header the checkpoint
 * points too. It's cheaper to send a full BeaconBlockHeader `3*32 + 2*8` than a proof to `state_root` `(3+1)*32`.
 *
 * ```ts
 * FinalizedCheckpointWitness = Container({
 *   witness: Vector[Bytes32, 5],
 *   root: Bytes32,
 *   epoch: Epoch,
 * })
 * ```
 *
 * ### When to store data?
 *
 * Lightclient servers don't really need to support serving data for light-client at all possible roots to have a
 * functional use-case.
 * - For init proofs light-clients will probably use a finalized weak-subjectivity checkpoint
 * - For sync updates, light-clients need any update within a given period
 *
 * Fully tree-backed states are not guaranteed to be available at any time but just after processing a block. Then,
 * the server must pre-compute all data for all blocks until there's certainity of what block becomes a checkpoint
 * and which blocks doesn't.
 *
 * - SyncAggregate -> ParentBlock -> FinalizedCheckpoint -> nextSyncCommittee
 *
 * After importing a new block + postState:
 * - Persist SyncCommitteeWitness, indexed by block root of state's witness, always
 * - Persist currentSyncCommittee, indexed by hashTreeRoot, once (not necessary after the first run)
 * - Persist nextSyncCommittee, indexed by hashTreeRoot, for each period + dependentRoot
 * - Persist FinalizedCheckpointWitness only if checkpoint period = syncAggregate period
 *
 * TODO: Prune strategy:
 * - [Low value] On finalized or in finalized lookup, prune SyncCommittee that's not finalized
 * - [High value] After some time prune un-used FinalizedCheckpointWitness + finalized headers
 * - [High value] After some time prune to-be-checkpoint items that will never become checkpoints
 * - After sync period is over all pending headers are useless
 *
 * !!! BEST = finalized + highest bit count + oldest (less chance of re-org, less writes)
 *
 * Then when light-client requests the best finalized update at period N:
 * - Fetch best finalized SyncAggregateHeader in period N
 * - Fetch FinalizedCheckpointWitness at that header's block root
 * - Fetch SyncCommitteeWitness at that FinalizedCheckpointWitness.header.root
 * - Fetch SyncCommittee at that SyncCommitteeWitness.nextSyncCommitteeRoot
 *
 * When light-client request best non-finalized update at period N:
 * - Fetch best non-finalized SyncAggregateHeader in period N
 * - Fetch SyncCommitteeWitness at that SyncAggregateHeader.header.root
 * - Fetch SyncCommittee at that SyncCommitteeWitness.nextSyncCommitteeRoot
 *
 * ```
 *                       Finalized               Block   Sync
 *                       Checkpoint              Header  Aggreate
 * ----------------------|-----------------------|-------|---------> time
 *                        <---------------------   <----
 *                         finalizes               signs
 * ```
 *
 * ### What's the cost of this data?
 *
 * To estimate the data costs, let's analyze monthly. Yearly may not make sense due to weak subjectivity:
 * - 219145 slots / month
 * - 6848 epochs / month
 * - 27 sync periods / month
 *
 * The byte size of a SyncCommittee (mainnet preset) is fixed to `48 * (512 + 1) = 24624`. So with SyncCommittee only
 * the data cost to store them is `24624 * 27 = 664848` ~ 0.6 MB/m.
 *
 * Storing 4 witness per block costs `219145 * 4 * 32 = 28050560 ~ 28 MB/m`.
 * Storing 4 witness per epoch costs `6848 * 4 * 32 = 876544 ~ 0.9 MB/m`.
 */
export declare class LightClientServer {
    private readonly opts;
    private readonly db;
    private readonly config;
    private readonly metrics;
    private readonly emitter;
    private readonly logger;
    private readonly knownSyncCommittee;
    private storedCurrentSyncCommittee;
    /**
     * Keep in memory since this data is very transient, not useful after a few slots
     */
    private readonly prevHeadData;
    private checkpointHeaders;
    private latestHeadUpdate;
    private readonly zero;
    private finalized;
    constructor(opts: LightClientServerOpts, modules: LightClientServerModules);
    /**
     * Call after importing a block head, having the postState available in memory for proof generation.
     * - Persist state witness
     * - Use block's syncAggregate
     */
    onImportBlockHead(block: BeaconBlock<ForkPostAltair>, postState: CachedBeaconStateAltair, parentBlockSlot: Slot): void;
    /**
     * API ROUTE to get `currentSyncCommittee` and `nextSyncCommittee` from a trusted state root
     */
    getBootstrap(blockRoot: Uint8Array): Promise<LightClientBootstrap>;
    /**
     * API ROUTE to get the best available update for `period` to transition to the next sync committee.
     * Criteria for best in priority order:
     * - Is finalized
     * - Has the most bits
     * - Signed header at the oldest slot
     */
    getUpdate(period: number): Promise<LightClientUpdate>;
    /**
     * API ROUTE to get the sync committee hash from the best available update for `period`.
     */
    getCommitteeRoot(period: number): Promise<Uint8Array>;
    /**
     * API ROUTE to poll LightclientHeaderUpdate.
     * Clients should use the SSE type `light_client_optimistic_update` if available
     */
    getOptimisticUpdate(): LightClientOptimisticUpdate | null;
    getFinalityUpdate(): LightClientFinalityUpdate | null;
    /**
     * With forkchoice data compute which block roots will never become checkpoints and prune them.
     */
    pruneNonCheckpointData(nonCheckpointBlockRoots: Uint8Array[]): Promise<void>;
    private persistPostBlockImportData;
    /**
     * 1. Subscribe to gossip topics `sync_committee_{subnet_id}` and collect `sync_committee_message`
     * ```
     * slot: Slot
     * beacon_block_root: Root
     * validator_index: ValidatorIndex
     * signature: BLSSignature
     * ```
     *
     * 2. Subscribe to `sync_committee_contribution_and_proof` and collect `signed_contribution_and_proof`
     * ```
     * slot: Slot
     * beacon_block_root: Root
     * subcommittee_index: uint64
     * aggregation_bits: Bitvector[SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT]
     * signature: BLSSignature
     * ```
     *
     * 3. On new blocks use `block.body.sync_aggregate`, `block.parent_root` and `block.slot - 1`
     *
     * @param syncPeriod The sync period of the sync aggregate and signed block root
     */
    private onSyncAggregate;
    /**
     * Given a new `syncAggregate` maybe persist a new best partial update if its better than the current stored for
     * that sync period.
     */
    private maybeStoreNewBestUpdate;
    private storeSyncCommittee;
    /**
     * Get finalized header from db. Keeps a small in-memory cache to speed up most of the lookups
     */
    private getFinalizedHeader;
}
export declare function sumBits(bits: BitArray): number;
export declare function blockToLightClientHeader(fork: ForkName, block: BeaconBlock<ForkPostAltair>): LightClientHeader;
export {};
//# sourceMappingURL=index.d.ts.map