Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use v1.1.7 test vectors #3231

Merged
merged 2 commits into from
Jan 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions ConsensusSpecPreset-mainnet.md
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,6 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
+ Testing ForkData OK
+ Testing HistoricalBatch OK
+ Testing IndexedAttestation OK
+ Testing LightClientSnapshot OK
+ Testing LightClientUpdate OK
+ Testing PendingAttestation OK
+ Testing ProposerSlashing OK
Expand All @@ -757,12 +756,12 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
+ Testing Validator OK
+ Testing VoluntaryExit OK
```
OK: 36/36 Fail: 0/36 Skip: 0/36
OK: 35/35 Fail: 0/35 Skip: 0/35
## Ethereum Foundation - Altair - Unittests - Sync protocol [Preset: mainnet]
```diff
+ process_light_client_update_finality_updated OK
+ process_light_client_update_not_updated OK
+ process_light_client_update_timeout OK
+ test_process_light_client_update_not_timeout OK
```
OK: 3/3 Fail: 0/3 Skip: 0/3
## Ethereum Foundation - ForkChoice [Preset: mainnet]
Expand Down Expand Up @@ -902,7 +901,6 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
+ Testing ForkData OK
+ Testing HistoricalBatch OK
+ Testing IndexedAttestation OK
+ Testing LightClientSnapshot OK
+ Testing LightClientUpdate OK
+ Testing PendingAttestation OK
+ Testing PowBlock OK
Expand All @@ -921,7 +919,7 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
+ Testing Validator OK
+ Testing VoluntaryExit OK
```
OK: 39/39 Fail: 0/39 Skip: 0/39
OK: 38/38 Fail: 0/38 Skip: 0/38
## Ethereum Foundation - Phase 0 - Epoch Processing - Effective balance updates [Preset: mainnet]
```diff
+ Effective balance updates - effective_balance_hysteresis [Preset: mainnet] OK
Expand Down Expand Up @@ -1167,4 +1165,4 @@ OK: 44/44 Fail: 0/44 Skip: 0/44
OK: 27/27 Fail: 0/27 Skip: 0/27

---TOTAL---
OK: 991/993 Fail: 0/993 Skip: 2/993
OK: 989/991 Fail: 0/991 Skip: 2/991
10 changes: 4 additions & 6 deletions ConsensusSpecPreset-minimal.md
Original file line number Diff line number Diff line change
Expand Up @@ -775,7 +775,6 @@ OK: 5/5 Fail: 0/5 Skip: 0/5
+ Testing ForkData OK
+ Testing HistoricalBatch OK
+ Testing IndexedAttestation OK
+ Testing LightClientSnapshot OK
+ Testing LightClientUpdate OK
+ Testing PendingAttestation OK
+ Testing ProposerSlashing OK
Expand All @@ -793,12 +792,12 @@ OK: 5/5 Fail: 0/5 Skip: 0/5
+ Testing Validator OK
+ Testing VoluntaryExit OK
```
OK: 36/36 Fail: 0/36 Skip: 0/36
OK: 35/35 Fail: 0/35 Skip: 0/35
## Ethereum Foundation - Altair - Unittests - Sync protocol [Preset: minimal]
```diff
+ process_light_client_update_finality_updated OK
+ process_light_client_update_not_updated OK
+ process_light_client_update_timeout OK
+ test_process_light_client_update_not_timeout OK
```
OK: 3/3 Fail: 0/3 Skip: 0/3
## Ethereum Foundation - ForkChoice [Preset: minimal]
Expand Down Expand Up @@ -963,7 +962,6 @@ OK: 5/5 Fail: 0/5 Skip: 0/5
+ Testing ForkData OK
+ Testing HistoricalBatch OK
+ Testing IndexedAttestation OK
+ Testing LightClientSnapshot OK
+ Testing LightClientUpdate OK
+ Testing PendingAttestation OK
+ Testing PowBlock OK
Expand All @@ -982,7 +980,7 @@ OK: 5/5 Fail: 0/5 Skip: 0/5
+ Testing Validator OK
+ Testing VoluntaryExit OK
```
OK: 39/39 Fail: 0/39 Skip: 0/39
OK: 38/38 Fail: 0/38 Skip: 0/38
## Ethereum Foundation - Phase 0 - Epoch Processing - Effective balance updates [Preset: minimal]
```diff
+ Effective balance updates - effective_balance_hysteresis [Preset: minimal] OK
Expand Down Expand Up @@ -1239,4 +1237,4 @@ OK: 48/48 Fail: 0/48 Skip: 0/48
OK: 30/30 Fail: 0/30 Skip: 0/30

