cryptarchia: fix try_create_fork to find parent block (#84)

This commit is contained in:
Youngjoon Lee 2024-03-21 09:55:38 +09:00 committed by GitHub
parent a0175e16f3
commit 601598f814
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 52 additions and 9 deletions

View File

@ -244,14 +244,11 @@ class Chain:
def length(self) -> int: def length(self) -> int:
return len(self.blocks) return len(self.blocks)
def contains_block(self, block: BlockHeader) -> bool: def block_position(self, block: Id) -> Optional[int]:
return block in self.blocks
def block_position(self, block: BlockHeader) -> int:
assert self.contains_block(block)
for i, b in enumerate(self.blocks): for i, b in enumerate(self.blocks):
if b == block: if b.id() == block:
return i return i
return None
@dataclass @dataclass
@ -420,10 +417,10 @@ class Follower:
chains = self.forks + [self.local_chain] chains = self.forks + [self.local_chain]
for chain in chains: for chain in chains:
if chain.contains_block(block): block_position = chain.block_position(block.parent)
block_position = chain.block_position(block) if block_position is not None:
return Chain( return Chain(
blocks=chain.blocks[:block_position], blocks=chain.blocks[: block_position + 1],
genesis=self.genesis_state.block, genesis=self.genesis_state.block,
) )

View File

@ -77,6 +77,52 @@ class TestLedgerStateUpdate(TestCase):
# and the original coin_1 should now be removed from the spent pool # and the original coin_1 should now be removed from the spent pool
assert follower.tip_state().verify_unspent(coin_1.nullifier()) assert follower.tip_state().verify_unspent(coin_1.nullifier())
def test_fork_creation(self):
coins = [Coin(sk=i, value=100) for i in range(7)]
genesis = mk_genesis_state(coins)
follower = Follower(genesis, mk_config())
# coin_0 & coin_1 both concurrently win slot 0 based on the genesis block
# Both blocks are accepted, and a fork is created "from the genesis block"
block_1 = mk_block(parent=genesis.block, slot=0, coin=coins[0])
block_2 = mk_block(parent=genesis.block, slot=0, coin=coins[1])
follower.on_block(block_1)
follower.on_block(block_2)
assert follower.tip() == block_1
assert len(follower.forks) == 1, f"{len(follower.forks)}"
assert follower.forks[0].tip() == block_2
# coin_2 wins slot 1 and chooses to extend from block_1
# coin_3 also wins slot 1 and but chooses to extend from block_2
# Both blocks are accepted. Both the local chain and the fork grow. No fork is newly created.
block_3 = mk_block(parent=block_1.id(), slot=1, coin=coins[2])
block_4 = mk_block(parent=block_2.id(), slot=1, coin=coins[3])
follower.on_block(block_3)
follower.on_block(block_4)
assert follower.tip() == block_3
assert len(follower.forks) == 1, f"{len(follower.forks)}"
assert follower.forks[0].tip() == block_4
# coin_4 wins slot 1 and but chooses to extend from block_2 as well
# The block is accepted. A new fork is created "from the block_2".
block_5 = mk_block(parent=block_2.id(), slot=1, coin=coins[4])
follower.on_block(block_5)
assert follower.tip() == block_3
assert len(follower.forks) == 2, f"{len(follower.forks)}"
assert follower.forks[0].tip() == block_4
assert follower.forks[1].tip() == block_5
# A block based on an unknown parent is not accepted.
# Nothing changes from the local chain and forks.
unknown_block = mk_block(parent=block_5.id(), slot=2, coin=coins[5])
block_6 = mk_block(parent=unknown_block.id(), slot=2, coin=coins[6])
follower.on_block(block_6)
assert follower.tip() == block_3
assert len(follower.forks) == 2, f"{len(follower.forks)}"
assert follower.forks[0].tip() == block_4
assert follower.forks[1].tip() == block_5
def test_epoch_transition(self): def test_epoch_transition(self):
leader_coins = [Coin(sk=i, value=100) for i in range(4)] leader_coins = [Coin(sk=i, value=100) for i in range(4)]
genesis = mk_genesis_state(leader_coins) genesis = mk_genesis_state(leader_coins)