2022-06-10 14:16:37 +00:00
|
|
|
# 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 http://opensource.org/licenses/MIT).
|
|
|
|
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
2022-07-29 10:53:42 +00:00
|
|
|
when (NimMajor, NimMinor) < (1, 4):
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
else:
|
|
|
|
{.push raises: [].}
|
2022-06-10 14:16:37 +00:00
|
|
|
{.used.}
|
|
|
|
|
|
|
|
import
|
|
|
|
# Status libraries
|
|
|
|
eth/keys, taskpools,
|
|
|
|
# Beacon chain internals
|
|
|
|
../beacon_chain/consensus_object_pools/
|
|
|
|
[block_clearance_light_client, block_clearance,
|
|
|
|
block_quarantine, blockchain_dag],
|
|
|
|
../beacon_chain/spec/state_transition,
|
|
|
|
# Test utilities
|
|
|
|
./testutil, ./testdbutil
|
|
|
|
|
|
|
|
suite "Block clearance (light client)" & preset():
|
|
|
|
let
|
|
|
|
cfg = block:
|
|
|
|
var res = defaultRuntimeConfig
|
|
|
|
res.ALTAIR_FORK_EPOCH = GENESIS_EPOCH + 1
|
|
|
|
res
|
|
|
|
taskpool = Taskpool.new()
|
|
|
|
|
|
|
|
proc newTestDag(): ChainDAGRef =
|
|
|
|
const num_validators = SLOTS_PER_EPOCH
|
|
|
|
let
|
|
|
|
validatorMonitor = newClone(ValidatorMonitor.init())
|
|
|
|
dag = ChainDAGRef.init(
|
|
|
|
cfg, makeTestDB(num_validators), validatorMonitor, {})
|
|
|
|
dag
|
|
|
|
|
|
|
|
proc addBlocks(
|
|
|
|
dag: ChainDAGRef,
|
|
|
|
numBlocks: int,
|
|
|
|
finalizedCheckpoints: var seq[Checkpoint],
|
|
|
|
syncCommitteeRatio = 0.0,
|
|
|
|
numSkippedSlots = 0.uint64) =
|
|
|
|
let quarantine = newClone(Quarantine.init())
|
|
|
|
var
|
|
|
|
cache: StateCache
|
|
|
|
verifier = BatchVerifier(rng: keys.newRng(), taskpool: taskpool)
|
|
|
|
if numSkippedSlots > 0:
|
|
|
|
var info: ForkedEpochInfo
|
|
|
|
let slot = getStateField(dag.headState, slot) + numSkippedSlots
|
|
|
|
process_slots(
|
|
|
|
cfg, dag.headState, slot, cache, info, flags = {}).expect("no failure")
|
|
|
|
for blck in makeTestBlocks(dag.headState, cache, numBlocks,
|
|
|
|
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)
|
|
|
|
check: added.isOk()
|
|
|
|
dag.updateHead(added[], quarantine[])
|
|
|
|
withState(dag.headState):
|
|
|
|
if finalizedCheckpoints.len == 0 or
|
|
|
|
state.data.finalized_checkpoint != finalizedCheckpoints[^1]:
|
|
|
|
finalizedCheckpoints.add(state.data.finalized_checkpoint)
|
|
|
|
|
|
|
|
proc checkBlocks(lcBlocks: LCBlocks, dag: ChainDAGRef, slots: Slice[Slot]) =
|
|
|
|
for slot in slots.a .. slots.b:
|
|
|
|
let
|
|
|
|
latestLcBlck = lcBlocks.getLatestBlockThroughSlot(slot)
|
|
|
|
lcBlck = lcBlocks.getBlockAtSlot(slot)
|
|
|
|
bsi = dag.getBlockIdAtSlot(slot)
|
|
|
|
dagBlck =
|
|
|
|
if bsi.isOk:
|
|
|
|
dag.getForkedBlock(bsi.get.bid)
|
|
|
|
else:
|
|
|
|
Opt[ForkedTrustedSignedBeaconBlock].err()
|
|
|
|
check:
|
|
|
|
lcBlck.isOk == dagBlck.isOk
|
|
|
|
lcBlck.isOk == latestLcBlck.isOk
|
|
|
|
if lcBlck.isOk:
|
|
|
|
check:
|
|
|
|
lcBlck.get.root == dagBlck.get.root
|
|
|
|
lcBlck.get.root == latestLcBlck.get.root
|
|
|
|
|
|
|
|
setup:
|
|
|
|
let dag = newTestDag()
|
|
|
|
var finalizedCheckpoints: seq[Checkpoint] = @[]
|
|
|
|
dag.addBlocks(200, finalizedCheckpoints)
|
|
|
|
|
|
|
|
test "Initial sync":
|
|
|
|
const maxSlots = 160
|
|
|
|
var lcBlocks = initLCBlocks(maxSlots)
|
|
|
|
let minSlot = dag.head.slot + 1 - maxSlots
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == FAR_FUTURE_SLOT
|
|
|
|
lcBlocks.getFinalizedSlot() == GENESIS_SLOT
|
|
|
|
lcBlocks.getFrontfillSlot() == GENESIS_SLOT
|
|
|
|
lcBlocks.getBackfillSlot() == GENESIS_SLOT
|
|
|
|
lcBlocks.setHeadBid(dag.head.bid)
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == dag.head.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == GENESIS_SLOT
|
|
|
|
lcBlocks.getFrontfillSlot() == minSlot
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot + 1
|
|
|
|
lcBlocks.setFinalizedBid(dag.finalizedHead.blck.bid)
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == dag.head.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == dag.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == minSlot
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot + 1
|
|
|
|
var bid = dag.head.bid
|
|
|
|
while lcBlocks.getBackfillSlot() > lcBlocks.getFrontfillSlot():
|
|
|
|
let
|
|
|
|
bdata = dag.getForkedBlock(bid).valueOr:
|
|
|
|
break
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check:
|
|
|
|
res.isOk
|
|
|
|
lcBlocks.getHeadSlot() == dag.head.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == dag.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == minSlot
|
|
|
|
lcBlocks.getBackfillSlot() == max(bdata.slot, minSlot)
|
|
|
|
bid = dag.parent(bid).valueOr:
|
|
|
|
break
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == dag.head.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == dag.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == minSlot
|
|
|
|
lcBlocks.getBackfillSlot() == minSlot
|
|
|
|
lcBlocks.checkBlocks(dag, minSlot .. dag.head.slot)
|
|
|
|
|
|
|
|
test "Delayed finality update":
|
|
|
|
const maxSlots = 160
|
|
|
|
var lcBlocks = initLCBlocks(maxSlots)
|
|
|
|
let minSlot = dag.head.slot + 1 - maxSlots
|
|
|
|
lcBlocks.setHeadBid(dag.head.bid)
|
|
|
|
var bid = dag.head.bid
|
|
|
|
while lcBlocks.getBackfillSlot() > lcBlocks.getFrontfillSlot():
|
|
|
|
let
|
|
|
|
bdata = dag.getForkedBlock(bid).valueOr:
|
|
|
|
break
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check res.isOk
|
|
|
|
bid = dag.parent(bid).valueOr:
|
|
|
|
break
|
|
|
|
|
|
|
|
for finalizedCheckpoint in finalizedCheckpoints:
|
|
|
|
let bsi = dag.getBlockIdAtSlot(finalizedCheckpoint.epoch.start_slot)
|
|
|
|
check bsi.isOk
|
|
|
|
lcBlocks.setFinalizedBid(bsi.get.bid)
|
|
|
|
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == dag.head.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == dag.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == minSlot
|
|
|
|
lcBlocks.getBackfillSlot() == minSlot
|
|
|
|
lcBlocks.checkBlocks(dag, minSlot .. dag.head.slot)
|
|
|
|
|
|
|
|
test "Incremental sync":
|
|
|
|
const maxSlots = 160
|
|
|
|
var lcBlocks = initLCBlocks(maxSlots)
|
|
|
|
let
|
|
|
|
oldHeadSlot = dag.head.slot
|
|
|
|
oldMinSlot = dag.head.slot + 1 - maxSlots
|
|
|
|
lcBlocks.setHeadBid(dag.head.bid)
|
|
|
|
lcBlocks.setFinalizedBid(dag.finalizedHead.blck.bid)
|
|
|
|
var bid = dag.head.bid
|
|
|
|
while lcBlocks.getBackfillSlot() > lcBlocks.getFrontfillSlot():
|
|
|
|
let
|
|
|
|
bdata = dag.getForkedBlock(bid).valueOr:
|
|
|
|
break
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check res.isOk
|
|
|
|
bid = dag.parent(bid).valueOr:
|
|
|
|
break
|
|
|
|
|
|
|
|
dag.addBlocks(20, finalizedCheckpoints)
|
|
|
|
lcBlocks.setHeadBid(dag.head.bid)
|
|
|
|
lcBlocks.setFinalizedBid(dag.finalizedHead.blck.bid)
|
|
|
|
let newMinSlot = dag.head.slot + 1 - maxSlots
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == dag.head.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == dag.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == newMinSlot
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot + 1
|
|
|
|
|
|
|
|
bid = dag.head.bid
|
|
|
|
while lcBlocks.getBackfillSlot() > lcBlocks.getFrontfillSlot():
|
|
|
|
let
|
|
|
|
bdata = dag.getForkedBlock(bid).valueOr:
|
|
|
|
break
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check res.isOk
|
|
|
|
bid = dag.parent(bid).valueOr:
|
|
|
|
break
|
|
|
|
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == dag.head.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == dag.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == newMinSlot
|
|
|
|
lcBlocks.getBackfillSlot() == newMinSlot
|
|
|
|
lcBlocks.checkBlocks(dag, newMinSlot .. dag.head.slot)
|
|
|
|
|
|
|
|
dag.addBlocks(200, finalizedCheckpoints)
|
|
|
|
lcBlocks.setHeadBid(dag.head.bid)
|
|
|
|
lcBlocks.setFinalizedBid(dag.finalizedHead.blck.bid)
|
|
|
|
let minSlot = dag.head.slot + 1 - maxSlots
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == dag.head.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == dag.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == minSlot
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot + 1
|
|
|
|
|
|
|
|
bid = dag.head.bid
|
|
|
|
while lcBlocks.getBackfillSlot() > lcBlocks.getFrontfillSlot():
|
|
|
|
let
|
|
|
|
bdata = dag.getForkedBlock(bid).valueOr:
|
|
|
|
break
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check res.isOk
|
|
|
|
bid = dag.parent(bid).valueOr:
|
|
|
|
break
|
|
|
|
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == dag.head.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == dag.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == minSlot
|
|
|
|
lcBlocks.getBackfillSlot() == minSlot
|
|
|
|
lcBlocks.checkBlocks(dag, minSlot .. dag.head.slot)
|
|
|
|
|
|
|
|
test "Reverse incremental sync":
|
|
|
|
const maxSlots = 160
|
|
|
|
var lcBlocks = initLCBlocks(maxSlots)
|
|
|
|
let
|
|
|
|
newHeadBid = dag.head.bid
|
|
|
|
newFinalizedBid = dag.finalizedHead.blck.bid
|
|
|
|
|
|
|
|
dag.addBlocks(20, finalizedCheckpoints)
|
|
|
|
lcBlocks.setHeadBid(dag.head.bid)
|
|
|
|
lcBlocks.setFinalizedBid(dag.finalizedHead.blck.bid)
|
|
|
|
let oldMinSlot = dag.head.slot + 1 - maxSlots
|
|
|
|
|
|
|
|
var bid = dag.head.bid
|
|
|
|
while lcBlocks.getBackfillSlot() > lcBlocks.getFrontfillSlot():
|
|
|
|
let
|
|
|
|
bdata = dag.getForkedBlock(bid).valueOr:
|
|
|
|
break
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check res.isOk
|
|
|
|
bid = dag.parent(bid).valueOr:
|
|
|
|
break
|
|
|
|
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == dag.head.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == dag.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == oldMinSlot
|
|
|
|
lcBlocks.getBackfillSlot() == oldMinSlot
|
|
|
|
lcBlocks.checkBlocks(dag, oldMinSlot .. dag.head.slot)
|
|
|
|
|
|
|
|
lcBlocks.setHeadBid(newHeadBid)
|
|
|
|
lcBlocks.setFinalizedBid(newFinalizedBid)
|
|
|
|
let newMinSlot = newHeadBid.slot + 1 - maxSlots
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == newHeadBid.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == newFinalizedBid.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == newMinSlot
|
|
|
|
lcBlocks.getBackfillSlot() == oldMinSlot
|
|
|
|
|
|
|
|
while lcBlocks.getBackfillSlot() > lcBlocks.getFrontfillSlot():
|
|
|
|
let
|
|
|
|
bdata = dag.getForkedBlock(bid).valueOr:
|
|
|
|
break
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check res.isOk
|
|
|
|
bid = dag.parent(bid).valueOr:
|
|
|
|
break
|
|
|
|
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == newHeadBid.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == newFinalizedBid.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == newMinSlot
|
|
|
|
lcBlocks.getBackfillSlot() == newMinSlot
|
|
|
|
lcBlocks.checkBlocks(dag, newMinSlot .. newHeadBid.slot)
|
|
|
|
|
|
|
|
test "Reorg":
|
|
|
|
const maxSlots = 160
|
|
|
|
var lcBlocks = initLCBlocks(maxSlots)
|
|
|
|
let minSlot = dag.head.slot + 1 - maxSlots
|
|
|
|
lcBlocks.setHeadBid(dag.head.bid)
|
|
|
|
lcBlocks.setFinalizedBid(dag.finalizedHead.blck.bid)
|
|
|
|
var bid = dag.head.bid
|
|
|
|
while lcBlocks.getBackfillSlot() > lcBlocks.getFrontfillSlot():
|
|
|
|
let
|
|
|
|
bdata = dag.getForkedBlock(bid).valueOr:
|
|
|
|
break
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check res.isOk
|
|
|
|
bid = dag.parent(bid).valueOr:
|
|
|
|
break
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == dag.head.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == dag.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == minSlot
|
|
|
|
lcBlocks.getBackfillSlot() == minSlot
|
|
|
|
lcBlocks.checkBlocks(dag, minSlot .. dag.head.slot)
|
|
|
|
|
|
|
|
let dag2 = newTestDag()
|
|
|
|
var finalizedCheckpoints2: seq[Checkpoint] = @[]
|
|
|
|
dag2.addBlocks(200, finalizedCheckpoints2, syncCommitteeRatio = 0.1)
|
|
|
|
lcBlocks.setHeadBid(dag2.head.bid)
|
|
|
|
lcBlocks.setFinalizedBid(dag2.finalizedHead.blck.bid)
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == dag2.head.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == dag2.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == minSlot
|
|
|
|
lcBlocks.getBackfillSlot() == dag2.head.slot + 1
|
|
|
|
bid = dag2.head.bid
|
|
|
|
while lcBlocks.getBackfillSlot() > lcBlocks.getFrontfillSlot():
|
|
|
|
let
|
|
|
|
bdata = dag2.getForkedBlock(bid).valueOr:
|
|
|
|
break
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check res.isOk
|
|
|
|
bid = dag2.parent(bid).valueOr:
|
|
|
|
break
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == dag2.head.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == dag2.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == minSlot
|
|
|
|
lcBlocks.getBackfillSlot() == minSlot
|
|
|
|
lcBlocks.checkBlocks(dag2, minSlot .. dag2.head.slot)
|
|
|
|
|
|
|
|
lcBlocks.setFinalizedBid(dag.finalizedHead.blck.bid)
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == dag.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == dag.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFrontfillSlot() ==
|
|
|
|
max(dag.finalizedHead.slot, maxSlots.Slot) + 1 - maxSlots
|
|
|
|
lcBlocks.getBackfillSlot() == dag.finalizedHead.blck.slot + 1
|
|
|
|
bid = dag.finalizedHead.blck.bid
|
|
|
|
while lcBlocks.getBackfillSlot() > lcBlocks.getFrontfillSlot():
|
|
|
|
let
|
|
|
|
bdata = dag.getForkedBlock(bid).valueOr:
|
|
|
|
break
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check res.isOk
|
|
|
|
bid = dag.parent(bid).valueOr:
|
|
|
|
break
|
|
|
|
lcBlocks.setHeadBid(dag.head.bid)
|
|
|
|
bid = dag.head.bid
|
|
|
|
while lcBlocks.getBackfillSlot() > lcBlocks.getFrontfillSlot():
|
|
|
|
let
|
|
|
|
bdata = dag.getForkedBlock(bid).valueOr:
|
|
|
|
break
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check res.isOk
|
|
|
|
bid = dag.parent(bid).valueOr:
|
|
|
|
break
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == dag.head.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == dag.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == minSlot
|
|
|
|
lcBlocks.getBackfillSlot() == minSlot
|
|
|
|
lcBlocks.checkBlocks(dag, minSlot .. dag.head.slot)
|
|
|
|
|
|
|
|
test "Low slot numbers":
|
|
|
|
const maxSlots = 320 # DAG slot numbers are smaller than `maxSlots`
|
|
|
|
var lcBlocks = initLCBlocks(maxSlots)
|
|
|
|
let
|
|
|
|
oldHeadBid = dag.head.bid
|
|
|
|
oldFinalizedBid = dag.finalizedHead.blck.bid
|
|
|
|
lcBlocks.setHeadBid(dag.head.bid)
|
|
|
|
lcBlocks.setFinalizedBid(dag.finalizedHead.blck.bid)
|
|
|
|
var bid = dag.head.bid
|
|
|
|
while lcBlocks.getBackfillSlot() > lcBlocks.getFrontfillSlot():
|
|
|
|
let
|
|
|
|
bdata = dag.getForkedBlock(bid).valueOr:
|
|
|
|
break
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check res.isOk
|
|
|
|
bid = dag.parent(bid).valueOr:
|
|
|
|
break
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == dag.head.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == dag.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == GENESIS_SLOT
|
|
|
|
lcBlocks.getBackfillSlot() == GENESIS_SLOT
|
|
|
|
lcBlocks.checkBlocks(dag, GENESIS_SLOT .. dag.head.slot)
|
|
|
|
|
|
|
|
dag.addBlocks(20, finalizedCheckpoints)
|
|
|
|
lcBlocks.setHeadBid(dag.head.bid)
|
|
|
|
lcBlocks.setFinalizedBid(dag.finalizedHead.blck.bid)
|
|
|
|
bid = dag.head.bid
|
|
|
|
while lcBlocks.getBackfillSlot() > lcBlocks.getFrontfillSlot():
|
|
|
|
let
|
|
|
|
bdata = dag.getForkedBlock(bid).valueOr:
|
|
|
|
break
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check res.isOk
|
|
|
|
bid = dag.parent(bid).valueOr:
|
|
|
|
break
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == dag.head.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == dag.finalizedHead.blck.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == GENESIS_SLOT
|
|
|
|
lcBlocks.getBackfillSlot() == GENESIS_SLOT
|
|
|
|
|
|
|
|
lcBlocks.setHeadBid(oldHeadBid)
|
|
|
|
lcBlocks.setFinalizedBid(oldFinalizedBid)
|
|
|
|
check:
|
|
|
|
lcBlocks.getHeadSlot() == oldHeadBid.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == oldFinalizedBid.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == GENESIS_SLOT
|
|
|
|
lcBlocks.getBackfillSlot() == GENESIS_SLOT
|
|
|
|
|
|
|
|
test "Error conditions":
|
|
|
|
let dag2 = newTestDag()
|
|
|
|
var finalizedCheckpoints2: seq[Checkpoint] = @[]
|
|
|
|
dag2.addBlocks(200, finalizedCheckpoints2, syncCommitteeRatio = 0.1)
|
|
|
|
|
|
|
|
const maxSlots = 2
|
|
|
|
var lcBlocks = initLCBlocks(maxSlots)
|
|
|
|
check:
|
|
|
|
lcBlocks.getBlockAtSlot(GENESIS_SLOT).isErr
|
|
|
|
lcBlocks.getBlockAtSlot(FAR_FUTURE_SLOT).isErr
|
|
|
|
lcBlocks.getLatestBlockThroughSlot(GENESIS_SLOT).isErr
|
|
|
|
lcBlocks.getLatestBlockThroughSlot(FAR_FUTURE_SLOT).isErr
|
|
|
|
lcBlocks.setHeadBid(dag.head.bid)
|
|
|
|
lcBlocks.setFinalizedBid(dag.finalizedHead.blck.bid)
|
|
|
|
check:
|
|
|
|
lcBlocks.getBlockAtSlot(GENESIS_SLOT).isErr
|
|
|
|
lcBlocks.getBlockAtSlot(FAR_FUTURE_SLOT).isErr
|
|
|
|
lcBlocks.getBlockAtSlot(dag.head.slot).isErr
|
|
|
|
lcBlocks.getBlockAtSlot(dag.finalizedHead.blck.slot).isErr
|
|
|
|
lcBlocks.getLatestBlockThroughSlot(GENESIS_SLOT).isErr
|
|
|
|
lcBlocks.getLatestBlockThroughSlot(FAR_FUTURE_SLOT).isErr
|
|
|
|
lcBlocks.getLatestBlockThroughSlot(dag.head.slot).isErr
|
|
|
|
lcBlocks.getLatestBlockThroughSlot(dag.finalizedHead.blck.slot).isErr
|
|
|
|
let
|
|
|
|
parentBid = dag.parent(dag.head.bid).expect("Parent exists")
|
|
|
|
parentBdata = dag.getForkedBlock(parentBid).expect("Parent block exists")
|
|
|
|
var res = lcBlocks.addBlock(parentBdata.asSigned())
|
|
|
|
check:
|
|
|
|
res.isErr
|
|
|
|
res.error == BlockError.MissingParent
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot + 1
|
|
|
|
let bdata2 = dag2.getForkedBlock(dag2.head.bid).expect("DAG 2 block exists")
|
|
|
|
res = lcBlocks.addBlock(bdata2.asSigned())
|
|
|
|
check:
|
|
|
|
res.isErr
|
|
|
|
res.error == BlockError.UnviableFork
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot + 1
|
|
|
|
let bdata = dag.getForkedBlock(dag.head.bid).expect("DAG block exists")
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check:
|
|
|
|
res.isOk
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot
|
|
|
|
res = lcBlocks.addBlock(bdata2.asSigned())
|
|
|
|
check:
|
|
|
|
res.isErr
|
|
|
|
res.error == BlockError.UnviableFork
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check:
|
|
|
|
res.isErr
|
|
|
|
res.error == BlockError.Duplicate
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot
|
|
|
|
let
|
|
|
|
onePastBid = dag.parent(parentBid).expect("Parent of parent exists")
|
|
|
|
onePastBdata = dag.getForkedBlock(onePastBid).expect("Block exists")
|
|
|
|
res = lcBlocks.addBlock(onePastBdata.asSigned())
|
|
|
|
check:
|
|
|
|
res.isErr
|
|
|
|
res.error == BlockError.MissingParent
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot
|
|
|
|
res = lcBlocks.addBlock(parentBdata.asSigned())
|
|
|
|
check:
|
|
|
|
res.isOk
|
|
|
|
lcBlocks.getBackfillSlot() == parentBdata.slot
|
|
|
|
lcBlocks.getBlockAtSlot(parentBdata.slot).isOk
|
|
|
|
lcBlocks.getLatestBlockThroughSlot(parentBdata.slot).isOk
|
|
|
|
res = lcBlocks.addBlock(onePastBdata.asSigned())
|
|
|
|
check:
|
|
|
|
res.isOk
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot + 1 - maxSlots
|
|
|
|
lcBlocks.getBlockAtSlot(onePastBdata.slot).isErr
|
|
|
|
lcBlocks.getLatestBlockThroughSlot(onePastBdata.slot).isErr
|
|
|
|
res = lcBlocks.addBlock(onePastBdata.asSigned())
|
|
|
|
check:
|
|
|
|
res.isErr
|
|
|
|
res.error == BlockError.Duplicate
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot + 1 - maxSlots
|
|
|
|
|
|
|
|
let oldHeadBid = dag.head.bid
|
|
|
|
dag.addBlocks(1, finalizedCheckpoints, numSkippedSlots = 3) # ---X
|
|
|
|
dag2.addBlocks(2, finalizedCheckpoints2, numSkippedSlots = 2) # --XX
|
|
|
|
lcBlocks.setHeadBid(dag.head.bid)
|
|
|
|
lcBlocks.setFinalizedBid(dag.finalizedHead.blck.bid)
|
|
|
|
|
|
|
|
let newBdata = dag.getForkedBlock(dag.head.bid).expect("New block ok")
|
|
|
|
res = lcBlocks.addBlock(newBdata.asSigned())
|
|
|
|
check:
|
|
|
|
res.isOk
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check:
|
|
|
|
res.isOk
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot + 1 - maxSlots
|
|
|
|
lcBlocks.getBlockAtSlot(dag.head.slot).isOk
|
|
|
|
lcBlocks.getBlockAtSlot(dag.head.slot - 1).isErr
|
|
|
|
lcBlocks.getBlockAtSlot(dag.head.slot - 2).isErr
|
|
|
|
let
|
|
|
|
newParentBid2 = dag2.parent(dag2.head.bid).expect("New parent 2 exists")
|
|
|
|
newParentBdata2 = dag2.getForkedBlock(newParentBid2).expect("Parent 2 ok")
|
|
|
|
res = lcBlocks.addBlock(newParentBdata2.asSigned())
|
|
|
|
check:
|
|
|
|
res.isErr
|
|
|
|
res.error == BlockError.UnviableFork
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot + 1 - maxSlots
|
|
|
|
|
|
|
|
lcBlocks.setHeadBid(dag2.head.bid)
|
|
|
|
lcBlocks.setFinalizedBid(newParentBid2)
|
|
|
|
let newBdata2 = dag2.getForkedBlock(dag2.head.bid).expect("New block 2 ok")
|
|
|
|
res = lcBlocks.addBlock(newBdata2.asSigned())
|
|
|
|
check:
|
|
|
|
res.isOk
|
|
|
|
lcBlocks.getBackfillSlot() == dag2.head.slot
|
|
|
|
res = lcBlocks.addBlock(newParentBdata2.asSigned())
|
|
|
|
check:
|
|
|
|
res.isOk
|
|
|
|
lcBlocks.getBackfillSlot() == dag2.head.slot + 1 - maxSlots
|
|
|
|
|
|
|
|
lcBlocks.setHeadBid(dag.head.bid)
|
|
|
|
res = lcBlocks.addBlock(newBdata.asSigned())
|
|
|
|
check:
|
|
|
|
res.isOk
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check:
|
|
|
|
res.isErr
|
|
|
|
res.error == BlockError.UnviableFork
|
|
|
|
lcBlocks.getHeadSlot() == newParentBid2.slot
|
|
|
|
lcBlocks.getFinalizedSlot() == newParentBid2.slot
|
|
|
|
lcBlocks.getFrontfillSlot() == newParentBid2.slot + 1 - maxSlots
|
|
|
|
lcBlocks.getBackfillSlot() == newParentBid2.slot + 1
|
|
|
|
res = lcBlocks.addBlock(newParentBdata2.asSigned())
|
|
|
|
check:
|
|
|
|
res.isOk
|
|
|
|
lcBlocks.getBackfillSlot() == newParentBid2.slot
|
|
|
|
res = lcBlocks.addBlock(bdata2.asSigned())
|
|
|
|
check:
|
|
|
|
res.isOk
|
|
|
|
lcBlocks.getBackfillSlot() == newParentBid2.slot + 1 - maxSlots
|
|
|
|
|
|
|
|
lcBlocks.setHeadBid(dag2.head.bid)
|
|
|
|
lcBlocks.setFinalizedBid(oldHeadBid)
|
|
|
|
res = lcBlocks.addBlock(newBdata2.asSigned())
|
|
|
|
check:
|
|
|
|
res.isOk
|
|
|
|
lcBlocks.getBackfillSlot() == dag2.head.slot
|
|
|
|
res = lcBlocks.addBlock(newParentBdata2.asSigned())
|
|
|
|
check:
|
|
|
|
res.isOk
|
|
|
|
lcBlocks.getBackfillSlot() == newParentBid2.slot
|
|
|
|
res = lcBlocks.addBlock(bdata.asSigned())
|
|
|
|
check:
|
|
|
|
res.isErr
|
|
|
|
res.error == BlockError.MissingParent
|
|
|
|
lcBlocks.getBackfillSlot() == newParentBid2.slot
|
|
|
|
|
|
|
|
lcBlocks = initLCBlocks(maxSlots = 0)
|
|
|
|
lcBlocks.setHeadBid(dag.head.bid)
|
|
|
|
lcBlocks.setFinalizedBid(dag.finalizedHead.blck.bid)
|
|
|
|
res = lcBlocks.addBlock(newBdata2.asSigned())
|
|
|
|
check:
|
|
|
|
res.isErr
|
|
|
|
res.error == BlockError.UnviableFork
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot + 1
|
|
|
|
res = lcBlocks.addBlock(newBdata.asSigned())
|
|
|
|
check:
|
|
|
|
res.isOk
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot + 1
|
|
|
|
res = lcBlocks.addBlock(newBdata2.asSigned())
|
|
|
|
check:
|
|
|
|
res.isErr
|
|
|
|
res.error == BlockError.UnviableFork
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot + 1
|
|
|
|
res = lcBlocks.addBlock(newBdata.asSigned())
|
|
|
|
check:
|
|
|
|
res.isErr
|
|
|
|
res.error == BlockError.Duplicate
|
|
|
|
lcBlocks.getBackfillSlot() == dag.head.slot + 1
|