nomos-specs/cryptarchia/test_fork_choice.py

103 lines
3.3 KiB
Python

from unittest import TestCase
from itertools import repeat
import numpy as np
import hashlib
from copy import deepcopy
from cryptarchia.cryptarchia import (
maxvalid_bg,
Chain,
BlockHeader,
Slot,
Id,
MockLeaderProof,
Coin,
)
def make_block(parent_id: Id, slot: Slot, content: bytes) -> BlockHeader:
assert len(parent_id) == 32
content_id = hashlib.sha256(content).digest()
return BlockHeader(
parent=parent_id,
content_size=1,
slot=slot,
content_id=content_id,
leader_proof=MockLeaderProof.new(
Coin(sk=0, value=10), slot=slot, parent=parent_id
),
)
class TestLeader(TestCase):
def test_fork_choice_long_sparse_chain(self):
# The longest chain is not dense after the fork
common = [make_block(bytes(32), Slot(i), bytes(i)) for i in range(1, 50)]
long_chain = deepcopy(common)
short_chain = deepcopy(common)
for slot in range(50, 100):
# make arbitrary ids for the different chain so that the blocks appear to be different
long_content = f"{slot}-long".encode()
short_content = f"{slot}-short".encode()
if slot % 2 == 0:
long_chain.append(make_block(bytes(32), Slot(slot), long_content))
short_chain.append(make_block(bytes(32), Slot(slot), short_content))
# add more blocks to the long chain
for slot in range(100, 200):
long_content = f"{slot}-long".encode()
long_chain.append(make_block(bytes(32), Slot(slot), long_content))
assert len(long_chain) > len(short_chain)
# by setting a low k we trigger the density choice rule
k = 1
s = 50
short_chain = Chain(short_chain, genesis=bytes(32))
long_chain = Chain(long_chain, genesis=bytes(32))
assert (
maxvalid_bg(
short_chain,
[long_chain],
k,
s,
)
== short_chain
)
# However, if we set k to the fork length, it will be accepted
k = long_chain.length()
assert (
maxvalid_bg(
short_chain,
[long_chain],
k,
s,
)
== long_chain
)
def test_fork_choice_long_dense_chain(self):
# The longest chain is also the densest after the fork
common = [make_block(bytes(32), Slot(i), bytes(i)) for i in range(1, 50)]
long_chain = deepcopy(common)
short_chain = deepcopy(common)
for slot in range(50, 100):
# make arbitrary ids for the different chain so that the blocks appear to be different
long_content = f"{slot}-long".encode()
short_content = f"{slot}-short".encode()
long_chain.append(make_block(bytes(32), Slot(slot), long_content))
if slot % 2 == 0:
short_chain.append(make_block(bytes(32), Slot(slot), short_content))
k = 1
s = 50
short_chain = Chain(short_chain, genesis=bytes(32))
long_chain = Chain(long_chain, genesis=bytes(32))
assert (
maxvalid_bg(
short_chain,
[long_chain],
k,
s,
)
== long_chain
)