@lodestar/beacon-node
Version:
A Typescript implementation of the beacon chain
73 lines • 3.71 kB
JavaScript
import { GENESIS_SLOT, MAX_REQUEST_BLOCKS, MAX_REQUEST_BLOCKS_DENEB, isForkPostDeneb } from "@lodestar/params";
import { RespStatus, ResponseError } from "@lodestar/reqresp";
import { computeEpochAtSlot } from "@lodestar/state-transition";
import { fromHex } from "@lodestar/utils";
// TODO: Unit test
export async function* onBeaconBlocksByRange(request, chain, db) {
const { startSlot, count } = validateBeaconBlocksByRangeRequest(chain.config, request);
const endSlot = startSlot + count;
const finalized = db.blockArchive;
const unfinalized = db.block;
const finalizedSlot = chain.forkChoice.getFinalizedBlock().slot;
// 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 headRoot = chain.forkChoice.getHeadRoot();
// TODO DENEB: forkChoice should mantain an array of canonical blocks, and change only on reorg
const headChain = chain.forkChoice.getAllAncestorBlocks(headRoot);
// 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 unfinalized.getBinary(fromHex(block.blockRoot));
if (!blockBytes) {
// Handle the same to onBeaconBlocksByRange
throw new ResponseError(RespStatus.SERVER_ERROR, `No item for root ${block.blockRoot} slot ${block.slot}`);
}
yield {
data: blockBytes,
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))
? MAX_REQUEST_BLOCKS_DENEB
: MAX_REQUEST_BLOCKS;
if (count > maxRequestBlocks) {
count = maxRequestBlocks;
}
return { startSlot, count };
}
//# sourceMappingURL=beaconBlocksByRange.js.map