From bbf1d6030c0e2b443faff2d404982981e1d45fad Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Thu, 8 Dec 2022 17:24:16 +0100 Subject: [PATCH] add hooks for observing LC progress (#4401) For Fluffy injection, add observer callbacks that get called whenever new light client data is sucecssfully processed. ``` proc onLightClientObject( lightClient: LightClient, obj: SomeLightClientObject) = info "New LC object", obj lightClient.bootstrapObserver = proc(lightClient: LightClient, obj: altair.LightClientBootstrap) = lightClient.onLightClientObject(obj) lightClient.updateObserver = proc(lightClient: LightClient, obj: altair.LightClientUpdate) = lightClient.onLightClientObject(obj) lightClient.finalityUpdateObserver = proc(lightClient: LightClient, obj: altair.LightClientFinalityUpdate) = lightClient.onLightClientObject(obj) lightClient.optimisticUpdateObserver = proc(lightClient: LightClient, obj: altair.LightClientOptimisticUpdate) = lightClient.onLightClientObject(obj) ``` --- .../light_client_processor.nim | 93 +++++++++++++++---- beacon_chain/light_client.nim | 34 ++++++- 2 files changed, 108 insertions(+), 19 deletions(-) diff --git a/beacon_chain/gossip_processing/light_client_processor.nim b/beacon_chain/gossip_processing/light_client_processor.nim index 73baee7b5..ce824ecfe 100644 --- a/beacon_chain/gossip_processing/light_client_processor.nim +++ b/beacon_chain/gossip_processing/light_client_processor.nim @@ -31,11 +31,24 @@ declareHistogram light_client_store_object_duration_seconds, "storeObject() duration", buckets = [0.25, 0.5, 1, 2, 4, 8, Inf] type + Nothing = object + GetTrustedBlockRootCallback* = proc(): Option[Eth2Digest] {.gcsafe, raises: [Defect].} VoidCallback* = proc() {.gcsafe, raises: [Defect].} + ValueObserver[V] = + proc(v: V) {.gcsafe, raises: [Defect].} + BootstrapObserver* = + ValueObserver[altair.LightClientBootstrap] + UpdateObserver* = + ValueObserver[altair.LightClientUpdate] + FinalityUpdateObserver* = + ValueObserver[altair.LightClientFinalityUpdate] + OptimisticUpdateObserver* = + ValueObserver[altair.LightClientOptimisticUpdate] + LightClientFinalizationMode* {.pure.} = enum Strict ## Only finalize light client data that: @@ -91,6 +104,10 @@ type getBeaconTime: GetBeaconTimeFn getTrustedBlockRoot: GetTrustedBlockRootCallback onStoreInitialized, onFinalizedHeader, onOptimisticHeader: VoidCallback + bootstrapObserver: BootstrapObserver + updateObserver: UpdateObserver + finalityUpdateObserver: FinalityUpdateObserver + optimisticUpdateObserver: OptimisticUpdateObserver cfg: RuntimeConfig genesis_validators_root: Eth2Digest @@ -127,7 +144,11 @@ proc new*( getTrustedBlockRoot: GetTrustedBlockRootCallback, onStoreInitialized: VoidCallback = nil, onFinalizedHeader: VoidCallback = nil, - onOptimisticHeader: VoidCallback = nil + onOptimisticHeader: VoidCallback = nil, + bootstrapObserver: BootstrapObserver = nil, + updateObserver: UpdateObserver = nil, + finalityUpdateObserver: FinalityUpdateObserver = nil, + optimisticUpdateObserver: OptimisticUpdateObserver = nil ): ref LightClientProcessor = (ref LightClientProcessor)( dumpEnabled: dumpEnabled, @@ -139,6 +160,10 @@ proc new*( onStoreInitialized: onStoreInitialized, onFinalizedHeader: onFinalizedHeader, onOptimisticHeader: onOptimisticHeader, + bootstrapObserver: bootstrapObserver, + updateObserver: updateObserver, + finalityUpdateObserver: finalityUpdateObserver, + optimisticUpdateObserver: optimisticUpdateObserver, cfg: cfg, genesis_validators_root: genesis_validators_root, finalizationMode: finalizationMode) @@ -252,10 +277,16 @@ proc processObject( res -template withReportedProgress(expectFinalityUpdate: bool, body: untyped): bool = +template withReportedProgress( + obj: SomeLightClientObject | Nothing, body: untyped): bool = block: let previousWasInitialized = store[].isSome + previousNextCommitteeKnown = + if store[].isSome: + store[].get.is_next_sync_committee_known + else: + false previousFinalized = if store[].isSome: store[].get.finalized_header @@ -269,29 +300,55 @@ template withReportedProgress(expectFinalityUpdate: bool, body: untyped): bool = body - var didProgress = false + var + didProgress = false + didSignificantProgress = false if store[].isSome != previousWasInitialized: didProgress = true + didSignificantProgress = true if self.onStoreInitialized != nil: self.onStoreInitialized() self.onStoreInitialized = nil - if store[].get.optimistic_header != previousOptimistic: - when not expectFinalityUpdate: + if store[].isSome: + if store[].get.optimistic_header != previousOptimistic: didProgress = true - if self.onOptimisticHeader != nil: - self.onOptimisticHeader() + when obj isnot SomeLightClientUpdateWithFinality: + didSignificantProgress = true + if self.onOptimisticHeader != nil: + self.onOptimisticHeader() - if store[].get.finalized_header != previousFinalized: - didProgress = true - if self.onFinalizedHeader != nil: - self.onFinalizedHeader() + if store[].get.finalized_header != previousFinalized: + didProgress = true + didSignificantProgress = true + if self.onFinalizedHeader != nil: + self.onFinalizedHeader() - didProgress + if store[].get.is_next_sync_committee_known != previousNextCommitteeKnown: + didProgress = true + + if didProgress: + when obj is Nothing: + discard + elif obj is altair.LightClientBootstrap: + if self.bootstrapObserver != nil: + self.bootstrapObserver(obj) + elif obj is altair.LightClientUpdate: + if self.updateObserver != nil: + self.updateObserver(obj) + elif obj is altair.LightClientFinalityUpdate: + if self.finalityUpdateObserver != nil: + self.finalityUpdateObserver(obj) + elif obj is altair.LightClientOptimisticUpdate: + if self.optimisticUpdateObserver != nil: + self.optimisticUpdateObserver(obj) + else: raiseAssert "Unreachable" + + didSignificantProgress template withReportedProgress(body: untyped): bool = - withReportedProgress(false, body) + withReportedProgress(Nothing(), body) proc storeObject*( self: var LightClientProcessor, @@ -304,8 +361,8 @@ proc storeObject*( startTick = Moment.now() store = self.store - didProgress = - withReportedProgress(obj is SomeLightClientUpdateWithFinality): + didSignificantProgress = + withReportedProgress(obj): ? self.processObject(obj, wallTime) let @@ -328,7 +385,7 @@ proc storeObject*( kind = typeof(obj).name, objectSlot = objSlot, storeObjectDur - ok didProgress + ok didSignificantProgress proc resetToFinalizedHeader*( self: var LightClientProcessor, @@ -393,8 +450,8 @@ func toValidationError( wallTime: BeaconTime, obj: SomeLightClientObject): Result[void, ValidationError] = if r.isOk: - let didProgress = r.get - if didProgress: + let didSignificantProgress = r.get + if didSignificantProgress: let signature_slot = obj.signature_slot currentTime = wallTime + MAXIMUM_GOSSIP_CLOCK_DISPARITY diff --git a/beacon_chain/light_client.nim b/beacon_chain/light_client.nim index 33a332972..10a1a22a0 100644 --- a/beacon_chain/light_client.nim +++ b/beacon_chain/light_client.nim @@ -29,6 +29,17 @@ type proc(lightClient: LightClient, header: BeaconBlockHeader) {. gcsafe, raises: [Defect].} + LightClientValueObserver[V] = + proc(lightClient: LightClient, v: V) {.gcsafe, raises: [Defect].} + LightClientBootstrapObserver* = + LightClientValueObserver[altair.LightClientBootstrap] + LightClientUpdateObserver* = + LightClientValueObserver[altair.LightClientUpdate] + LightClientFinalityUpdateObserver* = + LightClientValueObserver[altair.LightClientFinalityUpdate] + LightClientOptimisticUpdateObserver* = + LightClientValueObserver[altair.LightClientOptimisticUpdate] + LightClient* = ref object network: Eth2Node cfg: RuntimeConfig @@ -39,6 +50,10 @@ type manager: LightClientManager gossipState: GossipState onFinalizedHeader*, onOptimisticHeader*: LightClientHeaderCallback + bootstrapObserver*: LightClientBootstrapObserver + updateObserver*: LightClientUpdateObserver + finalityUpdateObserver*: LightClientFinalityUpdateObserver + optimisticUpdateObserver*: LightClientOptimisticUpdateObserver trustedBlockRoot*: Option[Eth2Digest] func finalizedHeader*(lightClient: LightClient): Opt[BeaconBlockHeader] = @@ -94,11 +109,28 @@ proc createLightClient( lightClient.onOptimisticHeader( lightClient, lightClient.optimisticHeader.get) + proc bootstrapObserver(obj: altair.LightClientBootstrap) = + if lightClient.bootstrapObserver != nil: + lightClient.bootstrapObserver(lightClient, obj) + + proc updateObserver(obj: altair.LightClientUpdate) = + if lightClient.updateObserver != nil: + lightClient.updateObserver(lightClient, obj) + + proc finalityObserver(obj: altair.LightClientFinalityUpdate) = + if lightClient.finalityUpdateObserver != nil: + lightClient.finalityUpdateObserver(lightClient, obj) + + proc optimisticObserver(obj: altair.LightClientOptimisticUpdate) = + if lightClient.optimisticUpdateObserver != nil: + lightClient.optimisticUpdateObserver(lightClient, obj) + lightClient.processor = LightClientProcessor.new( dumpEnabled, dumpDirInvalid, dumpDirIncoming, cfg, genesis_validators_root, finalizationMode, lightClient.store, getBeaconTime, getTrustedBlockRoot, - onStoreInitialized, onFinalizedHeader, onOptimisticHeader) + onStoreInitialized, onFinalizedHeader, onOptimisticHeader, + bootstrapObserver, updateObserver, finalityObserver, optimisticObserver) proc lightClientVerifier(obj: SomeLightClientObject): Future[Result[void, VerifierError]] =