Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/beacon-node/src/network/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export interface INetwork extends INetworkCorePublic {
// ReqResp
sendBeaconBlocksByRange(peerId: PeerIdStr, request: phase0.BeaconBlocksByRangeRequest): Promise<SignedBeaconBlock[]>;
sendBeaconBlocksByRoot(peerId: PeerIdStr, request: BeaconBlocksByRootRequest): Promise<SignedBeaconBlock[]>;
sendBeaconBlocksByHead(peerId: PeerIdStr, request: fulu.BeaconBlocksByHeadRequest): Promise<SignedBeaconBlock[]>;
sendBlobSidecarsByRange(peerId: PeerIdStr, request: deneb.BlobSidecarsByRangeRequest): Promise<deneb.BlobSidecar[]>;
sendBlobSidecarsByRoot(peerId: PeerIdStr, request: BlobSidecarsByRootRequest): Promise<deneb.BlobSidecar[]>;
sendDataColumnSidecarsByRange(
Expand Down
12 changes: 12 additions & 0 deletions packages/beacon-node/src/network/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,18 @@ export class Network implements INetwork {
);
}

async sendBeaconBlocksByHead(
peerId: PeerIdStr,
request: fulu.BeaconBlocksByHeadRequest
): Promise<SignedBeaconBlock[]> {
return collectMaxResponseTypedWithBytes(
this.sendReqRespRequest(peerId, ReqRespMethod.BeaconBlocksByHead, [Version.V1], request),
Math.min(request.count, this.config.MAX_REQUEST_BLOCKS_DENEB),
responseSszTypeByMethod[ReqRespMethod.BeaconBlocksByHead],
this.chain.serializedCache
);
}

async sendLightClientBootstrap(peerId: PeerIdStr, request: Root): Promise<LightClientBootstrap> {
return collectExactOneTyped(
this.sendReqRespRequest(peerId, ReqRespMethod.LightClientBootstrap, [Version.V1], request),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ export class ReqRespBeaconNode extends ReqResp {
// instead of protocol version. This is not easily fixable with our current architecture.
// See https://github.com/ChainSafe/lodestar/pull/8168 for more details.
[protocols.StatusV2(fork, this.config), this.onStatus.bind(this)],
[protocols.BeaconBlocksByHead(fork, this.config), this.getHandler(ReqRespMethod.BeaconBlocksByHead)],
[
protocols.DataColumnSidecarsByRoot(fork, this.config),
this.getHandler(ReqRespMethod.DataColumnSidecarsByRoot),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
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(
Comment thread
wemeetagain marked this conversation as resolved.
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(
Comment thread
wemeetagain marked this conversation as resolved.
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};
}
5 changes: 5 additions & 0 deletions packages/beacon-node/src/network/reqresp/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ExecutionPayloadEnvelopesByRootRequestType,
} from "../../../util/types.js";
import {GetReqRespHandlerFn, ReqRespMethod} from "../types.js";
import {onBeaconBlocksByHead} from "./beaconBlocksByHead.js";
import {onBeaconBlocksByRange} from "./beaconBlocksByRange.js";
import {onBeaconBlocksByRoot} from "./beaconBlocksByRoot.js";
import {onBlobSidecarsByRange} from "./blobSidecarsByRange.js";
Expand Down Expand Up @@ -47,6 +48,10 @@ export function getReqRespHandlers({db, chain}: {db: IBeaconDb; chain: IBeaconCh
const body = BeaconBlocksByRootRequestType(fork, chain.config).deserialize(req.data);
return onBeaconBlocksByRoot(body, chain);
},
[ReqRespMethod.BeaconBlocksByHead]: (req, peerId, peerClient) => {
const body = ssz.fulu.BeaconBlocksByHeadRequest.deserialize(req.data);
return onBeaconBlocksByHead(body, chain, peerId, peerClient);
},
[ReqRespMethod.BlobSidecarsByRoot]: (req) => {
const fork = chain.config.getForkName(chain.clock.currentSlot);
const body = BlobSidecarsByRootRequestType(fork, chain.config).deserialize(req.data);
Expand Down
2 changes: 1 addition & 1 deletion packages/beacon-node/src/network/reqresp/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export enum RespStatus {
*/
SERVER_ERROR = 2,
/**
* The responder does not have requested resource. The response payload adheres to the ErrorMessage schema (described below). Note: This response code is only valid as a response to BlocksByRange
* The responder does not have requested resource. The response payload adheres to the ErrorMessage schema.
*/
RESOURCE_UNAVAILABLE = 3,
/**
Expand Down
6 changes: 6 additions & 0 deletions packages/beacon-node/src/network/reqresp/protocols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ export const BeaconBlocksByRootV2 = toProtocol({
contextBytesType: ContextBytesType.ForkDigest,
});

export const BeaconBlocksByHead = toProtocol({
method: ReqRespMethod.BeaconBlocksByHead,
version: Version.V1,
contextBytesType: ContextBytesType.ForkDigest,
});

export const BlobSidecarsByRange = toProtocol({
method: ReqRespMethod.BlobSidecarsByRange,
version: Version.V1,
Expand Down
4 changes: 4 additions & 0 deletions packages/beacon-node/src/network/reqresp/rateLimit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export const rateLimitQuotas: (fork: ForkName, config: BeaconConfig) => Record<R
},
getRequestCount: getRequestCountFn(fork, config, ReqRespMethod.BeaconBlocksByRoot, (req) => req.length),
},
[ReqRespMethod.BeaconBlocksByHead]: {
byPeer: {quota: config.MAX_REQUEST_BLOCKS_DENEB, quotaTimeMs: 10_000},
getRequestCount: getRequestCountFn(fork, config, ReqRespMethod.BeaconBlocksByHead, (req) => req.count),
},
[ReqRespMethod.BlobSidecarsByRange]: {
// Rationale: MAX_REQUEST_BLOCKS_DENEB * MAX_BLOBS_PER_BLOCK
byPeer: {
Expand Down
1 change: 1 addition & 0 deletions packages/beacon-node/src/network/reqresp/score.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export function onOutgoingReqRespError(e: RequestError, method: ReqRespMethod):
return PeerAction.LowToleranceError;
case ReqRespMethod.BeaconBlocksByRange:
case ReqRespMethod.BeaconBlocksByRoot:
case ReqRespMethod.BeaconBlocksByHead:
case ReqRespMethod.ExecutionPayloadEnvelopesByRoot:
case ReqRespMethod.ExecutionPayloadEnvelopesByRange:
return PeerAction.MidToleranceError;
Expand Down
5 changes: 5 additions & 0 deletions packages/beacon-node/src/network/reqresp/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export enum ReqRespMethod {
Metadata = "metadata",
BeaconBlocksByRange = "beacon_blocks_by_range",
BeaconBlocksByRoot = "beacon_blocks_by_root",
BeaconBlocksByHead = "beacon_blocks_by_head",
BlobSidecarsByRange = "blob_sidecars_by_range",
BlobSidecarsByRoot = "blob_sidecars_by_root",
DataColumnSidecarsByRange = "data_column_sidecars_by_range",
Expand All @@ -62,6 +63,7 @@ export type RequestBodyByMethod = {
[ReqRespMethod.Metadata]: null;
[ReqRespMethod.BeaconBlocksByRange]: phase0.BeaconBlocksByRangeRequest;
[ReqRespMethod.BeaconBlocksByRoot]: BeaconBlocksByRootRequest;
[ReqRespMethod.BeaconBlocksByHead]: fulu.BeaconBlocksByHeadRequest;
[ReqRespMethod.BlobSidecarsByRange]: deneb.BlobSidecarsByRangeRequest;
[ReqRespMethod.BlobSidecarsByRoot]: BlobSidecarsByRootRequest;
[ReqRespMethod.DataColumnSidecarsByRange]: fulu.DataColumnSidecarsByRangeRequest;
Expand All @@ -82,6 +84,7 @@ type ResponseBodyByMethod = {
// Do not matter
[ReqRespMethod.BeaconBlocksByRange]: SignedBeaconBlock;
[ReqRespMethod.BeaconBlocksByRoot]: SignedBeaconBlock;
[ReqRespMethod.BeaconBlocksByHead]: SignedBeaconBlock;
[ReqRespMethod.BlobSidecarsByRange]: deneb.BlobSidecar;
[ReqRespMethod.BlobSidecarsByRoot]: deneb.BlobSidecar;
[ReqRespMethod.DataColumnSidecarsByRange]: DataColumnSidecar;
Expand Down Expand Up @@ -111,6 +114,7 @@ export const requestSszTypeByMethod: (

[ReqRespMethod.BeaconBlocksByRange]: ssz.phase0.BeaconBlocksByRangeRequest,
[ReqRespMethod.BeaconBlocksByRoot]: BeaconBlocksByRootRequestType(fork, config),
[ReqRespMethod.BeaconBlocksByHead]: ssz.fulu.BeaconBlocksByHeadRequest,
[ReqRespMethod.BlobSidecarsByRange]: ssz.deneb.BlobSidecarsByRangeRequest,
[ReqRespMethod.BlobSidecarsByRoot]: BlobSidecarsByRootRequestType(fork, config),
[ReqRespMethod.DataColumnSidecarsByRange]: ssz.fulu.DataColumnSidecarsByRangeRequest,
Expand Down Expand Up @@ -142,6 +146,7 @@ export const responseSszTypeByMethod: {[K in ReqRespMethod]: ResponseTypeGetter<
version === Version.V1 ? ssz.phase0.Metadata : version === Version.V2 ? ssz.altair.Metadata : ssz.fulu.Metadata,
[ReqRespMethod.BeaconBlocksByRange]: blocksResponseType,
[ReqRespMethod.BeaconBlocksByRoot]: blocksResponseType,
[ReqRespMethod.BeaconBlocksByHead]: (fork) => ssz[fork].SignedBeaconBlock,
[ReqRespMethod.BlobSidecarsByRange]: () => ssz.deneb.BlobSidecar,
[ReqRespMethod.BlobSidecarsByRoot]: () => ssz.deneb.BlobSidecar,
[ReqRespMethod.LightClientBootstrap]: (fork) => sszTypesFor(onlyPostAltairFork(fork)).LightClientBootstrap,
Expand Down
Loading
Loading