cryptarchia/ghost: common_prefix_depth returns depth of both chains

This commit is contained in:
David Rusu 2024-10-31 02:10:19 +04:00
parent 893ffb5915
commit 37eb040a99
2 changed files with 40 additions and 35 deletions

View File

@ -2,7 +2,7 @@ from typing import TypeAlias, List, Optional, Dict
from hashlib import sha256, blake2b
from math import floor
from copy import deepcopy
from itertools import chain
import itertools
import functools
from dataclasses import dataclass, field, replace
import logging
@ -347,7 +347,7 @@ class LedgerState:
self.nonce = h.digest()
self.block = block
for proof in chain(block.orphaned_proofs, [block]):
for proof in itertools.chain(block.orphaned_proofs, [block]):
self.apply_leader_proof(proof.leader_proof)
def apply_leader_proof(self, proof: MockLeaderProof):
@ -722,7 +722,7 @@ class Leader:
def common_prefix_depth(
local_chain: Id, fork: Id, states: Dict[Id, LedgerState]
) -> int:
) -> (int, int):
local_block = local_chain
fork_block = fork
@ -747,7 +747,7 @@ def common_prefix_depth(
if local_block in seen:
# we had seen this block from the fork chain
return depth
return depth, seen[local_block]
if local_block in states:
seen[local_block] = depth
@ -756,7 +756,7 @@ def common_prefix_depth(
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]
return seen[fork_block], depth
if fork_block in states:
seen[fork_block] = depth
@ -786,21 +786,26 @@ def maxvalid_bg(
k: int,
s: int,
) -> Chain:
# assert type(local_chain) == Id
# assert all(type(f) == Id for f in forks)
cmax = local_chain
for chain in forks:
m = common_prefix_depth(cmax.tip_id(), chain.tip_id(), states)
if m <= k:
for fork in forks:
local_depth, fork_depth = common_prefix_depth(
cmax.tip_id(), fork.tip_id(), states
)
if local_depth <= k:
# Classic longest chain rule with parameter k
if cmax.length() < chain.length():
cmax = chain
if local_depth < fork_depth:
cmax = fork
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 = Slot(cmax.blocks[-m].slot.absolute_slot + s)
forking_slot = Slot(cmax.blocks[-local_depth].slot.absolute_slot + s)
cmax_density = chain_density(cmax, forking_slot)
candidate_density = chain_density(chain, forking_slot)
candidate_density = chain_density(fork, forking_slot)
if cmax_density < candidate_density:
cmax = chain
cmax = fork
return cmax

View File

@ -44,28 +44,28 @@ class TestForkChoice(TestCase):
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
assert (d := common_prefix_depth(b0.id(), b0.id(), states)) == (0, 0), d
assert (d := common_prefix_depth(b1.id(), b0.id(), states)) == (1, 0), d
assert (d := common_prefix_depth(b0.id(), b1.id(), states)) == (0, 1), d
assert (d := common_prefix_depth(b1.id(), b1.id(), states)) == (0, 0), d
assert (d := common_prefix_depth(b2.id(), b0.id(), states)) == (2, 0), d
assert (d := common_prefix_depth(b0.id(), b2.id(), states)) == (0, 2), d
assert (d := common_prefix_depth(b3.id(), b0.id(), states)) == (3, 0), d
assert (d := common_prefix_depth(b0.id(), b3.id(), states)) == (0, 3), d
assert (d := common_prefix_depth(b1.id(), b4.id(), states)) == (1, 1), d
assert (d := common_prefix_depth(b4.id(), b1.id(), states)) == (1, 1), d
assert (d := common_prefix_depth(b1.id(), b5.id(), states)) == (1, 2), d
assert (d := common_prefix_depth(b5.id(), b1.id(), states)) == (2, 1), d
assert (d := common_prefix_depth(b2.id(), b5.id(), states)) == (2, 2), d
assert (d := common_prefix_depth(b5.id(), b2.id(), states)) == (2, 2), d
assert (d := common_prefix_depth(b3.id(), b5.id(), states)) == (3, 2), d
assert (d := common_prefix_depth(b5.id(), b3.id(), states)) == (2, 3), d
assert (d := common_prefix_depth(b3.id(), b6.id(), states)) == (1, 1), d
assert (d := common_prefix_depth(b6.id(), b3.id(), states)) == (1, 1), d
assert (d := common_prefix_depth(b3.id(), b7.id(), states)) == (1, 2), d
assert (d := common_prefix_depth(b7.id(), b3.id(), states)) == (2, 1), d
assert (d := common_prefix_depth(b5.id(), b7.id(), states)) == (2, 4), d
assert (d := common_prefix_depth(b7.id(), b5.id(), states)) == (4, 2), d
def test_fork_choice_long_sparse_chain(self):
# The longest chain is not dense after the fork