nimbus-eth2/tests/test_light_client_processor...

246 lines
8.7 KiB
Nim

# 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.}
import
# Status libraries
chronos, eth/keys,
# Beacon chain internals
../beacon_chain/consensus_object_pools/
[block_clearance, block_quarantine, blockchain_dag],
../beacon_chain/gossip_processing/light_client_processor,
../beacon_chain/spec/[beacon_time, light_client_sync, state_transition],
# Test utilities
./testutil, ./testdbutil
suite "Light client processor" & preset():
let
cfg = block:
var res = defaultRuntimeConfig
res.ALTAIR_FORK_EPOCH = GENESIS_EPOCH + 1
res
const numValidators = SLOTS_PER_EPOCH
let
validatorMonitor = newClone(ValidatorMonitor.init())
dag = ChainDAGRef.init(
cfg, makeTestDB(numValidators), validatorMonitor, {},
serveLightClientData = true,
importLightClientData = ImportLightClientData.OnlyNew)
quarantine = newClone(Quarantine.init())
taskpool = TaskPool.new()
var verifier = BatchVerifier(rng: keys.newRng(), taskpool: taskpool)
var cache: StateCache
proc addBlocks(blocks: uint64, syncCommitteeRatio: float) =
for blck in makeTestBlocks(dag.headState, cache, blocks.int,
attested = true, syncCommitteeRatio, cfg):
let added =
case blck.kind
of BeaconBlockFork.Phase0:
const nilCallback = OnPhase0BlockAdded(nil)
dag.addHeadBlock(verifier, blck.phase0Data, nilCallback)
of BeaconBlockFork.Altair:
const nilCallback = OnAltairBlockAdded(nil)
dag.addHeadBlock(verifier, blck.altairData, nilCallback)
of BeaconBlockFork.Bellatrix:
const nilCallback = OnBellatrixBlockAdded(nil)
dag.addHeadBlock(verifier, blck.bellatrixData, nilCallback)
doAssert added.isOk()
dag.updateHead(added[], quarantine[])
addBlocks(SLOTS_PER_EPOCH, 0.75)
let
genesisValidatorsRoot = dag.genesisValidatorsRoot
trustedBlockRoot = dag.head.root
const
lowPeriod = 0.SyncCommitteePeriod
lastPeriodWithSupermajority = 3.SyncCommitteePeriod
highPeriod = 5.SyncCommitteePeriod
for period in lowPeriod .. highPeriod:
const numFilledEpochsPerPeriod = 3
let slot = ((period + 1).start_epoch - numFilledEpochsPerPeriod).start_slot
var info: ForkedEpochInfo
doAssert process_slots(cfg, dag.headState, slot,
cache, info, flags = {}).isOk()
let syncCommitteeRatio =
if period > lastPeriodWithSupermajority:
0.25
else:
0.75
addBlocks(numFilledEpochsPerPeriod * SLOTS_PER_EPOCH, syncCommitteeRatio)
setup:
var time = chronos.seconds(0)
proc getBeaconTime(): BeaconTime =
BeaconTime(ns_since_genesis: time.nanoseconds)
func setTimeToSlot(slot: Slot) =
time = chronos.seconds((slot * SECONDS_PER_SLOT).int64)
var numDidInitializeStoreCalls = 0
proc didInitializeStore() = inc numDidInitializeStoreCalls
let store = (ref Option[LightClientStore])()
var
processor = LightClientProcessor.new(
false, "", "", cfg, genesisValidatorsRoot, trustedBlockRoot,
store, getBeaconTime, didInitializeStore)
res: Result[void, BlockError]
test "Standard sync" & preset():
let bootstrap = dag.getLightClientBootstrap(trustedBlockRoot)
check bootstrap.isOk
setTimeToSlot(bootstrap.get.header.slot)
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), bootstrap.get)
check:
res.isOk
numDidInitializeStoreCalls == 1
for period in lowPeriod .. lastPeriodWithSupermajority:
let update = dag.getBestLightClientUpdateForPeriod(period)
check update.isSome
setTimeToSlot(update.get.attested_header.slot + 1)
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), update.get)
check:
res.isOk
store[].isSome
store[].get.finalized_header == update.get.finalized_header
store[].get.optimistic_header == update.get.attested_header
test "Forced update" & preset():
let bootstrap = dag.getLightClientBootstrap(trustedBlockRoot)
check bootstrap.isOk
setTimeToSlot(bootstrap.get.header.slot)
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), bootstrap.get)
check:
res.isOk
numDidInitializeStoreCalls == 1
for period in lowPeriod .. lastPeriodWithSupermajority:
let update = dag.getBestLightClientUpdateForPeriod(period)
check update.isSome
setTimeToSlot(update.get.attested_header.slot + 1)
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), update.get)
check:
res.isOk
store[].isSome
store[].get.finalized_header == update.get.finalized_header
store[].get.optimistic_header == update.get.attested_header
for period in lastPeriodWithSupermajority + 1 .. highPeriod:
let update = dag.getBestLightClientUpdateForPeriod(period)
check update.isSome
setTimeToSlot(update.get.attested_header.slot + 1)
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), update.get)
check:
res.isOk
store[].isSome
store[].get.best_valid_update.isSome
store[].get.best_valid_update.get == update.get
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), update.get)
check:
res.isErr
res.error == BlockError.Duplicate
store[].isSome
store[].get.best_valid_update.isSome
store[].get.best_valid_update.get == update.get
time += chronos.minutes(15)
for _ in 0 ..< 150:
time += chronos.seconds(5)
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), update.get)
check:
res.isErr
res.error == BlockError.Duplicate
store[].isSome
store[].get.best_valid_update.isSome
store[].get.best_valid_update.get == update.get
time += chronos.minutes(15)
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), update.get)
check:
res.isErr
res.error == BlockError.Duplicate
store[].isSome
store[].get.best_valid_update.isNone
store[].get.finalized_header == update.get.finalized_header
let optimisticUpdate = dag.getOptimisticLightClientUpdate()
check optimisticUpdate.isSome
setTimeToSlot(optimisticUpdate.get.attested_header.slot + 1)
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), optimisticUpdate.get)
if res.isOk:
check:
store[].isSome
store[].get.optimistic_header == optimisticUpdate.get.attested_header
else:
check res.error == BlockError.Duplicate
check numDidInitializeStoreCalls == 1
test "Invalid bootstrap" & preset():
var bootstrap = dag.getLightClientBootstrap(trustedBlockRoot)
check bootstrap.isOk
bootstrap.get.header.slot.inc()
setTimeToSlot(bootstrap.get.header.slot)
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), bootstrap.get)
check:
res.isErr
res.error == BlockError.Invalid
numDidInitializeStoreCalls == 0
test "Duplicate bootstrap" & preset():
let bootstrap = dag.getLightClientBootstrap(trustedBlockRoot)
check bootstrap.isOk
setTimeToSlot(bootstrap.get.header.slot)
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), bootstrap.get)
check:
res.isOk
numDidInitializeStoreCalls == 1
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), bootstrap.get)
check:
res.isErr
res.error == BlockError.Duplicate
numDidInitializeStoreCalls == 1
test "Missing bootstrap (update)" & preset():
let update = dag.getBestLightClientUpdateForPeriod(lowPeriod)
check update.isSome
setTimeToSlot(update.get.attested_header.slot + 1)
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), update.get)
check:
res.isErr
res.error == BlockError.MissingParent
numDidInitializeStoreCalls == 0
test "Missing bootstrap (optimistic update)" & preset():
let optimisticUpdate = dag.getOptimisticLightClientUpdate()
check optimisticUpdate.isSome
setTimeToSlot(optimisticUpdate.get.attested_header.slot + 1)
res = processor[].storeObject(
MsgSource.gossip, getBeaconTime(), optimisticUpdate.get)
check:
res.isErr
res.error == BlockError.MissingParent
numDidInitializeStoreCalls == 0