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)
```
This commit is contained in:
Etan Kissling 2022-12-08 17:24:16 +01:00 committed by GitHub
parent dee5af58d6
commit bbf1d6030c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 108 additions and 19 deletions

View File

@ -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

View File

@ -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]] =