---TOTAL---
OK: 1037/1057 Fail: 0/1057 Skip: 20/1057
OK: 1035/1055 Fail: 0/1055 Skip: 20/1055
54 changes: 31 additions & 23 deletions beacon_chain/spec/datatypes/altair.nim
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ const
INACTIVITY_SCORE_BIAS* = 4
INACTIVITY_SCORE_RECOVERY_RATE* = 16

# https://github.com/ethereum/consensus-specs/blob/v1.1.7/specs/altair/sync-protocol.md#misc
# MIN_SYNC_COMMITTEE_PARTICIPANTS defined in presets
UPDATE_TIMEOUT* = SLOTS_PER_EPOCH * EPOCHS_PER_SYNC_COMMITTEE_PERIOD

SYNC_SUBCOMMITTEE_SIZE* = SYNC_COMMITTEE_SIZE div SYNC_COMMITTEE_SUBNET_COUNT

# "Note: The sum of the weights equal WEIGHT_DENOMINATOR."
Expand Down Expand Up @@ -146,41 +150,45 @@ type

### Modified/overloaded

# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/altair/sync-protocol.md#lightclientsnapshot
LightClientSnapshot* = object
header*: BeaconBlockHeader ##\
## Beacon block header

current_sync_committee*: SyncCommittee ##\
## Sync committees corresponding to the header

next_sync_committee*: SyncCommittee

# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/altair/sync-protocol.md#lightclientupdate
# https://github.com/ethereum/consensus-specs/blob/v1.1.7/specs/altair/sync-protocol.md#lightclientupdate
LightClientUpdate* = object
header*: BeaconBlockHeader ##\
## Update beacon block header
attested_header*: BeaconBlockHeader ##\
## The beacon block header that is attested to by the sync committee

# Next sync committee corresponding to the header
# Next sync committee corresponding to the active header
next_sync_committee*: SyncCommittee
next_sync_committee_branch*: array[log2trunc(NEXT_SYNC_COMMITTEE_INDEX), Eth2Digest]
next_sync_committee_branch*:
array[log2trunc(NEXT_SYNC_COMMITTEE_INDEX), Eth2Digest]

# Finality proof for the update header
finality_header*: BeaconBlockHeader
# The finalized beacon block header attested to by Merkle branch
finalized_header*: BeaconBlockHeader
finality_branch*: array[log2trunc(FINALIZED_ROOT_INDEX), Eth2Digest]

# Sync committee aggregate signature
sync_committee_bits*: BitArray[SYNC_COMMITTEE_SIZE]
sync_committee_signature*: ValidatorSig
sync_committee_aggregate*: SyncAggregate

fork_version*: Version ##\
## Fork version for the aggregate signature

# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/altair/sync-protocol.md#lightclientstore
# https://github.com/ethereum/consensus-specs/blob/v1.1.7/specs/altair/sync-protocol.md#lightclientstore
LightClientStore* = object
snapshot*: LightClientSnapshot
valid_updates*: HashSet[LightClientUpdate]
## TODO: This will benefit from being an ordered set
finalized_header*: BeaconBlockHeader ##\
## Beacon block header that is finalized

# Sync committees corresponding to the header
current_sync_committee*: SyncCommittee
next_sync_committee*: SyncCommittee

best_valid_update*: Option[LightClientUpdate] ##\
## Best available header to switch finalized head to if we see nothing else

optimistic_header*: BeaconBlockHeader ##\
## Most recent available reasonably-safe header

# Max number of active participants in a sync committee (used to calculate
# safety threshold)
previous_max_active_participants*: uint64
current_max_active_participants*: uint64

# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/altair/beacon-chain.md#beaconstate
BeaconState* = object
Expand Down
2 changes: 1 addition & 1 deletion beacon_chain/spec/datatypes/base.nim
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export
# Eventually, we could also differentiate between user/tainted data and
# internal state that's gone through sanity checks already.

const SPEC_VERSION* = "1.1.6"
const SPEC_VERSION* = "1.1.7"
## Spec version we're aiming to be compatible with, right now

const
Expand Down
163 changes: 99 additions & 64 deletions beacon_chain/spec/light_client_sync.nim
Original file line number Diff line number Diff line change
Expand Up @@ -4,112 +4,147 @@ import
datatypes/altair,
helpers

