update to latest LC test format (#3879)

The EF test format for the LC sync protocol is modified to verify checks
after each step: https://github.com/ethereum/consensus-specs/pull/2938 -
The test runner is updated accordingly.
This commit is contained in:
Etan Kissling 2022-07-23 07:54:01 +02:00 committed by GitHub
parent c3f9844de1
commit 3bc42994e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 350 additions and 363 deletions

View File

@ -422,16 +422,14 @@ ConsensusSpecPreset-mainnet
+ bellatrix_fork_random_3 OK
+ bellatrix_fork_random_low_balances OK
+ bellatrix_fork_random_misc_balances OK
+ finality_root_merkle_proof OK
+ fork_base_state OK
+ fork_many_next_epoch OK
+ fork_next_epoch OK
+ fork_next_epoch_with_block OK
+ fork_random_low_balances OK
+ fork_random_misc_balances OK
+ next_sync_committee_merkle_proof OK
```
OK: 421/428 Fail: 0/428 Skip: 7/428
OK: 419/426 Fail: 0/426 Skip: 7/426
## Attestation
```diff
+ [Invalid] EF - Altair - Operations - Attestation - after_epoch_slots OK
@ -852,17 +850,7 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
+ Testing VoluntaryExit OK
```
OK: 35/35 Fail: 0/35 Skip: 0/35
## EF - Altair - Sync protocol - Light client [Preset: mainnet]
```diff
All tests Skip
```
OK: 0/1 Fail: 0/1 Skip: 1/1
## EF - Altair - Sync protocol - Update ranking [Preset: mainnet]
```diff
All tests Skip
```
OK: 0/1 Fail: 0/1 Skip: 1/1
## EF - Altair - Unittests - Sync protocol [Preset: mainnet]
## EF - Altair - Unittests - Light client - Sync protocol [Preset: mainnet]
```diff
+ process_light_client_update_finality_updated OK
+ process_light_client_update_timeout OK
@ -1261,4 +1249,4 @@ OK: 44/44 Fail: 0/44 Skip: 0/44
OK: 27/27 Fail: 0/27 Skip: 0/27
---TOTAL---
OK: 1074/1083 Fail: 0/1083 Skip: 9/1083
OK: 1072/1079 Fail: 0/1079 Skip: 7/1079

View File

@ -471,7 +471,6 @@ ConsensusSpecPreset-minimal
+ bellatrix_fork_random_large_validator_set OK
+ bellatrix_fork_random_low_balances OK
+ bellatrix_fork_random_misc_balances OK
+ finality_root_merkle_proof OK
+ fork_base_state OK
+ fork_many_next_epoch OK
+ fork_next_epoch OK
@ -479,9 +478,8 @@ ConsensusSpecPreset-minimal
+ fork_random_large_validator_set OK
+ fork_random_low_balances OK
+ fork_random_misc_balances OK
+ next_sync_committee_merkle_proof OK
```
OK: 471/478 Fail: 0/478 Skip: 7/478
OK: 469/476 Fail: 0/476 Skip: 7/476
## Attestation
```diff
+ [Invalid] EF - Altair - Operations - Attestation - after_epoch_slots OK
@ -917,17 +915,7 @@ OK: 5/5 Fail: 0/5 Skip: 0/5
+ Testing VoluntaryExit OK
```
OK: 35/35 Fail: 0/35 Skip: 0/35
## EF - Altair - Sync protocol - Light client [Preset: minimal]
```diff
All tests Skip
```
OK: 0/1 Fail: 0/1 Skip: 1/1
## EF - Altair - Sync protocol - Update ranking [Preset: minimal]
```diff
All tests Skip
```
OK: 0/1 Fail: 0/1 Skip: 1/1
## EF - Altair - Unittests - Sync protocol [Preset: minimal]
## EF - Altair - Unittests - Light client - Sync protocol [Preset: minimal]
```diff
+ process_light_client_update_finality_updated OK
+ process_light_client_update_timeout OK
@ -1352,4 +1340,4 @@ OK: 48/48 Fail: 0/48 Skip: 0/48
OK: 30/30 Fail: 0/30 Skip: 0/30
---TOTAL---
OK: 1157/1166 Fail: 0/1166 Skip: 9/1166
OK: 1155/1162 Fail: 0/1162 Skip: 7/1162

View File

@ -172,7 +172,7 @@ proc tryForceUpdate(
if store[].isSome:
doAssert self.finalizationMode == LightClientFinalizationMode.Optimistic
case store[].get.try_light_client_store_force_update(wallSlot)
case store[].get.process_light_client_store_force_update(wallSlot)
of NoUpdate:
discard
of DidUpdateWithoutSupermajority:

View File

@ -180,14 +180,14 @@ func apply_light_client_update(
didProgress = true
didProgress
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#try_light_client_store_force_update
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#process_light_client_store_force_update
type
ForceUpdateResult* = enum
NoUpdate,
DidUpdateWithoutSupermajority,
DidUpdateWithoutFinality
func try_light_client_store_force_update*(
func process_light_client_store_force_update*(
store: var LightClientStore,
current_slot: Slot): ForceUpdateResult {.discardable.} =
var res = NoUpdate

View File

@ -9,13 +9,10 @@
import
./test_fixture_fork,
./test_fixture_merkle_single_proof,
./test_fixture_light_client_sync_protocol,
./test_fixture_operations,
./test_fixture_sanity_blocks,
./test_fixture_sanity_slots,
./test_fixture_ssz_consensus_objects,
./test_fixture_state_transition_epoch,
./test_fixture_sync_protocol_light_client_sync,
./test_fixture_sync_protocol_update_ranking,
./test_fixture_sync_protocol,
./test_fixture_transition

View File

@ -153,7 +153,7 @@ func initialize_light_client_store(state: auto): LightClientStore =
current_max_active_participants: 0,
)
suite "EF - Altair - Unittests - Sync protocol" & preset():
suite "EF - Altair - Unittests - Light client - Sync protocol" & preset():
let
cfg = block:
var res = defaultRuntimeConfig
@ -161,7 +161,7 @@ suite "EF - Altair - Unittests - Sync protocol" & preset():
res
genesisState = newClone(initGenesisState(cfg = cfg))
# https://github.com/ethereum/consensus-specs/blob/vFuture/tests/core/pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py#L25-L67
# https://github.com/ethereum/consensus-specs/blob/vFuture/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py#L25-L67
test "test_process_light_client_update_not_timeout":
let forked = assignClone(genesisState[])
template state(): auto = forked[].altairData.data
@ -207,7 +207,7 @@ suite "EF - Altair - Unittests - Sync protocol" & preset():
store.optimistic_header == update.attested_header
store.current_max_active_participants > 0
# https://github.com/ethereum/consensus-specs/blob/vFuture/tests/core/pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py#L70-L116
# https://github.com/ethereum/consensus-specs/blob/vFuture/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py#L70-L116
test "test_process_light_client_update_at_period_boundary":
var forked = assignClone(genesisState[])
template state(): auto = forked[].altairData.data
@ -260,7 +260,7 @@ suite "EF - Altair - Unittests - Sync protocol" & preset():
store.optimistic_header == update.attested_header
store.current_max_active_participants > 0
# https://github.com/ethereum/consensus-specs/blob/vFuture/tests/core/pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py#L119-L166
# https://github.com/ethereum/consensus-specs/blob/vFuture/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py#L119-L166
test "process_light_client_update_timeout":
let forked = assignClone(genesisState[])
template state(): auto = forked[].altairData.data
@ -316,7 +316,7 @@ suite "EF - Altair - Unittests - Sync protocol" & preset():
store.optimistic_header == update.attested_header
store.current_max_active_participants > 0
# https://github.com/ethereum/consensus-specs/blob/vFuture/tests/core/pyspec/eth2spec/test/altair/unittests/test_sync_protocol.py#L169-L223
# https://github.com/ethereum/consensus-specs/blob/vFuture/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py#L169-L223
test "process_light_client_update_finality_updated":
let forked = assignClone(genesisState[])
template state(): auto = forked[].altairData.data

View File

@ -1,63 +0,0 @@
# beacon_chain
# Copyright (c) 2021-2022 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/[os, sequtils, streams],
# Status libraries
stew/bitops2,
# Third-party
yaml,
# Beacon chain internals
../../../beacon_chain/spec/datatypes/altair,
../../../beacon_chain/spec/helpers,
# Test utilities
../../testutil,
../fixtures_utils
const TestsDir =
SszTestsDir/const_preset/"altair"/"merkle"/"single_proof"/"pyspec_tests"
proc runTest(identifier: string) =
let testDir = TestsDir / identifier
proc `testImpl _ merkle_single_proof _ identifier`() =
test identifier:
type
TestProof = object
leaf: string
leaf_index: GeneralizedIndex
branch: seq[string]
let
proof = block:
let s = openFileStream(testDir/"proof.yaml")
defer: close(s)
var res: TestProof
yaml.load(s, res)
res
state = newClone(parseTest(testDir/"state.ssz_snappy", SSZ,
altair.BeaconState))
var computedProof = newSeq[Eth2Digest](log2trunc(proof.leaf_index))
build_proof(state[], proof.leaf_index, computedProof).get
check:
computedProof == proof.branch.mapIt(Eth2Digest.fromHex(it))
is_valid_merkle_branch(Eth2Digest.fromHex(proof.leaf), computedProof,
log2trunc(proof.leaf_index),
get_subtree_index(proof.leaf_index),
hash_tree_root(state[]))
`testImpl _ merkle_single_proof _ identifier`()
suite "EF - Altair - Merkle - Single proof" & preset():
for kind, path in walkDir(TestsDir, relative = true, checkDir = true):
runTest(path)

View File

@ -1,144 +0,0 @@
# beacon_chain
# Copyright (c) 2022 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.}
# This implements the pre-release proposal of the libp2p based light client sync
# protocol. See https://github.com/ethereum/consensus-specs/pull/2802
import
# Standard library
std/[json, os, streams],
# Status libraries
stew/bitops2,
# Third-party
yaml,
# Beacon chain internals
../../../beacon_chain/spec/light_client_sync,
../../../beacon_chain/spec/datatypes/altair,
# Test utilities
../../testutil,
../fixtures_utils
const TestsDir =
SszTestsDir/const_preset/"altair"/"sync_protocol"/"light_client_sync"/"pyspec_tests"
type
TestMeta = object
genesis_validators_root: string
trusted_block_root: string
TestStepKind {.pure.} = enum
ForceUpdate
ProcessUpdate
TestStep = object
case kind: TestStepKind
of TestStepKind.ForceUpdate:
discard
of TestStepKind.ProcessUpdate:
update: altair.LightClientUpdate
current_slot: Slot
proc loadSteps(path: string): seq[TestStep] =
let stepsYAML = readFile(path/"steps.yaml")
let steps = yaml.loadToJson(stepsYAML)
result = @[]
for step in steps[0]:
if step.hasKey"force_update":
let s = step["force_update"]
result.add TestStep(kind: TestStepKind.ForceUpdate,
current_slot: s["current_slot"].getInt().Slot)
elif step.hasKey"process_update":
let
s = step["process_update"]
filename = s["update"].getStr()
update = parseTest(path/filename & ".ssz_snappy", SSZ,
altair.LightClientUpdate)
result.add TestStep(kind: TestStepKind.ProcessUpdate,
update: update,
current_slot: s["current_slot"].getInt().Slot)
else:
doAssert false, "Unreachable: " & $step
proc runTest(identifier: string) =
let testDir = TestsDir / identifier
proc `testImpl _ sync_protocol_light_client_sync _ identifier`() =
test identifier:
let
meta = block:
var s = openFileStream(testDir/"meta.yaml")
defer: close(s)
var res: TestMeta
yaml.load(s, res)
res
genesis_validators_root =
Eth2Digest.fromHex(meta.genesis_validators_root)
trusted_block_root =
Eth2Digest.fromHex(meta.trusted_block_root)
bootstrap = parseTest(testDir/"bootstrap.ssz_snappy", SSZ,
altair.LightClientBootstrap)
steps = loadSteps(testDir)
expected_finalized_header =
parseTest(testDir/"expected_finalized_header.ssz_snappy", SSZ,
BeaconBlockHeader)
expected_optimistic_header =
parseTest(testDir/"expected_optimistic_header.ssz_snappy", SSZ,
BeaconBlockHeader)
var cfg = defaultRuntimeConfig
cfg.ALTAIR_FORK_EPOCH = GENESIS_EPOCH
var store =
initialize_light_client_store(trusted_block_root, bootstrap).get
for step in steps:
case step.kind
of TestStepKind.ForceUpdate:
try_light_client_store_force_update(
store, step.current_slot)
of TestStepKind.ProcessUpdate:
let res = process_light_client_update(
store, step.update, step.current_slot,
cfg, genesis_validators_root)
check res.isOk
check:
store.finalized_header == expected_finalized_header
store.optimistic_header == expected_optimistic_header
`testImpl _ sync_protocol_light_client_sync _ identifier`()
suite "EF - Altair - Sync protocol - Light client" & preset():
try:
for kind, path in walkDir(TestsDir, relative = true, checkDir = true):
runTest(path)
except OSError:
# These tests are for the pre-release proposal of the libp2p based light
# client sync protocol. Corresponding test vectors need manual integration.
# https://github.com/ethereum/consensus-specs/pull/2802
#
# To locally integrate the test vectors, clone the pre-release spec repo
# at latest commit of https://github.com/ethereum/consensus-specs/pull/2802
# and place it next to the `nimbus-eth2` repo, so that `nimbus-eth2` and
# `consensus-specs` are in the same directory.
#
# To generate the additional test vectors, from `consensus-specs`:
# $ rm -rf ../consensus-spec-tests && \
# doctoc specs && make lint && make gen_sync_protocol
#
# To integrate the additional test vectors into `nimbus-eth2`, first run
# `make test` from `nimbus-eth2` to ensure that the regular test vectors
# have been downloaded and extracted, then proceed from `nimbus-eth2` with:
# $ rsync -r ../consensus-spec-tests/tests/ \
# ../nimbus-eth2/vendor/nim-eth2-scenarios/tests-v1.2.0-rc.1/
test "All tests":
skip()

View File

@ -1,88 +0,0 @@
# beacon_chain
# Copyright (c) 2022 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.}
# This implements the pre-release proposal of the libp2p based light client sync
# protocol. See https://github.com/ethereum/consensus-specs/pull/2802
import
# Standard library
std/[algorithm, os, streams],
# Status libraries
stew/base10,
# Third-party
yaml,
# Beacon chain internals
../../../beacon_chain/spec/helpers,
../../../beacon_chain/spec/datatypes/altair,
# Test utilities
../../testutil,
../fixtures_utils
const TestsDir =
SszTestsDir/const_preset/"altair"/"sync_protocol"/"update_ranking"/"pyspec_tests"
type
TestMeta = object
updates_count: uint64
proc runTest(identifier: string) =
let testDir = TestsDir / identifier
proc `testImpl _ sync_protocol_update_ranking _ identifier`() =
test identifier:
let meta = block:
var s = openFileStream(testDir/"meta.yaml")
defer: close(s)
var res: TestMeta
yaml.load(s, res)
res
var updates = newSeqOfCap[altair.LightClientUpdate](meta.updates_count)
for i in 0 ..< meta.updates_count:
updates.add parseTest(
testDir/"updates_" & Base10.toString(i) & ".ssz_snappy",
SSZ, altair.LightClientUpdate)
proc cmp(a, b: altair.LightClientUpdate): int =
if a.is_better_update(b):
check: not b.is_better_update(a)
-1
elif b.is_better_update(a):
1
else:
0
check: updates.isSorted(cmp)
`testImpl _ sync_protocol_update_ranking _ identifier`()
suite "EF - Altair - Sync protocol - Update ranking" & preset():
try:
for kind, path in walkDir(TestsDir, relative = true, checkDir = true):
runTest(path)
except OSError:
# These tests are for the pre-release proposal of the libp2p based light
# client sync protocol. Corresponding test vectors need manual integration.
# https://github.com/ethereum/consensus-specs/pull/2802
#
# To locally integrate the test vectors, clone the pre-release spec repo
# at latest commit of https://github.com/ethereum/consensus-specs/pull/2802
# and place it next to the `nimbus-eth2` repo, so that `nimbus-eth2` and
# `consensus-specs` are in the same directory.
#
# To generate the additional test vectors, from `consensus-specs`:
# $ rm -rf ../consensus-spec-tests && \
# doctoc specs && make lint && make gen_sync_protocol
#
# To integrate the additional test vectors into `nimbus-eth2`, first run
# `make test` from `nimbus-eth2` to ensure that the regular test vectors
# have been downloaded and extracted, then proceed from `nimbus-eth2` with:
# $ rsync -r ../consensus-spec-tests/tests/ \
# ../nimbus-eth2/vendor/nim-eth2-scenarios/tests-v1.2.0-rc.1/
test "All tests":
skip()

View File

@ -14,6 +14,9 @@ import
./phase0/all_phase0_fixtures,
./altair/all_altair_fixtures,
./bellatrix/all_bellatrix_fixtures,
./test_fixture_fork_choice
./test_fixture_fork_choice,
./test_fixture_light_client_single_merkle_proof,
./test_fixture_light_client_sync,
./test_fixture_light_client_update_ranking
summarizeLongTests("ConsensusSpecPreset")

View File

@ -9,6 +9,7 @@ import
# Standard library
std/[os, strutils, typetraits],
# Internals
../../beacon_chain/spec/datatypes/[phase0, altair, bellatrix],
../../beacon_chain/spec/[
eth2_merkleization, eth2_ssz_serialization, forks],
# Status libs,
@ -112,3 +113,26 @@ proc parseTest*(path: string, Format: typedesc[SSZ], T: typedesc): T =
stderr.write $Format & " load issue for file \"", path, "\"\n"
stderr.write err.formatMsg(path), "\n"
quit 1
proc loadForkedState*(
path: string, fork: BeaconStateFork): ref ForkedHashedBeaconState =
# TODO stack usage. newClone and assignClone do not seem to
# prevent temporaries created by case objects
let forkedState = new ForkedHashedBeaconState
case fork
of BeaconStateFork.Bellatrix:
let state = newClone(parseTest(path, SSZ, bellatrix.BeaconState))
forkedState.kind = BeaconStateFork.Bellatrix
forkedState.bellatrixData.data = state[]
forkedState.bellatrixData.root = hash_tree_root(state[])
of BeaconStateFork.Altair:
let state = newClone(parseTest(path, SSZ, altair.BeaconState))
forkedState.kind = BeaconStateFork.Altair
forkedState.altairData.data = state[]
forkedState.altairData.root = hash_tree_root(state[])
of BeaconStateFork.Phase0:
let state = newClone(parseTest(path, SSZ, phase0.BeaconState))
forkedState.kind = BeaconStateFork.Phase0
forkedState.phase0Data.data = state[]
forkedState.phase0Data.root = hash_tree_root(state[])
forkedState

View File

@ -62,32 +62,14 @@ proc initialLoad(
path: string, db: BeaconChainDB,
StateType, BlockType: typedesc
): tuple[dag: ChainDAGRef, fkChoice: ref ForkChoice] =
let state = newClone(parseTest(
path/"anchor_state.ssz_snappy",
SSZ, StateType
))
let
forkedState = loadForkedState(
path/"anchor_state.ssz_snappy",
StateType.toFork)
# TODO stack usage. newClone and assignClone do not seem to
# prevent temporaries created by case objects
let forkedState = new ForkedHashedBeaconState
when StateType is bellatrix.BeaconState:
forkedState.kind = BeaconStateFork.Bellatrix
forkedState.bellatrixData.data = state[]
forkedState.bellatrixData.root = hash_tree_root(state[])
elif StateType is altair.BeaconState:
forkedState.kind = BeaconStateFork.Altair
forkedState.altairData.data = state[]
forkedState.altairData.root = hash_tree_root(state[])
elif StateType is phase0.BeaconState:
forkedState.kind = BeaconStateFork.Phase0
forkedState.phase0Data.data = state[]
forkedState.phase0Data.root = hash_tree_root(state[])
else: {.error: "Unknown state fork: " & name(StateType).}
let blck = parseTest(
path/"anchor_block.ssz_snappy",
SSZ, BlockType
)
blck = parseTest(
path/"anchor_block.ssz_snappy",
SSZ, BlockType)
when BlockType is bellatrix.BeaconBlock:
let signedBlock = ForkedSignedBeaconBlock.init(bellatrix.SignedBeaconBlock(

View File

@ -0,0 +1,66 @@
# beacon_chain
# Copyright (c) 2021-2022 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/[os, sequtils, streams],
# Status libraries
stew/bitops2,
# Third-party
yaml,
# Beacon chain internals
../../../beacon_chain/spec/datatypes/altair,
../../../beacon_chain/spec/helpers,
# Test utilities
../testutil,
./fixtures_utils
proc runTest(path: string, fork: BeaconStateFork) =
test "Light client - Single merkle proof - " & path.relativePath(SszTestsDir):
type
TestProof = object
leaf: string
leaf_index: GeneralizedIndex
branch: seq[string]
let
proof = block:
let s = openFileStream(path/"proof.yaml")
defer: close(s)
var res: TestProof
yaml.load(s, res)
res
state = loadForkedState(path/"state.ssz_snappy", fork)
withState(state[]):
var computedProof = newSeq[Eth2Digest](log2trunc(proof.leaf_index))
build_proof(state.data, proof.leaf_index, computedProof).get
check:
computedProof == proof.branch.mapIt(Eth2Digest.fromHex(it))
is_valid_merkle_branch(
Eth2Digest.fromHex(proof.leaf),
computedProof,
log2trunc(proof.leaf_index),
get_subtree_index(proof.leaf_index),
state.root)
suite "EF - Light client - Single merkle proof" & preset():
const presetPath = SszTestsDir/const_preset
for kind, path in walkDir(presetPath, relative = true, checkDir = true):
let testsPath = presetPath/path/"light_client"/"single_merkle_proof"
if kind != pcDir or not dirExists(testsPath):
continue
let
fork = forkForPathComponent(path).valueOr:
raiseAssert "Unknown test fork: " & testsPath
basePath = testsPath/"pyspec_tests"
for kind, path in walkDir(basePath, relative = true, checkDir = true):
runTest(basePath/path, fork)

View File

@ -0,0 +1,151 @@
# beacon_chain
# Copyright (c) 2022 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.}
# This implements the pre-release proposal of the libp2p based light client sync
# protocol. See https://github.com/ethereum/consensus-specs/pull/2802
# These tests are for the pre-release proposal of the libp2p based light
# client sync protocol. Corresponding test vectors need manual integration.
# https://github.com/ethereum/consensus-specs/pull/2802
#
# To locally integrate the test vectors, clone the pre-release spec repo
# at latest commit of https://github.com/ethereum/consensus-specs/pull/2802
# and place it next to the `nimbus-eth2` repo, so that `nimbus-eth2` and
# `consensus-specs` are in the same directory.
#
# To generate the additional test vectors, from `consensus-specs`:
# $ rm -rf ../consensus-spec-tests && \
# doctoc specs && make lint && make gen_light_client
#
# To integrate the additional test vectors into `nimbus-eth2`, first run
# `make test` from `nimbus-eth2` to ensure that the regular test vectors
# have been downloaded and extracted, then proceed from `nimbus-eth2` with:
# $ rsync -r ../consensus-spec-tests/tests/ \
# ../nimbus-eth2/vendor/nim-eth2-scenarios/tests-v1.2.0-rc.1/
import
# Standard library
std/[json, os, streams],
# Status libraries
stew/bitops2,
# Third-party
yaml,
# Beacon chain internals
../../../beacon_chain/spec/light_client_sync,
../../../beacon_chain/spec/datatypes/altair,
# Test utilities
../testutil,
./fixtures_utils
type
TestMeta = object
genesis_validators_root: string
trusted_block_root: string
TestChecks = object
finalized_slot: Slot
finalized_root: Eth2Digest
optimistic_slot: Slot
optimistic_root: Eth2Digest
TestStepKind {.pure.} = enum
ForceUpdate
ProcessUpdate
TestStep = object
case kind: TestStepKind
of TestStepKind.ForceUpdate:
discard
of TestStepKind.ProcessUpdate:
update: altair.LightClientUpdate
current_slot: Slot
checks: TestChecks
proc loadSteps(path: string): seq[TestStep] =
let stepsYAML = readFile(path/"steps.yaml")
let steps = yaml.loadToJson(stepsYAML)
result = @[]
for step in steps[0]:
func getChecks(c: JsonNode): TestChecks =
TestChecks(
finalized_slot:
c["finalized_header"]["slot"].getInt().Slot,
finalized_root:
Eth2Digest.fromHex(c["finalized_header"]["root"].getStr()),
optimistic_slot:
c["optimistic_header"]["slot"].getInt().Slot,
optimistic_root:
Eth2Digest.fromHex(c["optimistic_header"]["root"].getStr()))
if step.hasKey"force_update":
let s = step["force_update"]
result.add TestStep(kind: TestStepKind.ForceUpdate,
current_slot: s["current_slot"].getInt().Slot,
checks: s["checks"].getChecks())
elif step.hasKey"process_update":
let
s = step["process_update"]
filename = s["update"].getStr()
update = parseTest(path/filename & ".ssz_snappy", SSZ,
altair.LightClientUpdate)
result.add TestStep(kind: TestStepKind.ProcessUpdate,
update: update,
current_slot: s["current_slot"].getInt().Slot,
checks: s["checks"].getChecks())
else:
doAssert false, "Unknown test step: " & $step
proc runTest(path: string) =
test "Light client - Sync - " & path.relativePath(SszTestsDir):
let
(cfg, unknowns) = readRuntimeConfig(path/"config.yaml")
meta = block:
var s = openFileStream(path/"meta.yaml")
defer: close(s)
var res: TestMeta
yaml.load(s, res)
res
genesis_validators_root =
Eth2Digest.fromHex(meta.genesis_validators_root)
trusted_block_root =
Eth2Digest.fromHex(meta.trusted_block_root)
bootstrap = parseTest(path/"bootstrap.ssz_snappy", SSZ,
altair.LightClientBootstrap)
steps = loadSteps(path)
doAssert unknowns.len == 0, "Unknown config constants: " & $unknowns
var store =
initialize_light_client_store(trusted_block_root, bootstrap).get
for step in steps:
case step.kind
of TestStepKind.ForceUpdate:
process_light_client_store_force_update(
store, step.current_slot)
of TestStepKind.ProcessUpdate:
let res = process_light_client_update(
store, step.update, step.current_slot,
cfg, genesis_validators_root)
check res.isOk
check:
store.finalized_header.slot == step.checks.finalized_slot
hash_tree_root(store.finalized_header) == step.checks.finalized_root
store.optimistic_header.slot == step.checks.optimistic_slot
hash_tree_root(store.optimistic_header) == step.checks.optimistic_root
suite "EF - Light client - Sync" & preset():
const presetPath = SszTestsDir/const_preset
for kind, path in walkDir(presetPath, relative = true, checkDir = true):
let basePath =
presetPath/path/"light_client"/"sync"/"pyspec_tests"
if kind != pcDir or not dirExists(basePath):
continue
for kind, path in walkDir(basePath, relative = true, checkDir = true):
runTest(basePath/path)

View File

@ -0,0 +1,83 @@
# beacon_chain
# Copyright (c) 2022 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.}
# This implements the pre-release proposal of the libp2p based light client sync
# protocol. See https://github.com/ethereum/consensus-specs/pull/2802
# These tests are for the pre-release proposal of the libp2p based light
# client sync protocol. Corresponding test vectors need manual integration.
# https://github.com/ethereum/consensus-specs/pull/2802
#
# To locally integrate the test vectors, clone the pre-release spec repo
# at latest commit of https://github.com/ethereum/consensus-specs/pull/2802
# and place it next to the `nimbus-eth2` repo, so that `nimbus-eth2` and
# `consensus-specs` are in the same directory.
#
# To generate the additional test vectors, from `consensus-specs`:
# $ rm -rf ../consensus-spec-tests && \
# doctoc specs && make lint && make gen_light_client
#
# To integrate the additional test vectors into `nimbus-eth2`, first run
# `make test` from `nimbus-eth2` to ensure that the regular test vectors
# have been downloaded and extracted, then proceed from `nimbus-eth2` with:
# $ rsync -r ../consensus-spec-tests/tests/ \
# ../nimbus-eth2/vendor/nim-eth2-scenarios/tests-v1.2.0-rc.1/
import
# Standard library
std/[algorithm, os, streams],
# Status libraries
stew/base10,
# Third-party
yaml,
# Beacon chain internals
../../../beacon_chain/spec/helpers,
../../../beacon_chain/spec/datatypes/altair,
# Test utilities
../testutil,
./fixtures_utils
type
TestMeta = object
updates_count: uint64
proc runTest(path: string) =
test "Light client - Update ranking - " & path.relativePath(SszTestsDir):
let meta = block:
var s = openFileStream(path/"meta.yaml")
defer: close(s)
var res: TestMeta
yaml.load(s, res)
res
var updates = newSeqOfCap[altair.LightClientUpdate](meta.updates_count)
for i in 0 ..< meta.updates_count:
updates.add parseTest(
path/"updates_" & Base10.toString(i) & ".ssz_snappy",
SSZ, altair.LightClientUpdate)
proc cmp(a, b: altair.LightClientUpdate): int =
if a.is_better_update(b):
check: not b.is_better_update(a)
-1
elif b.is_better_update(a):
1
else:
0
check: updates.isSorted(cmp)
suite "EF - Light client - Update ranking" & preset():
const presetPath = SszTestsDir/const_preset
for kind, path in walkDir(presetPath, relative = true, checkDir = true):
let basePath =
presetPath/path/"light_client"/"update_ranking"/"pyspec_tests"
if kind != pcDir or not dirExists(basePath):
continue
for kind, path in walkDir(basePath, relative = true, checkDir = true):
runTest(basePath/path)