import { ChainForkConfig } from "@lodestar/config";
import { ColumnIndex, DataColumnSidecar, RootHex, SignedBeaconBlock, Slot, deneb, fulu, gloas, phase0 } from "@lodestar/types";
import { LodestarError, Logger } from "@lodestar/utils";
import { DAType, IBlockInput } from "../../chain/blocks/blockInput/index.js";
import { PayloadEnvelopeInput } from "../../chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
import { SeenBlockInput } from "../../chain/seenCache/seenGossipBlockInput.js";
import { SeenPayloadEnvelopeInput } from "../../chain/seenCache/seenPayloadEnvelopeInput.js";
import { BeaconMetrics } from "../../metrics/metrics/beacon.js";
import { INetwork } from "../../network/index.js";
import { CustodyConfig } from "../../util/dataColumns.js";
import { PeerIdStr } from "../../util/peerId.js";
import { WarnResult } from "../../util/wrapError.js";
export type DownloadByRangeRequests = {
    blocksRequest?: phase0.BeaconBlocksByRangeRequest;
    blobsRequest?: deneb.BlobSidecarsByRangeRequest;
    columnsRequest?: fulu.DataColumnSidecarsByRangeRequest;
    envelopesRequest?: gloas.ExecutionPayloadEnvelopesByRangeRequest;
    /**
     * Post-Gloas only. Fetches the dangling-parent's payload envelope and/or its missing sampled
     * data columns by-root. Set by `Batch` for the first batch of a `SyncChain` after the first
     * block's parentRoot is known.
     */
    parentPayloadRequest?: {
        blockRoot?: Uint8Array;
        columns?: ColumnIndex[];
        envelopeBlockRoot?: Uint8Array;
    };
};
export type ParentPayloadCommitments = {
    blockRoot: Uint8Array;
    blockRootHex: RootHex;
    kzgCommitments: deneb.BlobKzgCommitments;
};
export type DownloadByRangeResponses = {
    blocks?: SignedBeaconBlock[];
    blobSidecars?: deneb.BlobSidecars;
    columnSidecars?: DataColumnSidecar[];
    payloadEnvelopes?: gloas.SignedExecutionPayloadEnvelope[];
};
export type DownloadAndCacheByRangeProps = DownloadByRangeRequests & {
    config: ChainForkConfig;
    network: INetwork;
    logger: Logger;
    peerIdStr: string;
    batchBlocks?: IBlockInput[];
    /** Required when `parentPayloadRequest` is set; supplies the data needed to validate the parent's columns. */
    parentPayloadCommitments?: ParentPayloadCommitments;
    peerDasMetrics?: BeaconMetrics["peerDas"] | null;
};
export type CacheByRangeResponsesProps = {
    cache: SeenBlockInput;
    seenPayloadEnvelopeInputCache: SeenPayloadEnvelopeInput;
    peerIdStr: string;
    responses: ValidatedResponses;
    batchBlocks: IBlockInput[];
    /** Raw envelopes downloaded in this batch, keyed by slot (from downloadByRange return) */
    downloadedPayloadEnvelopes: Map<Slot, gloas.SignedExecutionPayloadEnvelope> | null;
    /** Envelopes already wrapped from previous partial downloads on this batch */
    existingPayloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null;
    /** Sampled/custody column indices for building PayloadEnvelopeInputs */
    custodyConfig: Pick<CustodyConfig, "sampledColumns" | "custodyColumns">;
    seenTimestampSec: number;
};
export type ValidatedBlock = {
    blockRoot: Uint8Array;
    block: SignedBeaconBlock;
};
export type ValidatedBlobSidecars = {
    blockRoot: Uint8Array;
    blobSidecars: deneb.BlobSidecars;
};
export type ValidatedColumnSidecars = {
    blockRoot: Uint8Array;
    columnSidecars: DataColumnSidecar[];
};
export type ValidatedResponses = {
    validatedBlocks?: ValidatedBlock[];
    validatedBlobSidecars?: ValidatedBlobSidecars[];
    validatedColumnSidecars?: ValidatedColumnSidecars[];
};
/**
 * Given existing cached batch block inputs and newly validated responses, update the cache with the new data
 */