# https://github.com/ethereum/consensus-specs/blob/v1.1.0/specs/altair/sync-protocol.md#validate_light_client_update
proc validate_light_client_update*(snapshot: LightClientSnapshot,
# https://github.com/ethereum/consensus-specs/blob/v1.1.7/specs/altair/sync-protocol.md#get_active_header
func get_active_header(update: LightClientUpdate): BeaconBlockHeader =
# The "active header" is the header that the update is trying to convince
# us to accept. If a finalized header is present, it's the finalized
# header, otherwise it's the attested header
if update.finalized_header != BeaconBlockHeader():
update.finalized_header
else:
update.attested_header

# https://github.com/ethereum/consensus-specs/blob/v1.1.7/specs/altair/sync-protocol.md#validate_light_client_update
proc validate_light_client_update*(store: LightClientStore,
update: LightClientUpdate,
current_slot: Slot,
genesis_validators_root: Eth2Digest): bool =
# Verify update slot is larger than snapshot slot
if update.header.slot <= snapshot.header.slot:
# Verify update slot is larger than slot of current best finalized header
let active_header = get_active_header(update)
if not (current_slot >= active_header.slot and
active_header.slot > store.finalized_header.slot):
return false

# Verify update does not skip a sync committee period
let
snapshot_period = sync_committee_period(snapshot.header.slot)
update_period = sync_committee_period(update.header.slot)
if update_period notin [snapshot_period, snapshot_period + 1]:
finalized_period =
compute_epoch_at_slot(store.finalized_header.slot) div
EPOCHS_PER_SYNC_COMMITTEE_PERIOD
update_period =
compute_epoch_at_slot(active_header.slot) div
EPOCHS_PER_SYNC_COMMITTEE_PERIOD

if update_period notin [finalized_period, finalized_period + 1]:
return false

# Verify update header root is the finalized root of the finality header, if specified
# TODO: Use a view type instead of `unsafeAddr`
let signed_header = if update.finality_header.isZeroMemory:
# Verify that the `finalized_header`, if present, actually is the finalized
# header saved in the state of the `attested header`
if update.finalized_header.isZeroMemory:
if not update.finality_branch.isZeroMemory:
return false
unsafeAddr update.header
else:
if not is_valid_merkle_branch(hash_tree_root(update.header),
if not is_valid_merkle_branch(hash_tree_root(update.finalized_header),
update.finality_branch,
log2trunc(FINALIZED_ROOT_INDEX),
get_subtree_index(FINALIZED_ROOT_INDEX),
update.finality_header.state_root):
update.attested_header.state_root):
return false
unsafeAddr update.finality_header

# Verify update next sync committee if the update period incremented
# TODO: Use a view type instead of `unsafeAddr`
let sync_committee = if update_period == snapshot_period:
let sync_committee = if update_period == finalized_period:
if not update.next_sync_committee_branch.isZeroMemory:
return false
unsafeAddr snapshot.current_sync_committee
unsafeAddr store.current_sync_committee
else:
if not is_valid_merkle_branch(hash_tree_root(update.next_sync_committee),
update.next_sync_committee_branch,
log2trunc(NEXT_SYNC_COMMITTEE_INDEX),
get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX),
update.header.state_root):
active_header.state_root):
return false
unsafeAddr snapshot.next_sync_committee
unsafeAddr store.next_sync_committee

template sync_aggregate(): auto = update.sync_committee_aggregate
let sync_committee_participants_count = countOnes(sync_aggregate.sync_committee_bits)

let sync_committee_participants_count = countOnes(update.sync_committee_bits)
# Verify sync committee has sufficient participants
if sync_committee_participants_count < MIN_SYNC_COMMITTEE_PARTICIPANTS:
return false

# Verify sync committee aggregate signature
# participant_pubkeys = [pubkey for (bit, pubkey) in zip(update.sync_committee_bits, sync_committee.pubkeys) if bit]
# participant_pubkeys = [pubkey for (bit, pubkey) in zip(sync_aggregate.sync_committee_bits, sync_committee.pubkeys) if bit]
var participant_pubkeys = newSeqOfCap[ValidatorPubKey](sync_committee_participants_count)
for idx, bit in update.sync_committee_bits:
for idx, bit in sync_aggregate.sync_committee_bits:
if bit:
participant_pubkeys.add(sync_committee.pubkeys[idx])

let domain = compute_domain(DOMAIN_SYNC_COMMITTEE, update.fork_version, genesis_validators_root)
let signing_root = compute_signing_root(signed_header[], domain)
let domain = compute_domain(
DOMAIN_SYNC_COMMITTEE, update.fork_version, genesis_validators_root)
let signing_root = compute_signing_root(update.attested_header, domain)

