nimbus-eth2/tests/consensus_spec/test_fixture_light_client_s...

187 lines
7.0 KiB
Nim

# beacon_chain
# Copyright (c) 2022-2023 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/[json, os, streams],
# Third-party
yaml,
# Beacon chain internals
../../../beacon_chain/spec/[forks, light_client_sync],
# Test utilities
../testutil,
./fixtures_utils
type
TestMeta = object
genesis_validators_root: string
trusted_block_root: string
TestChecks = object
finalized_slot: Slot
finalized_beacon_root: Eth2Digest
optimistic_slot: Slot
optimistic_beacon_root: Eth2Digest
TestStepKind {.pure.} = enum
ForceUpdate
ProcessUpdate
TestStep = object
case kind: TestStepKind
of TestStepKind.ForceUpdate:
discard
of TestStepKind.ProcessUpdate:
update: ForkedLightClientUpdate
current_slot: Slot
checks: TestChecks
proc loadSteps(path: string, fork_digests: ForkDigests): 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_beacon_root:
Eth2Digest.fromHex(c["finalized_header"]["beacon_root"].getStr()),
optimistic_slot:
c["optimistic_header"]["slot"].getInt().Slot,
optimistic_beacon_root:
Eth2Digest.fromHex(c["optimistic_header"]["beacon_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"]
update_fork_digest = fork_digests.altair
update_state_fork =
fork_digests.stateForkForDigest(update_fork_digest)
.expect("Unknown update fork " & $update_fork_digest)
update_filename = s["update"].getStr()
var update {.noinit.}: ForkedLightClientUpdate
withLcDataFork(lcDataForkAtStateFork(update_state_fork)):
when lcDataFork > LightClientDataFork.None:
update = ForkedLightClientUpdate(kind: lcDataFork)
update.forky(lcDataFork) = parseTest(
path/update_filename & ".ssz_snappy", SSZ,
lcDataFork.LightClientUpdate)
else: raiseAssert "Unreachable update fork " & $update_fork_digest
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)
fork_digests =
ForkDigests.init(cfg, genesis_validators_root)
bootstrap_fork_digest = fork_digests.altair
store_fork_digest = fork_digests.altair
bootstrap_state_fork =
fork_digests.stateForkForDigest(bootstrap_fork_digest)
.expect("Unknown bootstrap fork " & $bootstrap_fork_digest)
store_state_fork =
fork_digests.stateForkForDigest(store_fork_digest)
.expect("Unknown store fork " & $store_fork_digest)
steps = loadSteps(path, fork_digests)
doAssert unknowns.len == 0, "Unknown config constants: " & $unknowns
var bootstrap {.noinit.}: ForkedLightClientBootstrap
withLcDataFork(lcDataForkAtStateFork(bootstrap_state_fork)):
when lcDataFork > LightClientDataFork.None:
bootstrap = ForkedLightClientBootstrap(kind: lcDataFork)
bootstrap.forky(lcDataFork) = parseTest(
path/"bootstrap.ssz_snappy", SSZ,
lcDataFork.LightClientBootstrap)
else: raiseAssert "Unsupported bootstrap fork " & $bootstrap_fork_digest
var store {.noinit.}: ForkedLightClientStore
withLcDataFork(lcDataForkAtStateFork(store_state_fork)):
when lcDataFork > LightClientDataFork.None:
store = ForkedLightClientStore(kind: lcDataFork)
check bootstrap.kind <= lcDataFork
let upgradedBootstrap = bootstrap.migratingToDataFork(lcDataFork)
store.forky(lcDataFork) = initialize_light_client_store(
trusted_block_root, upgradedBootstrap.forky(lcDataFork), cfg).get
else: raiseAssert "Unreachable store fork " & $store_fork_digest
for step in steps:
withForkyStore(store):
when lcDataFork > LightClientDataFork.None:
case step.kind
of TestStepKind.ForceUpdate:
process_light_client_store_force_update(
forkyStore, step.current_slot)
of TestStepKind.ProcessUpdate:
check step.update.kind <= lcDataFork
let
upgradedUpdate = step.update.migratingToDataFork(lcDataFork)
res = process_light_client_update(
forkyStore, upgradedUpdate.forky(lcDataFork), step.current_slot,
cfg, genesis_validators_root)
check res.isOk
else: raiseAssert "Unreachable"
withForkyStore(store):
when lcDataFork > LightClientDataFork.None:
let
finalized_slot =
forkyStore.finalized_header.beacon.slot
finalized_beacon_root =
hash_tree_root(forkyStore.finalized_header.beacon)
optimistic_slot =
forkyStore.optimistic_header.beacon.slot
optimistic_beacon_root =
hash_tree_root(forkyStore.optimistic_header.beacon)
check:
finalized_slot == step.checks.finalized_slot
finalized_beacon_root == step.checks.finalized_beacon_root
optimistic_slot == step.checks.optimistic_slot
optimistic_beacon_root == step.checks.optimistic_beacon_root
else: raiseAssert "Unreachable"
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):
let combinedPath = basePath/path
runTest(basePath/path)