diff --git a/cryptarchia/cryptarchia.py b/cryptarchia/cryptarchia.py index c410251..528fc8c 100644 --- a/cryptarchia/cryptarchia.py +++ b/cryptarchia/cryptarchia.py @@ -1,4 +1,4 @@ -from typing import TypeAlias, List, Optional +from typing import TypeAlias, List, Optional, Dict from hashlib import sha256, blake2b from math import floor from copy import deepcopy @@ -174,11 +174,11 @@ class Coin: @dataclass class MockLeaderProof: - commitment: Id - nullifier: Id - evolved_commitment: Id - slot: Slot - parent: Id + commitment: Id = bytes(32) + nullifier: Id = bytes(32) + evolved_commitment: Id = bytes(32) + slot: Slot = field(default_factory=lambda: Slot(0)) + parent: Id = bytes(32) @staticmethod def new(coin: Coin, slot: Slot, parent: Id): @@ -194,18 +194,31 @@ class MockLeaderProof: def verify(self, slot: Slot, parent: Id): # TODO: verification not implemented - return slot == self.slot and parent == self.parent + if slot != self.slot: + logger.warning("PoL: wrong slot") + return False + if parent != self.parent: + logger.warning("PoL: wrong parent") + return False + return True @dataclass class BlockHeader: slot: Slot - parent: Id - content_size: int - content_id: Id - leader_proof: MockLeaderProof + parent: Id = bytes(32) + content_size: int = 0 + content_id: Id = bytes(32) + leader_proof: MockLeaderProof = field(default_factory=MockLeaderProof) + orphaned_proofs: List["BlockHeader"] = field(default_factory=list) + def __post_init__(self): + assert type(self.slot) == Slot + assert type(self.parent) == Id + assert self.slot == self.leader_proof.slot + assert self.parent == self.leader_proof.parent + def update_header_hash(self, h): # version byte h.update(b"\x01") @@ -277,7 +290,7 @@ class LedgerState: A snapshot of the ledger state up to some block """ - block: Id = None + block: BlockHeader # This nonce is used to derive the seed for the slot leader lottery. # It's updated at every block by hashing the previous nonce with the @@ -324,7 +337,7 @@ class LedgerState: return nullifier not in self.nullifiers def apply(self, block: BlockHeader): - assert block.parent == self.block + assert block.parent == self.block.id() h = blake2b(digest_size=32) h.update("epoch-nonce".encode(encoding="utf-8")) @@ -333,7 +346,7 @@ class LedgerState: h.update(block.slot.encode()) self.nonce = h.digest() - self.block = block.id() + self.block = block for proof in chain(block.orphaned_proofs, [block]): self.apply_leader_proof(proof.leader_proof) @@ -385,9 +398,9 @@ class Follower: def __init__(self, genesis_state: LedgerState, config: Config): self.config = config self.forks = [] - self.local_chain = Chain([], genesis=genesis_state.block) + self.local_chain = Chain([], genesis=genesis_state.block.id()) self.genesis_state = genesis_state - self.ledger_state = {genesis_state.block: genesis_state.copy()} + self.ledger_state = {genesis_state.block.id(): genesis_state.copy()} self.epoch_state = {} def validate_header(self, block: BlockHeader, chain: Chain) -> bool: @@ -453,14 +466,21 @@ class Follower: # This will change once we start putting merkle roots in headers current_state: LedgerState, ) -> bool: - return ( - proof.verify(slot, parent) # verify slot leader proof - and ( - current_state.verify_eligible_to_lead(proof.commitment) - or epoch_state.verify_eligible_to_lead_due_to_age(proof.commitment) - ) - and current_state.verify_unspent(proof.nullifier) - ) + if not proof.verify(slot, parent): + logger.warning("invalid PoL") + return False + if not ( + current_state.verify_eligible_to_lead(proof.commitment) + or epoch_state.verify_eligible_to_lead_due_to_age(proof.commitment) + ): + logger.warning("invalid commitment") + return False + + if not current_state.verify_unspent(proof.nullifier): + logger.warning("PoL coin already spent") + return False + + return True # Try appending this block to an existing chain and return whether # the operation was successful @@ -475,9 +495,9 @@ class Follower: return None def try_create_fork(self, block: BlockHeader) -> Optional[Chain]: - if self.genesis_state.block == block.parent: + if self.genesis_state.block.id() == block.parent: # this block is forking off the genesis state - return Chain(blocks=[], genesis=self.genesis_state.block) + return Chain(blocks=[], genesis=self.genesis_state.block.id()) chains = self.forks + [self.local_chain] for chain in chains: @@ -485,7 +505,7 @@ class Follower: if block_position is not None: return Chain( blocks=chain.blocks[: block_position + 1], - genesis=self.genesis_state.block, + genesis=self.genesis_state.block.id(), ) return None @@ -509,6 +529,10 @@ class Follower: logger.warning("invalid header") return + new_state = self.ledger_state[block.parent].copy() + new_state.apply(block) + self.ledger_state[block.id()] = new_state + new_chain.blocks.append(block) # We may need to switch forks, lets run the fork choice rule to check. @@ -518,10 +542,6 @@ class Follower: self.forks.append(self.local_chain) self.local_chain = new_chain - new_state = self.ledger_state[block.parent].copy() - new_state.apply(block) - self.ledger_state[block.id()] = new_state - def unimported_orphans(self, tip: Id) -> list[BlockHeader]: """ Returns all unimported orphans w.r.t. the given tip's state. @@ -547,7 +567,11 @@ class Follower: # Evaluate the fork choice rule and return the chain we should be following def fork_choice(self) -> Chain: return maxvalid_bg( - self.local_chain, self.forks, k=self.config.k, s=self.config.s + self.local_chain, + self.forks, + self.ledger_state, + k=self.config.k, + s=self.config.s, ) def tip(self) -> BlockHeader: @@ -596,7 +620,7 @@ class Follower: nonce_snapshot = self.nonce_snapshot(epoch, chain) # we memoize epoch states to avoid recursion killing our performance - memo_block_id = nonce_snapshot.block + memo_block_id = nonce_snapshot.block.id() if state := self.epoch_state.get((epoch, memo_block_id)): return state @@ -696,7 +720,53 @@ class Leader: ) -def common_prefix_len(a: Chain, b: Chain) -> int: +def common_prefix_depth( + local_chain: Id, fork: Id, states: Dict[Id, LedgerState] +) -> int: + + local_block = local_chain + fork_block = fork + + seen = {} + depth = 0 + while True: + + if local_block not in states and fork_block not in states: + # conflicting genesis blocks + print("") + print("local\t", local_chain[:2]) + print("fork\t", fork[:2]) + print( + "states\n\t", + "\n\t".join( + [f"{b[:2]} -> {s.block.parent[:2]}" for b, s in states.items()] + ), + ) + print("seen\t", {s[:2] for s in seen}) + break + + if local_block in seen: + # we had seen this block from the fork chain + return depth + + if local_block in states: + seen[local_block] = depth + local_block = states[local_block].block.parent + + if fork_block in seen: + # we had seen the fork in the local chain + # return the depth w.r.t to the local chain + return seen[fork_block] + + if fork_block in states: + seen[fork_block] = depth + fork_block = states[fork_block].block.parent + depth += 1 + + assert False + + +def common_prefix_len(a, b) -> int: for i, (x, y) in enumerate(zip(a.blocks, b.blocks)): if x.id() != y.id(): return i @@ -716,11 +786,19 @@ def chain_density(chain: Chain, slot: Slot) -> int: # Implementation of the fork choice rule as defined in the Ouroboros Genesis paper # k defines the forking depth of chain we accept without more analysis # s defines the length of time (unit of slots) after the fork happened we will inspect for chain density -def maxvalid_bg(local_chain: Chain, forks: List[Chain], k: int, s: int) -> Chain: +def maxvalid_bg( + local_chain: Chain, + forks: List[Chain], + states: Dict[Id, LedgerState], + k: int, + s: int, +) -> Chain: cmax = local_chain for chain in forks: lowest_common_ancestor = common_prefix_len(cmax, chain) m = cmax.length() - lowest_common_ancestor + m2 = common_prefix_depth(cmax.tip_id(), chain.tip_id(), states) + assert m == m2, f"{m} != {m2}" if m <= k: # Classic longest chain rule with parameter k if cmax.length() < chain.length(): diff --git a/cryptarchia/test_common.py b/cryptarchia/test_common.py index 4b3dbc0..f7c4408 100644 --- a/cryptarchia/test_common.py +++ b/cryptarchia/test_common.py @@ -53,7 +53,7 @@ def mk_config(initial_stake_distribution: list[Coin]) -> Config: def mk_genesis_state(initial_stake_distribution: list[Coin]) -> LedgerState: return LedgerState( - block=bytes(32), + block=BlockHeader(slot=Slot(0), parent=bytes(32)), nonce=bytes(32), commitments_spend={c.commitment() for c in initial_stake_distribution}, commitments_lead={c.commitment() for c in initial_stake_distribution}, @@ -62,26 +62,30 @@ def mk_genesis_state(initial_stake_distribution: list[Coin]) -> LedgerState: def mk_block( - parent: Id, slot: int, coin: Coin, content=bytes(32), orphaned_proofs=[] + parent: BlockHeader, slot: int, coin: Coin, content=bytes(32), orphaned_proofs=[] ) -> BlockHeader: - assert len(parent) == 32 + assert type(parent) == BlockHeader, type(parent) + assert type(slot) == int, type(slot) from hashlib import sha256 return BlockHeader( slot=Slot(slot), - parent=parent, + parent=parent.id(), content_size=len(content), content_id=sha256(content).digest(), - leader_proof=MockLeaderProof.new(coin, Slot(slot), parent=parent), + leader_proof=MockLeaderProof.new(coin, Slot(slot), parent=parent.id()), orphaned_proofs=orphaned_proofs, ) -def mk_chain(parent, coin: Coin, slots: list[int]) -> tuple[list[BlockHeader], Coin]: +def mk_chain( + parent: BlockHeader, coin: Coin, slots: list[int] +) -> tuple[list[BlockHeader], Coin]: + assert type(parent) == BlockHeader chain = [] for s in slots: block = mk_block(parent=parent, slot=s, coin=coin) chain.append(block) - parent = block.id() + parent = block coin = coin.evolve() return chain, coin diff --git a/cryptarchia/test_fork_choice.py b/cryptarchia/test_fork_choice.py index ff048e1..12f6b4d 100644 --- a/cryptarchia/test_fork_choice.py +++ b/cryptarchia/test_fork_choice.py @@ -13,28 +13,77 @@ from cryptarchia.cryptarchia import ( MockLeaderProof, Coin, Follower, + common_prefix_depth, + LedgerState, ) from .test_common import mk_chain, mk_config, mk_genesis_state, mk_block class TestForkChoice(TestCase): + def test_common_prefix_depth(self): + + # 6 - 7 + # / + # 0 - 1 - 2 - 3 + # \ + # 4 - 5 + + coin = Coin(sk=1, value=100) + + b0 = BlockHeader(slot=Slot(0), parent=bytes(32)) + b1 = mk_block(b0, 1, coin) + b2 = mk_block(b1, 2, coin) + b3 = mk_block(b2, 3, coin) + b4 = mk_block(b0, 1, coin, content=b"b4") + b5 = mk_block(b4, 2, coin) + b6 = mk_block(b2, 3, coin, content=b"b6") + b7 = mk_block(b6, 4, coin) + + states = { + b.id(): LedgerState(block=b) for b in [b0, b1, b2, b3, b4, b5, b6, b7] + } + + assert (d := common_prefix_depth(b0.id(), b0.id(), states)) == 0, d + assert (d := common_prefix_depth(b1.id(), b0.id(), states)) == 1, d + assert (d := common_prefix_depth(b0.id(), b1.id(), states)) == 0, d + assert (d := common_prefix_depth(b1.id(), b1.id(), states)) == 0, d + assert (d := common_prefix_depth(b2.id(), b0.id(), states)) == 2, d + assert (d := common_prefix_depth(b0.id(), b2.id(), states)) == 0, d + assert (d := common_prefix_depth(b3.id(), b0.id(), states)) == 3, d + assert (d := common_prefix_depth(b0.id(), b3.id(), states)) == 0, d + assert (d := common_prefix_depth(b1.id(), b4.id(), states)) == 1, d + assert (d := common_prefix_depth(b4.id(), b1.id(), states)) == 1, d + assert (d := common_prefix_depth(b1.id(), b5.id(), states)) == 1, d + assert (d := common_prefix_depth(b5.id(), b1.id(), states)) == 2, d + assert (d := common_prefix_depth(b2.id(), b5.id(), states)) == 2, d + assert (d := common_prefix_depth(b5.id(), b2.id(), states)) == 2, d + assert (d := common_prefix_depth(b3.id(), b5.id(), states)) == 3, d + assert (d := common_prefix_depth(b5.id(), b3.id(), states)) == 2, d + assert (d := common_prefix_depth(b3.id(), b6.id(), states)) == 1, d + assert (d := common_prefix_depth(b6.id(), b3.id(), states)) == 1, d + assert (d := common_prefix_depth(b3.id(), b7.id(), states)) == 1, d + assert (d := common_prefix_depth(b7.id(), b3.id(), states)) == 2, d + assert (d := common_prefix_depth(b5.id(), b7.id(), states)) == 2, d + assert (d := common_prefix_depth(b7.id(), b5.id(), states)) == 4, d + def test_fork_choice_long_sparse_chain(self): # The longest chain is not dense after the fork + genesis = BlockHeader(slot=Slot(0), parent=bytes(32)) short_coin, long_coin = Coin(sk=0, value=100), Coin(sk=1, value=100) - common, long_coin = mk_chain(parent=bytes(32), coin=long_coin, slots=range(50)) + common, long_coin = mk_chain(parent=genesis, coin=long_coin, slots=range(50)) long_chain_sparse_ext, long_coin = mk_chain( - parent=common[-1].id(), coin=long_coin, slots=range(50, 100, 2) + parent=common[-1], coin=long_coin, slots=range(50, 100, 2) ) short_chain_dense_ext, _ = mk_chain( - parent=common[-1].id(), coin=short_coin, slots=range(50, 100) + parent=common[-1], coin=short_coin, slots=range(50, 100) ) # add more blocks to the long chain to ensure the long chain is indeed longer long_chain_further_ext, _ = mk_chain( - parent=long_chain_sparse_ext[-1].id(), coin=long_coin, slots=range(100, 126) + parent=long_chain_sparse_ext[-1], coin=long_coin, slots=range(100, 126) ) long_chain = deepcopy(common) + long_chain_sparse_ext + long_chain_further_ext @@ -47,24 +96,30 @@ class TestForkChoice(TestCase): 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 + states = { + b.id(): LedgerState(block=b) for b in short_chain.blocks + long_chain.blocks + } + + assert maxvalid_bg(short_chain, [long_chain], states, 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 + assert maxvalid_bg(short_chain, [long_chain], states, k, s) == long_chain def test_fork_choice_long_dense_chain(self): # The longest chain is also the densest after the fork short_coin, long_coin = Coin(sk=0, value=100), Coin(sk=1, value=100) common, long_coin = mk_chain( - parent=bytes(32), coin=long_coin, slots=range(1, 50) + parent=BlockHeader(slot=Slot(0), parent=bytes(32)), + coin=long_coin, + slots=range(1, 50), ) long_chain_dense_ext, _ = mk_chain( - parent=common[-1].id(), coin=long_coin, slots=range(50, 100) + parent=common[-1], coin=long_coin, slots=range(50, 100) ) short_chain_sparse_ext, _ = mk_chain( - parent=common[-1].id(), coin=short_coin, slots=range(50, 100, 2) + parent=common[-1], coin=short_coin, slots=range(50, 100, 2) ) long_chain = deepcopy(common) + long_chain_dense_ext @@ -74,7 +129,11 @@ class TestForkChoice(TestCase): 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 + states = { + b.id(): LedgerState(block=b) for b in short_chain.blocks + long_chain.blocks + } + + assert maxvalid_bg(short_chain, [long_chain], states, k, s) == long_chain def test_fork_choice_integration(self): c_a, c_b = Coin(sk=0, value=10), Coin(sk=1, value=10) @@ -99,8 +158,8 @@ class TestForkChoice(TestCase): # b3 # - b2, c_a = mk_block(b1.id(), 2, c_a), c_a.evolve() - b3, c_b = mk_block(b1.id(), 2, c_b), c_b.evolve() + b2, c_a = mk_block(b1, 2, c_a), c_a.evolve() + b3, c_b = mk_block(b1, 2, c_b), c_b.evolve() follower.on_block(b2) follower.on_block(b3) @@ -117,7 +176,7 @@ class TestForkChoice(TestCase): # b3 - b4 == tip # - b4, c_b = mk_block(b3.id(), 3, c_b), c_a.evolve() + b4, c_b = mk_block(b3, 3, c_b), c_a.evolve() follower.on_block(b4) assert follower.tip_id() == b4.id() diff --git a/cryptarchia/test_leader.py b/cryptarchia/test_leader.py index 3f6918c..136f964 100644 --- a/cryptarchia/test_leader.py +++ b/cryptarchia/test_leader.py @@ -18,8 +18,8 @@ from .test_common import mk_config class TestLeader(TestCase): def test_slot_leader_statistics(self): epoch = EpochState( - stake_distribution_snapshot=LedgerState(), - nonce_snapshot=LedgerState(nonce=b"1010101010"), + stake_distribution_snapshot=LedgerState(block=None), + nonce_snapshot=LedgerState(block=None, nonce=b"1010101010"), inferred_total_active_stake=1000, ) diff --git a/cryptarchia/test_ledger_state_update.py b/cryptarchia/test_ledger_state_update.py index f724891..40575ca 100644 --- a/cryptarchia/test_ledger_state_update.py +++ b/cryptarchia/test_ledger_state_update.py @@ -34,7 +34,7 @@ class TestLedgerStateUpdate(TestCase): # Follower should have updated their ledger state to mark the leader coin as spent assert follower.tip_state().verify_unspent(leader_coin.nullifier()) == False - reuse_coin_block = mk_block(slot=1, parent=block.id(), coin=leader_coin) + reuse_coin_block = mk_block(slot=1, parent=block, coin=leader_coin) follower.on_block(block) # Follower should *not* have accepted the block @@ -67,7 +67,7 @@ class TestLedgerStateUpdate(TestCase): # 4) then coin[2] wins slot 1 and chooses to extend from block_2 - block_3 = mk_block(parent=block_2.id(), slot=1, coin=coin[2]) + block_3 = mk_block(parent=block_2, slot=1, coin=coin[2]) follower.on_block(block_3) # the follower should have switched over to the block_2 fork assert follower.tip() == block_3 @@ -94,8 +94,8 @@ class TestLedgerStateUpdate(TestCase): # 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]) + block_3 = mk_block(parent=block_1, slot=1, coin=coins[2]) + block_4 = mk_block(parent=block_2, slot=1, coin=coins[3]) follower.on_block(block_3) follower.on_block(block_4) assert follower.tip() == block_3 @@ -104,7 +104,7 @@ class TestLedgerStateUpdate(TestCase): # 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]) + block_5 = mk_block(parent=block_2, slot=1, coin=coins[4]) follower.on_block(block_5) assert follower.tip() == block_3 assert len(follower.forks) == 2, f"{len(follower.forks)}" @@ -113,8 +113,8 @@ class TestLedgerStateUpdate(TestCase): # 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]) + unknown_block = mk_block(parent=block_5, slot=2, coin=coins[5]) + block_6 = mk_block(parent=unknown_block, slot=2, coin=coins[6]) follower.on_block(block_6) assert follower.tip() == block_3 assert len(follower.forks) == 2, f"{len(follower.forks)}" @@ -138,14 +138,14 @@ class TestLedgerStateUpdate(TestCase): assert follower.tip() == block_1 assert follower.tip().slot.epoch(config).epoch == 0 - block_2 = mk_block(slot=19, parent=block_1.id(), coin=leader_coins[1]) + block_2 = mk_block(slot=19, parent=block_1, coin=leader_coins[1]) follower.on_block(block_2) assert follower.tip() == block_2 assert follower.tip().slot.epoch(config).epoch == 0 # ---- EPOCH 1 ---- - block_3 = mk_block(slot=20, parent=block_2.id(), coin=leader_coins[2]) + block_3 = mk_block(slot=20, parent=block_2, coin=leader_coins[2]) follower.on_block(block_3) assert follower.tip() == block_3 assert follower.tip().slot.epoch(config).epoch == 1 @@ -157,7 +157,7 @@ class TestLedgerStateUpdate(TestCase): # To ensure this is the case, we add a new coin just to the state associated with that slot, # so that the new block can be accepted only if that is the snapshot used # first, verify that if we don't change the state, the block is not accepted - block_4 = mk_block(slot=40, parent=block_3.id(), coin=Coin(sk=4, value=100)) + block_4 = mk_block(slot=40, parent=block_3, coin=Coin(sk=4, value=100)) follower.on_block(block_4) assert follower.tip() == block_3 # then we add the coin to "spendable commitments" associated with slot 9 @@ -181,12 +181,12 @@ class TestLedgerStateUpdate(TestCase): assert follower.tip() == block_1 # coin can't be reused to win following slots: - block_2_reuse = mk_block(slot=1, parent=block_1.id(), coin=coin) + block_2_reuse = mk_block(slot=1, parent=block_1, coin=coin) follower.on_block(block_2_reuse) assert follower.tip() == block_1 # but the evolved coin is eligible - block_2_evolve = mk_block(slot=1, parent=block_1.id(), coin=coin.evolve()) + block_2_evolve = mk_block(slot=1, parent=block_1, coin=coin.evolve()) follower.on_block(block_2_evolve) assert follower.tip() == block_2_evolve @@ -212,12 +212,12 @@ class TestLedgerStateUpdate(TestCase): ) # the new coin is not yet eligible for elections - block_0_1_attempt = mk_block(slot=1, parent=block_0_0.id(), coin=coin_new) + block_0_1_attempt = mk_block(slot=1, parent=block_0_0, coin=coin_new) follower.on_block(block_0_1_attempt) assert follower.tip() == block_0_0 # whereas the evolved coin from genesis can be spent immediately - block_0_1 = mk_block(slot=1, parent=block_0_0.id(), coin=coin.evolve()) + block_0_1 = mk_block(slot=1, parent=block_0_0, coin=coin.evolve()) follower.on_block(block_0_1) assert follower.tip() == block_0_1 @@ -226,7 +226,7 @@ class TestLedgerStateUpdate(TestCase): # The newly minted coin is still not eligible in the following epoch since the # stake distribution snapshot is taken at the beginning of the previous epoch - block_1_0 = mk_block(slot=20, parent=block_0_1.id(), coin=coin_new) + block_1_0 = mk_block(slot=20, parent=block_0_1, coin=coin_new) follower.on_block(block_1_0) assert follower.tip() == block_0_1 @@ -234,16 +234,12 @@ class TestLedgerStateUpdate(TestCase): # The coin is finally eligible 2 epochs after it was first minted - block_2_0 = mk_block( - slot=40, - parent=block_0_1.id(), - coin=coin_new, - ) + block_2_0 = mk_block(slot=40, parent=block_0_1, coin=coin_new) follower.on_block(block_2_0) assert follower.tip() == block_2_0 # And now the minted coin can freely use the evolved coin for subsequent blocks - block_2_1 = mk_block(slot=40, parent=block_2_0.id(), coin=coin_new.evolve()) + block_2_1 = mk_block(slot=40, parent=block_2_0, coin=coin_new.evolve()) follower.on_block(block_2_1) assert follower.tip() == block_2_1 @@ -259,7 +255,7 @@ class TestLedgerStateUpdate(TestCase): coin_new = coin.evolve() coin_new_new = coin_new.evolve() - block_0_1 = mk_block(slot=1, parent=block_0_0.id(), coin=coin_new_new) + block_0_1 = mk_block(slot=1, parent=block_0_0, coin=coin_new_new) follower.on_block(block_0_1) # the coin evolved twice should not be accepted as it is not in the lead commitments assert follower.tip() == block_0_0 @@ -272,7 +268,7 @@ class TestLedgerStateUpdate(TestCase): orphan = mk_block(parent=genesis.block, slot=0, coin=coin_orphan) block_0_1 = mk_block( slot=1, - parent=block_0_0.id(), + parent=block_0_0, coin=coin_orphan.evolve(), orphaned_proofs=[orphan], ) diff --git a/cryptarchia/test_orphaned_proofs.py b/cryptarchia/test_orphaned_proofs.py index 41a1ccb..affb6a6 100644 --- a/cryptarchia/test_orphaned_proofs.py +++ b/cryptarchia/test_orphaned_proofs.py @@ -36,8 +36,8 @@ class TestOrphanedProofs(TestCase): # b1, c_a = mk_block(genesis.block, 1, c_a), c_a.evolve() - b2, c_a = mk_block(b1.id(), 2, c_a), c_a.evolve() - b3, c_b = mk_block(b1.id(), 2, c_b), c_b.evolve() + b2, c_a = mk_block(b1, 2, c_a), c_a.evolve() + b3, c_b = mk_block(b1, 2, c_b), c_b.evolve() for b in [b1, b2, b3]: follower.on_block(b) @@ -54,7 +54,7 @@ class TestOrphanedProofs(TestCase): # \ / # b3 # - b4, c_a = mk_block(b2.id(), 3, c_a, orphaned_proofs=[b3]), c_a.evolve() + b4, c_a = mk_block(b2, 3, c_a, orphaned_proofs=[b3]), c_a.evolve() follower.on_block(b4) assert follower.tip() == b4 @@ -79,11 +79,11 @@ class TestOrphanedProofs(TestCase): b1, c_a = mk_block(genesis.block, 1, c_a), c_a.evolve() - b2, c_a = mk_block(b1.id(), 2, c_a), c_a.evolve() - b3, c_a = mk_block(b2.id(), 3, c_a), c_a.evolve() + b2, c_a = mk_block(b1, 2, c_a), c_a.evolve() + b3, c_a = mk_block(b2, 3, c_a), c_a.evolve() - b4, c_b = mk_block(b1.id(), 2, c_b), c_b.evolve() - b5, c_b = mk_block(b4.id(), 3, c_b), c_b.evolve() + b4, c_b = mk_block(b1, 2, c_b), c_b.evolve() + b5, c_b = mk_block(b4, 3, c_b), c_b.evolve() for b in [b1, b2, b3, b4, b5]: follower.on_block(b) @@ -100,7 +100,7 @@ class TestOrphanedProofs(TestCase): # \ / / # b4 - b5 - b6, c_a = mk_block(b3.id(), 4, c_a, orphaned_proofs=[b4, b5]), c_a.evolve() + b6, c_a = mk_block(b3, 4, c_a, orphaned_proofs=[b4, b5]), c_a.evolve() follower.on_block(b6) assert follower.tip() == b6 @@ -123,13 +123,13 @@ class TestOrphanedProofs(TestCase): b1, c_a = mk_block(genesis.block, 1, c_a), c_a.evolve() - b2, c_a = mk_block(b1.id(), 2, c_a), c_a.evolve() - b3, c_a = mk_block(b2.id(), 3, c_a), c_a.evolve() - b4, c_a = mk_block(b3.id(), 4, c_a), c_a.evolve() + b2, c_a = mk_block(b1, 2, c_a), c_a.evolve() + b3, c_a = mk_block(b2, 3, c_a), c_a.evolve() + b4, c_a = mk_block(b3, 4, c_a), c_a.evolve() - b5, c_b = mk_block(b1.id(), 2, c_b), c_b.evolve() - b6, c_b = mk_block(b5.id(), 3, c_b), c_b.evolve() - b7, c_b = mk_block(b6.id(), 4, c_b), c_b.evolve() + b5, c_b = mk_block(b1, 2, c_b), c_b.evolve() + b6, c_b = mk_block(b5, 3, c_b), c_b.evolve() + b7, c_b = mk_block(b6, 4, c_b), c_b.evolve() for b in [b1, b2, b3, b4, b5, b6, b7]: follower.on_block(b) @@ -149,7 +149,7 @@ class TestOrphanedProofs(TestCase): # Earlier implementations of orphan proof validation failed to # validate b7 as an orphan here. - b8, c_a = mk_block(b4.id(), 5, c_a, orphaned_proofs=[b5, b6, b7]), c_a.evolve() + b8, c_a = mk_block(b4, 5, c_a, orphaned_proofs=[b5, b6, b7]), c_a.evolve() follower.on_block(b8) assert follower.tip() == b8 @@ -187,13 +187,13 @@ class TestOrphanedProofs(TestCase): b1, c_a = mk_block(genesis.block, 1, c_a), c_a.evolve() - b2, c_a = mk_block(b1.id(), 2, c_a), c_a.evolve() - b3, c_a = mk_block(b2.id(), 3, c_a), c_a.evolve() + b2, c_a = mk_block(b1, 2, c_a), c_a.evolve() + b3, c_a = mk_block(b2, 3, c_a), c_a.evolve() - b4, c_b = mk_block(b1.id(), 2, c_b), c_b.evolve() - b5, c_b = mk_block(b4.id(), 3, c_b), c_b.evolve() + b4, c_b = mk_block(b1, 2, c_b), c_b.evolve() + b5, c_b = mk_block(b4, 3, c_b), c_b.evolve() - b6, c_c = mk_block(b4.id(), 3, c_c), c_c.evolve() + b6, c_c = mk_block(b4, 3, c_c), c_c.evolve() for b in [b1, b2, b3, b4, b5, b6]: follower.on_block(b) @@ -202,7 +202,7 @@ class TestOrphanedProofs(TestCase): assert [f.tip() for f in follower.forks] == [b5, b6] assert follower.unimported_orphans(follower.tip_id()) == [b4, b5, b6] - b7, c_a = mk_block(b3.id(), 4, c_a, orphaned_proofs=[b4, b5, b6]), c_a.evolve() + b7, c_a = mk_block(b3, 4, c_a, orphaned_proofs=[b4, b5, b6]), c_a.evolve() follower.on_block(b7) assert follower.tip() == b7 @@ -235,16 +235,16 @@ class TestOrphanedProofs(TestCase): follower = Follower(genesis, config) b1, c_a = mk_block(genesis.block, 1, c_a), c_a.evolve() - b2, c_a = mk_block(b1.id(), 2, c_a), c_a.evolve() - b3, c_a = mk_block(b2.id(), 3, c_a), c_a.evolve() + b2, c_a = mk_block(b1, 2, c_a), c_a.evolve() + b3, c_a = mk_block(b2, 3, c_a), c_a.evolve() - b4, c_b = mk_block(b3.id(), 4, c_b), c_b.evolve() - b5, c_a = mk_block(b3.id(), 4, c_a), c_a.evolve() + b4, c_b = mk_block(b3, 4, c_b), c_b.evolve() + b5, c_a = mk_block(b3, 4, c_a), c_a.evolve() - b6, c_b = mk_block(b4.id(), 5, c_b, orphaned_proofs=[b5]), c_b.evolve() - b7, c_a = mk_block(b4.id(), 5, c_a, orphaned_proofs=[b5]), c_a.evolve() + b6, c_b = mk_block(b4, 5, c_b, orphaned_proofs=[b5]), c_b.evolve() + b7, c_a = mk_block(b4, 5, c_a, orphaned_proofs=[b5]), c_a.evolve() - b8, c_b = mk_block(b6.id(), 6, c_b, orphaned_proofs=[b7]), c_b.evolve() + b8, c_b = mk_block(b6, 6, c_b, orphaned_proofs=[b7]), c_b.evolve() for b in [b1, b2, b3, b4, b5]: follower.on_block(b) diff --git a/cryptarchia/test_stake_relativization.py b/cryptarchia/test_stake_relativization.py index 6cc3f36..91a0e3a 100644 --- a/cryptarchia/test_stake_relativization.py +++ b/cryptarchia/test_stake_relativization.py @@ -29,13 +29,13 @@ class TestStakeRelativization(TestCase): # on fork, tip state is not updated orphan = mk_block(genesis.block, slot=1, coin=c_b) follower.on_block(orphan) - assert follower.tip_state().block == b1.id() + assert follower.tip_state().block == b1 assert follower.tip_state().leader_count == 1 # after orphan is adopted, leader count should jumpy by 2 (each orphan counts as a leader) - b2 = mk_block(b1.id(), slot=2, coin=c_a.evolve(), orphaned_proofs=[orphan]) + b2 = mk_block(b1, slot=2, coin=c_a.evolve(), orphaned_proofs=[orphan]) follower.on_block(b2) - assert follower.tip_state().block == b2.id() + assert follower.tip_state().block == b2 assert follower.tip_state().leader_count == 3 def test_inference_on_empty_genesis_epoch(self):