blsFastAggregateVerify(participant_pubkeys, signing_root.data, update.sync_committee_signature)
blsFastAggregateVerify(
participant_pubkeys, signing_root.data, sync_aggregate.sync_committee_signature)

# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/altair/sync-protocol.md#apply_light_client_update
proc apply_light_client_update(snapshot: var LightClientSnapshot, update: LightClientUpdate) =
# https://github.com/ethereum/consensus-specs/blob/v1.1.7/specs/altair/sync-protocol.md#apply_light_client_update
func apply_light_client_update(
store: var LightClientStore, update: LightClientUpdate) =
let
snapshot_period = sync_committee_period(snapshot.header.slot)
update_period = sync_committee_period(update.header.slot)
if update_period == snapshot_period + 1:
snapshot.current_sync_committee = snapshot.next_sync_committee
snapshot.next_sync_committee = update.next_sync_committee
snapshot.header = update.header

# https://github.com/ethereum/consensus-specs/blob/v1.1.6/specs/altair/sync-protocol.md#process_light_client_update
active_header = get_active_header(update)
finalized_period =
compute_epoch_at_slot(store.finalized_header.slot) div
EPOCHS_PER_SYNC_COMMITTEE_PERIOD
update_period =
compute_epoch_at_slot(active_header.slot) div
EPOCHS_PER_SYNC_COMMITTEE_PERIOD
if update_period == finalized_period + 1:
store.current_sync_committee = store.next_sync_committee
store.next_sync_committee = update.next_sync_committee
store.finalized_header = active_header

# https://github.com/ethereum/consensus-specs/blob/v1.1.7/specs/altair/sync-protocol.md#get_safety_threshold
func get_safety_threshold(store: LightClientStore): uint64 =
max(
store.previous_max_active_participants,
store.current_max_active_participants
) div 2

# https://github.com/ethereum/consensus-specs/blob/v1.1.7/specs/altair/sync-protocol.md#process_light_client_update
proc process_light_client_update*(store: var LightClientStore,
update: LightClientUpdate,
current_slot: Slot,
genesis_validators_root: Eth2Digest): bool =
if not validate_light_client_update(store.snapshot, update, genesis_validators_root):
if not validate_light_client_update(
store, update, current_slot, genesis_validators_root):
return false
store.valid_updates.incl(update)

var update_timeout = SLOTS_PER_EPOCH * EPOCHS_PER_SYNC_COMMITTEE_PERIOD
let sync_committee_participants_count = countOnes(update.sync_committee_bits)
if sync_committee_participants_count * 3 >= update.sync_committee_bits.len * 2 and
not update.finality_header.isZeroMemory:
# Apply update if (1) 2/3 quorum is reached and (2) we have a finality proof.
# Note that (2) means that the current light client design needs finality.
# It may be changed to re-organizable light client design. See the on-going issue consensus-specs#2182.
apply_light_client_update(store.snapshot, update)
store.valid_updates.clear()
elif current_slot > store.snapshot.header.slot + update_timeout:
var best_update_participants = 0
# TODO:
# Use a view type to avoid the copying when a new best update
# is discovered.
# Alterantively, we could have a `set.max` operation returning
# the best item as a `lent` value. We would need an `OrderedSet`
# type with support for a custom comparison operation.
var best_update: LightClientUpdate
for update in store.valid_updates:
let update_participants = countOnes(update.sync_committee_bits)
if update_participants > best_update_participants:
best_update = update
best_update_participants = update_participants

# Forced best update when the update timeout has elapsed
apply_light_client_update(store.snapshot, best_update)
store.valid_updates.clear()

let
sync_committee_bits = update.sync_committee_aggregate.sync_committee_bits
sum_sync_committee_bits = countOnes(sync_committee_bits)

# Update the best update in case we have to force-update to it if the
# timeout elapses
if store.best_valid_update.isNone or
sum_sync_committee_bits > countOnes(
store.best_valid_update.get.sync_committee_aggregate.sync_committee_bits):
store.best_valid_update = some(update)

# Track the maximum number of active participants in the committee signatures
store.current_max_active_participants = max(
store.current_max_active_participants,
sum_sync_committee_bits.uint64,
)

# Update the optimistic header
if sum_sync_committee_bits.uint64 > get_safety_threshold(store) and
update.attested_header.slot > store.optimistic_header.slot:
store.optimistic_header = update.attested_header

# Update finalized header
if sum_sync_committee_bits * 3 >= len(sync_committee_bits) * 2 and
update.finalized_header != default(BeaconBlockHeader):
# Normal update through 2/3 threshold
apply_light_client_update(store, update)
store.best_valid_update = none(LightClientUpdate)

true
Loading