diff --git a/beacon_chain/networking/eth2_network.nim b/beacon_chain/networking/eth2_network.nim index b6030afac9..fcb218ac3f 100644 --- a/beacon_chain/networking/eth2_network.nim +++ b/beacon_chain/networking/eth2_network.nim @@ -2319,3 +2319,17 @@ proc broadcastSignedContributionAndProof*( node: Eth2Node, msg: SignedContributionAndProof) = let topic = getSyncCommitteeContributionAndProofTopic(node.forkDigests.altair) node.broadcast(topic, msg) + +proc broadcastOptimisticLightClientUpdate*( + node: Eth2Node, msg: OptimisticLightClientUpdate) = + let + forkDigest = + if msg.fork_version == node.cfg.SHARDING_FORK_VERSION: + node.forkDigests.sharding + elif msg.fork_version == node.cfg.BELLATRIX_FORK_VERSION: + node.forkDigests.bellatrix + else: + doAssert msg.fork_version == node.cfg.ALTAIR_FORK_VERSION + node.forkDigests.altair + topic = getOptimisticLightClientUpdateTopic(forkDigest) + node.broadcast(topic, msg) diff --git a/beacon_chain/spec/beacon_time.nim b/beacon_chain/spec/beacon_time.nim index 8d2270cdd0..92eae0e6b6 100644 --- a/beacon_chain/spec/beacon_time.nim +++ b/beacon_chain/spec/beacon_time.nim @@ -7,6 +7,10 @@ {.push raises: [Defect].} +# References to `vFuture` refer to the pre-release proposal of the libp2p based +# light client sync protocol. Conflicting release versions are not in use. +# https://github.com/ethereum/consensus-specs/pull/2802 + import std/[hashes, typetraits], chronicles, @@ -146,6 +150,9 @@ const # https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/altair/validator.md#broadcast-sync-committee-contribution syncContributionSlotOffset* = TimeDiff(nanoseconds: NANOSECONDS_PER_SLOT.int64 * 2 div INTERVALS_PER_SLOT) + # https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#block-proposal + optimisticLightClientUpdateSlotOffset* = TimeDiff(nanoseconds: + NANOSECONDS_PER_SLOT.int64 div INTERVALS_PER_SLOT) func toFloatSeconds*(t: TimeDiff): float = float(t.nanoseconds) / 1_000_000_000.0 @@ -167,6 +174,8 @@ func sync_committee_message_deadline*(s: Slot): BeaconTime = s.start_beacon_time + syncCommitteeMessageSlotOffset func sync_contribution_deadline*(s: Slot): BeaconTime = s.start_beacon_time + syncContributionSlotOffset +func optimistic_light_client_update_deadline*(s: Slot): BeaconTime = + s.start_beacon_time + optimisticLightClientUpdateSlotOffset func slotOrZero*(time: BeaconTime): Slot = let exSlot = time.toSlot diff --git a/beacon_chain/validators/validator_duties.nim b/beacon_chain/validators/validator_duties.nim index 1073fff9dc..3260aa8ecb 100644 --- a/beacon_chain/validators/validator_duties.nim +++ b/beacon_chain/validators/validator_duties.nim @@ -737,6 +737,18 @@ proc handleSyncCommitteeMessages(node: BeaconNode, head: BlockRef, slot: Slot) = asyncSpawn createAndSendSyncCommitteeMessage(node, slot, validator, subcommitteeIdx, head) +proc handleOptimisticLightClientUpdates( + node: BeaconNode, head: BlockRef, slot: Slot, didSubmitBlock: bool) = + if not didSubmitBlock or slot < node.dag.cfg.ALTAIR_FORK_EPOCH.start_slot(): + return + let msg = node.dag.lightClientCache.optimisticUpdate + if msg.attested_header.slot != head.parent.bid.slot: + notice "No optimistic light client update for proposed block", + slot = slot, block_root = shortLog(head.root) + return + node.network.broadcastOptimisticLightClientUpdate(msg) + notice "Sent optimistic light client update", message = shortLog(msg) + proc signAndSendContribution(node: BeaconNode, validator: AttachedValidator, contribution: SyncCommitteeContribution, @@ -1069,7 +1081,10 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} = curSlot += 1 - head = await handleProposal(node, head, slot) + let + newHead = await handleProposal(node, head, slot) + didSubmitBlock = (newHead != head) + head = newHead let # The latest point in time when we'll be sending out attestations @@ -1119,6 +1134,16 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} = handleAttestations(node, head, slot) handleSyncCommitteeMessages(node, head, slot) + if node.config.serveLightClientData: + let cutoff = node.beaconClock.fromNow( + slot.optimistic_light_client_update_deadline()) + if cutoff.inFuture: + debug "Waiting to send optimistic light client update", + head = shortLog(head), + optimisticLightClientUpdateCutoff = shortLog(cutoff.offset) + await sleepAsync(cutoff.offset) + handleOptimisticLightClientUpdates(node, head, slot, didSubmitBlock) + updateValidatorMetrics(node) # the important stuff is done, update the vanity numbers # https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/validator.md#broadcast-aggregate @@ -1276,6 +1301,22 @@ proc sendBeaconBlock*(node: BeaconNode, forked: ForkedSignedBeaconBlock notice "Block published", blockRoot = shortLog(blck.root), blck = shortLog(blck.message), signature = shortLog(blck.signature) + + if node.config.serveLightClientData: + proc publishOptimisticLightClientUpdate() {.async.} = + let cutoff = node.beaconClock.fromNow( + wallTime.slotOrZero.optimistic_light_client_update_deadline()) + if cutoff.inFuture: + debug "Waiting to publish optimistic light client update", + blockRoot = shortLog(blck.root), blck = shortLog(blck.message), + signature = shortLog(blck.signature), + optimisticLightClientUpdateCutoff = shortLog(cutoff.offset) + await sleepAsync(cutoff.offset) + handleOptimisticLightClientUpdates( + node, newBlockRef.get, wallTime.slotOrZero, didSubmitBlock = true) + + asyncSpawn publishOptimisticLightClientUpdate() + true else: warn "Unable to add proposed block to block pool",