@lodestar/beacon-node
Version:
A Typescript implementation of the beacon chain
202 lines (182 loc) • 8.61 kB
text/typescript
import {Type} from "@chainsafe/ssz";
import {BeaconConfig} from "@lodestar/config";
import {ForkName, ForkPostAltair, ForkPostFulu, isForkPostAltair, isForkPostFulu} from "@lodestar/params";
import {Protocol, ProtocolHandler, ReqRespRequest} from "@lodestar/reqresp";
import {
DataColumnSidecar,
LightClientBootstrap,
LightClientFinalityUpdate,
LightClientOptimisticUpdate,
LightClientUpdate,
Metadata,
Root,
SignedBeaconBlock,
Status,
altair,
deneb,
fulu,
gloas,
phase0,
ssz,
sszTypesFor,
} from "@lodestar/types";
import {
BeaconBlocksByRootRequest,
BeaconBlocksByRootRequestType,
BlobSidecarsByRootRequest,
BlobSidecarsByRootRequestType,
DataColumnSidecarsByRootRequest,
DataColumnSidecarsByRootRequestType,
ExecutionPayloadEnvelopesByRootRequest,
ExecutionPayloadEnvelopesByRootRequestType,
} from "../../util/types.js";
export type ProtocolNoHandler = Omit<Protocol, "handler">;
/** ReqResp protocol names or methods. Each ReqRespMethod can have multiple versions and encodings */
export enum ReqRespMethod {
// Phase 0
Status = "status",
Goodbye = "goodbye",
Ping = "ping",
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",
DataColumnSidecarsByRoot = "data_column_sidecars_by_root",
ExecutionPayloadEnvelopesByRoot = "execution_payload_envelopes_by_root",
ExecutionPayloadEnvelopesByRange = "execution_payload_envelopes_by_range",
LightClientBootstrap = "light_client_bootstrap",
LightClientUpdatesByRange = "light_client_updates_by_range",
LightClientFinalityUpdate = "light_client_finality_update",
LightClientOptimisticUpdate = "light_client_optimistic_update",
}
// To typesafe events to network
export type RequestBodyByMethod = {
[]: Status;
[]: phase0.Goodbye;
[]: phase0.Ping;
[]: null;
[]: phase0.BeaconBlocksByRangeRequest;
[]: BeaconBlocksByRootRequest;
[]: fulu.BeaconBlocksByHeadRequest;
[]: deneb.BlobSidecarsByRangeRequest;
[]: BlobSidecarsByRootRequest;
[]: fulu.DataColumnSidecarsByRangeRequest;
[]: DataColumnSidecarsByRootRequest;
[]: ExecutionPayloadEnvelopesByRootRequest;
[]: gloas.ExecutionPayloadEnvelopesByRangeRequest;
[]: Root;
[]: altair.LightClientUpdatesByRange;
[]: null;
[]: null;
};
type ResponseBodyByMethod = {
[]: Status;
[]: phase0.Goodbye;
[]: phase0.Ping;
[]: Metadata;
// Do not matter
[]: SignedBeaconBlock;
[]: SignedBeaconBlock;
[]: SignedBeaconBlock;
[]: deneb.BlobSidecar;
[]: deneb.BlobSidecar;
[]: DataColumnSidecar;
[]: DataColumnSidecar;
[]: gloas.SignedExecutionPayloadEnvelope;
[]: gloas.SignedExecutionPayloadEnvelope;
[]: LightClientBootstrap;
[]: LightClientUpdate;
[]: LightClientFinalityUpdate;
[]: LightClientOptimisticUpdate;
};
/** Request SSZ type for each method and ForkName */
export const requestSszTypeByMethod: (
fork: ForkName,
config: BeaconConfig
) => {
[]: RequestBodyByMethod[K] extends null ? null : Type<RequestBodyByMethod[K]>;
} = (fork, config) => ({
// Status type should ideally be determined by protocol version and not fork but since
// we only start using the new status version after the fork this is not an issue
[]: sszTypesFor(fork).Status,
[]: ssz.phase0.Goodbye,
[]: ssz.phase0.Ping,
[]: null,
[]: ssz.phase0.BeaconBlocksByRangeRequest,
[]: BeaconBlocksByRootRequestType(fork, config),
[]: ssz.fulu.BeaconBlocksByHeadRequest,
[]: ssz.deneb.BlobSidecarsByRangeRequest,
[]: BlobSidecarsByRootRequestType(fork, config),
[]: ssz.fulu.DataColumnSidecarsByRangeRequest,
[]: DataColumnSidecarsByRootRequestType(config),
[]: ExecutionPayloadEnvelopesByRootRequestType(config),
[]: ssz.gloas.ExecutionPayloadEnvelopesByRangeRequest,
[]: ssz.Root,
[]: ssz.altair.LightClientUpdatesByRange,
[]: null,
[]: null,
});
export type ResponseTypeGetter<T> = (fork: ForkName, version: number) => Type<T>;
const blocksResponseType: ResponseTypeGetter<SignedBeaconBlock> = (fork, version) => {
if (version === Version.V1) {
return ssz.phase0.SignedBeaconBlock;
}
return ssz[fork].SignedBeaconBlock;
};
export const responseSszTypeByMethod: {[K in ReqRespMethod]: ResponseTypeGetter<ResponseBodyByMethod[K]>} = {
[]: (_, version) => (version === Version.V2 ? ssz.fulu.Status : ssz.phase0.Status),
[]: () => ssz.phase0.Goodbye,
[]: () => ssz.phase0.Ping,
[]: (_, version) =>
version === Version.V1 ? ssz.phase0.Metadata : version === Version.V2 ? ssz.altair.Metadata : ssz.fulu.Metadata,
[]: blocksResponseType,
[]: blocksResponseType,
[]: (fork) => ssz[fork].SignedBeaconBlock,
[]: () => ssz.deneb.BlobSidecar,
[]: () => ssz.deneb.BlobSidecar,
[]: (fork) => sszTypesFor(onlyPostAltairFork(fork)).LightClientBootstrap,
[]: (fork) => sszTypesFor(onlyPostAltairFork(fork)).LightClientUpdate,
[]: (fork) => sszTypesFor(onlyPostAltairFork(fork)).LightClientFinalityUpdate,
[]: (fork) => sszTypesFor(onlyPostFuluFork(fork)).DataColumnSidecar,
[]: (fork) => sszTypesFor(onlyPostFuluFork(fork)).DataColumnSidecar,
[]: () => ssz.gloas.SignedExecutionPayloadEnvelope,
[]: () => ssz.gloas.SignedExecutionPayloadEnvelope,
[]: (fork) =>
sszTypesFor(onlyPostAltairFork(fork)).LightClientOptimisticUpdate,
};
function onlyPostAltairFork(fork: ForkName): ForkPostAltair {
if (isForkPostAltair(fork)) {
return fork;
}
throw Error(`Not a post-altair fork ${fork}`);
}
function onlyPostFuluFork(fork: ForkName): ForkPostFulu {
if (isForkPostFulu(fork)) {
return fork;
}
throw Error(`Not a post-fulu fork ${fork}`);
}
export type RequestTypedContainer = {
[]: {method: K; body: RequestBodyByMethod[K]};
}[ReqRespMethod];
export enum Version {
V1 = 1,
V2 = 2,
V3 = 3,
}
export type OutgoingRequestArgs = {
peerId: string;
method: ReqRespMethod;
versions: number[];
requestData: Uint8Array;
};
export type IncomingRequestArgs = {
method: ReqRespMethod;
req: ReqRespRequest;
peerId: string;
peerClient: string;
};
export type GetReqRespHandlerFn = (method: ReqRespMethod) => ProtocolHandler;