generalize `commonAncestor` function to `BlockId` (#5192)

To enable additional use cases, e.g., `/states/###/randao` beacon API,
`ShufflingRef` acceleration logic needs to be able to operate on parts
of the DAG that do not have `BlockRef`. Changing `commonAncestor` to
act on `BlockId` instead of `BlockRef` is a step toward that and also
simplifies the logic some more.
This commit is contained in:
Etan Kissling 2023-07-18 17:37:53 +02:00 committed by GitHub
parent 3a818ecb93
commit f98c33ad03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 336 additions and 327 deletions

View File

@ -1,5 +1,10 @@
AllTests-mainnet
===
## Ancestry
```diff
+ ancestorSlot OK
```
OK: 1/1 Fail: 0/1 Skip: 0/1
## Attestation pool processing [Preset: mainnet]
```diff
+ Attestation from different branch [Preset: mainnet] OK
@ -100,11 +105,10 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
OK: 2/2 Fail: 0/2 Skip: 0/2
## BlockRef and helpers
```diff
+ commonAncestor sanity OK
+ get_ancestor sanity OK
+ isAncestorOf sanity OK
```
OK: 3/3 Fail: 0/3 Skip: 0/3
OK: 2/2 Fail: 0/2 Skip: 0/2
## BlockSlot and helpers
```diff
+ atSlot sanity OK

View File

@ -155,34 +155,6 @@ func get_ancestor*(blck: BlockRef, slot: Slot,
blck = blck.parent
func commonAncestor*(a, b: BlockRef, lowSlot: Slot): Opt[BlockRef] =
## Return the common ancestor with highest slot of two non-nil `BlockRef`,
## limited by `lowSlot` (`err` if exceeded).
doAssert a != nil
doAssert b != nil
if a.slot < lowSlot or b.slot < lowSlot:
return err()
if a.parent == nil:
return ok a # All `BlockRef` lead to `finalizedHead`
if b.parent == nil:
return ok b # All `BlockRef` lead to `finalizedHead`
var
aa = a
bb = b
while aa != bb:
if aa.slot >= bb.slot:
aa = aa.parent
doAssert aa != nil, "All `BlockRef` lead to `finalizedHead`"
if aa.slot < lowSlot:
return err()
else:
bb = bb.parent
doAssert bb != nil, "All `BlockRef` lead to `finalizedHead`"
if bb.slot < lowSlot:
return err()
ok aa
func atSlot*(blck: BlockRef, slot: Slot): BlockSlot =
## Return a BlockSlot at a given slot, with the block set to the closest block
## available. If slot comes from before the block, a suitable block ancestor

View File

@ -1330,7 +1330,45 @@ proc getFinalizedEpochRef*(dag: ChainDAGRef): EpochRef =
dag.finalizedHead.blck, dag.finalizedHead.slot.epoch, false).expect(
"getEpochRef for finalized head should always succeed")
func ancestorSlotForAttesterShuffling*(
proc getBlockIdAtSlot(
dag: ChainDAGRef, state: ForkyHashedBeaconState, slot: Slot): Opt[BlockId] =
if slot >= state.data.slot:
Opt.some state.latest_block_id
elif state.data.slot <= slot + SLOTS_PER_HISTORICAL_ROOT:
dag.getBlockId(state.data.get_block_root_at_slot(slot))
else:
Opt.none(BlockId)
proc ancestorSlot*(
dag: ChainDAGRef, state: ForkyHashedBeaconState, bid: BlockId,
lowSlot: Slot): Opt[Slot] =
## Return common ancestor slot of `bid` and `state`, if at least `lowSlot`.
## Return `none` if no common ancestor is found with slot >= `lowSlot`.
if state.data.slot < lowSlot or bid.slot < lowSlot:
return Opt.none(Slot)
var stateBid = ? dag.getBlockIdAtSlot(state, bid.slot)
if stateBid.slot < lowSlot:
return Opt.none(Slot)
var blockBid = (? dag.atSlot(bid, stateBid.slot)).bid
if blockBid.slot < lowSlot:
return Opt.none(Slot)
while stateBid != blockBid:
if stateBid.slot >= blockBid.slot:
stateBid = ? dag.getBlockIdAtSlot(
state, min(blockBid.slot, stateBid.slot - 1))
if stateBid.slot < lowSlot:
return Opt.none(Slot)
else:
blockBid = ? dag.parent(blockBid)
if blockBid.slot < lowSlot:
return Opt.none(Slot)
Opt.some stateBid.slot
proc ancestorSlotForAttesterShuffling*(
dag: ChainDAGRef, state: ForkyHashedBeaconState,
blck: BlockRef, epoch: Epoch): Opt[Slot] =
## Return slot of `blck` ancestor to which `state` can be rewinded
@ -1342,48 +1380,8 @@ func ancestorSlotForAttesterShuffling*(
const numDelayEpochs = compute_activation_exit_epoch(GENESIS_EPOCH).uint64
let
lowEpoch = max(epoch, (numDelayEpochs - 1).Epoch) - (numDelayEpochs - 1)
lowSlot = lowEpoch.start_slot
if state.data.slot < lowSlot or blck.slot < lowSlot:
return err()
# Check that state is related to the information stored in the DAG,
# and determine the corresponding `BlockRef`, or `finalizedHead` if finalized
let
stateBid = state.latest_block_id
stateBlck =
if dag.finalizedHead.blck == nil:
return err()
elif stateBid.slot > dag.finalizedHead.blck.slot:
? dag.getBlockRef(stateBid.root)
elif stateBid.slot == dag.finalizedHead.blck.slot:
if stateBid.root != dag.finalizedHead.blck.root:
return err()
dag.finalizedHead.blck
else:
let bsi = ? dag.getBlockIdAtSlot(stateBid.slot)
if bsi.bid != stateBid:
return err()
dag.finalizedHead.blck
# Check that history up to `lowSlot` is included in `state`,
# otherwise `get_active_validator_indices` may still change
if lowSlot <= dag.finalizedHead.blck.slot:
let
bsi = ? dag.getBlockIdAtSlot(lowSlot)
stateLowBlockRoot =
if state.data.slot == lowSlot:
stateBid.root
else:
state.data.get_block_root_at_slot(lowSlot)
if stateLowBlockRoot != bsi.bid.root:
return err()
# Compute ancestor slot for starting RANDAO recovery
let
ancestorBlck = ? commonAncestor(blck, stateBlck, lowSlot)
dependentSlot = epoch.attester_dependent_slot
doAssert dependentSlot >= lowSlot
ok min(min(stateBid.slot, ancestorBlck.slot), dependentSlot)
ancestorSlot = ? dag.ancestorSlot(state, blck.bid, lowEpoch.start_slot)
Opt.some min(ancestorSlot, epoch.attester_dependent_slot)
type AttesterRandaoMix = tuple[dependentBid: BlockId, mix: Eth2Digest]

View File

@ -51,260 +51,6 @@ suite "BlockRef and helpers":
s4.get_ancestor(Slot(3)) == s2
s4.get_ancestor(Slot(4)) == s4
test "commonAncestor sanity":
# s0
# / \
# s1 s3
# / \
# s2 s6
# / \ \
# s4 s5 s7
# \
# s8
# \
# s9
let
s0 = BlockRef(bid: BlockId(slot: Slot(0)))
s1 = BlockRef(bid: BlockId(slot: Slot(1)), parent: s0)
s2 = BlockRef(bid: BlockId(slot: Slot(2)), parent: s1)
s3 = BlockRef(bid: BlockId(slot: Slot(3)), parent: s0)
s4 = BlockRef(bid: BlockId(slot: Slot(4)), parent: s2)
s5 = BlockRef(bid: BlockId(slot: Slot(5)), parent: s2)
s6 = BlockRef(bid: BlockId(slot: Slot(6)), parent: s3)
s7 = BlockRef(bid: BlockId(slot: Slot(7)), parent: s6)
s8 = BlockRef(bid: BlockId(slot: Slot(8)), parent: s4)
s9 = BlockRef(bid: BlockId(slot: Slot(9)), parent: s8)
check:
commonAncestor(s0, s0, Slot(0)) == Opt.some(s0)
commonAncestor(s0, s1, Slot(0)) == Opt.some(s0)
commonAncestor(s0, s2, Slot(0)) == Opt.some(s0)
commonAncestor(s0, s3, Slot(0)) == Opt.some(s0)
commonAncestor(s0, s4, Slot(0)) == Opt.some(s0)
commonAncestor(s0, s5, Slot(0)) == Opt.some(s0)
commonAncestor(s0, s6, Slot(0)) == Opt.some(s0)
commonAncestor(s0, s7, Slot(0)) == Opt.some(s0)
commonAncestor(s0, s8, Slot(0)) == Opt.some(s0)
commonAncestor(s0, s9, Slot(0)) == Opt.some(s0)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check commonAncestor(s0, b, Slot(1)) == Opt.none(BlockRef)
check:
commonAncestor(s1, s0, Slot(0)) == Opt.some(s0)
commonAncestor(s1, s1, Slot(0)) == Opt.some(s1)
commonAncestor(s1, s2, Slot(0)) == Opt.some(s1)
commonAncestor(s1, s3, Slot(0)) == Opt.some(s0)
commonAncestor(s1, s4, Slot(0)) == Opt.some(s1)
commonAncestor(s1, s5, Slot(0)) == Opt.some(s1)
commonAncestor(s1, s6, Slot(0)) == Opt.some(s0)
commonAncestor(s1, s7, Slot(0)) == Opt.some(s0)
commonAncestor(s1, s8, Slot(0)) == Opt.some(s1)
commonAncestor(s1, s9, Slot(0)) == Opt.some(s1)
for b in [s0, s3, s6, s7]:
check commonAncestor(s1, b, Slot(1)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check commonAncestor(s1, b, Slot(2)) == Opt.none(BlockRef)
check:
commonAncestor(s2, s0, Slot(0)) == Opt.some(s0)
commonAncestor(s2, s1, Slot(0)) == Opt.some(s1)
commonAncestor(s2, s2, Slot(0)) == Opt.some(s2)
commonAncestor(s2, s3, Slot(0)) == Opt.some(s0)
commonAncestor(s2, s4, Slot(0)) == Opt.some(s2)
commonAncestor(s2, s5, Slot(0)) == Opt.some(s2)
commonAncestor(s2, s6, Slot(0)) == Opt.some(s0)
commonAncestor(s2, s7, Slot(0)) == Opt.some(s0)
commonAncestor(s2, s8, Slot(0)) == Opt.some(s2)
commonAncestor(s2, s9, Slot(0)) == Opt.some(s2)
for b in [s0, s3, s6, s7]:
check commonAncestor(s2, b, Slot(1)) == Opt.none(BlockRef)
for b in [s0, s1, s3, s6, s7]:
check commonAncestor(s2, b, Slot(2)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check commonAncestor(s2, b, Slot(3)) == Opt.none(BlockRef)
check:
commonAncestor(s3, s0, Slot(0)) == Opt.some(s0)
commonAncestor(s3, s1, Slot(0)) == Opt.some(s0)
commonAncestor(s3, s2, Slot(0)) == Opt.some(s0)
commonAncestor(s3, s3, Slot(0)) == Opt.some(s3)
commonAncestor(s3, s4, Slot(0)) == Opt.some(s0)
commonAncestor(s3, s5, Slot(0)) == Opt.some(s0)
commonAncestor(s3, s6, Slot(0)) == Opt.some(s3)
commonAncestor(s3, s7, Slot(0)) == Opt.some(s3)
commonAncestor(s3, s8, Slot(0)) == Opt.some(s0)
commonAncestor(s3, s9, Slot(0)) == Opt.some(s0)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check commonAncestor(s3, b, Slot(1)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check commonAncestor(s3, b, Slot(2)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check commonAncestor(s3, b, Slot(3)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check commonAncestor(s3, b, Slot(4)) == Opt.none(BlockRef)
check:
commonAncestor(s4, s0, Slot(0)) == Opt.some(s0)
commonAncestor(s4, s1, Slot(0)) == Opt.some(s1)
commonAncestor(s4, s2, Slot(0)) == Opt.some(s2)
commonAncestor(s4, s3, Slot(0)) == Opt.some(s0)
commonAncestor(s4, s4, Slot(0)) == Opt.some(s4)
commonAncestor(s4, s5, Slot(0)) == Opt.some(s2)
commonAncestor(s4, s6, Slot(0)) == Opt.some(s0)
commonAncestor(s4, s7, Slot(0)) == Opt.some(s0)
commonAncestor(s4, s8, Slot(0)) == Opt.some(s4)
commonAncestor(s4, s9, Slot(0)) == Opt.some(s4)
for b in [s0, s3, s6, s7]:
check commonAncestor(s4, b, Slot(1)) == Opt.none(BlockRef)
for b in [s0, s1, s3, s6, s7]:
check commonAncestor(s4, b, Slot(2)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s5, s6, s7]:
check commonAncestor(s4, b, Slot(3)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s5, s6, s7]:
check commonAncestor(s4, b, Slot(4)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check commonAncestor(s4, b, Slot(5)) == Opt.none(BlockRef)
check:
commonAncestor(s5, s0, Slot(0)) == Opt.some(s0)
commonAncestor(s5, s1, Slot(0)) == Opt.some(s1)
commonAncestor(s5, s2, Slot(0)) == Opt.some(s2)
commonAncestor(s5, s3, Slot(0)) == Opt.some(s0)
commonAncestor(s5, s4, Slot(0)) == Opt.some(s2)
commonAncestor(s5, s5, Slot(0)) == Opt.some(s5)
commonAncestor(s5, s6, Slot(0)) == Opt.some(s0)
commonAncestor(s5, s7, Slot(0)) == Opt.some(s0)
commonAncestor(s5, s8, Slot(0)) == Opt.some(s2)
commonAncestor(s5, s9, Slot(0)) == Opt.some(s2)
for b in [s0, s3, s6, s7]:
check commonAncestor(s5, b, Slot(1)) == Opt.none(BlockRef)
for b in [s0, s1, s3, s6, s7]:
check commonAncestor(s5, b, Slot(2)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s6, s7, s8, s9]:
check commonAncestor(s5, b, Slot(3)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s6, s7, s8, s9]:
check commonAncestor(s5, b, Slot(4)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s6, s7, s8, s9]:
check commonAncestor(s5, b, Slot(5)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check commonAncestor(s5, b, Slot(6)) == Opt.none(BlockRef)
check:
commonAncestor(s6, s0, Slot(0)) == Opt.some(s0)
commonAncestor(s6, s1, Slot(0)) == Opt.some(s0)
commonAncestor(s6, s2, Slot(0)) == Opt.some(s0)
commonAncestor(s6, s3, Slot(0)) == Opt.some(s3)
commonAncestor(s6, s4, Slot(0)) == Opt.some(s0)
commonAncestor(s6, s5, Slot(0)) == Opt.some(s0)
commonAncestor(s6, s6, Slot(0)) == Opt.some(s6)
commonAncestor(s6, s7, Slot(0)) == Opt.some(s6)
commonAncestor(s6, s8, Slot(0)) == Opt.some(s0)
commonAncestor(s6, s9, Slot(0)) == Opt.some(s0)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check commonAncestor(s6, b, Slot(1)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check commonAncestor(s6, b, Slot(2)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check commonAncestor(s6, b, Slot(3)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s8, s9]:
check commonAncestor(s6, b, Slot(4)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s8, s9]:
check commonAncestor(s6, b, Slot(5)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s8, s9]:
check commonAncestor(s6, b, Slot(6)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check commonAncestor(s6, b, Slot(7)) == Opt.none(BlockRef)
check:
commonAncestor(s7, s0, Slot(0)) == Opt.some(s0)
commonAncestor(s7, s1, Slot(0)) == Opt.some(s0)
commonAncestor(s7, s2, Slot(0)) == Opt.some(s0)
commonAncestor(s7, s3, Slot(0)) == Opt.some(s3)
commonAncestor(s7, s4, Slot(0)) == Opt.some(s0)
commonAncestor(s7, s5, Slot(0)) == Opt.some(s0)
commonAncestor(s7, s6, Slot(0)) == Opt.some(s6)
commonAncestor(s7, s7, Slot(0)) == Opt.some(s7)
commonAncestor(s7, s8, Slot(0)) == Opt.some(s0)
commonAncestor(s7, s9, Slot(0)) == Opt.some(s0)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check commonAncestor(s7, b, Slot(1)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check commonAncestor(s7, b, Slot(2)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check commonAncestor(s7, b, Slot(3)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s8, s9]:
check commonAncestor(s7, b, Slot(4)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s8, s9]:
check commonAncestor(s7, b, Slot(5)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s8, s9]:
check commonAncestor(s7, b, Slot(6)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s8, s9]:
check commonAncestor(s7, b, Slot(7)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check commonAncestor(s7, b, Slot(8)) == Opt.none(BlockRef)
check:
commonAncestor(s8, s0, Slot(0)) == Opt.some(s0)
commonAncestor(s8, s1, Slot(0)) == Opt.some(s1)
commonAncestor(s8, s2, Slot(0)) == Opt.some(s2)
commonAncestor(s8, s3, Slot(0)) == Opt.some(s0)
commonAncestor(s8, s4, Slot(0)) == Opt.some(s4)
commonAncestor(s8, s5, Slot(0)) == Opt.some(s2)
commonAncestor(s8, s6, Slot(0)) == Opt.some(s0)
commonAncestor(s8, s7, Slot(0)) == Opt.some(s0)
commonAncestor(s8, s8, Slot(0)) == Opt.some(s8)
commonAncestor(s8, s9, Slot(0)) == Opt.some(s8)
for b in [s0, s3, s6, s7]:
check commonAncestor(s8, b, Slot(1)) == Opt.none(BlockRef)
for b in [s0, s1, s3, s6, s7]:
check commonAncestor(s8, b, Slot(2)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s5, s6, s7]:
check commonAncestor(s8, b, Slot(3)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s5, s6, s7]:
check commonAncestor(s8, b, Slot(4)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7]:
check commonAncestor(s8, b, Slot(5)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7]:
check commonAncestor(s8, b, Slot(6)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7]:
check commonAncestor(s8, b, Slot(7)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7]:
check commonAncestor(s8, b, Slot(8)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check commonAncestor(s8, b, Slot(9)) == Opt.none(BlockRef)
check:
commonAncestor(s9, s0, Slot(0)) == Opt.some(s0)
commonAncestor(s9, s1, Slot(0)) == Opt.some(s1)
commonAncestor(s9, s2, Slot(0)) == Opt.some(s2)
commonAncestor(s9, s3, Slot(0)) == Opt.some(s0)
commonAncestor(s9, s4, Slot(0)) == Opt.some(s4)
commonAncestor(s9, s5, Slot(0)) == Opt.some(s2)
commonAncestor(s9, s6, Slot(0)) == Opt.some(s0)
commonAncestor(s9, s7, Slot(0)) == Opt.some(s0)
commonAncestor(s9, s8, Slot(0)) == Opt.some(s8)
commonAncestor(s9, s9, Slot(0)) == Opt.some(s9)
for b in [s0, s3, s6, s7]:
check commonAncestor(s9, b, Slot(1)) == Opt.none(BlockRef)
for b in [s0, s1, s3, s6, s7]:
check commonAncestor(s9, b, Slot(2)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s5, s6, s7]:
check commonAncestor(s9, b, Slot(3)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s5, s6, s7]:
check commonAncestor(s9, b, Slot(4)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7]:
check commonAncestor(s9, b, Slot(5)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7]:
check commonAncestor(s9, b, Slot(6)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7]:
check commonAncestor(s9, b, Slot(7)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7]:
check commonAncestor(s9, b, Slot(8)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8]:
check commonAncestor(s9, b, Slot(9)) == Opt.none(BlockRef)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check commonAncestor(s9, b, Slot(10)) == Opt.none(BlockRef)
suite "BlockSlot and helpers":
test "atSlot sanity":
let

View File

@ -1180,6 +1180,295 @@ suite "Pruning":
dag.tail.slot == Epoch(EPOCHS_PER_STATE_SNAPSHOT).start_slot - 1
not db.containsBlock(blocks[1].root)
suite "Ancestry":
test "ancestorSlot":
const numValidators = SLOTS_PER_EPOCH
let
cfg = defaultRuntimeConfig
validatorMonitor = newClone(ValidatorMonitor.init())
dag = ChainDAGRef.init(
cfg, makeTestDB(numValidators, cfg = cfg),
validatorMonitor, {})
quarantine = newClone(Quarantine.init())
rng = HmacDrbgContext.new()
taskpool = Taskpool.new()
type Node = tuple[blck: BlockRef, state: ref phase0.HashedBeaconState]
template bid(n: Node): BlockId = n.blck.bid
var verifier = BatchVerifier(rng: rng, taskpool: taskpool)
proc addBlock(parent: Node, slot: Slot): Node =
dag.updateHead(parent.blck, quarantine[], [])
var
cache: StateCache
info: ForkedEpochInfo
let res = process_slots(cfg, dag.headState, slot, cache, info, flags = {})
check res.isOk
let
blck = dag.headState.addTestBlock(cache, nextSlot = false, cfg = cfg)
added = block:
const nilCallback = OnPhase0BlockAdded(nil)
dag.addHeadBlock(verifier, blck.phase0Data, nilCallback)
check added.isOk()
dag.updateHead(added[], quarantine[], [])
(blck: dag.head, state: newClone(dag.headState.phase0Data))
# s0
# / \
# s1 s3
# / \
# s2 s6
# / \ \
# s4 s5 s7
# \
# s8
# \
# s9
let
sg = (blck: dag.head, state: newClone(dag.headState.phase0Data))
s0 = sg.addBlock(Slot(10))
s1 = s0.addBlock(Slot(11))
s2 = s1.addBlock(Slot(12))
s3 = s0.addBlock(Slot(13))
s4 = s2.addBlock(Slot(14))
s5 = s2.addBlock(Slot(15))
s6 = s3.addBlock(Slot(16))
s7 = s6.addBlock(Slot(17))
s8 = s4.addBlock(Slot(18))
s9 = s8.addBlock(Slot(19))
check:
dag.ancestorSlot(s0.state[], s0.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s0.state[], s1.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s0.state[], s2.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s0.state[], s3.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s0.state[], s4.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s0.state[], s5.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s0.state[], s6.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s0.state[], s7.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s0.state[], s8.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s0.state[], s9.bid, Slot(10)) == Opt.some(s0.bid.slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check dag.ancestorSlot(s0.state[], b.bid, Slot(11)) == Opt.none(Slot)
check:
dag.ancestorSlot(s1.state[], s0.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s1.state[], s1.bid, Slot(10)) == Opt.some(s1.bid.slot)
dag.ancestorSlot(s1.state[], s2.bid, Slot(10)) == Opt.some(s1.bid.slot)
dag.ancestorSlot(s1.state[], s3.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s1.state[], s4.bid, Slot(10)) == Opt.some(s1.bid.slot)
dag.ancestorSlot(s1.state[], s5.bid, Slot(10)) == Opt.some(s1.bid.slot)
dag.ancestorSlot(s1.state[], s6.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s1.state[], s7.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s1.state[], s8.bid, Slot(10)) == Opt.some(s1.bid.slot)
dag.ancestorSlot(s1.state[], s9.bid, Slot(10)) == Opt.some(s1.bid.slot)
for b in [s0, s3, s6, s7]:
check dag.ancestorSlot(s1.state[], b.bid, Slot(11)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check dag.ancestorSlot(s1.state[], b.bid, Slot(12)) == Opt.none(Slot)
check:
dag.ancestorSlot(s2.state[], s0.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s2.state[], s1.bid, Slot(10)) == Opt.some(s1.bid.slot)
dag.ancestorSlot(s2.state[], s2.bid, Slot(10)) == Opt.some(s2.bid.slot)
dag.ancestorSlot(s2.state[], s3.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s2.state[], s4.bid, Slot(10)) == Opt.some(s2.bid.slot)
dag.ancestorSlot(s2.state[], s5.bid, Slot(10)) == Opt.some(s2.bid.slot)
dag.ancestorSlot(s2.state[], s6.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s2.state[], s7.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s2.state[], s8.bid, Slot(10)) == Opt.some(s2.bid.slot)
dag.ancestorSlot(s2.state[], s9.bid, Slot(10)) == Opt.some(s2.bid.slot)
for b in [s0, s3, s6, s7]:
check dag.ancestorSlot(s2.state[], b.bid, Slot(11)) == Opt.none(Slot)
for b in [s0, s1, s3, s6, s7]:
check dag.ancestorSlot(s2.state[], b.bid, Slot(12)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check dag.ancestorSlot(s2.state[], b.bid, Slot(13)) == Opt.none(Slot)
check:
dag.ancestorSlot(s3.state[], s0.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s3.state[], s1.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s3.state[], s2.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s3.state[], s3.bid, Slot(10)) == Opt.some(s3.bid.slot)
dag.ancestorSlot(s3.state[], s4.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s3.state[], s5.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s3.state[], s6.bid, Slot(10)) == Opt.some(s3.bid.slot)
dag.ancestorSlot(s3.state[], s7.bid, Slot(10)) == Opt.some(s3.bid.slot)
dag.ancestorSlot(s3.state[], s8.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s3.state[], s9.bid, Slot(10)) == Opt.some(s0.bid.slot)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check dag.ancestorSlot(s3.state[], b.bid, Slot(11)) == Opt.none(Slot)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check dag.ancestorSlot(s3.state[], b.bid, Slot(12)) == Opt.none(Slot)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check dag.ancestorSlot(s3.state[], b.bid, Slot(13)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check dag.ancestorSlot(s3.state[], b.bid, Slot(14)) == Opt.none(Slot)
check:
dag.ancestorSlot(s4.state[], s0.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s4.state[], s1.bid, Slot(10)) == Opt.some(s1.bid.slot)
dag.ancestorSlot(s4.state[], s2.bid, Slot(10)) == Opt.some(s2.bid.slot)
dag.ancestorSlot(s4.state[], s3.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s4.state[], s4.bid, Slot(10)) == Opt.some(s4.bid.slot)
dag.ancestorSlot(s4.state[], s5.bid, Slot(10)) == Opt.some(s2.bid.slot)
dag.ancestorSlot(s4.state[], s6.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s4.state[], s7.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s4.state[], s8.bid, Slot(10)) == Opt.some(s4.bid.slot)
dag.ancestorSlot(s4.state[], s9.bid, Slot(10)) == Opt.some(s4.bid.slot)
for b in [s0, s3, s6, s7]:
check dag.ancestorSlot(s4.state[], b.bid, Slot(11)) == Opt.none(Slot)
for b in [s0, s1, s3, s6, s7]:
check dag.ancestorSlot(s4.state[], b.bid, Slot(12)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s5, s6, s7]:
check dag.ancestorSlot(s4.state[], b.bid, Slot(13)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s5, s6, s7]:
check dag.ancestorSlot(s4.state[], b.bid, Slot(14)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check dag.ancestorSlot(s4.state[], b.bid, Slot(15)) == Opt.none(Slot)
check:
dag.ancestorSlot(s5.state[], s0.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s5.state[], s1.bid, Slot(10)) == Opt.some(s1.bid.slot)
dag.ancestorSlot(s5.state[], s2.bid, Slot(10)) == Opt.some(s2.bid.slot)
dag.ancestorSlot(s5.state[], s3.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s5.state[], s4.bid, Slot(10)) == Opt.some(s2.bid.slot)
dag.ancestorSlot(s5.state[], s5.bid, Slot(10)) == Opt.some(s5.bid.slot)
dag.ancestorSlot(s5.state[], s6.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s5.state[], s7.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s5.state[], s8.bid, Slot(10)) == Opt.some(s2.bid.slot)
dag.ancestorSlot(s5.state[], s9.bid, Slot(10)) == Opt.some(s2.bid.slot)
for b in [s0, s3, s6, s7]:
check dag.ancestorSlot(s5.state[], b.bid, Slot(11)) == Opt.none(Slot)
for b in [s0, s1, s3, s6, s7]:
check dag.ancestorSlot(s5.state[], b.bid, Slot(12)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s6, s7, s8, s9]:
check dag.ancestorSlot(s5.state[], b.bid, Slot(13)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s6, s7, s8, s9]:
check dag.ancestorSlot(s5.state[], b.bid, Slot(14)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s6, s7, s8, s9]:
check dag.ancestorSlot(s5.state[], b.bid, Slot(15)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check dag.ancestorSlot(s5.state[], b.bid, Slot(16)) == Opt.none(Slot)
check:
dag.ancestorSlot(s6.state[], s0.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s6.state[], s1.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s6.state[], s2.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s6.state[], s3.bid, Slot(10)) == Opt.some(s3.bid.slot)
dag.ancestorSlot(s6.state[], s4.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s6.state[], s5.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s6.state[], s6.bid, Slot(10)) == Opt.some(s6.bid.slot)
dag.ancestorSlot(s6.state[], s7.bid, Slot(10)) == Opt.some(s6.bid.slot)
dag.ancestorSlot(s6.state[], s8.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s6.state[], s9.bid, Slot(10)) == Opt.some(s0.bid.slot)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check dag.ancestorSlot(s6.state[], b.bid, Slot(11)) == Opt.none(Slot)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check dag.ancestorSlot(s6.state[], b.bid, Slot(12)) == Opt.none(Slot)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check dag.ancestorSlot(s6.state[], b.bid, Slot(13)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s8, s9]:
check dag.ancestorSlot(s6.state[], b.bid, Slot(14)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s8, s9]:
check dag.ancestorSlot(s6.state[], b.bid, Slot(15)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s8, s9]:
check dag.ancestorSlot(s6.state[], b.bid, Slot(16)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check dag.ancestorSlot(s6.state[], b.bid, Slot(17)) == Opt.none(Slot)
check:
dag.ancestorSlot(s7.state[], s0.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s7.state[], s1.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s7.state[], s2.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s7.state[], s3.bid, Slot(10)) == Opt.some(s3.bid.slot)
dag.ancestorSlot(s7.state[], s4.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s7.state[], s5.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s7.state[], s6.bid, Slot(10)) == Opt.some(s6.bid.slot)
dag.ancestorSlot(s7.state[], s7.bid, Slot(10)) == Opt.some(s7.bid.slot)
dag.ancestorSlot(s7.state[], s8.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s7.state[], s9.bid, Slot(10)) == Opt.some(s0.bid.slot)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check dag.ancestorSlot(s7.state[], b.bid, Slot(11)) == Opt.none(Slot)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check dag.ancestorSlot(s7.state[], b.bid, Slot(12)) == Opt.none(Slot)
for b in [s0, s1, s2, s4, s5, s8, s9]:
check dag.ancestorSlot(s7.state[], b.bid, Slot(13)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s8, s9]:
check dag.ancestorSlot(s7.state[], b.bid, Slot(14)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s8, s9]:
check dag.ancestorSlot(s7.state[], b.bid, Slot(15)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s8, s9]:
check dag.ancestorSlot(s7.state[], b.bid, Slot(16)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s8, s9]:
check dag.ancestorSlot(s7.state[], b.bid, Slot(17)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check dag.ancestorSlot(s7.state[], b.bid, Slot(18)) == Opt.none(Slot)
check:
dag.ancestorSlot(s8.state[], s0.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s8.state[], s1.bid, Slot(10)) == Opt.some(s1.bid.slot)
dag.ancestorSlot(s8.state[], s2.bid, Slot(10)) == Opt.some(s2.bid.slot)
dag.ancestorSlot(s8.state[], s3.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s8.state[], s4.bid, Slot(10)) == Opt.some(s4.bid.slot)
dag.ancestorSlot(s8.state[], s5.bid, Slot(10)) == Opt.some(s2.bid.slot)
dag.ancestorSlot(s8.state[], s6.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s8.state[], s7.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s8.state[], s8.bid, Slot(10)) == Opt.some(s8.bid.slot)
dag.ancestorSlot(s8.state[], s9.bid, Slot(10)) == Opt.some(s8.bid.slot)
for b in [s0, s3, s6, s7]:
check dag.ancestorSlot(s8.state[], b.bid, Slot(11)) == Opt.none(Slot)
for b in [s0, s1, s3, s6, s7]:
check dag.ancestorSlot(s8.state[], b.bid, Slot(12)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s5, s6, s7]:
check dag.ancestorSlot(s8.state[], b.bid, Slot(13)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s5, s6, s7]:
check dag.ancestorSlot(s8.state[], b.bid, Slot(14)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7]:
check dag.ancestorSlot(s8.state[], b.bid, Slot(15)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7]:
check dag.ancestorSlot(s8.state[], b.bid, Slot(16)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7]:
check dag.ancestorSlot(s8.state[], b.bid, Slot(17)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7]:
check dag.ancestorSlot(s8.state[], b.bid, Slot(18)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check dag.ancestorSlot(s8.state[], b.bid, Slot(19)) == Opt.none(Slot)
check:
dag.ancestorSlot(s9.state[], s0.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s9.state[], s1.bid, Slot(10)) == Opt.some(s1.bid.slot)
dag.ancestorSlot(s9.state[], s2.bid, Slot(10)) == Opt.some(s2.bid.slot)
dag.ancestorSlot(s9.state[], s3.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s9.state[], s4.bid, Slot(10)) == Opt.some(s4.bid.slot)
dag.ancestorSlot(s9.state[], s5.bid, Slot(10)) == Opt.some(s2.bid.slot)
dag.ancestorSlot(s9.state[], s6.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s9.state[], s7.bid, Slot(10)) == Opt.some(s0.bid.slot)
dag.ancestorSlot(s9.state[], s8.bid, Slot(10)) == Opt.some(s8.bid.slot)
dag.ancestorSlot(s9.state[], s9.bid, Slot(10)) == Opt.some(s9.bid.slot)
for b in [s0, s3, s6, s7]:
check dag.ancestorSlot(s9.state[], b.bid, Slot(11)) == Opt.none(Slot)
for b in [s0, s1, s3, s6, s7]:
check dag.ancestorSlot(s9.state[], b.bid, Slot(12)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s5, s6, s7]:
check dag.ancestorSlot(s9.state[], b.bid, Slot(13)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s5, s6, s7]:
check dag.ancestorSlot(s9.state[], b.bid, Slot(14)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7]:
check dag.ancestorSlot(s9.state[], b.bid, Slot(15)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7]:
check dag.ancestorSlot(s9.state[], b.bid, Slot(16)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7]:
check dag.ancestorSlot(s9.state[], b.bid, Slot(17)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7]:
check dag.ancestorSlot(s9.state[], b.bid, Slot(18)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8]:
check dag.ancestorSlot(s9.state[], b.bid, Slot(19)) == Opt.none(Slot)
for b in [s0, s1, s2, s3, s4, s5, s6, s7, s8, s9]:
check dag.ancestorSlot(s9.state[], b.bid, Slot(20)) == Opt.none(Slot)
template runShufflingTests(cfg: RuntimeConfig, numRandomTests: int) =
const
numValidators = SLOTS_PER_EPOCH