Skip to content

Commit

Permalink
send LC finality update on event stream on supermajority
Browse files Browse the repository at this point in the history
When new finality is reached without supermajority sync committee
support, trigger another event push on beacon-API and libp2p once
the finality gains supermajority support.

- ethereum/consensus-specs#3549
  • Loading branch information
etan-status committed Nov 15, 2023
1 parent 0919ff0 commit 013da98
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,11 @@ proc createLightClientUpdates(
var finalized_slot = attested_data.finalized_slot
if finalized_slot == forkyLatest.finalized_header.beacon.slot:
forkyLatest.finality_branch = attested_data.finality_branch
let old_num_active_participants =
forkyLatest.sync_aggregate.num_active_participants.uint64
if not hasSupermajoritySyncParticipation(old_num_active_participants) and
hasSupermajoritySyncParticipation(num_active_participants):
newFinality = true
elif finalized_slot < dag.tail.slot or
not load_finalized_bid(finalized_slot):
forkyLatest.finalized_header.reset()
Expand Down
4 changes: 4 additions & 0 deletions beacon_chain/consensus_object_pools/light_client_pool.nim
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ type
## Latest finality update that was forwarded on libp2p gossip.
## Tracks `finality_update.finalized_header.beacon.slot`.

latestForwardedFinalityHasSupermajority*: bool
## Whether or not the latest finality update that was forwarded on
## libp2p gossip had supermajority (> 2/3) sync committee participation.

latestForwardedOptimisticSlot*: Slot
## Latest optimistic update that was forwarded on libp2p gossip.
## Tracks `optimistic_update.attested_header.beacon.slot`.
Expand Down
20 changes: 17 additions & 3 deletions beacon_chain/gossip_processing/gossip_validation.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1371,15 +1371,28 @@ proc validateLightClientFinalityUpdate*(
pool: var LightClientPool, dag: ChainDAGRef,
finality_update: ForkedLightClientFinalityUpdate,
wallTime: BeaconTime): Result[void, ValidationError] =
# [IGNORE] The `finalized_header.beacon.slot` is greater than that of all
# previously forwarded `finality_update`s, or it matches the highest
# previously forwarded slot and also has a `sync_aggregate` indicating
# supermajority (> 2/3) sync committee participation while the previously
# forwarded `finality_update` for that slot did not indicate supermajority
let finalized_slot = withForkyFinalityUpdate(finality_update):
when lcDataFork > LightClientDataFork.None:
forkyFinalityUpdate.finalized_header.beacon.slot
else:
GENESIS_SLOT
if finalized_slot <= pool.latestForwardedFinalitySlot:
# [IGNORE] The `finalized_header.beacon.slot` is greater than that of all
# previously forwarded `finality_update`s
if finalized_slot < pool.latestForwardedFinalitySlot:
return errIgnore("LightClientFinalityUpdate: slot already forwarded")
let has_supermajority = withForkyFinalityUpdate(finality_update):
when lcDataFork > LightClientDataFork.None:
forkyFinalityUpdate.sync_aggregate.hasSupermajoritySyncParticipation
else:
false
if finalized_slot == pool.latestForwardedFinalitySlot:
if pool.latestForwardedFinalityHasSupermajority:
return errIgnore("LightClientFinalityUpdate: already have supermajority")
if not has_supermajority:
return errIgnore("LightClientFinalityUpdate: no new supermajority")

let
signature_slot = withForkyFinalityUpdate(finality_update):
Expand All @@ -1400,6 +1413,7 @@ proc validateLightClientFinalityUpdate*(
return errIgnore("LightClientFinalityUpdate: not matching local")

pool.latestForwardedFinalitySlot = finalized_slot
pool.latestForwardedFinalityHasSupermajority = has_supermajority
ok()

# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.4/specs/altair/light-client/p2p-interface.md#light_client_optimistic_update
Expand Down
8 changes: 8 additions & 0 deletions beacon_chain/spec/datatypes/altair.nim
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,14 @@ func init*(T: type SyncAggregate): SyncAggregate =
func num_active_participants*(v: SomeSyncAggregate): int =
countOnes(v.sync_committee_bits)

func hasSupermajoritySyncParticipation*(
num_active_participants: uint64): bool =
const max_active_participants = SYNC_COMMITTEE_SIZE.uint64
num_active_participants * 3 >= static(max_active_participants * 2)

func hasSupermajoritySyncParticipation*(v: SomeSyncAggregate): bool =
hasSupermajoritySyncParticipation(v.num_active_participants.uint64)

func shortLog*(v: SyncAggregate): auto =
$(v.sync_committee_bits)

Expand Down
5 changes: 2 additions & 3 deletions beacon_chain/spec/helpers.nim
Original file line number Diff line number Diff line change
Expand Up @@ -327,12 +327,11 @@ template toMeta*(update: ForkedLightClientUpdate): LightClientUpdateMetadata =

func is_better_data*(new_meta, old_meta: LightClientUpdateMetadata): bool =
# Compare supermajority (> 2/3) sync committee participation
const max_active_participants = SYNC_COMMITTEE_SIZE.uint64
let
new_has_supermajority =
new_meta.num_active_participants * 3 >= max_active_participants * 2
hasSupermajoritySyncParticipation(new_meta.num_active_participants)
old_has_supermajority =
old_meta.num_active_participants * 3 >= max_active_participants * 2
hasSupermajoritySyncParticipation(old_meta.num_active_participants)
if new_has_supermajority != old_has_supermajority:
return new_has_supermajority > old_has_supermajority
if not new_has_supermajority:
Expand Down
26 changes: 21 additions & 5 deletions beacon_chain/validators/beacon_validators.nim
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ proc isSynced*(node: BeaconNode, head: BlockRef): bool =
head.slot + node.config.syncHorizon >= wallSlot.slot

proc handleLightClientUpdates*(node: BeaconNode, slot: Slot) {.async.} =
template pool: untyped = node.lightClientPool[]

static: doAssert lightClientFinalityUpdateSlotOffset ==
lightClientOptimisticUpdateSlotOffset
let sendTime = node.beaconClock.fromNow(
Expand All @@ -280,14 +282,28 @@ proc handleLightClientUpdates*(node: BeaconNode, slot: Slot) {.async.} =
if num_active_participants < MIN_SYNC_COMMITTEE_PARTICIPANTS:
return

let finalized_slot = forkyFinalityUpdate.finalized_header.beacon.slot
if finalized_slot > node.lightClientPool[].latestForwardedFinalitySlot:
let
finalized_slot =
forkyFinalityUpdate.finalized_header.beacon.slot
has_supermajority =
hasSupermajoritySyncParticipation(num_active_participants.uint64)
newFinality =
if finalized_slot > pool.latestForwardedFinalitySlot:
true
elif finalized_slot < pool.latestForwardedFinalitySlot:
false
elif pool.latestForwardedFinalityHasSupermajority:
false
else:
has_supermajority
if newFinality:
template msg(): auto = forkyFinalityUpdate
let sendResult =
await node.network.broadcastLightClientFinalityUpdate(msg)

# Optimization for message with ephemeral validity, whether sent or not
node.lightClientPool[].latestForwardedFinalitySlot = finalized_slot
pool.latestForwardedFinalitySlot = finalized_slot
pool.latestForwardedFinalityHasSupermajority = has_supermajority

if sendResult.isOk:
beacon_light_client_finality_updates_sent.inc()
Expand All @@ -297,13 +313,13 @@ proc handleLightClientUpdates*(node: BeaconNode, slot: Slot) {.async.} =
error = sendResult.error()

let attested_slot = forkyFinalityUpdate.attested_header.beacon.slot
if attested_slot > node.lightClientPool[].latestForwardedOptimisticSlot:
if attested_slot > pool.latestForwardedOptimisticSlot:
let msg = forkyFinalityUpdate.toOptimistic
let sendResult =
await node.network.broadcastLightClientOptimisticUpdate(msg)

# Optimization for message with ephemeral validity, whether sent or not
node.lightClientPool[].latestForwardedOptimisticSlot = attested_slot
pool.latestForwardedOptimisticSlot = attested_slot

if sendResult.isOk:
beacon_light_client_optimistic_updates_sent.inc()
Expand Down

0 comments on commit 013da98

Please sign in to comment.