nimbus-eth2/tests/test_block_dag.nim

386 lines
16 KiB
Nim
Raw Normal View History

# beacon_chain
accelerate `getShufflingRef` (#4911) When an uncached `ShufflingRef` is requested, we currently replay state which can take several seconds. Acceleration is possible by: 1. Start from any state with locked-in `get_active_validator_indices`. Any blocks / slots applied to such a state can only affect that result for future epochs, so are viable for querying target epoch. `compute_activation_exit_epoch(state.slot.epoch) > target.epoch` 2. Determine highest common ancestor among `state` and `target.blck`. At the ancestor slot, same rules re `get_active_validator_indices`. `compute_activation_exit_epoch(ancestorSlot.epoch) > target.epoch` 3. We now have a `state` that shares history with `target.blck` up through a common ancestor slot. Any blocks / slots that the `state` contains, which are not part of the `target.blck` history, affect `get_active_validator_indices` at epochs _after_ `target.epoch`. 4. Select `state.randao_mixes[N]` that is closest to common ancestor. Either direction is fine (above / below ancestor). 5. From that RANDAO mix, mix in / out all RANDAO reveals from blocks in-between. This is just an XOR operation, so fully reversible. `mix = mix xor SHA256(blck.message.body.randao_reveal)` 6. Compute the attester dependent slot from `target.epoch`. `if epoch >= 2: (target.epoch - 1).start_slot - 1 else: GENESIS_SLOT` 7. Trace back from `target.blck` to the attester dependent slot. We now have the destination for which we want to obtain RANDAO. 8. Mix in all RANDAO reveals from blocks up through the `dependentBlck`. Same method, no special handling necessary for epoch transitions. 9. Combine `get_active_validator_indices` from `state` at `target.epoch` with the recovered RANDAO value at `dependentBlck` to obtain the requested shuffling, and construct the `ShufflingRef` without replay. * more tests and simplify logic * test with different number of deposits per branch * Update beacon_chain/consensus_object_pools/blockchain_dag.nim Co-authored-by: Jacek Sieka <jacek@status.im> * `commonAncestor` tests * lint --------- Co-authored-by: Jacek Sieka <jacek@status.im>
2023-05-12 17:36:59 +00:00
# Copyright (c) 2018-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
unittest2,
../beacon_chain/consensus_object_pools/block_dag
func `$`(x: BlockRef): string = shortLog(x)
suite "BlockRef and helpers":
test "isAncestorOf sanity":
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)
check:
s0.isAncestorOf(s0)
s0.isAncestorOf(s1)
s0.isAncestorOf(s2)
s1.isAncestorOf(s1)
s1.isAncestorOf(s2)
not s2.isAncestorOf(s0)
not s2.isAncestorOf(s1)
not s1.isAncestorOf(s0)
test "get_ancestor sanity":
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)
s4 = BlockRef(bid: BlockId(slot: Slot(4)), parent: s2)
check:
s0.get_ancestor(Slot(0)) == s0
s0.get_ancestor(Slot(1)) == s0
s1.get_ancestor(Slot(0)) == s0
s1.get_ancestor(Slot(1)) == s1
s4.get_ancestor(Slot(0)) == s0
s4.get_ancestor(Slot(1)) == s1
s4.get_ancestor(Slot(2)) == s2
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
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)
s4 = BlockRef(bid: BlockId(slot: Slot(4)), parent: s2)
se1 = BlockRef(bid:
BlockId(slot: Epoch(1).start_slot()), parent: s2)
se2 = BlockRef(bid:
BlockId(slot: Epoch(2).start_slot()), parent: se1)
check:
s0.atSlot(Slot(0)).blck == s0
s0.atSlot(Slot(0)) == s1.atSlot(Slot(0))
s1.atSlot(Slot(1)).blck == s1
s4.atSlot(Slot(0)).blck == s0
s4.atSlot() == s4.atSlot(s4.slot)
test "parent sanity":
let
root = block:
var d: Eth2Digest
d.data[0] = 1
d
s0 = BlockRef(bid: BlockId(slot: Slot(0)))
s00 = BlockSlot(blck: s0, slot: Slot(0))
s01 = BlockSlot(blck: s0, slot: Slot(1))
s2 = BlockRef(bid: BlockId(slot: Slot(2), root: root), parent: s0)
s22 = BlockSlot(blck: s2, slot: Slot(2))
s24 = BlockSlot(blck: s2, slot: Slot(4))
check:
s00.parent == BlockSlot(blck: nil, slot: Slot(0))
s01.parent == s00
s01.parentOrSlot == s00
s22.parent == s01
s22.parentOrSlot == BlockSlot(blck: s0, slot: Slot(2))
s24.parent == BlockSlot(blck: s2, slot: Slot(3))
s24.parent.parent == s22
s22.isProposed()
not s24.isProposed()
suite "BlockId and helpers":
test "atSlot sanity":
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)
s4 = BlockRef(bid: BlockId(slot: Slot(4)), parent: s2)
check:
s0.atSlot(Slot(0)).blck == s0
s0.atSlot(Slot(0)) == s1.atSlot(Slot(0))
s1.atSlot(Slot(1)).blck == s1
s4.atSlot(Slot(0)).blck == s0
test "parent sanity":
let
s0 = BlockRef(bid: BlockId(slot: Slot(0)))
s00 = BlockSlot(blck: s0, slot: Slot(0))
s01 = BlockSlot(blck: s0, slot: Slot(1))
s2 = BlockRef(bid: BlockId(slot: Slot(2)), parent: s0)
s22 = BlockSlot(blck: s2, slot: Slot(2))
s24 = BlockSlot(blck: s2, slot: Slot(4))
check:
s00.parent == BlockSlot(blck: nil, slot: Slot(0))
s01.parent == s00
s01.parentOrSlot == s00
s22.parent == s01
s22.parentOrSlot == BlockSlot(blck: s0, slot: Slot(2))
s24.parent == BlockSlot(blck: s2, slot: Slot(3))
s24.parent.parent == s22