export declare function cacheByRangeResponses({ cache, seenPayloadEnvelopeInputCache, peerIdStr, responses, batchBlocks, downloadedPayloadEnvelopes, existingPayloadEnvelopes, custodyConfig, seenTimestampSec }: CacheByRangeResponsesProps): {
    blocks: IBlockInput[];
    payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null;
};
export declare function downloadByRange({ config, network, peerIdStr, batchBlocks, blocksRequest, blobsRequest, columnsRequest, envelopesRequest, parentPayloadRequest, parentPayloadCommitments, peerDasMetrics }: DownloadAndCacheByRangeProps): Promise<WarnResult<{
    responses: ValidatedResponses;
    payloadEnvelopes: Map<Slot, gloas.SignedExecutionPayloadEnvelope> | null;
}, DownloadByRangeError>>;
/**
 * Should not be called directly. Only exported for unit testing purposes
 */
export declare function requestByRange({ network, peerIdStr, blocksRequest, blobsRequest, columnsRequest, envelopesRequest, parentPayloadRequest }: DownloadByRangeRequests & {
    network: INetwork;
    peerIdStr: PeerIdStr;
}): Promise<DownloadByRangeResponses>;
/**
 * Should not be called directly. Only exported for unit testing purposes
 */
export declare function validateResponses({ config, batchBlocks, blocksRequest, blobsRequest, columnsRequest, envelopesRequest, parentPayloadRequest, parentPayloadCommitments, blocks, blobSidecars, columnSidecars, payloadEnvelopes, peerDasMetrics }: DownloadByRangeRequests & DownloadByRangeResponses & {
    config: ChainForkConfig;
    batchBlocks?: IBlockInput[];
    parentPayloadCommitments?: ParentPayloadCommitments;
    peerDasMetrics?: BeaconMetrics["peerDas"] | null;
}): Promise<WarnResult<{
    responses: ValidatedResponses;
    payloadEnvelopes: Map<Slot, gloas.SignedExecutionPayloadEnvelope> | null;
}, DownloadByRangeError>>;
/**
 * Should not be called directly. Only exported for unit testing purposes
 *
 * - check all slots are within range of startSlot (inclusive) through startSlot + count (exclusive)
 * - don't have more than count number of blocks
 * - slots are in ascending order
 * - must allow for skip slots
 * - check is a chain of blocks where via parentRoot matches hashTreeRoot of block before
 */
export declare function validateBlockByRangeResponse(config: ChainForkConfig, blocksRequest: phase0.BeaconBlocksByRangeRequest, blocks: SignedBeaconBlock[]): WarnResult<ValidatedBlock[], DownloadByRangeError>;
/**
 * Should not be called directly. Only exported for unit testing purposes.
 * This is used only in Deneb and Electra
 */
export declare function validateBlobsByRangeResponse(dataRequestBlocks: ValidatedBlock[], blobSidecars: deneb.BlobSidecars): Promise<ValidatedBlobSidecars[]>;
/**
 * Should not be called directly. Only exported for unit testing purposes
 *
 * Spec states:
 * 1) must be within range [start_slot, start_slot + count]
 * 2) should respond with all columns in the range or and 3:ResourceUnavailable (and potentially get down-scored)
 * 3) must response with at least the sidecars of the first blob-carrying block that exists in the range
 * 4) must include all sidecars from each block from which there are blobs
 * 5) where they exists, sidecars must be sent in (slot, index) order
 * 6) clients may limit the number of sidecars in a response
 * 7) clients may stop responding mid-response if their view of fork-choice changes
 *
 * We will interpret the spec as follows
 * - Errors when validating: 1, 3, 5
 * - Warnings when validating: 2, 4, 6, 7
 *
 * For "warning" cases, where we get a partial response but sidecars are validated and correct with respect to the
 * blocks, then they will be kept.  This loosening of the spec is to help ensure sync goes smoothly and we can find
 * the data needed in difficult network situations.
 *
 * Assume for the following two examples we request indices 5, 10, 15 for a range of slots 32-63
 *
 * For slots where we receive no sidecars, example slot 45, but blobs exist we will stop validating subsequent
 * slots, 45-63.  The next round of requests will get structured to pull the from the slot that had columns
 * missing to the end of the range for all columns indices that were requested for the current partially failed
 * request (slots 45-63 and indices 5, 10, 15).
 *
 * For slots where only some of the requested sidecars are received we will proceed with validation. For simplicity sake
 * we will assume that if we only get some indices back for a (or several) slot(s) that the indices we get will be
 * consistent. IE if a peer returns only index 5, they will most likely return that same index for subsequent slot
 * (index 5 for slots 34, 35, 36, etc). They will not likely return 5 on slot 34, 10 on slot 35, 15 on slot 36, etc.
 * This assumption makes the code simpler. For both cases the request for the next round will be structured correctly
 * to pull any missing column indices for whatever range remains.  The simplification just leads to re-verification
 * of the columns but the number of columns downloaded will be the same regardless of if they are validated twice.
 *
 * validateColumnsByRangeResponse makes some assumptions about the data being passed in
 * blocks are:
 * - slotwise in order
 * - form a chain
 * - non-sparse response (any missing block is a skipped slot not a bad response)
 * - last block is last slot received
 */
