import { GENESIS_SLOT, isForkPostDeneb, isForkPostFulu } from "@lodestar/params";
import { RespStatus, ResponseError } from "@lodestar/reqresp";
import { computeEpochAtSlot } from "@lodestar/state-transition";
import { prettyPrintPeerId } from "../../util.js";
// TODO: Unit test
export async function* onBeaconBlocksByRange(request, chain, db, peerId, peerClient) {
    const { startSlot, count } = validateBeaconBlocksByRangeRequest(chain.config, request);
    const endSlot = startSlot + count;
    const finalized = db.blockArchive;
    // in the case of initializing from a non-finalized state, we don't have the finalized block so this api does not work
    // chain.forkChoice.getFinalizeBlock().slot
    const finalizedSlot = chain.forkChoice.getFinalizedCheckpointSlot();
    const forkName = chain.config.getForkName(startSlot);
    if (isForkPostFulu(forkName) && startSlot < chain.earliestAvailableSlot) {
        chain.logger.verbose("Peer did not respect earliestAvailableSlot for BeaconBlocksByRange", {
            peer: prettyPrintPeerId(peerId),
            client: peerClient,
        });
        return;
    }
    // Finalized range of blocks
    if (startSlot <= finalizedSlot) {
        // Chain of blobs won't change
        for await (const { key, value } of finalized.binaryEntriesStream({ gte: startSlot, lt: endSlot })) {
            yield {
                data: value,
                boundary: chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(finalized.decodeKey(key))),
            };
        }
    }
    // Non-finalized range of blocks
    if (endSlot > finalizedSlot) {
        const headBlock = chain.forkChoice.getHead();
        const headRoot = headBlock.blockRoot;
        // TODO DENEB: forkChoice should mantain an array of canonical blocks, and change only on reorg
        const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot, headBlock.payloadStatus);
        // getAllAncestorBlocks response includes the head node, so it's the full chain.
        // Iterate head chain with ascending block numbers
        for (let i = headChain.length - 1; i >= 0; i--) {
            const block = headChain[i];
            // Must include only blocks in the range requested
            if (block.slot >= startSlot && block.slot < endSlot) {
                // Note: Here the forkChoice head may change due to a re-org, so the headChain reflects the canonical chain
                // at the time of the start of the request. Spec is clear the chain of blobs must be consistent, but on
                // re-org there's no need to abort the request
                // Spec: https://github.com/ethereum/consensus-specs/blob/a1e46d1ae47dd9d097725801575b46907c12a1f8/specs/eip4844/p2p-interface.md#blobssidecarsbyrange-v1
                const blockBytes = await chain.getSerializedBlockByRoot(block.blockRoot);
                if (!blockBytes) {
                    throw new ResponseError(RespStatus.SERVER_ERROR, `No block for root ${block.blockRoot} slot ${block.slot}, startSlot=${startSlot} endSlot=${endSlot} finalizedSlot=${finalizedSlot}`);
                }
                yield {
                    data: blockBytes.block,
                    boundary: chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(block.slot)),
                };
            }
            // If block is after endSlot, stop iterating
            else if (block.slot >= endSlot) {
                break;
            }
        }
    }
}
export function validateBeaconBlocksByRangeRequest(config, request) {
    const { startSlot } = request;
    let { count } = request;
    if (count < 1) {
        throw new ResponseError(RespStatus.INVALID_REQUEST, "count < 1");
    }
    // TODO: validate against MIN_EPOCHS_FOR_BLOCK_REQUESTS
    if (startSlot < GENESIS_SLOT) {
        throw new ResponseError(RespStatus.INVALID_REQUEST, "startSlot < genesis");
    }
    // step > 1 is deprecated, see https://github.com/ethereum/consensus-specs/pull/2856
    const maxRequestBlocks = isForkPostDeneb(config.getForkName(startSlot))
        ? config.MAX_REQUEST_BLOCKS_DENEB
        : config.MAX_REQUEST_BLOCKS;
    if (count > maxRequestBlocks) {
        count = maxRequestBlocks;
    }
    return { startSlot, count };
}
//# sourceMappingURL=beaconBlocksByRange.js.map