From 989e92635e182b96c481946011222c8c90624887 Mon Sep 17 00:00:00 2001 From: Giacomo Pasini Date: Mon, 29 Jan 2024 11:13:59 +0100 Subject: [PATCH] add fork choice rule --- cryptarchia/cryptarchia.py | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/cryptarchia/cryptarchia.py b/cryptarchia/cryptarchia.py index 5d94d05..0db6fa4 100644 --- a/cryptarchia/cryptarchia.py +++ b/cryptarchia/cryptarchia.py @@ -124,8 +124,8 @@ class Follower: # in that case, just ignore the block # Evaluate the fork choice rule and return the block header of the block that should be the head of the chain - def fork_choice(local_chain: Chain, forks: List[Chain]) -> BlockHeader: - pass + def fork_choice(local_chain: Chain, forks: List[Chain]) -> Chain: + return maxvalid_bg(local_chain, forks) def tip(self) -> BlockHeader: return self.fork_choice() @@ -218,5 +218,35 @@ class Leader: return BlockHeader(parent=parent.id(), slot=slot) +def common_prefix_len(a: Chain, b: Chain) -> int: + for i, (x, y) in enumerate(zip(a.blocks, b.blocks)): + if x.id() != y.id(): + return i + return min(len(a.blocks), len(b.blocks)) + + +def chain_density(chain: Chain, slot: Slot) -> int: + return count(block for block in chain.blocks if block.slot < slot) + +def maxvalid_bg(local_chain: Chain, forks: List[Chain], 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 + if m <= k: + # Classic longest chain rule with parameter k + if cmax.length() < chain.length(): + cmax = chain + else: + # The chain is forking too much, we need to pay a bit more attention + # In particular, select the chain that is the densest after the fork + forking_slot = cmax.blocks[lowest_common_ancestor].slot + cmax_density = chain_density(cmax, forking_slot + s) + chain_density = chain_density(chain, forking_slot + s) + if cmax_density < chain_density: + cmax = chain + + return cmax + if __name__ == "__main__": pass