@lodestar/beacon-node
Version:
A Typescript implementation of the beacon chain
92 lines (79 loc) • 3.13 kB
text/typescript
import {PeerId} from "@libp2p/interface";
import {BeaconConfig} from "@lodestar/config";
import {ForkName, GENESIS_EPOCH, GENESIS_SLOT, isForkPostDeneb} from "@lodestar/params";
import {RespStatus, ResponseError, ResponseOutgoing} from "@lodestar/reqresp";
import {computeEpochAtSlot, computeStartSlotAtEpoch} from "@lodestar/state-transition";
import {fulu} from "@lodestar/types";
import {toRootHex} from "@lodestar/utils";
import {IBeaconChain} from "../../../chain/index.js";
import {getParentRootFromSignedBeaconBlockSerialized} from "../../../util/sszBytes.js";
import {prettyPrintPeerId} from "../../util.js";
// See https://github.com/ethereum/consensus-specs/pull/5181
export async function* onBeaconBlocksByHead(
request: fulu.BeaconBlocksByHeadRequest,
chain: IBeaconChain,
peerId: PeerId,
peerClient: string
): AsyncIterable<ResponseOutgoing> {
const currentFork = chain.config.getForkName(chain.clock.currentSlot);
const {beaconRoot, count} = validateBeaconBlocksByHeadRequest(currentFork, chain.config, request);
const requestedRootHex = toRootHex(beaconRoot);
let blockRootHex = requestedRootHex;
const minimumRequestEpoch = Math.max(
GENESIS_EPOCH,
chain.clock.currentEpoch - chain.config.MIN_EPOCHS_FOR_BLOCK_REQUESTS
);
const minimumRequestSlot = computeStartSlotAtEpoch(minimumRequestEpoch);
for (let blocksSent = 0; blocksSent < count; blocksSent++) {
const blockBytes = await chain.getSerializedBlockByRoot(blockRootHex);
if (!blockBytes) {
if (blocksSent === 0) {
throw new ResponseError(RespStatus.RESOURCE_UNAVAILABLE, `Unknown block root ${requestedRootHex}`);
}
return;
}
if (blockBytes.slot < minimumRequestSlot) {
if (blocksSent === 0) {
chain.logger.verbose("Peer requested unavailable block for BeaconBlocksByHead", {
peer: prettyPrintPeerId(peerId),
client: peerClient,
requestedRoot: requestedRootHex,
slot: blockBytes.slot,
minimumRequestSlot,
});
}
return;
}
yield {
data: blockBytes.block,
boundary: chain.config.getForkBoundaryAtEpoch(computeEpochAtSlot(blockBytes.slot)),
};
if (blockBytes.slot === GENESIS_SLOT) {
return;
}
const parentRootHex = getParentRootFromSignedBeaconBlockSerialized(blockBytes.block);
if (parentRootHex === null) {
throw new ResponseError(
RespStatus.SERVER_ERROR,
`Invalid block bytes for root ${blockRootHex} slot ${blockBytes.slot}`
);
}
blockRootHex = parentRootHex;
}
}
export function validateBeaconBlocksByHeadRequest(
fork: ForkName,
config: BeaconConfig,
request: fulu.BeaconBlocksByHeadRequest
): fulu.BeaconBlocksByHeadRequest {
const {beaconRoot} = request;
let {count} = request;
if (count < 1) {
throw new ResponseError(RespStatus.INVALID_REQUEST, "count < 1");
}
const maxRequestBlocks = isForkPostDeneb(fork) ? config.MAX_REQUEST_BLOCKS_DENEB : config.MAX_REQUEST_BLOCKS;
if (count > maxRequestBlocks) {
count = maxRequestBlocks;
}
return {beaconRoot, count};
}