export declare function validateColumnsByRangeResponse(config: ChainForkConfig, request: fulu.DataColumnSidecarsByRangeRequest, blocks: ValidatedBlock[], columnSidecars: DataColumnSidecar[], peerDasMetrics?: BeaconMetrics["peerDas"] | null): Promise<WarnResult<ValidatedColumnSidecars[], DownloadByRangeError>>;
/**
 * Given a data request, return only the blocks and roots that correspond to the data request (sorted). Assumes that
 * cached have slots that are all before the current batch of downloaded blocks
 */
export declare function getBlocksForDataValidation(dataRequest: {
    startSlot: Slot;
    count: number;
}, cached: IBlockInput[] | undefined, current: ValidatedBlock[] | undefined): ValidatedBlock[];
export declare enum DownloadByRangeErrorCode {
    MISSING_BLOCKS_RESPONSE = "DOWNLOAD_BY_RANGE_ERROR_MISSING_BLOCK_RESPONSE",
    MISSING_BLOBS_RESPONSE = "DOWNLOAD_BY_RANGE_ERROR_MISSING_BLOBS_RESPONSE",
    MISSING_COLUMNS_RESPONSE = "DOWNLOAD_BY_RANGE_ERROR_MISSING_COLUMNS_RESPONSE",
    /** Error at the reqresp layer */
    REQ_RESP_ERROR = "DOWNLOAD_BY_RANGE_ERROR_REQ_RESP_ERROR",
    PARENT_ROOT_MISMATCH = "DOWNLOAD_BY_RANGE_ERROR_PARENT_ROOT_MISMATCH",
    EXTRA_BLOCKS = "DOWNLOAD_BY_RANGE_ERROR_EXTRA_BLOCKS",
    OUT_OF_RANGE_BLOCKS = "DOWNLOAD_BY_RANGE_OUT_OF_RANGE_BLOCKS",
    OUT_OF_ORDER_BLOCKS = "DOWNLOAD_BY_RANGE_OUT_OF_ORDER_BLOCKS",
    MISSING_BLOBS = "DOWNLOAD_BY_RANGE_ERROR_MISSING_BLOBS",
    OUT_OF_ORDER_BLOBS = "DOWNLOAD_BY_RANGE_ERROR_OUT_OF_ORDER_BLOBS",
    EXTRA_BLOBS = "DOWNLOAD_BY_RANGE_ERROR_EXTRA_BLOBS",
    MISSING_COLUMNS = "DOWNLOAD_BY_RANGE_ERROR_MISSING_COLUMNS",
    OVER_COLUMNS = "DOWNLOAD_BY_RANGE_ERROR_OVER_COLUMNS",
    EXTRA_COLUMNS = "DOWNLOAD_BY_RANGE_ERROR_EXTRA_COLUMNS",
    NO_COLUMNS_FOR_BLOCK = "DOWNLOAD_BY_RANGE_ERROR_NO_COLUMNS_FOR_BLOCK",
    DUPLICATE_COLUMN = "DOWNLOAD_BY_RANGE_ERROR_DUPLICATE_COLUMN",
    OUT_OF_ORDER_COLUMNS = "DOWNLOAD_BY_RANGE_OUT_OF_ORDER_COLUMNS",
    /** Cached block input type mismatches new data */
    MISMATCH_BLOCK_FORK = "DOWNLOAD_BY_RANGE_ERROR_MISMATCH_BLOCK_FORK",
    MISMATCH_BLOCK_INPUT_TYPE = "DOWNLOAD_BY_RANGE_ERROR_MISMATCH_BLOCK_INPUT_TYPE",
    /** Envelope beaconBlockRoot does not match the block's root */
    INVALID_ENVELOPE_BEACON_BLOCK_ROOT = "DOWNLOAD_BY_RANGE_ERROR_INVALID_ENVELOPE_BEACON_BLOCK_ROOT",
    /** Block segment + envelopes failed chain-segment linearity / FULL-chain checks */
    INVALID_CHAIN_SEGMENT = "DOWNLOAD_BY_RANGE_ERROR_INVALID_CHAIN_SEGMENT"
}
export type DownloadByRangeErrorType = {
    code: DownloadByRangeErrorCode.MISSING_BLOCKS_RESPONSE | DownloadByRangeErrorCode.MISSING_BLOBS_RESPONSE | DownloadByRangeErrorCode.MISSING_COLUMNS_RESPONSE;
    blockStartSlot?: number;
    blockCount?: number;
    blobStartSlot?: number;
    blobCount?: number;
    columnStartSlot?: number;
    columnCount?: number;
} | {
    code: DownloadByRangeErrorCode.OUT_OF_RANGE_BLOCKS;
    slot: number;
} | {
    code: DownloadByRangeErrorCode.MISMATCH_BLOCK_FORK;
    slot: number;
    dataFork: string;
    blockFork: string;
} | {
    code: DownloadByRangeErrorCode.OUT_OF_ORDER_BLOCKS;
} | {
    code: DownloadByRangeErrorCode.REQ_RESP_ERROR;
    blockStartSlot?: number;
    blockCount?: number;
    blobStartSlot?: number;
    blobCount?: number;
    columnStartSlot?: number;
    columnCount?: number;
    reason: string;
} | {
    code: DownloadByRangeErrorCode.PARENT_ROOT_MISMATCH;
    slot: number;
    expected: string;
    actual: string;
} | {
    code: DownloadByRangeErrorCode.EXTRA_BLOCKS;
    expected: number;
    actual: number;
} | {
    code: DownloadByRangeErrorCode.MISSING_BLOBS;
    expected: number;
    actual: number;
} | {
    code: DownloadByRangeErrorCode.OUT_OF_ORDER_BLOBS | DownloadByRangeErrorCode.OUT_OF_ORDER_COLUMNS;
    slot: number;
} | {
    code: DownloadByRangeErrorCode.EXTRA_BLOBS;
    expected: number;
    actual: number;
} | {
    code: DownloadByRangeErrorCode.OVER_COLUMNS;
    max: number;
    actual: number;
} | {
    code: DownloadByRangeErrorCode.MISSING_COLUMNS;
    slot: Slot;
    blockRoot: string;
    missingIndices: string;
} | {
    code: DownloadByRangeErrorCode.DUPLICATE_COLUMN;
    slot: Slot;
    index: number;
} | {
    code: DownloadByRangeErrorCode.EXTRA_COLUMNS | DownloadByRangeErrorCode.NO_COLUMNS_FOR_BLOCK;
    slot: Slot;
    blockRoot: string;
    invalidIndices: string;
} | {
    code: DownloadByRangeErrorCode.MISMATCH_BLOCK_INPUT_TYPE;
    slot: number;
    blockRoot: string;
    expected: DAType;
    actual: DAType;
} | {
    code: DownloadByRangeErrorCode.INVALID_ENVELOPE_BEACON_BLOCK_ROOT;
    slot: Slot;
    expected: string;
    actual: string;
} | {
    code: DownloadByRangeErrorCode.INVALID_CHAIN_SEGMENT;
    slot: Slot;
    reason: string;
};
export declare class DownloadByRangeError extends LodestarError<DownloadByRangeErrorType> {
}
/**
 * Validates SignedExecutionPayloadEnvelopes received for a range request.
 *
 * Three categories of envelope slots:
 *   - In-batch slot whose block we have: verify envelope.beaconBlockRoot matches.
 *   - Dangling-parent envelope (only when `parentPayloadCommitments` is set, i.e. the first
 *     batch of a `SyncChain`): keep if `envelope.beaconBlockRoot === parentPayloadCommitments.blockRoot`.
 *   - Other "orphan" envelopes (e.g. unrelated slots): ignored.
 */
export declare function validateEnvelopesByRangeResponse(validatedBlocks: ValidatedBlock[], batchBlocks: IBlockInput[] | undefined, payloadEnvelopes: gloas.SignedExecutionPayloadEnvelope[], parentPayloadCommitments?: ParentPayloadCommitments): Map<Slot, gloas.SignedExecutionPayloadEnvelope>;
//# sourceMappingURL=downloadByRange.d.ts.map