import { BLOB_SIDECAR_FIXED_SIZE, GENESIS_SLOT } from "@lodestar/params";
import { RespStatus, ResponseError } from "@lodestar/reqresp";
import { computeEpochAtSlot } from "@lodestar/state-transition";
import { fromHex } from "@lodestar/utils";
import { BLOB_SIDECARS_IN_WRAPPER_INDEX } from "../../../db/repositories/blobSidecars.js";
export async function* onBlobSidecarsByRange(request, chain, db) {
    // Non-finalized range of blobs
    const { startSlot, count } = validateBlobSidecarsByRangeRequest(chain.config, request);
    const endSlot = startSlot + count;
    const finalized = db.blobSidecarsArchive;
    const unfinalized = db.blobSidecars;
    const finalizedSlot = chain.forkChoice.getFinalizedBlock().slot;
    // Finalized range of blobs
    if (startSlot <= finalizedSlot) {
        // Chain of blobs won't change
        for await (const { key, value: blobSideCarsBytesWrapped } of finalized.binaryEntriesStream({
            gte: startSlot,
            lt: endSlot,
        })) {
            yield* iterateBlobBytesFromWrapper(chain, blobSideCarsBytesWrapped, finalized.decodeKey(key));
        }
    }
    // Non-finalized range of blobs
    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);
        // Iterate head chain with ascending block numbers
        for (let i = headChain.length - 1; i >= 0; i--) {
            const block = headChain[i];
            // Must include only blobs 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 blobSideCarsBytesWrapped = await unfinalized.getBinary(fromHex(block.blockRoot));
                if (!blobSideCarsBytesWrapped) {
                    // Handle the same to onBeaconBlocksByRange
                    throw new ResponseError(RespStatus.SERVER_ERROR, `No item for root ${block.blockRoot} slot ${block.slot}`);
                }
                yield* iterateBlobBytesFromWrapper(chain, blobSideCarsBytesWrapped, block.slot);
            }
            // If block is after endSlot, stop iterating
            else if (block.slot >= endSlot) {
                break;
            }
        }
    }
}
export function* iterateBlobBytesFromWrapper(chain, blobSideCarsBytesWrapped, blockSlot) {
    const allBlobSideCarsBytes = blobSideCarsBytesWrapped.slice(BLOB_SIDECARS_IN_WRAPPER_INDEX);
    const blobsLen = allBlobSideCarsBytes.length / BLOB_SIDECAR_FIXED_SIZE;
    for (let index = 0; index < blobsLen; index++) {
        const blobSideCarBytes = allBlobSideCarsBytes.slice(index * BLOB_SIDECAR_FIXED_SIZE, (index + 1) * BLOB_SIDECAR_FIXED_SIZE);
        if (blobSideCarBytes.length !== BLOB_SIDECAR_FIXED_SIZE) {
            throw new ResponseError(RespStatus.SERVER_ERROR, `Invalid blobSidecar index=${index} bytes length=${blobSideCarBytes.length} expected=${BLOB_SIDECAR_FIXED_SIZE} for slot ${blockSlot} blobsLen=${blobsLen}`);
        }
        yield {
            data: blobSideCarBytes,
            boundary: chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(blockSlot)),
        };
    }
}
export function validateBlobSidecarsByRangeRequest(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");
    }
    if (count > config.MAX_REQUEST_BLOCKS_DENEB) {
        count = config.MAX_REQUEST_BLOCKS_DENEB;
    }
    return { startSlot, count };
}
//# sourceMappingURL=blobSidecarsByRange.js.map