port EF light client tests

EF has a number of unit tests related to light client functionality, see
https://github.com/ethereum/consensus-specs/blob/v1.1.0/tests/core/pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py
The tests have been ported over and integrated into our test suite.
This commit is contained in:
Etan Kissling 2021-09-13 18:35:13 +02:00 committed by zah
parent fcbc54bc08
commit f10a2b18bb
7 changed files with 332 additions and 8 deletions

View File

@ -531,6 +531,13 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
+ Testing VoluntaryExit OK
```
OK: 36/36 Fail: 0/36 Skip: 0/36
## 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
```
OK: 3/3 Fail: 0/3 Skip: 0/3
## Ethereum Foundation - Merge - Epoch Processing - Effective balance updates [Preset: mainnet]
```diff
+ Effective balance updates - effective_balance_hysteresis [Preset: mainnet] OK
@ -775,4 +782,4 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
OK: 27/27 Fail: 0/27 Skip: 0/27
---TOTAL---
OK: 641/641 Fail: 0/641 Skip: 0/641
OK: 644/644 Fail: 0/644 Skip: 0/644

View File

@ -542,6 +542,13 @@ OK: 5/5 Fail: 0/5 Skip: 0/5
+ Testing VoluntaryExit OK
```
OK: 36/36 Fail: 0/36 Skip: 0/36
## 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
```
OK: 3/3 Fail: 0/3 Skip: 0/3
## Ethereum Foundation - Merge - Epoch Processing - Effective balance updates [Preset: minimal]
```diff
+ Effective balance updates - effective_balance_hysteresis [Preset: minimal] OK
@ -805,4 +812,4 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
OK: 27/27 Fail: 0/27 Skip: 0/27
---TOTAL---
OK: 663/663 Fail: 0/663 Skip: 0/663
OK: 666/666 Fail: 0/666 Skip: 0/666

View File

@ -531,6 +531,13 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
+ Testing VoluntaryExit OK
```
OK: 36/36 Fail: 0/36 Skip: 0/36
## 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
```
OK: 3/3 Fail: 0/3 Skip: 0/3
## Ethereum Foundation - Merge - SSZ consensus objects [Preset: mainnet]
```diff
+ Testing AggregateAndProof OK
@ -675,4 +682,4 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
OK: 27/27 Fail: 0/27 Skip: 0/27
---TOTAL---
OK: 581/581 Fail: 0/581 Skip: 0/581
OK: 584/584 Fail: 0/584 Skip: 0/584

View File

@ -542,6 +542,13 @@ OK: 5/5 Fail: 0/5 Skip: 0/5
+ Testing VoluntaryExit OK
```
OK: 36/36 Fail: 0/36 Skip: 0/36
## 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
```
OK: 3/3 Fail: 0/3 Skip: 0/3
## Ethereum Foundation - Merge - SSZ consensus objects [Preset: minimal]
```diff
+ Testing AggregateAndProof OK
@ -690,4 +697,4 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
OK: 27/27 Fail: 0/27 Skip: 0/27
---TOTAL---
OK: 592/592 Fail: 0/592 Skip: 0/592
OK: 595/595 Fail: 0/595 Skip: 0/595

View File

@ -75,10 +75,10 @@ proc apply_light_client_update(snapshot: var LightClientSnapshot, update: LightC
snapshot.header = update.header
# https://github.com/ethereum/consensus-specs/blob/v1.1.0-beta.3/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 =
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):
return false
store.valid_updates.incl(update)

View File

@ -25,4 +25,5 @@ import
./test_fixture_operations_sync_aggregate,
./test_fixture_operations_voluntary_exit,
./test_fixture_fork,
./test_fixture_sync_protocol,
./test_fixture_transition

View File

