258 lines
7.2 KiB
Nim
258 lines
7.2 KiB
Nim
# beacon_chain
|
|
# 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.
|
|
|
|
{.push raises: [].}
|
|
|
|
# import ../interpreter # included to be able to use "suite"
|
|
|
|
func setup_no_votes(): tuple[fork_choice: ForkChoiceBackend, ops: seq[Operation]] =
|
|
let balances = newSeq[Gwei](16)
|
|
let GenesisRoot = fakeHash(0)
|
|
|
|
# Initialize the fork choice context
|
|
# We start with epoch 0 fully finalized to avoid epoch 0 special cases.
|
|
result.fork_choice = ForkChoiceBackend.init(
|
|
FinalityCheckpoints(
|
|
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
|
finalized: Checkpoint(root: GenesisRoot, epoch: Epoch(1))))
|
|
|
|
# ----------------------------------
|
|
|
|
# Head should be genesis
|
|
result.ops.add Operation(
|
|
kind: FindHead,
|
|
checkpoints: FinalityCheckpoints(
|
|
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
|
finalized: Checkpoint(root: GenesisRoot, epoch: Epoch(1))),
|
|
justified_state_balances: balances,
|
|
expected_head: GenesisRoot)
|
|
|
|
# Add block 2
|
|
#
|
|
# 0
|
|
# /
|
|
# 2
|
|
result.ops.add Operation(
|
|
kind: ProcessBlock,
|
|
root: fakeHash(2),
|
|
parent_root: GenesisRoot,
|
|
blk_checkpoints: FinalityCheckpoints(
|
|
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
|
finalized: Checkpoint(root: GenesisRoot, epoch: Epoch(1))))
|
|
|
|
# Head should be 2
|
|
#
|
|
# 0
|
|
# /
|
|
# 2 <- head
|
|
result.ops.add Operation(
|
|
kind: FindHead,
|
|
checkpoints: FinalityCheckpoints(
|
|
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
|
finalized: Checkpoint(root: GenesisRoot, epoch: Epoch(1))),
|
|
justified_state_balances: balances,
|
|
expected_head: fakeHash(2))
|
|
|
|
# Add block 1 as a fork
|
|
#
|
|
# 0
|
|
# / \
|
|
# 2 1
|
|
result.ops.add Operation(
|
|
kind: ProcessBlock,
|
|
root: fakeHash(1),
|
|
parent_root: GenesisRoot,
|
|
blk_checkpoints: FinalityCheckpoints(
|
|
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
|
finalized: Checkpoint(root: GenesisRoot, epoch: Epoch(1))))
|
|
|
|
# Head is still 2 due to tiebreaker as fakeHash(2) (0xD8...) > fakeHash(1) (0x7C...)
|
|
#
|
|
# 0
|
|
# / \
|
|
# head-> 2 1
|
|
result.ops.add Operation(
|
|
kind: FindHead,
|
|
checkpoints: FinalityCheckpoints(
|
|
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
|
finalized: Checkpoint(root: GenesisRoot, epoch: Epoch(1))),
|
|
justified_state_balances: balances,
|
|
expected_head: fakeHash(2))
|
|
|
|
# Add block 3
|
|
#
|
|
# 0
|
|
# / \
|
|
# 2 1
|
|
# |
|
|
# 3
|
|
result.ops.add Operation(
|
|
kind: ProcessBlock,
|
|
root: fakeHash(3),
|
|
parent_root: fakeHash(1),
|
|
blk_checkpoints: FinalityCheckpoints(
|
|
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
|
finalized: Checkpoint(root: GenesisRoot, epoch: Epoch(1))))
|
|
|
|
# Head is still 2
|
|
#
|
|
# 0
|
|
# / \
|
|
# head-> 2 1
|
|
# |
|
|
# 3
|
|
result.ops.add Operation(
|
|
kind: FindHead,
|
|
checkpoints: FinalityCheckpoints(
|
|
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
|
finalized: Checkpoint(root: GenesisRoot, epoch: Epoch(1))),
|
|
justified_state_balances: balances,
|
|
expected_head: fakeHash(2))
|
|
|
|
# Add block 4
|
|
#
|
|
# 0
|
|
# / \
|
|
# 2 1
|
|
# | |
|
|
# 4 3
|
|
result.ops.add Operation(
|
|
kind: ProcessBlock,
|
|
root: fakeHash(4),
|
|
parent_root: fakeHash(2),
|
|
blk_checkpoints: FinalityCheckpoints(
|
|
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
|
finalized: Checkpoint(root: GenesisRoot, epoch: Epoch(1))))
|
|
|
|
# Check that head is 4
|
|
#
|
|
# 0
|
|
# / \
|
|
# 2 1
|
|
# | |
|
|
# head-> 4 3
|
|
result.ops.add Operation(
|
|
kind: FindHead,
|
|
checkpoints: FinalityCheckpoints(
|
|
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
|
finalized: Checkpoint(root: GenesisRoot, epoch: Epoch(1))),
|
|
justified_state_balances: balances,
|
|
expected_head: fakeHash(4))
|
|
|
|
# Add block 5 with justified epoch of 2
|
|
#
|
|
# 0
|
|
# / \
|
|
# 2 1
|
|
# | |
|
|
# 4 3
|
|
# |
|
|
# 5 <- justified epoch = 2
|
|
result.ops.add Operation(
|
|
kind: ProcessBlock,
|
|
root: fakeHash(5),
|
|
parent_root: fakeHash(4),
|
|
blk_checkpoints: FinalityCheckpoints(
|
|
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
|
|
finalized: Checkpoint(root: GenesisRoot, epoch: Epoch(1))))
|
|
|
|
# Ensure the head is still 4 whilst the justified epoch is 0.
|
|
#
|
|
# 0
|
|
# / \
|
|
# 2 1
|
|
# | |
|
|
# head-> 4 3
|
|
# |
|
|
# 5
|
|
result.ops.add Operation(
|
|
kind: FindHead,
|
|
checkpoints: FinalityCheckpoints(
|
|
justified: Checkpoint(root: GenesisRoot, epoch: Epoch(1)),
|
|
finalized: Checkpoint(root: GenesisRoot, epoch: Epoch(1))),
|
|
justified_state_balances: balances,
|
|
expected_head: fakeHash(4))
|
|
|
|
# Ensure that there is an error when starting from a block with the wrong justified epoch
|
|
# 0
|
|
# / \
|
|
# 2 1
|
|
# | |
|
|
# 4 3
|
|
# |
|
|
# 5 <- starting from 5 with justified epoch 0 should error.
|
|
result.ops.add Operation(
|
|
kind: InvalidFindHead,
|
|
checkpoints: FinalityCheckpoints(
|
|
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(1)),
|
|
finalized: Checkpoint(root: GenesisRoot, epoch: Epoch(1))),
|
|
justified_state_balances: balances)
|
|
|
|
# Set the justified epoch to 2 and the start block to 5 and ensure 5 is the head.
|
|
# 0
|
|
# / \
|
|
# 2 1
|
|
# | |
|
|
# 4 3
|
|
# |
|
|
# 5 <- head
|
|
result.ops.add Operation(
|
|
kind: FindHead,
|
|
checkpoints: FinalityCheckpoints(
|
|
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
|
|
finalized: Checkpoint(root: GenesisRoot, epoch: Epoch(1))),
|
|
justified_state_balances: balances,
|
|
expected_head: fakeHash(5))
|
|
|
|
# Add block 6
|
|
#
|
|
# 0
|
|
# / \
|
|
# 2 1
|
|
# | |
|
|
# 4 3
|
|
# |
|
|
# 5 <- justified root
|
|
# |
|
|
# 6
|
|
result.ops.add Operation(
|
|
kind: ProcessBlock,
|
|
root: fakeHash(6),
|
|
parent_root: fakeHash(5),
|
|
blk_checkpoints: FinalityCheckpoints(
|
|
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
|
|
finalized: Checkpoint(root: GenesisRoot, epoch: Epoch(1))))
|
|
|
|
# Ensure 6 is the head
|
|
# 0
|
|
# / \
|
|
# 2 1
|
|
# | |
|
|
# 4 3
|
|
# |
|
|
# 5 <- justified root
|
|
# |
|
|
# 6 <- head
|
|
result.ops.add Operation(
|
|
kind: FindHead,
|
|
checkpoints: FinalityCheckpoints(
|
|
justified: Checkpoint(root: fakeHash(5), epoch: Epoch(2)),
|
|
finalized: Checkpoint(root: GenesisRoot, epoch: Epoch(1))),
|
|
justified_state_balances: balances,
|
|
expected_head: fakeHash(6))
|
|
|
|
proc test_no_votes() =
|
|
test "fork_choice - testing no votes":
|
|
# for i in 0 ..< 6:
|
|
# echo " block (", i, ") hash: ", fakeHash(i)
|
|
# echo " ------------------------------------------------------"
|
|
|
|
var (ctx, ops) = setup_no_votes()
|
|
ctx.run(ops)
|
|
|
|
test_no_votes()
|