diff --git a/packages/api/src/beacon/routes/events.ts b/packages/api/src/beacon/routes/events.ts index 105da89fced5..83c0613288aa 100644 --- a/packages/api/src/beacon/routes/events.ts +++ b/packages/api/src/beacon/routes/events.ts @@ -107,8 +107,8 @@ export type EventData = { executionOptimistic: boolean; }; [EventType.contributionAndProof]: altair.SignedContributionAndProof; - [EventType.lightClientOptimisticUpdate]: allForks.LightClientOptimisticUpdate; - [EventType.lightClientFinalityUpdate]: allForks.LightClientFinalityUpdate; + [EventType.lightClientOptimisticUpdate]: {version: ForkName; data: allForks.LightClientOptimisticUpdate}; + [EventType.lightClientFinalityUpdate]: {version: ForkName; data: allForks.LightClientFinalityUpdate}; [EventType.lightClientUpdate]: allForks.LightClientUpdate; [EventType.payloadAttributes]: {version: ForkName; data: allForks.SSEPayloadAttributes}; [EventType.blobSidecar]: BlobSidecarSSE; @@ -208,28 +208,24 @@ export function getTypeByEvent(config: ChainForkConfig): {[K in EventType]: Type ), [EventType.blobSidecar]: blobSidecarSSE, - [EventType.lightClientOptimisticUpdate]: { - toJson: (data) => - getLightClientTypeFromHeader((data as unknown as allForks.LightClientOptimisticUpdate).attestedHeader)[ + [EventType.lightClientOptimisticUpdate]: WithVersion( + (_, data) => getLightClientTypeFromHeader((data as allForks.LightClientOptimisticUpdate).attestedHeader)[ "LightClientOptimisticUpdate" - ].toJson(data), - fromJson: (data) => - getLightClientTypeFromHeader( + ], + (_, data) => getLightClientTypeFromHeader( // eslint-disable-next-line @typescript-eslint/naming-convention (data as {attested_header: allForks.LightClientHeader}).attested_header - )["LightClientOptimisticUpdate"].fromJson(data), - }, - [EventType.lightClientFinalityUpdate]: { - toJson: (data) => - getLightClientTypeFromHeader((data as unknown as allForks.LightClientFinalityUpdate).attestedHeader)[ + )["LightClientOptimisticUpdate"] + ), + [EventType.lightClientFinalityUpdate]: WithVersion( + (_, data) => getLightClientTypeFromHeader((data as unknown as allForks.LightClientFinalityUpdate).attestedHeader)[ "LightClientFinalityUpdate" - ].toJson(data), - fromJson: (data) => - getLightClientTypeFromHeader( + ], + (_, data) => getLightClientTypeFromHeader( // eslint-disable-next-line @typescript-eslint/naming-convention (data as {attested_header: allForks.LightClientHeader}).attested_header - )["LightClientFinalityUpdate"].fromJson(data), - }, + )["LightClientFinalityUpdate"] + ), [EventType.lightClientUpdate]: { toJson: (data) => getLightClientTypeFromHeader((data as unknown as allForks.LightClientUpdate).attestedHeader)[ diff --git a/packages/api/src/utils/types.ts b/packages/api/src/utils/types.ts index b01649a0ef38..75db14708d4e 100644 --- a/packages/api/src/utils/types.ts +++ b/packages/api/src/utils/types.ts @@ -139,10 +139,10 @@ export function ContainerDataExecutionOptimistic( * version: ForkName * ``` */ -export function WithVersion(getType: (fork: ForkName) => TypeJson): TypeJson<{data: T; version: ForkName}> { +export function WithVersion(getTypeTo: (fork: ForkName, data: unknown) => TypeJson, getTypeFrom: (fork: ForkName, data: unknown) => TypeJson = getTypeTo): TypeJson<{data: T; version: ForkName}> { return { toJson: ({data, version}) => ({ - data: getType(version ?? ForkName.phase0).toJson(data), + data: getTypeTo(version ?? ForkName.phase0, data).toJson(data), version, }), fromJson: ({data, version}: {data: unknown; version: string}) => { @@ -153,7 +153,7 @@ export function WithVersion(getType: (fork: ForkName) => TypeJson): TypeJs if (!(version in ForkName)) throw Error(`Invalid version ${version}`); return { - data: getType(version as ForkName).fromJson(data), + data: getTypeFrom(version as ForkName, data).fromJson(data), version: version as ForkName, }; }, diff --git a/packages/api/test/unit/beacon/testData/events.ts b/packages/api/test/unit/beacon/testData/events.ts index af33f4a2b011..b60a95694ed7 100644 --- a/packages/api/test/unit/beacon/testData/events.ts +++ b/packages/api/test/unit/beacon/testData/events.ts @@ -93,16 +93,56 @@ export const eventTestData: EventData = { "0xac118511474a94f857300b315c50585c32a713e4452e26a6bb98cdb619936370f126ed3b6bb64469259ee92e69791d9e12d324ce6fd90081680ce72f39d85d50b0ff977260a8667465e613362c6d6e6e745e1f9323ec1d6f16041c4e358839ac", }), [EventType.lightClientOptimisticUpdate]: { - syncAggregate: ssz.altair.SyncAggregate.defaultValue(), - attestedHeader: ssz.altair.LightClientHeader.defaultValue(), - signatureSlot: ssz.Slot.defaultValue(), + version: ForkName.altair, + data: { + syncAggregate: ssz.altair.SyncAggregate.fromJson({ + "sync_committee_bits": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "sync_committee_signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + }), + attestedHeader: ssz.altair.LightClientHeader.fromJson({ + "beacon": { + "body_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "proposer_index": "1", + "slot": "1", + "state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + } + }), + signatureSlot: 1, + } }, [EventType.lightClientFinalityUpdate]: { - attestedHeader: ssz.altair.LightClientHeader.defaultValue(), - finalizedHeader: ssz.altair.LightClientHeader.defaultValue(), - finalityBranch: [root], - syncAggregate: ssz.altair.SyncAggregate.defaultValue(), - signatureSlot: ssz.Slot.defaultValue(), + version: ForkName.altair, + data: { + attestedHeader: ssz.altair.LightClientHeader.fromJson({ + "beacon": { + "slot": "1", + "proposer_index": "1", + "parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "body_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"} + }), + finalizedHeader: ssz.altair.LightClientHeader.fromJson({ + "beacon": { + "slot": "1", + "proposer_index": "1", + "parent_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "state_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "body_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"} + }), + finalityBranch: ssz.altair.FinalityBranch.fromJson([ + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"]), + syncAggregate: ssz.altair.SyncAggregate.fromJson({ + "sync_committee_bits": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "sync_committee_signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505" + }), + signatureSlot: 1, + } }, [EventType.lightClientUpdate]: ssz.altair.LightClientUpdate.defaultValue(), [EventType.payloadAttributes]: { diff --git a/packages/beacon-node/src/chain/blocks/importBlock.ts b/packages/beacon-node/src/chain/blocks/importBlock.ts index 12b43359fa4e..6fdcaad965a2 100644 --- a/packages/beacon-node/src/chain/blocks/importBlock.ts +++ b/packages/beacon-node/src/chain/blocks/importBlock.ts @@ -294,7 +294,8 @@ export async function importBlock( this.lightClientServer.onImportBlockHead( block.message as allForks.AllForksLightClient["BeaconBlock"], postState as CachedBeaconStateAltair, - parentBlockSlot + parentBlockSlot, + this.config.getForkName(block.message.slot) ); } catch (e) { this.logger.verbose("Error lightClientServer.onImportBlock", {slot: block.message.slot}, e as Error); diff --git a/packages/beacon-node/src/chain/lightClient/index.ts b/packages/beacon-node/src/chain/lightClient/index.ts index 1a09652dc233..9107bede5be7 100644 --- a/packages/beacon-node/src/chain/lightClient/index.ts +++ b/packages/beacon-node/src/chain/lightClient/index.ts @@ -227,7 +227,8 @@ export class LightClientServer { onImportBlockHead( block: allForks.AllForksLightClient["BeaconBlock"], postState: CachedBeaconStateAltair, - parentBlockSlot: Slot + parentBlockSlot: Slot, + fork: ForkName ): void { // TEMP: To disable this functionality for fork_choice spec tests. // Since the tests have deep-reorgs attested data is not available often printing lots of error logs. @@ -247,7 +248,7 @@ export class LightClientServer { const signedBlockRoot = block.parentRoot; const syncPeriod = computeSyncPeriodAtSlot(block.slot); - this.onSyncAggregate(syncPeriod, block.body.syncAggregate, block.slot, signedBlockRoot).catch((e) => { + this.onSyncAggregate(syncPeriod, block.body.syncAggregate, block.slot, signedBlockRoot, fork).catch((e) => { this.logger.error("Error onSyncAggregate", {}, e); this.metrics?.lightclientServer.onSyncAggregate.inc({event: "error"}); }); @@ -455,7 +456,8 @@ export class LightClientServer { syncPeriod: SyncPeriod, syncAggregate: altair.SyncAggregate, signatureSlot: Slot, - signedBlockRoot: Root + signedBlockRoot: Root, + fork: ForkName ): Promise { this.metrics?.lightclientServer.onSyncAggregate.inc({event: "processed"}); @@ -496,7 +498,7 @@ export class LightClientServer { // Emit update // Note: Always emit optimistic update even if we have emitted one with higher or equal attested_header.slot - this.emitter.emit(routes.events.EventType.lightClientOptimisticUpdate, headerUpdate); + this.emitter.emit(routes.events.EventType.lightClientOptimisticUpdate, {version: fork, data: headerUpdate}); // Persist latest best update for getLatestHeadUpdate() // TODO: Once SyncAggregate are constructed from P2P too, count bits to decide "best" @@ -529,8 +531,8 @@ export class LightClientServer { }; this.metrics?.lightclientServer.onSyncAggregate.inc({event: "update_latest_finalized_update"}); - // Note: Ignores gossip rule to always emit finaly_update with higher finalized_header.slot, for simplicity - this.emitter.emit(routes.events.EventType.lightClientFinalityUpdate, this.finalized); + // Note: Ignores gossip rule to always emit finality_update with higher finalized_header.slot, for simplicity + this.emitter.emit(routes.events.EventType.lightClientFinalityUpdate, {version: attestedFork, data: this.finalized}); } } diff --git a/packages/beacon-node/src/network/network.ts b/packages/beacon-node/src/network/network.ts index 200bd4fd3a8d..9f63a6f89563 100644 --- a/packages/beacon-node/src/network/network.ts +++ b/packages/beacon-node/src/network/network.ts @@ -105,8 +105,8 @@ export class Network implements INetwork { this.events.on(NetworkEvent.peerConnected, this.onPeerConnected); this.events.on(NetworkEvent.peerDisconnected, this.onPeerDisconnected); this.chain.emitter.on(routes.events.EventType.head, this.onHead); - this.chain.emitter.on(routes.events.EventType.lightClientFinalityUpdate, this.onLightClientFinalityUpdate); - this.chain.emitter.on(routes.events.EventType.lightClientOptimisticUpdate, this.onLightClientOptimisticUpdate); + this.chain.emitter.on(routes.events.EventType.lightClientFinalityUpdate, ({data}) => this.onLightClientFinalityUpdate(data)); + this.chain.emitter.on(routes.events.EventType.lightClientOptimisticUpdate, ({data}) => this.onLightClientOptimisticUpdate(data)); } static async init({ diff --git a/packages/light-client/src/transport/rest.ts b/packages/light-client/src/transport/rest.ts index 765e55d7f5c5..ebbd8e52f691 100644 --- a/packages/light-client/src/transport/rest.ts +++ b/packages/light-client/src/transport/rest.ts @@ -80,11 +80,11 @@ export class LightClientRestTransport extends (EventEmitter as {new (): RestEven (event) => { switch (event.type) { case routes.events.EventType.lightClientOptimisticUpdate: - this.eventEmitter.emit(routes.events.EventType.lightClientOptimisticUpdate, event.message); + this.eventEmitter.emit(routes.events.EventType.lightClientOptimisticUpdate, event.message.data); break; case routes.events.EventType.lightClientFinalityUpdate: - this.eventEmitter.emit(routes.events.EventType.lightClientFinalityUpdate, event.message); + this.eventEmitter.emit(routes.events.EventType.lightClientFinalityUpdate, event.message.data); break; } } diff --git a/packages/light-client/test/unit/sync.node.test.ts b/packages/light-client/test/unit/sync.node.test.ts index 168bfeceb5f9..2505a8a8baca 100644 --- a/packages/light-client/test/unit/sync.node.test.ts +++ b/packages/light-client/test/unit/sync.node.test.ts @@ -155,7 +155,7 @@ describe("sync", () => { }; lightclientServerApi.latestHeadUpdate = headUpdate; - eventsServerApi.emit({type: routes.events.EventType.lightClientOptimisticUpdate, message: headUpdate}); + eventsServerApi.emit({type: routes.events.EventType.lightClientOptimisticUpdate, message: {version: config.getForkName(slot), data: headUpdate}}); testLogger.debug("Emitted EventType.lightClientOptimisticUpdate", {slot}); } }); diff --git a/packages/types/src/altair/sszTypes.ts b/packages/types/src/altair/sszTypes.ts index 8998571a9f0d..e7406f0912e1 100644 --- a/packages/types/src/altair/sszTypes.ts +++ b/packages/types/src/altair/sszTypes.ts @@ -186,13 +186,15 @@ export const LightClientBootstrap = new ContainerType( {typeName: "LightClientBootstrap", jsonCase: "eth2"} ); +export const FinalityBranch = new VectorCompositeType(Bytes32, FINALIZED_ROOT_DEPTH); + export const LightClientUpdate = new ContainerType( { attestedHeader: LightClientHeader, nextSyncCommittee: SyncCommittee, nextSyncCommitteeBranch: new VectorCompositeType(Bytes32, NEXT_SYNC_COMMITTEE_DEPTH), finalizedHeader: LightClientHeader, - finalityBranch: new VectorCompositeType(Bytes32, FINALIZED_ROOT_DEPTH), + finalityBranch: FinalityBranch, syncAggregate: SyncAggregate, signatureSlot: Slot, }, @@ -203,7 +205,7 @@ export const LightClientFinalityUpdate = new ContainerType( { attestedHeader: LightClientHeader, finalizedHeader: LightClientHeader, - finalityBranch: new VectorCompositeType(Bytes32, FINALIZED_ROOT_DEPTH), + finalityBranch: FinalityBranch, syncAggregate: SyncAggregate, signatureSlot: Slot, },