@ -0,0 +1,295 @@
# beacon_chain
# Copyright (c) 2021 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
{.used.}
import
# Standard library
std/[algorithm, sequtils, sets],
# Status libraries
stew/bitops2,
# Beacon chain internals
../../../beacon_chain/spec/
[forks, helpers, light_client_sync, signatures, state_transition],
# Mock helpers
../../mocking/[mock_blocks, mock_genesis],
# Test utilities
../../testutil, ../../testblockutil
# https://github.com/ethereum/consensus-specs/blob/v1.1.0/tests/core/pyspec/eth2spec/test/helpers/sync_committee.py#L27-L44
proc compute_aggregate_sync_committee_signature(
forked: ForkedHashedBeaconState,
participants: openArray[ValidatorIndex],
block_root = ZERO_HASH): ValidatorSig =
template state: untyped {.inject.} = forked.hbsAltair.data
if len(participants) == 0:
return ValidatorSig.infinity
let
root =
if block_root != ZERO_HASH: block_root
else: mockBlockForNextSlot(forked).altairBlock.message.parent_root
signing_root = sync_committee_msg_signing_root(
state.fork, state.slot.epoch, state.genesis_validators_root, root)
var
aggregateSig {.noInit.}: AggregateSignature
initialized = false
for validator_index in participants:
let
privkey = MockPrivKeys[validator_index]
signature = blsSign(privkey, signing_root.data)
if not initialized:
initialized = true
aggregateSig.init(signature)
else:
aggregateSig.aggregate(signature)
aggregateSig.finish.toValidatorSig
proc block_for_next_slot(
cfg: RuntimeConfig,
forked: var ForkedHashedBeaconState,
cache: var StateCache,
withAttestations = false): ForkedSignedBeaconBlock =
template state: untyped {.inject.} = forked.hbsAltair.data
let parent_root = block:
var previous_block_header = state.latest_block_header
if previous_block_header.state_root == ZERO_HASH:
previous_block_header.state_root = state.hash_tree_root()
previous_block_header.hash_tree_root()
let attestations =
if withAttestations:
makeFullAttestations(forked, parent_root, state.slot, cache)
else:
@[]
addTestBlock(
forked, parent_root, cache, attestations = attestations, cfg = cfg)
let full_sync_committee_bits = block:
var res: BitArray[SYNC_COMMITTEE_SIZE]
res.bytes.fill(byte.high)
res
suite "Ethereum Foundation - Altair - Unittests - Sync protocol" & preset():
let
cfg = block:
var res = defaultRuntimeConfig
res.ALTAIR_FORK_EPOCH = GENESIS_EPOCH
res
genesisState = newClone(initGenesisState(cfg = cfg))
# https://github.com/ethereum/consensus-specs/blob/v1.1.0/tests/core/pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py#L25-L81
test "process_light_client_update_not_updated":
var forked = assignClone(genesisState[])
template state: untyped {.inject.} = forked[].hbsAltair.data
let pre_snapshot = LightClientSnapshot(
current_sync_committee: state.current_sync_committee,
next_sync_committee: state.next_sync_committee)
var store = LightClientStore(snapshot: pre_snapshot)
# Block at slot 1 doesn't increase sync committee period,
# so it won't update snapshot
var cache = StateCache()
let
signed_block = block_for_next_slot(cfg, forked[], cache).altairBlock
block_header = BeaconBlockHeader(
slot: signed_block.message.slot,
proposer_index: signed_block.message.proposer_index,
parent_root: signed_block.message.parent_root,
state_root: signed_block.message.state_root,
body_root: signed_block.message.body.hash_tree_root())
# Sync committee signing the header
all_pubkeys = state.validators.mapIt(it.pubkey)
committee = state.current_sync_committee.pubkeys
.mapIt(all_pubkeys.find(it).ValidatorIndex)
sync_committee_bits = full_sync_committee_bits
sync_committee_signature = compute_aggregate_sync_committee_signature(
forked[], committee)
var next_sync_committee_branch:
array[log2trunc(NEXT_SYNC_COMMITTEE_INDEX), Eth2Digest]
# Ensure that finality checkpoint is genesis
check: state.finalized_checkpoint.epoch == 0
# Finality is unchanged
let finality_header = BeaconBlockHeader()
var finality_branch: array[log2trunc(FINALIZED_ROOT_INDEX), Eth2Digest]
let update = LightClientUpdate(
header: block_header,
next_sync_committee: state.next_sync_committee,
next_sync_committee_branch: next_sync_committee_branch,
finality_header: finality_header,
finality_branch: finality_branch,
sync_committee_bits: sync_committee_bits,
sync_committee_signature: sync_committee_signature,
fork_version: state.fork.current_version)
check:
process_light_client_update(
store, update, state.slot, state.genesis_validators_root)
len(store.valid_updates) == 1
store.valid_updates.pop() == update
store.snapshot == pre_snapshot
# https://github.com/ethereum/consensus-specs/blob/v1.1.0/tests/core/pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py#L84-L147
test "process_light_client_update_timeout":
var forked = assignClone(genesisState[])
template state: untyped {.inject.} = forked[].hbsAltair.data
let pre_snapshot = LightClientSnapshot(
current_sync_committee: state.current_sync_committee,
next_sync_committee: state.next_sync_committee)
var store = LightClientStore(snapshot: pre_snapshot)
# Forward to next sync committee period
var
cache = StateCache()
rewards = RewardInfo()
doAssert process_slots(
cfg, forked[], Slot(SLOTS_PER_EPOCH * EPOCHS_PER_SYNC_COMMITTEE_PERIOD),
cache, rewards, flags = {})
let
snapshot_period =
pre_snapshot.header.slot.epoch div EPOCHS_PER_SYNC_COMMITTEE_PERIOD
update_period = state.slot.epoch div EPOCHS_PER_SYNC_COMMITTEE_PERIOD
check: snapshot_period + 1 == update_period
let
signed_block = block_for_next_slot(cfg, forked[], cache).altairBlock
block_header = BeaconBlockHeader(
slot: signed_block.message.slot,
proposer_index: signed_block.message.proposer_index,
parent_root: signed_block.message.parent_root,
state_root: signed_block.message.state_root,
body_root: signed_block.message.body.hash_tree_root())
# Sync committee signing the finalized_block_header
all_pubkeys = state.validators.mapIt(it.pubkey)
committee = state.current_sync_committee.pubkeys
.mapIt(all_pubkeys.find(it).ValidatorIndex)
sync_committee_bits = full_sync_committee_bits
sync_committee_signature = compute_aggregate_sync_committee_signature(
forked[], committee, block_root = block_header.hash_tree_root())
# Sync committee is updated
var next_sync_committee_branch {.noinit.}:
array[log2trunc(NEXT_SYNC_COMMITTEE_INDEX), Eth2Digest]
build_proof(state, NEXT_SYNC_COMMITTEE_INDEX, next_sync_committee_branch)
# Finality is unchanged
let finality_header = BeaconBlockHeader()
var finality_branch: array[log2trunc(FINALIZED_ROOT_INDEX), Eth2Digest]
let update = LightClientUpdate(
header: block_header,
next_sync_committee: state.next_sync_committee,
next_sync_committee_branch: next_sync_committee_branch,
finality_header: finality_header,
finality_branch: finality_branch,
sync_committee_bits: sync_committee_bits,
sync_committee_signature: sync_committee_signature,
fork_version: state.fork.current_version)
check:
process_light_client_update(
store, update, state.slot, state.genesis_validators_root)
# snapshot has been updated
len(store.valid_updates) == 0
store.snapshot.header == update.header
# https://github.com/ethereum/consensus-specs/blob/v1.1.0/tests/core/pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py#L150-L221
test "process_light_client_update_finality_updated":
var forked = assignClone(genesisState[])
template state: untyped {.inject.} = forked[].hbsAltair.data
let pre_snapshot = LightClientSnapshot(
current_sync_committee: state.current_sync_committee,
next_sync_committee: state.next_sync_committee)
var store = LightClientStore(snapshot: pre_snapshot)
# Change finality
var
cache = StateCache()
rewards = RewardInfo()
blocks = newSeq[ForkedSignedBeaconBlock]()
doAssert process_slots(
cfg, forked[], Slot(SLOTS_PER_EPOCH * 2),
cache, rewards, flags = {})
for epoch in 0 ..< 3:
for slot in 0 ..< SLOTS_PER_EPOCH:
blocks.add block_for_next_slot(cfg, forked[], cache,
withAttestations = true)
# Ensure that finality checkpoint has changed
check: state.finalized_checkpoint.epoch == 3
# Ensure that it's same period
let
snapshot_period =
pre_snapshot.header.slot.epoch div EPOCHS_PER_SYNC_COMMITTEE_PERIOD
update_period = state.slot.epoch div EPOCHS_PER_SYNC_COMMITTEE_PERIOD
check: snapshot_period == update_period
# Updated sync_committee and finality
var next_sync_committee_branch:
array[log2trunc(NEXT_SYNC_COMMITTEE_INDEX), Eth2Digest]
let
finalized_block = blocks[SLOTS_PER_EPOCH - 1].altairBlock
finalized_block_header = BeaconBlockHeader(
slot: finalized_block.message.slot,
proposer_index: finalized_block.message.proposer_index,
parent_root: finalized_block.message.parent_root,
state_root: finalized_block.message.state_root,
body_root: finalized_block.message.body.hash_tree_root())
check:
finalized_block_header.slot ==
compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)
finalized_block_header.hash_tree_root() ==
state.finalized_checkpoint.root
var finality_branch {.noinit.}:
array[log2trunc(FINALIZED_ROOT_INDEX), Eth2Digest]
build_proof(state, FINALIZED_ROOT_INDEX, finality_branch)
# Build block header
let
blck = mockBlock(forked[], state.slot, cfg = cfg).altairBlock.message
block_header = BeaconBlockHeader(
slot: blck.slot,
proposer_index: blck.proposer_index,
parent_root: blck.parent_root,
state_root: state.hash_tree_root(),
body_root: blck.body.hash_tree_root())
# Sync committee signing the finalized_block_header
all_pubkeys = state.validators.mapIt(it.pubkey)
committee = state.current_sync_committee.pubkeys
.mapIt(all_pubkeys.find(it).ValidatorIndex)
sync_committee_bits = full_sync_committee_bits
sync_committee_signature = compute_aggregate_sync_committee_signature(
forked[], committee, block_root = block_header.hash_tree_root())
update = LightClientUpdate(
header: finalized_block_header,
next_sync_committee: state.next_sync_committee,
next_sync_committee_branch: next_sync_committee_branch,
finality_header: block_header,
finality_branch: finality_branch,
sync_committee_bits: sync_committee_bits,
sync_committee_signature: sync_committee_signature,
fork_version: state.fork.current_version)
check:
process_light_client_update(
store, update, state.slot, state.genesis_validators_root)
# snapshot has been updated
len(store.valid_updates) == 0
store.snapshot.header == update.header