UNPKG

@lodestar/beacon-node

Version:

A Typescript implementation of the beacon chain

241 lines (211 loc) • 9.77 kB
import type {Message, TopicValidatorResult} from "@libp2p/gossipsub"; import type {PeerIdStr} from "@libp2p/gossipsub/types"; import type {Libp2p} from "libp2p"; import {BeaconConfig, ForkBoundary} from "@lodestar/config"; import { AttesterSlashing, DataColumnSidecar, LightClientFinalityUpdate, LightClientOptimisticUpdate, SignedAggregateAndProof, SignedBeaconBlock, SingleAttestation, Slot, SubnetID, altair, capella, deneb, gloas, phase0, } from "@lodestar/types"; import {Logger} from "@lodestar/utils"; import {AttestationError, AttestationErrorType} from "../../chain/errors/attestationError.js"; import {GossipActionError} from "../../chain/errors/gossipValidation.js"; import {IBeaconChain} from "../../chain/index.js"; import {JobItemQueue} from "../../util/queue/index.js"; export enum GossipType { beacon_block = "beacon_block", blob_sidecar = "blob_sidecar", data_column_sidecar = "data_column_sidecar", beacon_aggregate_and_proof = "beacon_aggregate_and_proof", beacon_attestation = "beacon_attestation", voluntary_exit = "voluntary_exit", proposer_slashing = "proposer_slashing", attester_slashing = "attester_slashing", sync_committee_contribution_and_proof = "sync_committee_contribution_and_proof", sync_committee = "sync_committee", light_client_finality_update = "light_client_finality_update", light_client_optimistic_update = "light_client_optimistic_update", bls_to_execution_change = "bls_to_execution_change", execution_payload = "execution_payload", payload_attestation_message = "payload_attestation_message", execution_payload_bid = "execution_payload_bid", proposer_preferences = "proposer_preferences", } export type SequentialGossipType = Exclude<GossipType, GossipType.beacon_attestation>; export type BatchGossipType = GossipType.beacon_attestation; export enum GossipEncoding { ssz_snappy = "ssz_snappy", } /** * Note: `IGossipTopic`s are all relative to the local `genesisValidatorsRoot` */ export interface IGossipTopic { type: GossipType; boundary: ForkBoundary; encoding?: GossipEncoding; } export type GossipTopicTypeMap = { [GossipType.beacon_block]: {type: GossipType.beacon_block}; [GossipType.blob_sidecar]: {type: GossipType.blob_sidecar; subnet: SubnetID}; [GossipType.data_column_sidecar]: {type: GossipType.data_column_sidecar; subnet: SubnetID}; [GossipType.beacon_aggregate_and_proof]: {type: GossipType.beacon_aggregate_and_proof}; [GossipType.beacon_attestation]: {type: GossipType.beacon_attestation; subnet: SubnetID}; [GossipType.voluntary_exit]: {type: GossipType.voluntary_exit}; [GossipType.proposer_slashing]: {type: GossipType.proposer_slashing}; [GossipType.attester_slashing]: {type: GossipType.attester_slashing}; [GossipType.sync_committee_contribution_and_proof]: { type: GossipType.sync_committee_contribution_and_proof; }; [GossipType.sync_committee]: {type: GossipType.sync_committee; subnet: SubnetID}; [GossipType.light_client_finality_update]: {type: GossipType.light_client_finality_update}; [GossipType.light_client_optimistic_update]: {type: GossipType.light_client_optimistic_update}; [GossipType.bls_to_execution_change]: {type: GossipType.bls_to_execution_change}; [GossipType.execution_payload]: {type: GossipType.execution_payload}; [GossipType.payload_attestation_message]: {type: GossipType.payload_attestation_message}; [GossipType.execution_payload_bid]: {type: GossipType.execution_payload_bid}; [GossipType.proposer_preferences]: {type: GossipType.proposer_preferences}; }; export type GossipTopicMap = { [K in keyof GossipTopicTypeMap]: GossipTopicTypeMap[K] & IGossipTopic; }; /** * Gossip topic split into a struct */ export type GossipTopic = GossipTopicMap[keyof GossipTopicMap]; export type SSZTypeOfGossipTopic<T extends GossipTopic> = T extends {type: infer K extends GossipType} ? GossipTypeMap[K] : never; export type GossipTypeMap = { [GossipType.beacon_block]: SignedBeaconBlock; [GossipType.blob_sidecar]: deneb.BlobSidecar; [GossipType.beacon_aggregate_and_proof]: SignedAggregateAndProof; [GossipType.beacon_attestation]: SingleAttestation; [GossipType.data_column_sidecar]: DataColumnSidecar; [GossipType.voluntary_exit]: phase0.SignedVoluntaryExit; [GossipType.proposer_slashing]: phase0.ProposerSlashing; [GossipType.attester_slashing]: AttesterSlashing; [GossipType.sync_committee_contribution_and_proof]: altair.SignedContributionAndProof; [GossipType.sync_committee]: altair.SyncCommitteeMessage; [GossipType.light_client_finality_update]: LightClientFinalityUpdate; [GossipType.light_client_optimistic_update]: LightClientOptimisticUpdate; [GossipType.bls_to_execution_change]: capella.SignedBLSToExecutionChange; [GossipType.execution_payload]: gloas.SignedExecutionPayloadEnvelope; [GossipType.payload_attestation_message]: gloas.PayloadAttestationMessage; [GossipType.execution_payload_bid]: gloas.SignedExecutionPayloadBid; [GossipType.proposer_preferences]: gloas.SignedProposerPreferences; }; export type GossipFnByType = { [GossipType.beacon_block]: (signedBlock: SignedBeaconBlock) => Promise<void> | void; [GossipType.blob_sidecar]: (blobSidecar: deneb.BlobSidecar) => Promise<void> | void; [GossipType.beacon_aggregate_and_proof]: (aggregateAndProof: SignedAggregateAndProof) => Promise<void> | void; [GossipType.beacon_attestation]: (attestation: SingleAttestation) => Promise<void> | void; [GossipType.data_column_sidecar]: (dataColumnSidecar: DataColumnSidecar) => Promise<void> | void; [GossipType.voluntary_exit]: (voluntaryExit: phase0.SignedVoluntaryExit) => Promise<void> | void; [GossipType.proposer_slashing]: (proposerSlashing: phase0.ProposerSlashing) => Promise<void> | void; [GossipType.attester_slashing]: (attesterSlashing: AttesterSlashing) => Promise<void> | void; [GossipType.sync_committee_contribution_and_proof]: ( signedContributionAndProof: altair.SignedContributionAndProof ) => Promise<void> | void; [GossipType.sync_committee]: (syncCommittee: altair.SyncCommitteeMessage) => Promise<void> | void; [GossipType.light_client_finality_update]: ( lightClientFinalityUpdate: LightClientFinalityUpdate ) => Promise<void> | void; [GossipType.light_client_optimistic_update]: ( lightClientOptimisticUpdate: LightClientOptimisticUpdate ) => Promise<void> | void; [GossipType.bls_to_execution_change]: ( blsToExecutionChange: capella.SignedBLSToExecutionChange ) => Promise<void> | void; [GossipType.execution_payload]: ( executionPayloadEnvelope: gloas.SignedExecutionPayloadEnvelope ) => Promise<void> | void; [GossipType.payload_attestation_message]: ( payloadAttestationMessage: gloas.PayloadAttestationMessage ) => Promise<void> | void; [GossipType.execution_payload_bid]: (executionPayloadBid: gloas.SignedExecutionPayloadBid) => Promise<void> | void; [GossipType.proposer_preferences]: ( signedProposerPreferences: gloas.SignedProposerPreferences ) => Promise<void> | void; }; export type GossipFn = GossipFnByType[keyof GossipFnByType]; export type GossipModules = { config: BeaconConfig; libp2p: Libp2p; logger: Logger; chain: IBeaconChain; }; /** * Contains various methods for validation of incoming gossip topic data. * The conditions for valid gossip topics and how they are handled are specified here: * https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/p2p-interface.md#global-topics */ /** * Top-level type for gossip validation functions * * js-libp2p-gossipsub expects validation functions that look like this */ export type GossipMessageInfo = { topic: GossipTopic; msg: Message; propagationSource: PeerIdStr; clientAgent: string; clientVersion: string; seenTimestampSec: number; msgSlot: Slot | null; indexed?: string; }; export type GossipValidatorFn = (messageInfo: GossipMessageInfo) => Promise<TopicValidatorResult>; export type GossipValidatorBatchFn = (messageInfos: GossipMessageInfo[]) => Promise<TopicValidatorResult[]>; export type ValidatorFnsByType = {[K in GossipType]: GossipValidatorFn}; export type GossipJobQueues = { [K in GossipType]: JobItemQueue<Parameters<GossipValidatorFn>, ResolvedType<GossipValidatorFn>>; }; export type GossipData = { serializedData: Uint8Array; msgSlot?: Slot | null; indexed?: string; }; export type GossipHandlerParam = { gossipData: GossipData; topic: GossipTopicMap[GossipType]; peerIdStr: string; seenTimestampSec: number; }; export type GossipHandlerFn = (gossipHandlerParam: GossipHandlerParam) => Promise<void>; export type BatchGossipHandlerFn = (gossipHandlerParam: GossipHandlerParam[]) => Promise<(null | AttestationError)[]>; export type GossipHandlerParamGeneric<T extends GossipType> = { gossipData: GossipData; topic: GossipTopicMap[T]; peerIdStr: string; seenTimestampSec: number; }; export type GossipHandlers = { [K in GossipType]: SequentialGossipHandler<K> | BatchGossipHandler<K>; }; export type SequentialGossipHandler<K extends GossipType> = ( gossipHandlerParam: GossipHandlerParamGeneric<K> ) => Promise<void>; export type SequentialGossipHandlers = { [K in SequentialGossipType]: SequentialGossipHandler<K>; }; export type BatchGossipHandlers = { [K in BatchGossipType]: BatchGossipHandler<K>; }; export type BatchGossipHandler<K extends GossipType> = ( gossipHandlerParams: GossipHandlerParamGeneric<K>[] ) => Promise<(null | GossipActionError<AttestationErrorType>)[]>; // biome-ignore lint/suspicious/noExplicitAny: Need the usage of `any` here to infer any type export type ResolvedType<F extends (...args: any) => Promise<any>> = F extends (...args: any) => Promise<infer T> ? T : never;