Skip to content

Commit 6e3e472

Browse files
committed
serve libp2p protocol for light client sync
This extends the `--serve-light-client-data` launch option to serve locally collected light client data via libp2p. See ethereum/consensus-specs#2802
1 parent 37d344a commit 6e3e472

File tree

8 files changed

+120
-9
lines changed

8 files changed

+120
-9
lines changed

beacon_chain/gossip_processing/eth2_processor.nim

+24
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ declareCounter beacon_sync_committee_contributions_received,
5858
"Number of valid sync committee contributions processed by this node"
5959
declareCounter beacon_sync_committee_contributions_dropped,
6060
"Number of invalid sync committee contributions dropped by this node", labels = ["reason"]
61+
declareCounter beacon_optimistic_light_client_updates_received,
62+
"Number of valid optimistic light client updates processed by this node"
63+
declareCounter beacon_optimistic_light_client_updates_dropped,
64+
"Number of invalid optimistic light client updates dropped by this node", labels = ["reason"]
6165

6266
const delayBuckets = [2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, Inf]
6367

@@ -528,3 +532,23 @@ proc contributionValidator*(
528532
beacon_sync_committee_contributions_dropped.inc(1, [$v.error[0]])
529533

530534
err(v.error())
535+
536+
proc optimisticLightClientUpdateValidator*(
537+
self: var Eth2Processor, src: MsgSource,
538+
optimistic_update: altair.OptimisticLightClientUpdate
539+
): Result[void, ValidationError] =
540+
logScope:
541+
optimisticUpdate = shortLog(optimistic_update)
542+
543+
debug "Optimistic light client update received"
544+
545+
let v = self.dag.validateOptimisticLightClientUpdate(optimistic_update)
546+
if v.isOk():
547+
trace "Optimistic light client update validated"
548+
549+
beacon_optimistic_light_client_updates_received.inc()
550+
else:
551+
debug "Dropping optimistic light client update", error = v.error
552+
beacon_optimistic_light_client_updates_dropped.inc(1, [$v.error[0]])
553+
554+
v

beacon_chain/gossip_processing/gossip_validation.nim

+17
Original file line numberDiff line numberDiff line change
@@ -984,3 +984,20 @@ proc validateContribution*(
984984
sig.get()
985985

986986
return ok((sig, participants))
987+
988+
# https://github.com/ethereum/consensus-specs/blob/v1.1.8/specs/altair/sync-protocol.md#optimistic_light_client_update
989+
proc validateOptimisticLightClientUpdate*(
990+
dag: ChainDAGRef, optimistic_update: altair.OptimisticLightClientUpdate):
991+
Result[void, ValidationError] =
992+
template latest_local_update(): auto = dag.optimisticLightClientUpdate
993+
994+
if optimistic_update != latest_local_update:
995+
# [IGNORE] The optimistic update is not attesting to the latest block's
996+
# parent block.
997+
if optimistic_update.attested_header != latest_local_update.attested_header:
998+
return errIgnore("OptimisticLightClientUpdate: different attested block")
999+
1000+
# [REJECT] The optimistic update does not match the expected value.
1001+
return errReject("OptimisticLightClientUpdate: update does not match block")
1002+
1003+
ok()

beacon_chain/networking/eth2_network.nim

+14
Original file line numberDiff line numberDiff line change
@@ -2225,3 +2225,17 @@ proc broadcastSignedContributionAndProof*(
22252225
node: Eth2Node, msg: SignedContributionAndProof) =
22262226
let topic = getSyncCommitteeContributionAndProofTopic(node.forkDigests.altair)
22272227
node.broadcast(topic, msg)
2228+
2229+
proc broadcastOptimisticLightClientUpdate*(
2230+
node: Eth2Node, msg: altair.OptimisticLightClientUpdate) =
2231+
let
2232+
forkDigest =
2233+
if msg.fork_version == node.cfg.SHARDING_FORK_VERSION:
2234+
node.forkDigests.sharding
2235+
elif msg.fork_version == node.cfg.BELLATRIX_FORK_VERSION:
2236+
node.forkDigests.bellatrix
2237+
else:
2238+
doAssert msg.fork_version == node.cfg.ALTAIR_FORK_VERSION
2239+
node.forkDigests.altair
2240+
topic = getOptimisticLightClientUpdateTopic(forkDigest)
2241+
node.broadcast(topic, msg)

beacon_chain/nimbus_beacon_node.nim

+19
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,10 @@ proc addAltairMessageHandlers(node: BeaconNode, forkDigest: ForkDigest, slot: Sl
730730
getSyncCommitteeContributionAndProofTopic(forkDigest), basicParams)
731731
node.network.updateSyncnetsMetadata(syncnets)
732732

733+
if node.config.serveLightClientData:
734+
node.network.subscribe(
735+
getOptimisticLightClientUpdateTopic(forkDigest), basicParams)
736+
733737
proc removeAltairMessageHandlers(node: BeaconNode, forkDigest: ForkDigest) =
734738
node.removePhase0MessageHandlers(forkDigest)
735739

@@ -742,6 +746,9 @@ proc removeAltairMessageHandlers(node: BeaconNode, forkDigest: ForkDigest) =
742746
node.network.unsubscribe(
743747
getSyncCommitteeContributionAndProofTopic(forkDigest))
744748

749+
if node.config.serveLightClientData:
750+
node.network.unsubscribe(getOptimisticLightClientUpdateTopic(forkDigest))
751+
745752
proc trackSyncCommitteeTopics*(node: BeaconNode) =
746753
# TODO
747754
discard
@@ -1149,6 +1156,18 @@ proc installMessageValidators(node: BeaconNode) =
11491156
installSyncCommitteeeValidators(node.dag.forkDigests.altair)
11501157
installSyncCommitteeeValidators(node.dag.forkDigests.bellatrix)
11511158

1159+
if node.config.serveLightClientData:
1160+
template installOptimisticLightClientUpdateValidator(digest: auto) =
1161+
node.network.addValidator(
1162+
getOptimisticLightClientUpdateTopic(digest),
1163+
proc(msg: altair.OptimisticLightClientUpdate): ValidationResult =
1164+
toValidationResult(
1165+
node.processor[].optimisticLightClientUpdateValidator(
1166+
MsgSource.gossip, msg)))
1167+
1168+
installOptimisticLightClientUpdateValidator(node.dag.forkDigests.altair)
1169+
installOptimisticLightClientUpdateValidator(node.dag.forkDigests.bellatrix)
1170+
11521171
proc stop(node: BeaconNode) =
11531172
bnStatus = BeaconNodeStatus.Stopping
11541173
notice "Graceful shutdown"

beacon_chain/spec/beacon_time.nim

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# beacon_chain
2-
# Copyright (c) 2018-2021 Status Research & Development GmbH
2+
# Copyright (c) 2018-2022 Status Research & Development GmbH
33
# Licensed and distributed under either of
44
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
55
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
@@ -146,6 +146,9 @@ const
146146
# https://github.com/ethereum/consensus-specs/blob/v1.1.8/specs/altair/validator.md#broadcast-sync-committee-contribution
147147
syncContributionSlotOffset* = TimeDiff(nanoseconds:
148148
NANOSECONDS_PER_SLOT.int64 * 2 div INTERVALS_PER_SLOT)
149+
# https://github.com/ethereum/consensus-specs/blob/v1.1.8/specs/altair/sync-protocol.md#block-proposal
150+
optimisticLightClientUpdateSlotOffset* = TimeDiff(nanoseconds:
151+
NANOSECONDS_PER_SLOT.int64 div INTERVALS_PER_SLOT)
149152

150153
func toFloatSeconds*(t: TimeDiff): float =
151154
float(t.nanoseconds) / 1_000_000_000.0
@@ -167,6 +170,8 @@ func sync_committee_message_deadline*(s: Slot): BeaconTime =
167170
s.start_beacon_time + syncCommitteeMessageSlotOffset
168171
func sync_contribution_deadline*(s: Slot): BeaconTime =
169172
s.start_beacon_time + syncContributionSlotOffset
173+
func optimistic_light_client_update_deadline*(s: Slot): BeaconTime =
174+
s.start_beacon_time + optimisticLightClientUpdateSlotOffset
170175

171176
func slotOrZero*(time: BeaconTime): Slot =
172177
let exSlot = time.toSlot

beacon_chain/spec/datatypes/altair.nim

+6
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,12 @@ chronicles.formatIt SignedContributionAndProof: shortLog(it)
604604
template hash*(x: LightClientUpdate): Hash =
605605
hash(x.header)
606606

607+
func shortLog*(v: OptimisticLightClientUpdate): auto =
608+
(
609+
attested_header: shortLog(v.attested_header),
610+
sync_aggregate: shortLog(v.sync_aggregate)
611+
)
612+
607613
func clear*(info: var EpochInfo) =
608614
info.validators.setLen(0)
609615
info.balances = UnslashedParticipatingBalances()

beacon_chain/spec/network.nim

+5
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ func getSyncCommitteeContributionAndProofTopic*(forkDigest: ForkDigest): string
9494
## For subscribing and unsubscribing to/from a subnet.
9595
eth2Prefix(forkDigest) & "sync_committee_contribution_and_proof/ssz_snappy"
9696

97+
# https://github.com/ethereum/consensus-specs/blob/v1.1.8/specs/altair/sync-protocol.md#optimistic_light_client_update
98+
func getOptimisticLightClientUpdateTopic*(forkDigest: ForkDigest): string =
99+
## For broadcasting the latest `OptimisticLightClientUpdate` to light clients.
100+
eth2Prefix(forkDigest) & "optimistic_light_client_update/ssz_snappy"
101+
97102
func getENRForkID*(cfg: RuntimeConfig,
98103
epoch: Epoch,
99104
genesis_validators_root: Eth2Digest): ENRForkID =

beacon_chain/validators/validator_duties.nim

+29-8
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,18 @@ proc handleSyncCommitteeMessages(node: BeaconNode, head: BlockRef, slot: Slot) =
739739
asyncSpawn createAndSendSyncCommitteeMessage(node, slot, validator,
740740
subcommitteeIdx, head)
741741

742+
proc handleOptimisticLightClientUpdates(
743+
node: BeaconNode, head: BlockRef, slot: Slot, didPropose: bool) =
744+
if not didPropose:
745+
return
746+
let msg = node.dag.optimisticLightClientUpdate
747+
if msg.attested_header.slot != head.parent.bid.slot:
748+
notice "No optimistic light client update for proposed block",
749+
slot = slot, block_root = shortLog(head.root)
750+
return
751+
node.network.broadcastOptimisticLightClientUpdate(msg)
752+
notice "Sent optimistic light client update", message = shortLog(msg)
753+
742754
proc signAndSendContribution(node: BeaconNode,
743755
validator: AttachedValidator,
744756
contribution: SyncCommitteeContribution,
@@ -841,14 +853,14 @@ proc handleSyncCommitteeContributions(node: BeaconNode,
841853
slot, head, subnet_id = candidateAggregators[i].subcommitteeIdx
842854

843855
proc handleProposal(node: BeaconNode, head: BlockRef, slot: Slot):
844-
Future[BlockRef] {.async.} =
856+
Future[tuple[head: BlockRef, didPropose: bool]] {.async.} =
845857
## Perform the proposal for the given slot, iff we have a validator attached
846858
## that is supposed to do so, given the shuffling at that slot for the given
847859
## head - to compute the proposer, we need to advance a state to the given
848860
## slot
849861
let proposer = node.dag.getProposer(head, slot)
850862
if proposer.isNone():
851-
return head
863+
return (head: head, didPropose: false)
852864

853865
let
854866
proposerKey = node.dag.validatorKey(proposer.get).get().toPubKey
@@ -862,9 +874,12 @@ proc handleProposal(node: BeaconNode, head: BlockRef, slot: Slot):
862874
proposer_index = proposer.get(),
863875
proposer = shortLog(proposerKey)
864876

865-
head
877+
(head: head, didPropose: false)
866878
else:
867-
await proposeBlock(node, validator, proposer.get(), head, slot)
879+
(
880+
head: await proposeBlock(node, validator, proposer.get(), head, slot),
881+
didPropose: true
882+
)
868883

869884
proc makeAggregateAndProof*(
870885
pool: var AttestationPool, epochRef: EpochRef, slot: Slot, index: CommitteeIndex,
@@ -1052,6 +1067,7 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} =
10521067

10531068
# Start by checking if there's work we should have done in the past that we
10541069
# can still meaningfully do
1070+
var didPropose = false
10551071
while curSlot < slot:
10561072
notice "Catching up on validator duties",
10571073
curSlot = shortLog(curSlot),
@@ -1061,7 +1077,7 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} =
10611077
# For every slot we're catching up, we'll propose then send
10621078
# attestations - head should normally be advancing along the same branch
10631079
# in this case
1064-
head = await handleProposal(node, head, curSlot)
1080+
(head, didPropose) = await handleProposal(node, head, curSlot)
10651081

10661082
# For each slot we missed, we need to send out attestations - if we were
10671083
# proposing during this time, we'll use the newly proposed head, else just
@@ -1071,7 +1087,7 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} =
10711087

10721088
curSlot += 1
10731089

1074-
head = await handleProposal(node, head, slot)
1090+
(head, didPropose) = await handleProposal(node, head, slot)
10751091

10761092
let
10771093
# The latest point in time when we'll be sending out attestations
@@ -1116,11 +1132,16 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} =
11161132
node.consensusManager[].updateHead(slot)
11171133
head = node.dag.head
11181134

1119-
static: doAssert attestationSlotOffset == syncCommitteeMessageSlotOffset
1120-
1135+
static:
1136+
doAssert attestationSlotOffset == syncCommitteeMessageSlotOffset
11211137
handleAttestations(node, head, slot)
11221138
handleSyncCommitteeMessages(node, head, slot)
11231139

1140+
if node.config.serveLightClientData:
1141+
static:
1142+
doAssert attestationSlotOffset == optimisticLightClientUpdateSlotOffset
1143+
handleOptimisticLightClientUpdates(node, head, slot, didPropose)
1144+
11241145
updateValidatorMetrics(node) # the important stuff is done, update the vanity numbers
11251146

11261147
# https://github.com/ethereum/consensus-specs/blob/v1.1.8/specs/phase0/validator.md#broadcast-aggregate

0 commit comments

Comments
 (0)