broadcast optimistic light client updates (#3499)

After proposing a new block, broadcasts a `OptimisticLightClientUpdate`.
Works for both locally proposed blocks as well as VC submitted ones.
This commit is contained in:
Etan Kissling 2022-03-17 21:11:29 +01:00 committed by GitHub
parent 05ffe7b2bf
commit 9f8894fb43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 73 additions and 1 deletions

View File

@ -2355,3 +2355,17 @@ proc broadcastSignedContributionAndProof*(
node: Eth2Node, msg: SignedContributionAndProof) = node: Eth2Node, msg: SignedContributionAndProof) =
let topic = getSyncCommitteeContributionAndProofTopic(node.forkDigests.altair) let topic = getSyncCommitteeContributionAndProofTopic(node.forkDigests.altair)
node.broadcast(topic, msg) 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)

View File

@ -7,6 +7,10 @@
{.push raises: [Defect].} {.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 import
std/[hashes, typetraits], std/[hashes, typetraits],
chronicles, chronicles,
@ -146,6 +150,9 @@ const
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/altair/validator.md#broadcast-sync-committee-contribution # https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/altair/validator.md#broadcast-sync-committee-contribution
syncContributionSlotOffset* = TimeDiff(nanoseconds: syncContributionSlotOffset* = TimeDiff(nanoseconds:
NANOSECONDS_PER_SLOT.int64 * 2 div INTERVALS_PER_SLOT) 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 = func toFloatSeconds*(t: TimeDiff): float =
float(t.nanoseconds) / 1_000_000_000.0 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 s.start_beacon_time + syncCommitteeMessageSlotOffset
func sync_contribution_deadline*(s: Slot): BeaconTime = func sync_contribution_deadline*(s: Slot): BeaconTime =
s.start_beacon_time + syncContributionSlotOffset s.start_beacon_time + syncContributionSlotOffset
func optimistic_light_client_update_time*(s: Slot): BeaconTime =
s.start_beacon_time + optimisticLightClientUpdateSlotOffset
func slotOrZero*(time: BeaconTime): Slot = func slotOrZero*(time: BeaconTime): Slot =
let exSlot = time.toSlot let exSlot = time.toSlot

View File

@ -7,6 +7,10 @@
{.push raises: [Defect].} {.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 import
# Standard library # Standard library
std/[os, osproc, sequtils, streams, tables], std/[os, osproc, sequtils, streams, tables],
@ -738,6 +742,19 @@ proc handleSyncCommitteeMessages(node: BeaconNode, head: BlockRef, slot: Slot) =
asyncSpawn createAndSendSyncCommitteeMessage(node, slot, validator, asyncSpawn createAndSendSyncCommitteeMessage(node, slot, validator,
subcommitteeIdx, head) subcommitteeIdx, head)
proc handleOptimisticLightClientUpdates(
node: BeaconNode, head: BlockRef, slot: Slot) =
if slot < node.dag.cfg.ALTAIR_FORK_EPOCH.start_slot():
return
doAssert head.parent != nil, "Newly proposed block lacks parent reference"
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, proc signAndSendContribution(node: BeaconNode,
validator: AttachedValidator, validator: AttachedValidator,
contribution: SyncCommitteeContribution, contribution: SyncCommitteeContribution,
@ -1070,7 +1087,10 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} =
curSlot += 1 curSlot += 1
head = await handleProposal(node, head, slot) let
newHead = await handleProposal(node, head, slot)
didSubmitBlock = (newHead != head)
head = newHead
let let
# The latest point in time when we'll be sending out attestations # The latest point in time when we'll be sending out attestations
@ -1120,6 +1140,16 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} =
handleAttestations(node, head, slot) handleAttestations(node, head, slot)
handleSyncCommitteeMessages(node, head, slot) handleSyncCommitteeMessages(node, head, slot)
if node.config.serveLightClientData and didSubmitBlock:
let cutoff = node.beaconClock.fromNow(
slot.optimistic_light_client_update_time())
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)
updateValidatorMetrics(node) # the important stuff is done, update the vanity numbers 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 # https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/validator.md#broadcast-aggregate
@ -1277,6 +1307,25 @@ proc sendBeaconBlock*(node: BeaconNode, forked: ForkedSignedBeaconBlock
notice "Block published", notice "Block published",
blockRoot = shortLog(blck.root), blck = shortLog(blck.message), blockRoot = shortLog(blck.root), blck = shortLog(blck.message),
signature = shortLog(blck.signature) signature = shortLog(blck.signature)
if node.config.serveLightClientData:
# The optimistic light client update is sent with a delay because it
# only validates once the new block has been processed by the peers.
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#block-proposal
proc publishOptimisticLightClientUpdate() {.async.} =
let cutoff = node.beaconClock.fromNow(
wallTime.slotOrZero.optimistic_light_client_update_time())
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)
asyncSpawn publishOptimisticLightClientUpdate()
true true
else: else:
warn "Unable to add proposed block to block pool", warn "Unable to add proposed block to block pool",