Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions packages/beacon-node/src/network/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {BlockInputSource} from "../chain/blocks/blockInput/types.js";
import {CustodyConfig} from "../util/dataColumns.js";
import {PeerIdStr} from "../util/peerId.js";
import {
BeaconBlocksByHeadRequest,
BeaconBlocksByRootRequest,
BlobSidecarsByRootRequest,
DataColumnSidecarsByRootRequest,
Expand Down Expand Up @@ -78,6 +79,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: BeaconBlocksByHeadRequest): Promise<SignedBeaconBlock[]>;
sendBlobSidecarsByRange(peerId: PeerIdStr, request: deneb.BlobSidecarsByRangeRequest): Promise<deneb.BlobSidecar[]>;
sendBlobSidecarsByRoot(peerId: PeerIdStr, request: BlobSidecarsByRootRequest): Promise<deneb.BlobSidecar[]>;
sendDataColumnSidecarsByRange(
Expand Down
10 changes: 10 additions & 0 deletions packages/beacon-node/src/network/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {CustodyConfig} from "../util/dataColumns.js";
import {PeerIdStr, peerIdToString} from "../util/peerId.js";
import {promiseAllMaybeAsync} from "../util/promises.js";
import {
BeaconBlocksByHeadRequest,
BeaconBlocksByRootRequest,
BlobSidecarsByRootRequest,
DataColumnSidecarsByRootRequest,
Expand Down Expand Up @@ -578,6 +579,15 @@ export class Network implements INetwork {
);
}

async sendBeaconBlocksByHead(peerId: PeerIdStr, request: 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,86 @@
import {PeerId} from "@libp2p/interface";
import {BeaconConfig} from "@lodestar/config";
import {GENESIS_EPOCH, GENESIS_SLOT} from "@lodestar/params";
import {RespStatus, ResponseError, ResponseOutgoing} from "@lodestar/reqresp";
import {computeEpochAtSlot, computeStartSlotAtEpoch} from "@lodestar/state-transition";
import {toRootHex} from "@lodestar/utils";
import {IBeaconChain} from "../../../chain/index.js";
import {getParentRootFromSignedBeaconBlockSerialized} from "../../../util/sszBytes.js";
import {BeaconBlocksByHeadRequest} from "../../../util/types.js";
import {prettyPrintPeerId} from "../../util.js";

export async function* onBeaconBlocksByHead(
Comment thread
wemeetagain marked this conversation as resolved.
request: BeaconBlocksByHeadRequest,
chain: IBeaconChain,
peerId: PeerId,
peerClient: string
): AsyncIterable<ResponseOutgoing> {
const {beaconRoot, count} = validateBeaconBlocksByHeadRequest(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.
config: BeaconConfig,
request: BeaconBlocksByHeadRequest
): BeaconBlocksByHeadRequest {
const {beaconRoot} = request;
let {count} = request;

if (count < 0) {
Comment thread
wemeetagain marked this conversation as resolved.
Outdated
throw new ResponseError(RespStatus.INVALID_REQUEST, "count < 0");
}

if (count > config.MAX_REQUEST_BLOCKS_DENEB) {
count = config.MAX_REQUEST_BLOCKS_DENEB;
}

return {beaconRoot, count};
}
6 changes: 6 additions & 0 deletions packages/beacon-node/src/network/reqresp/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import {ssz} from "@lodestar/types";
import {IBeaconChain} from "../../../chain/index.js";
import {IBeaconDb} from "../../../db/index.js";
import {
BeaconBlocksByHeadRequestType,
BeaconBlocksByRootRequestType,
BlobSidecarsByRootRequestType,
DataColumnSidecarsByRootRequestType,
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 +49,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 = BeaconBlocksByHeadRequestType.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
7 changes: 7 additions & 0 deletions packages/beacon-node/src/network/reqresp/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
sszTypesFor,
} from "@lodestar/types";
import {
BeaconBlocksByHeadRequest,
BeaconBlocksByHeadRequestType,
BeaconBlocksByRootRequest,
BeaconBlocksByRootRequestType,
BlobSidecarsByRootRequest,
Expand All @@ -42,6 +44,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 +65,7 @@ export type RequestBodyByMethod = {
[ReqRespMethod.Metadata]: null;
[ReqRespMethod.BeaconBlocksByRange]: phase0.BeaconBlocksByRangeRequest;
[ReqRespMethod.BeaconBlocksByRoot]: BeaconBlocksByRootRequest;
[ReqRespMethod.BeaconBlocksByHead]: BeaconBlocksByHeadRequest;
[ReqRespMethod.BlobSidecarsByRange]: deneb.BlobSidecarsByRangeRequest;
[ReqRespMethod.BlobSidecarsByRoot]: BlobSidecarsByRootRequest;
[ReqRespMethod.DataColumnSidecarsByRange]: fulu.DataColumnSidecarsByRangeRequest;
Expand All @@ -82,6 +86,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 +116,7 @@ export const requestSszTypeByMethod: (

[ReqRespMethod.BeaconBlocksByRange]: ssz.phase0.BeaconBlocksByRangeRequest,
[ReqRespMethod.BeaconBlocksByRoot]: BeaconBlocksByRootRequestType(fork, config),
[ReqRespMethod.BeaconBlocksByHead]: BeaconBlocksByHeadRequestType,
[ReqRespMethod.BlobSidecarsByRange]: ssz.deneb.BlobSidecarsByRangeRequest,
[ReqRespMethod.BlobSidecarsByRoot]: BlobSidecarsByRootRequestType(fork, config),
[ReqRespMethod.DataColumnSidecarsByRange]: ssz.fulu.DataColumnSidecarsByRangeRequest,
Expand Down Expand Up @@ -142,6 +148,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
9 changes: 9 additions & 0 deletions packages/beacon-node/src/util/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ export const BeaconBlocksByRootRequestType = (fork: ForkName, config: BeaconConf
new ListCompositeType(ssz.Root, isForkPostDeneb(fork) ? config.MAX_REQUEST_BLOCKS_DENEB : config.MAX_REQUEST_BLOCKS);
export type BeaconBlocksByRootRequest = ValueOf<ReturnType<typeof BeaconBlocksByRootRequestType>>;

export const BeaconBlocksByHeadRequestType = new ContainerType(
Comment thread
wemeetagain marked this conversation as resolved.
Outdated
{
beaconRoot: ssz.Root,
count: ssz.UintNum64,
},
{typeName: "BeaconBlocksByHeadRequest", jsonCase: "eth2"}
);
export type BeaconBlocksByHeadRequest = ValueOf<typeof BeaconBlocksByHeadRequestType>;

export const BlobSidecarsByRootRequestType = (fork: ForkName, config: BeaconConfig) =>
new ListCompositeType(
ssz.deneb.BlobIdentifier,
Expand Down
Loading
Loading