@lodestar/beacon-node
Version:
A Typescript implementation of the beacon chain
87 lines • 4.79 kB
JavaScript
import { ForkSeq } from "@lodestar/params";
import { computeEpochAtSlot } from "@lodestar/state-transition";
import { BlobsSource, BlockSource, getBlockInput } from "../../chain/blocks/types.js";
export async function beaconBlocksMaybeBlobsByRange(config, network, peerId, request, currentEpoch) {
// Code below assumes the request is in the same epoch
// Range sync satisfies this condition, but double check here for sanity
const { startSlot, count } = request;
if (count < 1) {
return [];
}
const endSlot = startSlot + count - 1;
const startEpoch = computeEpochAtSlot(startSlot);
const endEpoch = computeEpochAtSlot(endSlot);
if (startEpoch !== endEpoch) {
throw Error(`BeaconBlocksByRangeRequest must be in the same epoch startEpoch=${startEpoch} != endEpoch=${endEpoch}`);
}
// Note: Assumes all blocks in the same epoch
if (config.getForkSeq(startSlot) < ForkSeq.deneb) {
const blocks = await network.sendBeaconBlocksByRange(peerId, request);
return blocks.map((block) => getBlockInput.preData(config, block.data, BlockSource.byRange));
}
// From Deneb
// Only request blobs if they are recent enough
if (startEpoch >= currentEpoch - config.MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS) {
const [allBlocks, allBlobSidecars] = await Promise.all([
network.sendBeaconBlocksByRange(peerId, request),
network.sendBlobSidecarsByRange(peerId, request),
]);
return matchBlockWithBlobs(config, allBlocks, allBlobSidecars, endSlot, BlockSource.byRange, BlobsSource.byRange);
}
// Data is out of range, only request blocks
const blocks = await network.sendBeaconBlocksByRange(peerId, request);
return blocks.map((block) => getBlockInput.outOfRangeData(config, block.data, BlockSource.byRange));
}
// Assumes that the blobs are in the same sequence as blocks, doesn't require block to be sorted
export function matchBlockWithBlobs(config, allBlocks, allBlobSidecars, endSlot, blockSource, blobsSource) {
const blockInputs = [];
let blobSideCarIndex = 0;
let lastMatchedSlot = -1;
// Match blobSideCar with the block as some blocks would have no blobs and hence
// would be omitted from the response. If there are any inconsitencies in the
// response, the validations during import will reject the block and hence this
// entire segment.
//
// Assuming that the blocks and blobs will come in same sorted order
for (let i = 0; i < allBlocks.length; i++) {
const block = allBlocks[i];
if (config.getForkSeq(block.data.message.slot) < ForkSeq.deneb) {
blockInputs.push(getBlockInput.preData(config, block.data, blockSource));
}
else {
const blobSidecars = [];
let blobSidecar;
while (
// biome-ignore lint/suspicious/noAssignInExpressions: <explanation>
(blobSidecar = allBlobSidecars[blobSideCarIndex])?.signedBlockHeader.message.slot === block.data.message.slot) {
blobSidecars.push(blobSidecar);
lastMatchedSlot = block.data.message.slot;
blobSideCarIndex++;
}
// Quick inspect how many blobSidecars was expected
const blobKzgCommitmentsLen = block.data.message.body.blobKzgCommitments.length;
if (blobKzgCommitmentsLen !== blobSidecars.length) {
throw Error(`Missing blobSidecars for blockSlot=${block.data.message.slot} with blobKzgCommitmentsLen=${blobKzgCommitmentsLen} blobSidecars=${blobSidecars.length}`);
}
const blockData = {
fork: config.getForkName(block.data.message.slot),
blobs: blobSidecars,
blobsSource,
};
// TODO DENEB: instead of null, pass payload in bytes
blockInputs.push(getBlockInput.availableData(config, block.data, blockSource, blockData));
}
}
// If there are still unconsumed blobs this means that the response was inconsistent
// and matching was wrong and hence we should throw error
if (allBlobSidecars[blobSideCarIndex] !== undefined &&
// If there are no blobs, the blobs request can give 1 block outside the requested range
allBlobSidecars[blobSideCarIndex].signedBlockHeader.message.slot <= endSlot) {
throw Error(`Unmatched blobSidecars, blocks=${allBlocks.length}, blobs=${allBlobSidecars.length} lastMatchedSlot=${lastMatchedSlot}, pending blobSidecars slots=${allBlobSidecars
.slice(blobSideCarIndex)
.map((blb) => blb.signedBlockHeader.message.slot)
.join(",")}`);
}
return blockInputs;
}
//# sourceMappingURL=beaconBlocksMaybeBlobsByRange.js.map