From b99e31f28b3e233fdff1026380b198093c48a036 Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Mon, 15 May 2023 14:01:21 -0700 Subject: [PATCH 01/20] update confirmation score calc --- fork_choice/confirmation-rule.md | 51 +++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index 24b3457d66..16dd1d913d 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -266,15 +266,46 @@ prevent a block from being confirmed. ### Helper Functions + +##### `get_committee_weight` + +```python +def get_committee_weight(store: Store, start_slot: Slot, end_slot: Slot) -> Gwei: + """Returns the total weight of committees between ``start_slot`` and ``end_slot`` (inclusive of both). + Uses the justified state to compute committee weights. + """ + + justified_state = store.checkpoint_states[store.justified_checkpoint] + total_active_balance = get_total_active_balance(state) + + # If an entire epoch is covered by the range, return the total active balance + start_epoch = compute_epoch_at_slot(start_slot) + end_epoch = compute_epoch_at_slot(end_slot) + if end_epoch > start_epoch + 1: + return total_active_balance + + # A range that does not span any full epoch needs pro-rata calculation + committee_weight = get_total_active_balance(state) // SLOTS_PER_EPOCH + num_committees = 0 + # First, calculate the weight from the end epoch + epoch_boundary_slot = compute_start_slot_at_epoch(end_epoch) + num_committees += end_slot - epoch_boundary_slot + 1 + # Next, calculate the weight from the previous epoch + # Each committee from the previous epoch only contributes a pro-rated weight + # NOTE: using float arithmetic here. is that allowed here in spec? probably yes, since this is not consensus code. + multiplier = (SLOTS_PER_EPOCH - end_slot - 1) / SLOTS_PER_EPOCH + num_committees += (epoch_boundary_slot - start_slot) * multiplier + return num_committees * committee_weight +``` + + ```python -def get_score_for_one_confirmation(store: Store, block_root: Root, current_slot: Slot) -> int: +def get_score_for_one_confirmation(store: Store, block_root: Root) -> int: + current_slot = get_current_slot(store) block = store.blocks[block_root] - justified_checkpoint_state = store.checkpoint_states[store.justified_checkpoint] parent_block = store.blocks[block.parent_root] support = int(get_weight(store, block_root)) - maximum_support = int(get_committee_weight_between_slots(justified_checkpoint_state, Slot(parent_block.slot + 1), current_slot)) - - committee_weight = get_total_active_balance(justified_checkpoint_state) // SLOTS_PER_EPOCH + maximum_support = int(get_committee_weight(parent_block.slot + 1, current_slot)) proposer_score = int((committee_weight * PROPOSER_SCORE_BOOST) // 100) # We need to return a value confirmation_byzantine_threshold such that the following inequality is true @@ -284,7 +315,8 @@ def get_score_for_one_confirmation(store: Store, block_root: Root, current_slot: ``` ```python -def get_score_for_LMD_confirmation(store: Store, block_root: Root, current_slot: Slot) -> int: +def get_score_for_LMD_confirmation(store: Store, block_root: Root) -> int: + current_slot = get_current_slot(store) if block_root == store.finalized_checkpoint.root: return 100 // 3 else: @@ -300,11 +332,8 @@ def get_score_for_LMD_confirmation(store: Store, block_root: Root, current_slot: ``` ```python -def get_score_for_FFG_confirmation( - store: Store, - block_root: Root, - current_slot: Slot -) -> int: +def get_score_for_FFG_confirmation(store: Store, block_root: Root) -> int: + current_slot = get_current_slot(store) block = store.blocks[block_root] assert get_current_epoch(store) == compute_epoch_at_slot(block.slot) From 0117eedd9122c54ffb17d2d7f2750c9ab362d64f Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Mon, 15 May 2023 14:07:24 -0700 Subject: [PATCH 02/20] remove current_slot parameter --- fork_choice/confirmation-rule.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index 16dd1d913d..1fd0b304b9 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -67,7 +67,8 @@ def get_committee_weight_between_slots(state: BeaconState, from_slot: Slot, to_s ``` ```python -def is_one_confirmed(store: Store, confirmation_byzantine_threshold: int, block_root: Root, current_slot: Slot) -> bool: +def is_one_confirmed(store: Store, confirmation_byzantine_threshold: int, block_root: Root) -> bool: + current_slot = get_current_slot(store) block = store.blocks[block_root] justified_checkpoint_state = store.checkpoint_states[store.justified_checkpoint] parent_block = store.blocks[block.parent_root] @@ -82,7 +83,8 @@ def is_one_confirmed(store: Store, confirmation_byzantine_threshold: int, block_ ``` ```python -def is_LMD_confirmed(store: Store, confirmation_byzantine_threshold: int, block_root: Root, current_slot: Slot) -> bool: +def is_LMD_confirmed(store: Store, confirmation_byzantine_threshold: int, block_root: Root) -> bool: + current_slot = get_current_slot(store) if block_root == store.finalized_checkpoint.root: return True else: @@ -92,8 +94,8 @@ def is_LMD_confirmed(store: Store, confirmation_byzantine_threshold: int, block_ return False else: return ( - is_one_confirmed(store, confirmation_byzantine_threshold, block_root, current_slot) and - is_LMD_confirmed(store, confirmation_byzantine_threshold, block.parent_root, current_slot) + is_one_confirmed(store, confirmation_byzantine_threshold, block_root) and + is_LMD_confirmed(store, confirmation_byzantine_threshold, block.parent_root) ) ``` @@ -151,8 +153,8 @@ def is_ffg_confirmed( confirmation_byzantine_threshold: int, confirmation_slashing_threshold: int, block_root: Root, - current_slot: Slot ) -> bool: + current_slot = get_current_slot(store) block = store.blocks[block_root] assert get_current_epoch(store) == compute_epoch_at_slot(block.slot) @@ -202,8 +204,8 @@ def is_confirmed( assert compute_epoch_at_slot(block.slot) == current_epoch return ( - is_LMD_confirmed(store, confirmation_byzantine_threshold, block_root, current_slot) and - is_ffg_confirmed(store, confirmation_byzantine_threshold, confirmation_slashing_threshold, block_root, current_slot) and + is_LMD_confirmed(store, confirmation_byzantine_threshold, block_root) and + is_ffg_confirmed(store, confirmation_byzantine_threshold, confirmation_slashing_threshold, block_root) and block_state.current_justified_checkpoint.epoch + 1 == current_epoch ) ``` @@ -326,8 +328,8 @@ def get_score_for_LMD_confirmation(store: Store, block_root: Root) -> int: return -1 else: return min( - get_score_for_one_confirmation(store, block_root, current_slot), - get_score_for_LMD_confirmation(store, block.parent_root, current_slot) + get_score_for_one_confirmation(store, block_root), + get_score_for_LMD_confirmation(store, block.parent_root) ) ``` @@ -405,7 +407,6 @@ def get_confirmation_score( otherwise it returns the maximum percentage of adversary weight that is admissible in order to consider `block_root` confirmed. """ - current_slot = get_current_slot(store) current_epoch = get_current_epoch(store) block = store.blocks[block_root] @@ -414,8 +415,8 @@ def get_confirmation_score( assert compute_epoch_at_slot(block.slot) == current_epoch return min( - get_score_for_LMD_confirmation(store, block_root, current_slot), - get_score_for_FFG_confirmation(store, block_root, current_slot) + get_score_for_LMD_confirmation(store, block_root), + get_score_for_FFG_confirmation(store, block_root) ) ``` From 446baeb93e2a9ce181e7002f6cf23bf0427a2787 Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Mon, 15 May 2023 14:19:53 -0700 Subject: [PATCH 03/20] use simplified comm weight calc for safe block --- fork_choice/confirmation-rule.md | 80 +++++++++++--------------------- 1 file changed, 27 insertions(+), 53 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index 1fd0b304b9..71f00668ff 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -42,40 +42,46 @@ This section specifies an algorithm to determine whether a block is confirmed. T ### Helper Functions -```python -def get_full_committee_at_slot(state: BeaconState, slot: Slot) -> Sequence[ValidatorIndex]: - epoch = compute_epoch_at_slot(slot) - validator_indexes = [] # type: List[ValidatorIndex] - for i in get_committee_count_per_slot(state, epoch): - validator_indexes.append(get_beacon_committee(state, slot, i)) +##### `get_committee_weight_between_slots` - return validator_indexes -``` ```python -def get_committee_weight_between_slots(state: BeaconState, from_slot: Slot, to_slot: Slot) -> Gwei: - validator_index_set = set() - for slot in range(from_slot, to_slot + 1): - validator_index_set.add(set(get_full_committee_at_slot(state, Slot(slot)))) +def get_committee_weight_between_slots(store: Store, start_slot: Slot, end_slot: Slot) -> Gwei: + """Returns the total weight of committees between ``start_slot`` and ``end_slot`` (inclusive of both). + Uses the justified state to compute committee weights. + """ - total_weight = Gwei(0) + justified_state = store.checkpoint_states[store.justified_checkpoint] + total_active_balance = get_total_active_balance(state) - for validator_index in validator_index_set: - total_weight += state.validators[validator_index].effective_balance + # If an entire epoch is covered by the range, return the total active balance + start_epoch = compute_epoch_at_slot(start_slot) + end_epoch = compute_epoch_at_slot(end_slot) + if end_epoch > start_epoch + 1: + return total_active_balance - return total_weight + # A range that does not span any full epoch needs pro-rata calculation + committee_weight = get_total_active_balance(state) // SLOTS_PER_EPOCH + num_committees = 0 + # First, calculate the weight from the end epoch + epoch_boundary_slot = compute_start_slot_at_epoch(end_epoch) + num_committees += end_slot - epoch_boundary_slot + 1 + # Next, calculate the weight from the previous epoch + # Each committee from the previous epoch only contributes a pro-rated weight + # NOTE: using float arithmetic here. is that allowed here in spec? probably yes, since this is not consensus code. + multiplier = (SLOTS_PER_EPOCH - end_slot - 1) / SLOTS_PER_EPOCH + num_committees += (epoch_boundary_slot - start_slot) * multiplier + return num_committees * committee_weight ``` + ```python def is_one_confirmed(store: Store, confirmation_byzantine_threshold: int, block_root: Root) -> bool: current_slot = get_current_slot(store) block = store.blocks[block_root] - justified_checkpoint_state = store.checkpoint_states[store.justified_checkpoint] parent_block = store.blocks[block.parent_root] support = int(get_weight(store, block_root)) - maximum_support = int(get_committee_weight_between_slots(justified_checkpoint_state, Slot(parent_block.slot + 1), current_slot)) - - committee_weight = get_total_active_balance(justified_checkpoint_state) // SLOTS_PER_EPOCH + maximum_support = int(get_committee_weight_between_slots(Slot(parent_block.slot + 1), current_slot)) proposer_score = int((committee_weight * PROPOSER_SCORE_BOOST) // 100) # support / maximum_support > 1/2 * (1 + proposer_score / maximum_support) + confirmation_byzantine_threshold/100 => @@ -103,7 +109,7 @@ def is_LMD_confirmed(store: Store, confirmation_byzantine_threshold: int, block_ def get_future_vote_weight_in_epoch(state: BeaconState, current_slot: Slot) -> Gwei: # Returns the total weight of votes for this epoch from future committees after the current slot first_slot_next_epoch = compute_start_slot_at_epoch(Epoch(compute_epoch_at_slot(current_slot) + 1)) - return get_committee_weight_between_slots(state, Slot(current_slot + 1), Slot(first_slot_next_epoch - 1)) + return get_committee_weight_between_slots(Slot(current_slot + 1), Slot(first_slot_next_epoch - 1)) ``` @@ -269,38 +275,6 @@ prevent a block from being confirmed. ### Helper Functions -##### `get_committee_weight` - -```python -def get_committee_weight(store: Store, start_slot: Slot, end_slot: Slot) -> Gwei: - """Returns the total weight of committees between ``start_slot`` and ``end_slot`` (inclusive of both). - Uses the justified state to compute committee weights. - """ - - justified_state = store.checkpoint_states[store.justified_checkpoint] - total_active_balance = get_total_active_balance(state) - - # If an entire epoch is covered by the range, return the total active balance - start_epoch = compute_epoch_at_slot(start_slot) - end_epoch = compute_epoch_at_slot(end_slot) - if end_epoch > start_epoch + 1: - return total_active_balance - - # A range that does not span any full epoch needs pro-rata calculation - committee_weight = get_total_active_balance(state) // SLOTS_PER_EPOCH - num_committees = 0 - # First, calculate the weight from the end epoch - epoch_boundary_slot = compute_start_slot_at_epoch(end_epoch) - num_committees += end_slot - epoch_boundary_slot + 1 - # Next, calculate the weight from the previous epoch - # Each committee from the previous epoch only contributes a pro-rated weight - # NOTE: using float arithmetic here. is that allowed here in spec? probably yes, since this is not consensus code. - multiplier = (SLOTS_PER_EPOCH - end_slot - 1) / SLOTS_PER_EPOCH - num_committees += (epoch_boundary_slot - start_slot) * multiplier - return num_committees * committee_weight -``` - - ```python def get_score_for_one_confirmation(store: Store, block_root: Root) -> int: current_slot = get_current_slot(store) From 673b4f4bd534846514d3493cd6e8a1d7d81288be Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Mon, 15 May 2023 16:15:07 -0700 Subject: [PATCH 04/20] add function for computing proposer score --- fork_choice/confirmation-rule.md | 4 ++-- specs/phase0/fork-choice.md | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index 71f00668ff..c058f10b0d 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -82,7 +82,7 @@ def is_one_confirmed(store: Store, confirmation_byzantine_threshold: int, block_ parent_block = store.blocks[block.parent_root] support = int(get_weight(store, block_root)) maximum_support = int(get_committee_weight_between_slots(Slot(parent_block.slot + 1), current_slot)) - proposer_score = int((committee_weight * PROPOSER_SCORE_BOOST) // 100) + proposer_score = int(get_proposer_score(store)) # support / maximum_support > 1/2 * (1 + proposer_score / maximum_support) + confirmation_byzantine_threshold/100 => return 100 * support > 50 * maximum_support + 50 * proposer_score + confirmation_byzantine_threshold * maximum_support @@ -282,7 +282,7 @@ def get_score_for_one_confirmation(store: Store, block_root: Root) -> int: parent_block = store.blocks[block.parent_root] support = int(get_weight(store, block_root)) maximum_support = int(get_committee_weight(parent_block.slot + 1, current_slot)) - proposer_score = int((committee_weight * PROPOSER_SCORE_BOOST) // 100) + proposer_score = int(get_proposer_score(store)) # We need to return a value confirmation_byzantine_threshold such that the following inequality is true # 100 * support > 50 * maximum_support + 50 * proposer_score + confirmation_byzantine_threshold * maximum_support diff --git a/specs/phase0/fork-choice.md b/specs/phase0/fork-choice.md index 9616411891..d0f064f026 100644 --- a/specs/phase0/fork-choice.md +++ b/specs/phase0/fork-choice.md @@ -209,6 +209,14 @@ def get_checkpoint_block(store: Store, root: Root, epoch: Epoch) -> Root: return get_ancestor(store, root, epoch_first_slot) ``` +#### `get_proposer_score_boost` + +```python +def get_proposer_score(store: Store) -> Gwei: + committee_weight = get_total_active_balance(state) // SLOTS_PER_EPOCH + return (committee_weight * PROPOSER_SCORE_BOOST) // 100 +``` + #### `get_weight` ```python @@ -232,8 +240,7 @@ def get_weight(store: Store, root: Root) -> Gwei: proposer_score = Gwei(0) # Boost is applied if ``root`` is an ancestor of ``proposer_boost_root`` if get_ancestor(store, store.proposer_boost_root, store.blocks[root].slot) == root: - committee_weight = get_total_active_balance(state) // SLOTS_PER_EPOCH - proposer_score = (committee_weight * PROPOSER_SCORE_BOOST) // 100 + proposer_score = get_proposer_score(store) return attestation_score + proposer_score ``` @@ -526,7 +533,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: block.parent_root, store.finalized_checkpoint.epoch, ) - assert store.finalized_checkpoint.root == finalized_checkpoint_block + assert store.finalized_checkpoint.root == finalized_checkpoint_block # Check the block is valid and compute the post-state state = pre_state.copy() From 38ace65b0ca3fd3d266d7f3b8c14a64801e1a611 Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Mon, 15 May 2023 16:15:42 -0700 Subject: [PATCH 05/20] remove old code --- fork_choice/confirmation-rule.md | 50 -------------------------------- 1 file changed, 50 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index c058f10b0d..edb2acab51 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -393,53 +393,3 @@ def get_confirmation_score( get_score_for_FFG_confirmation(store, block_root) ) ``` - - -## Old functions kept for reference - -```python -def get_descendants_in_current_epoch(store: Store, block_root: Root) -> Set[Root]: - block = store.blocks[block_root] - children = [ - root for root in store.blocks.keys() - if store.blocks[root].parent_root == block_root - ] - - descendants = set() - current_epoch = compute_epoch_at_slot(get_current_slot(store)) - - if compute_epoch_at_slot(block.slot) == current_epoch: - descendants.add(block_root) - - if any(children): - for child in children: - descendants.update(get_descendants_in_current_epoch(store, child)) - - return descendants -``` - -```python -def get_ffg_support_using_latest_messages(store: Store, block_root: Root) -> Gwei: - block = store.blocks[block_root] - assert get_current_epoch(store) == compute_epoch_at_slot(block.slot) - - current_epoch = get_current_epoch(store) - - block_checkpoint_root = get_block_root(store.block_states[block_root], current_epoch) - - block_checkpoint_state = store.block_states[block_checkpoint_root] - - active_indices = get_active_validator_indices(block_checkpoint_state, current_epoch) - - return Gwei(sum( - block_checkpoint_state.validators[i].effective_balance for i in active_indices - if get_checkpoint_block(store, store.latest_messages[i].root, current_epoch) == block_checkpoint_root - )) -``` - -```python -def get_current_vote_weight_in_epoch(state: BeaconState, current_slot: Slot) -> Gwei: - # Returns the total weight of votes for this epoch from committees up til the current slot - first_slot_current_epoch = compute_start_slot_at_epoch(compute_epoch_at_slot(current_slot)) - return get_committee_weight_between_slots(state, first_slot_current_epoch, current_slot) -``` From ca5006d7c5bdeefa7fe67664d5ff734ec339b8de Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Mon, 15 May 2023 16:22:06 -0700 Subject: [PATCH 06/20] use get_checkpoint_block --- fork_choice/confirmation-rule.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index edb2acab51..c9ef759be6 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -165,9 +165,7 @@ def is_ffg_confirmed( assert get_current_epoch(store) == compute_epoch_at_slot(block.slot) current_epoch = get_current_epoch(store) - - # The following could be replaced by get_checkpoint_block once merged in - block_checkpoint_root = get_block_root(store.block_states[block_root], current_epoch) + block_checkpoint_root = get_checkpoint_block(store, block_root, current_epoch) block_checkpoint_state = store.block_states[block_checkpoint_root] total_active_balance = int(get_total_active_balance(block_checkpoint_state)) @@ -314,9 +312,7 @@ def get_score_for_FFG_confirmation(store: Store, block_root: Root) -> int: assert get_current_epoch(store) == compute_epoch_at_slot(block.slot) current_epoch = get_current_epoch(store) - - # The following could be replaced by get_checkpoint_block once merged in - block_checkpoint_root = get_block_root(store.block_states[block_root], current_epoch) + block_checkpoint_root = get_checkpoint_block(store, block_root, current_epoch) block_checkpoint_state = store.block_states[block_checkpoint_root] total_active_balance = int(get_total_active_balance(block_checkpoint_state)) From 9475b621973a1bd6c479ad2789f104cfeaf0ea05 Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Mon, 15 May 2023 19:30:25 -0700 Subject: [PATCH 07/20] readability refactor --- fork_choice/confirmation-rule.md | 109 +++++++++++++++---------------- 1 file changed, 52 insertions(+), 57 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index c9ef759be6..9140ccd5ae 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -50,9 +50,8 @@ def get_committee_weight_between_slots(store: Store, start_slot: Slot, end_slot: """Returns the total weight of committees between ``start_slot`` and ``end_slot`` (inclusive of both). Uses the justified state to compute committee weights. """ - justified_state = store.checkpoint_states[store.justified_checkpoint] - total_active_balance = get_total_active_balance(state) + total_active_balance = get_total_active_balance(justified_state) # If an entire epoch is covered by the range, return the total active balance start_epoch = compute_epoch_at_slot(start_slot) @@ -61,7 +60,7 @@ def get_committee_weight_between_slots(store: Store, start_slot: Slot, end_slot: return total_active_balance # A range that does not span any full epoch needs pro-rata calculation - committee_weight = get_total_active_balance(state) // SLOTS_PER_EPOCH + committee_weight = get_total_active_balance(justified_state) // SLOTS_PER_EPOCH num_committees = 0 # First, calculate the weight from the end epoch epoch_boundary_slot = compute_start_slot_at_epoch(end_epoch) @@ -90,15 +89,16 @@ def is_one_confirmed(store: Store, confirmation_byzantine_threshold: int, block_ ```python def is_LMD_confirmed(store: Store, confirmation_byzantine_threshold: int, block_root: Root) -> bool: - current_slot = get_current_slot(store) if block_root == store.finalized_checkpoint.root: return True else: block = store.blocks[block_root] finalized_block = store.blocks[store.finalized_checkpoint.root] if block.slot <= finalized_block.slot: + # This block is not in the finalized chain. return False else: + # Check is_one_confirmed for this block and is_LMD_confirmed for the preceding chain. return ( is_one_confirmed(store, confirmation_byzantine_threshold, block_root) and is_LMD_confirmed(store, confirmation_byzantine_threshold, block.parent_root) @@ -106,7 +106,7 @@ def is_LMD_confirmed(store: Store, confirmation_byzantine_threshold: int, block_ ``` ```python -def get_future_vote_weight_in_epoch(state: BeaconState, current_slot: Slot) -> Gwei: +def get_remaining_weight_in_epoch(state: BeaconState, current_slot: Slot) -> Gwei: # Returns the total weight of votes for this epoch from future committees after the current slot first_slot_next_epoch = compute_start_slot_at_epoch(Epoch(compute_epoch_at_slot(current_slot) + 1)) return get_committee_weight_between_slots(Slot(current_slot + 1), Slot(first_slot_next_epoch - 1)) @@ -121,10 +121,11 @@ def get_leaf_block_roots(store: Store, block_root: Root) -> Set[Root]: ] if any(children): + # Get leaves of all children and add to the set. leaves = set().union(*[get_leaf_block_roots(store, child) for child in children]) - return leaves else: + # This block is a leaf. return set(block_root) ``` @@ -136,20 +137,15 @@ def get_ffg_support(store: Store, block_root: Root) -> Gwei: block = store.blocks[block_root] assert get_current_epoch(store) == compute_epoch_at_slot(block.slot) - current_epoch = get_current_epoch(store) - leave_roots = get_leaf_block_roots(store, block_root) - # current_epoch_attestations contains only attestations with source matching block.current_justified_checkpoint attestations_in_leaves: Set[PendingAttestation] = set().union(*[store.block_states[root].current_epoch_attestations for root in leave_roots]) - block_checkpoint_root = get_checkpoint_block(store, block_root, current_epoch) - - attestations_in_leaves_for_block_checkpoint = {a for a in attestations_in_leaves if a.data.target.root == block_checkpoint_root} - - block_checkpoint_state = store.block_states[block_checkpoint_root] - - return get_attesting_balance(block_checkpoint_state, list(attestations_in_leaves_for_block_checkpoint)) + current_epoch = get_current_epoch(store) + checkpoint_root = get_checkpoint_block(store, block_root, current_epoch) + support_for_checkpoint = {a for a in attestations_in_leaves if a.data.target.root == checkpoint_root} + checkpoint_state = store.block_states[checkpoint_root] + return get_attesting_balance(checkpoint_state, list(support_for_checkpoint)) ``` ```python @@ -165,28 +161,25 @@ def is_ffg_confirmed( assert get_current_epoch(store) == compute_epoch_at_slot(block.slot) current_epoch = get_current_epoch(store) - block_checkpoint_root = get_checkpoint_block(store, block_root, current_epoch) - block_checkpoint_state = store.block_states[block_checkpoint_root] - - total_active_balance = int(get_total_active_balance(block_checkpoint_state)) + checkpoint_root = get_checkpoint_block(store, block_root, current_epoch) + checkpoint_state = store.block_states[checkpoint_root] - remaining_ffg_voting_weight = int(get_future_vote_weight_in_epoch(block_checkpoint_state, current_slot)) + remaining_ffg_weight = int(get_remaining_weight_in_epoch(checkpoint_state, current_slot)) + total_active_balance = int(get_total_active_balance(checkpoint_state)) + current_weight_in_epoch = total_active_balance - remaining_ffg_weight + assert current_weight_in_epoch >= 0 - current_vote_weight_in_epoch = total_active_balance - remaining_ffg_voting_weight - assert current_vote_weight_in_epoch >= 0 - - ffg_weight_supporting_checkpoint_for_block_to_be_confirmed = int(get_ffg_support(store, block_root)) - - max_ffg_weight_the_adversary_can_subtract_from_ffg_support = int( + ffg_suport_for_checkpoint = int(get_ffg_support(store, block_root)) + max_adversarial_ffg_suport_for_checkpoint = int( min( - (current_vote_weight_in_epoch * confirmation_byzantine_threshold - 1) // 100 + 1, + (current_weight_in_epoch * confirmation_byzantine_threshold - 1) // 100 + 1, confirmation_slashing_threshold, - ffg_weight_supporting_checkpoint_for_block_to_be_confirmed + ffg_suport_for_checkpoint ) ) - # ffg_weight_supporting_checkpoint_for_block_to_be_confirmed - max_ffg_weight_the_adversary_can_subtract_from_ffg_support + (1 - confirmation_byzantine_threshold/100) * remaining_ffg_voting_weight >= 2/3 * total_active_balance => - return ffg_weight_supporting_checkpoint_for_block_to_be_confirmed * 300 + (300 - 3 * confirmation_byzantine_threshold) * remaining_ffg_voting_weight - max_ffg_weight_the_adversary_can_subtract_from_ffg_support * 300 >= 200 * total_active_balance + # ffg_suport_for_checkpoint - max_adversarial_ffg_suport_for_checkpoint + (1 - confirmation_byzantine_threshold/100) * remaining_ffg_weight >= 2/3 * total_active_balance => + return ffg_suport_for_checkpoint * 300 + (300 - 3 * confirmation_byzantine_threshold) * remaining_ffg_weight - max_adversarial_ffg_suport_for_checkpoint * 300 >= 200 * total_active_balance ``` ### Main Function @@ -198,19 +191,19 @@ def is_confirmed( confirmation_slashing_threshold: int, block_root: Root ) -> bool: - current_slot = get_current_slot(store) current_epoch = get_current_epoch(store) block = store.blocks[block_root] block_state = store.block_states[block_root] + block_justified_checkpoint = block_state.current_justified_checkpoint.epoch - # We can only apply isConfirmed to blocks created in the current epoch + # This function is only applicable to current epoch blocks assert compute_epoch_at_slot(block.slot) == current_epoch return ( is_LMD_confirmed(store, confirmation_byzantine_threshold, block_root) and is_ffg_confirmed(store, confirmation_byzantine_threshold, confirmation_slashing_threshold, block_root) and - block_state.current_justified_checkpoint.epoch + 1 == current_epoch + block_justified_checkpoint + 1 == current_epoch ) ``` @@ -297,8 +290,10 @@ def get_score_for_LMD_confirmation(store: Store, block_root: Root) -> int: block = store.blocks[block_root] finalized_block = store.blocks[store.finalized_checkpoint.root] if block.slot <= finalized_block.slot: + # This block is not in the finalized chain. return -1 else: + # Check one_confirmed score for this block and LMD_confirmed score for the preceding chain. return min( get_score_for_one_confirmation(store, block_root), get_score_for_LMD_confirmation(store, block.parent_root) @@ -312,57 +307,57 @@ def get_score_for_FFG_confirmation(store: Store, block_root: Root) -> int: assert get_current_epoch(store) == compute_epoch_at_slot(block.slot) current_epoch = get_current_epoch(store) - block_checkpoint_root = get_checkpoint_block(store, block_root, current_epoch) - block_checkpoint_state = store.block_states[block_checkpoint_root] + checkpoint_root = get_checkpoint_block(store, block_root, current_epoch) + checkpoint_state = store.block_states[checkpoint_root] - total_active_balance = int(get_total_active_balance(block_checkpoint_state)) + total_active_balance = int(get_total_active_balance(checkpoint_state)) - remaining_ffg_voting_weight = int(get_future_vote_weight_in_epoch(block_checkpoint_state, current_slot)) + remaining_ffg_weight = int(get_remaining_weight_in_epoch(checkpoint_state, current_slot)) - ffg_voting_weight_so_far = total_active_balance - remaining_ffg_voting_weight + ffg_voting_weight_so_far = total_active_balance - remaining_ffg_weight assert ffg_voting_weight_so_far >= 0 - ffg_weight_supporting_checkpoint_for_block_to_be_confirmed = int(get_ffg_support(store, block_root)) + ffg_suport_for_checkpoint = int(get_ffg_support(store, block_root)) # We assume confirmation_slashing_threshold = + infinity # So, we want to return a value confirmation_byzantine_threshold such that the following statement is true - # ffg_weight_supporting_checkpoint_for_block_to_be_confirmed - # - min(ffg_weight_supporting_checkpoint_for_block_to_be_confirmed, ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100) - # + (1 - confirmation_byzantine_threshold/100) * remaining_ffg_voting_weight + # ffg_suport_for_checkpoint + # - min(ffg_suport_for_checkpoint, ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100) + # + (1 - confirmation_byzantine_threshold/100) * remaining_ffg_weight # >= 2/3 * total_active_balance - # First, we check whether confirmation_byzantine_threshold >= ffg_weight_supporting_checkpoint_for_block_to_be_confirmed / ffg_voting_weight_so_far * 100 - # To do this we check whether in the case that confirmation_byzantine_threshold == ffg_weight_supporting_checkpoint_for_block_to_be_confirmed / ffg_voting_weight_so_far * 100 + # First, we check whether confirmation_byzantine_threshold >= ffg_suport_for_checkpoint / ffg_voting_weight_so_far * 100 + # To do this we check whether in the case that confirmation_byzantine_threshold == ffg_suport_for_checkpoint / ffg_voting_weight_so_far * 100 # our target statement is true # This amount to checking that - # (1 - ffg_weight_supporting_checkpoint_for_block_to_be_confirmed / ffg_voting_weight_so_far) * remaining_ffg_voting_weight >= 2/3 * total_active_balance + # (1 - ffg_suport_for_checkpoint / ffg_voting_weight_so_far) * remaining_ffg_weight >= 2/3 * total_active_balance # multiplying each side by 3 * ffg_voting_weight_so_far, we get (assuming ffg_voting_weight_so_far != 0): - if ffg_voting_weight_so_far > 0 and 3 * (ffg_voting_weight_so_far - ffg_weight_supporting_checkpoint_for_block_to_be_confirmed) * remaining_ffg_voting_weight >= 2 * total_active_balance * ffg_voting_weight_so_far: - # We know that confirmation_byzantine_threshold >= ffg_weight_supporting_checkpoint_for_block_to_be_confirmed / ffg_voting_weight_so_far + if ffg_voting_weight_so_far > 0 and 3 * (ffg_voting_weight_so_far - ffg_suport_for_checkpoint) * remaining_ffg_weight >= 2 * total_active_balance * ffg_voting_weight_so_far: + # We know that confirmation_byzantine_threshold >= ffg_suport_for_checkpoint / ffg_voting_weight_so_far # Then our target statement reduces to - # (1 - confirmation_byzantine_threshold/100) * remaining_ffg_voting_weight >= 2/3 * total_active_balance + # (1 - confirmation_byzantine_threshold/100) * remaining_ffg_weight >= 2/3 * total_active_balance # Therefore # confirmation_byzantine_threshold <= - # (1 - (2/3 * total_active_balance / remaining_ffg_voting_weight)) * 100 = - # by bringing all to the denominator (3 * remaining_ffg_voting_weight), we get - return (300 * remaining_ffg_voting_weight - 200 * total_active_balance) // (3 * remaining_ffg_voting_weight) + # (1 - (2/3 * total_active_balance / remaining_ffg_weight)) * 100 = + # by bringing all to the denominator (3 * remaining_ffg_weight), we get + return (300 * remaining_ffg_weight - 200 * total_active_balance) // (3 * remaining_ffg_weight) else: - # We know that confirmation_byzantine_threshold <= ffg_weight_supporting_checkpoint_for_block_to_be_confirmed / ffg_voting_weight_so_far + # We know that confirmation_byzantine_threshold <= ffg_suport_for_checkpoint / ffg_voting_weight_so_far # Then our target statement reduces to - # ffg_weight_supporting_checkpoint_for_block_to_be_confirmed + # ffg_suport_for_checkpoint # - ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100 - # + (1 - confirmation_byzantine_threshold/100) * remaining_ffg_voting_weight + # + (1 - confirmation_byzantine_threshold/100) * remaining_ffg_weight # >= 2/3 * total_active_balance # Therfore: - # confirmation_byzantine_threshold <= ((ffg_weight_supporting_checkpoint_for_block_to_be_confirmed + remaining_ffg_voting_weight)/total_active_balance - 2/3) * 100 + # confirmation_byzantine_threshold <= ((ffg_suport_for_checkpoint + remaining_ffg_weight)/total_active_balance - 2/3) * 100 # by bringing all to the denominator (3 * total_active_balance), we get - return (300 * (ffg_weight_supporting_checkpoint_for_block_to_be_confirmed + remaining_ffg_voting_weight) - 200 * total_active_balance) // (3 * total_active_balance) + return (300 * (ffg_suport_for_checkpoint + remaining_ffg_weight) - 200 * total_active_balance) // (3 * total_active_balance) ``` ### Main Function From 668c97dae5f3faf7e41eddd0639ec13f5be463a7 Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Mon, 15 May 2023 19:35:22 -0700 Subject: [PATCH 08/20] use float calculations --- fork_choice/confirmation-rule.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index 9140ccd5ae..d22ea3ea3c 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -67,7 +67,6 @@ def get_committee_weight_between_slots(store: Store, start_slot: Slot, end_slot: num_committees += end_slot - epoch_boundary_slot + 1 # Next, calculate the weight from the previous epoch # Each committee from the previous epoch only contributes a pro-rated weight - # NOTE: using float arithmetic here. is that allowed here in spec? probably yes, since this is not consensus code. multiplier = (SLOTS_PER_EPOCH - end_slot - 1) / SLOTS_PER_EPOCH num_committees += (epoch_boundary_slot - start_slot) * multiplier return num_committees * committee_weight @@ -83,8 +82,7 @@ def is_one_confirmed(store: Store, confirmation_byzantine_threshold: int, block_ maximum_support = int(get_committee_weight_between_slots(Slot(parent_block.slot + 1), current_slot)) proposer_score = int(get_proposer_score(store)) - # support / maximum_support > 1/2 * (1 + proposer_score / maximum_support) + confirmation_byzantine_threshold/100 => - return 100 * support > 50 * maximum_support + 50 * proposer_score + confirmation_byzantine_threshold * maximum_support + return support / maximum_support > 0.5 * (1 + proposer_score / maximum_support) + confirmation_byzantine_threshold / 100 ``` ```python @@ -178,8 +176,7 @@ def is_ffg_confirmed( ) ) - # ffg_suport_for_checkpoint - max_adversarial_ffg_suport_for_checkpoint + (1 - confirmation_byzantine_threshold/100) * remaining_ffg_weight >= 2/3 * total_active_balance => - return ffg_suport_for_checkpoint * 300 + (300 - 3 * confirmation_byzantine_threshold) * remaining_ffg_weight - max_adversarial_ffg_suport_for_checkpoint * 300 >= 200 * total_active_balance + return ffg_suport_for_checkpoint - max_adversarial_ffg_suport_for_checkpoint + (1 - confirmation_byzantine_threshold/100) * remaining_ffg_weight >= 2/3 * total_active_balance ``` ### Main Function From 6ccc528c2dc71a6fc68b6de7d89c9eb43943a823 Mon Sep 17 00:00:00 2001 From: Roberto Saltini Date: Tue, 16 May 2023 21:05:07 +1000 Subject: [PATCH 09/20] Fix get_proposer_score --- specs/phase0/fork-choice.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/phase0/fork-choice.md b/specs/phase0/fork-choice.md index d0f064f026..246dcecabb 100644 --- a/specs/phase0/fork-choice.md +++ b/specs/phase0/fork-choice.md @@ -213,7 +213,8 @@ def get_checkpoint_block(store: Store, root: Root, epoch: Epoch) -> Root: ```python def get_proposer_score(store: Store) -> Gwei: - committee_weight = get_total_active_balance(state) // SLOTS_PER_EPOCH + justified_checkpoint_state = store.checkpoint_states[store.justified_checkpoint] + committee_weight = get_total_active_balance(justified_checkpoint_state) // SLOTS_PER_EPOCH return (committee_weight * PROPOSER_SCORE_BOOST) // 100 ``` From 97dc8bef15a27d12c97955d6e240fb5af1ec0697 Mon Sep 17 00:00:00 2001 From: Roberto Saltini Date: Tue, 16 May 2023 21:14:16 +1000 Subject: [PATCH 10/20] Fix lint error --- fork_choice/confirmation-rule.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index d22ea3ea3c..0290f81741 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -79,7 +79,7 @@ def is_one_confirmed(store: Store, confirmation_byzantine_threshold: int, block_ block = store.blocks[block_root] parent_block = store.blocks[block.parent_root] support = int(get_weight(store, block_root)) - maximum_support = int(get_committee_weight_between_slots(Slot(parent_block.slot + 1), current_slot)) + maximum_support = int(get_committee_weight_between_slots(store, Slot(parent_block.slot + 1), current_slot)) proposer_score = int(get_proposer_score(store)) return support / maximum_support > 0.5 * (1 + proposer_score / maximum_support) + confirmation_byzantine_threshold / 100 @@ -104,10 +104,10 @@ def is_LMD_confirmed(store: Store, confirmation_byzantine_threshold: int, block_ ``` ```python -def get_remaining_weight_in_epoch(state: BeaconState, current_slot: Slot) -> Gwei: +def get_remaining_weight_in_epoch(store: Store, current_slot: Slot) -> Gwei: # Returns the total weight of votes for this epoch from future committees after the current slot first_slot_next_epoch = compute_start_slot_at_epoch(Epoch(compute_epoch_at_slot(current_slot) + 1)) - return get_committee_weight_between_slots(Slot(current_slot + 1), Slot(first_slot_next_epoch - 1)) + return get_committee_weight_between_slots(store, Slot(current_slot + 1), Slot(first_slot_next_epoch - 1)) ``` @@ -162,7 +162,7 @@ def is_ffg_confirmed( checkpoint_root = get_checkpoint_block(store, block_root, current_epoch) checkpoint_state = store.block_states[checkpoint_root] - remaining_ffg_weight = int(get_remaining_weight_in_epoch(checkpoint_state, current_slot)) + remaining_ffg_weight = int(get_remaining_weight_in_epoch(store, current_slot)) total_active_balance = int(get_total_active_balance(checkpoint_state)) current_weight_in_epoch = total_active_balance - remaining_ffg_weight assert current_weight_in_epoch >= 0 @@ -176,7 +176,7 @@ def is_ffg_confirmed( ) ) - return ffg_suport_for_checkpoint - max_adversarial_ffg_suport_for_checkpoint + (1 - confirmation_byzantine_threshold/100) * remaining_ffg_weight >= 2/3 * total_active_balance + return ffg_suport_for_checkpoint - max_adversarial_ffg_suport_for_checkpoint + (1 - confirmation_byzantine_threshold / 100) * remaining_ffg_weight >= 2 / 3 * total_active_balance ``` ### Main Function @@ -269,7 +269,7 @@ def get_score_for_one_confirmation(store: Store, block_root: Root) -> int: block = store.blocks[block_root] parent_block = store.blocks[block.parent_root] support = int(get_weight(store, block_root)) - maximum_support = int(get_committee_weight(parent_block.slot + 1, current_slot)) + maximum_support = int(get_committee_weight_between_slots(store, Slot(parent_block.slot + 1), current_slot)) proposer_score = int(get_proposer_score(store)) # We need to return a value confirmation_byzantine_threshold such that the following inequality is true @@ -280,7 +280,6 @@ def get_score_for_one_confirmation(store: Store, block_root: Root) -> int: ```python def get_score_for_LMD_confirmation(store: Store, block_root: Root) -> int: - current_slot = get_current_slot(store) if block_root == store.finalized_checkpoint.root: return 100 // 3 else: @@ -309,7 +308,7 @@ def get_score_for_FFG_confirmation(store: Store, block_root: Root) -> int: total_active_balance = int(get_total_active_balance(checkpoint_state)) - remaining_ffg_weight = int(get_remaining_weight_in_epoch(checkpoint_state, current_slot)) + remaining_ffg_weight = int(get_remaining_weight_in_epoch(store, current_slot)) ffg_voting_weight_so_far = total_active_balance - remaining_ffg_weight assert ffg_voting_weight_so_far >= 0 From 49a1365d183e7fe1ed508e6d2ef7049f6840b70b Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Tue, 16 May 2023 16:24:37 -0700 Subject: [PATCH 11/20] fix lint errors --- fork_choice/confirmation-rule.md | 29 ++++++++++++++++++++--------- linter.ini | 2 +- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index 0290f81741..625ed79cd1 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -6,8 +6,10 @@ - [Introduction](#introduction) + - [Usage](#usage) - [Confirmation Rule](#confirmation-rule) - [Helper Functions](#helper-functions) + - [`get_committee_weight_between_slots`](#get_committee_weight_between_slots) - [Main Function](#main-function) - [`get_safe_execution_payload_hash`](#get_safe_execution_payload_hash) - [Helper](#helper) @@ -15,7 +17,6 @@ - [Confirmation Score](#confirmation-score) - [Helper Functions](#helper-functions-1) - [Main Function](#main-function-2) -- [Old functions kept for reference](#old-functions-kept-for-reference) @@ -82,7 +83,8 @@ def is_one_confirmed(store: Store, confirmation_byzantine_threshold: int, block_ maximum_support = int(get_committee_weight_between_slots(store, Slot(parent_block.slot + 1), current_slot)) proposer_score = int(get_proposer_score(store)) - return support / maximum_support > 0.5 * (1 + proposer_score / maximum_support) + confirmation_byzantine_threshold / 100 + return support / maximum_support > \ + 0.5 * (1 + proposer_score / maximum_support) + confirmation_byzantine_threshold / 100 ``` ```python @@ -137,7 +139,9 @@ def get_ffg_support(store: Store, block_root: Root) -> Gwei: leave_roots = get_leaf_block_roots(store, block_root) # current_epoch_attestations contains only attestations with source matching block.current_justified_checkpoint - attestations_in_leaves: Set[PendingAttestation] = set().union(*[store.block_states[root].current_epoch_attestations for root in leave_roots]) + attestations_in_leaves = set().union( + *[store.block_states[root].current_epoch_attestations for root in leave_roots] + ) current_epoch = get_current_epoch(store) checkpoint_root = get_checkpoint_block(store, block_root, current_epoch) @@ -176,7 +180,9 @@ def is_ffg_confirmed( ) ) - return ffg_suport_for_checkpoint - max_adversarial_ffg_suport_for_checkpoint + (1 - confirmation_byzantine_threshold / 100) * remaining_ffg_weight >= 2 / 3 * total_active_balance + return 2 / 3 * total_active_balance <= \ + ffg_suport_for_checkpoint - max_adversarial_ffg_suport_for_checkpoint + \ + (1 - confirmation_byzantine_threshold / 100) * remaining_ffg_weight ``` ### Main Function @@ -207,7 +213,7 @@ def is_confirmed( ## `get_safe_execution_payload_hash` -This function is used to compute the value of the `safeBlockHash` field which is passed from CL to EL in the `forkchoiceUpdated` engine api call. +This function is used to compute the value of the `safeBlockHash` field which is passed from CL to EL in the `forkchoiceUpdated` Engine API call. ### Helper @@ -228,7 +234,8 @@ def find_confirmed_block( if is_confirmed(store, confirmation_byzantine_threshold, confirmation_slashing_threshold, block_root): return block_root else: - return find_confirmed_block(store, confirmation_byzantine_threshold, confirmation_slashing_threshold, block.parent_root) + return find_confirmed_block(store, confirmation_byzantine_threshold, + confirmation_slashing_threshold, block.parent_root) ``` @@ -242,7 +249,8 @@ def get_safe_execution_payload_hash( ) -> Hash32: head_root = get_head(store) - confirmed_block_root = find_confirmed_block(store, confirmation_byzantine_threshold, confirmation_slashing_threshold, head_root) + confirmed_block_root = find_confirmed_block(store, confirmation_byzantine_threshold, + confirmation_slashing_threshold, head_root) confirmed_block = store.blocks[confirmed_block_root] if compute_epoch_at_slot(confirmed_block.slot) >= BELLATRIX_FORK_EPOCH: @@ -330,7 +338,9 @@ def get_score_for_FFG_confirmation(store: Store, block_root: Root) -> int: # (1 - ffg_suport_for_checkpoint / ffg_voting_weight_so_far) * remaining_ffg_weight >= 2/3 * total_active_balance # multiplying each side by 3 * ffg_voting_weight_so_far, we get (assuming ffg_voting_weight_so_far != 0): - if ffg_voting_weight_so_far > 0 and 3 * (ffg_voting_weight_so_far - ffg_suport_for_checkpoint) * remaining_ffg_weight >= 2 * total_active_balance * ffg_voting_weight_so_far: + if ffg_voting_weight_so_far > 0 and \ + 3 * (ffg_voting_weight_so_far - ffg_suport_for_checkpoint) * remaining_ffg_weight >= \ + 2 * total_active_balance * ffg_voting_weight_so_far: # We know that confirmation_byzantine_threshold >= ffg_suport_for_checkpoint / ffg_voting_weight_so_far # Then our target statement reduces to @@ -353,7 +363,8 @@ def get_score_for_FFG_confirmation(store: Store, block_root: Root) -> int: # Therfore: # confirmation_byzantine_threshold <= ((ffg_suport_for_checkpoint + remaining_ffg_weight)/total_active_balance - 2/3) * 100 # by bringing all to the denominator (3 * total_active_balance), we get - return (300 * (ffg_suport_for_checkpoint + remaining_ffg_weight) - 200 * total_active_balance) // (3 * total_active_balance) + return (300 * (ffg_suport_for_checkpoint + remaining_ffg_weight) - 200 * total_active_balance) // \ + (3 * total_active_balance) ``` ### Main Function diff --git a/linter.ini b/linter.ini index 8c4e8b9ca5..52a3aec0e0 100644 --- a/linter.ini +++ b/linter.ini @@ -1,6 +1,6 @@ [flake8] ignore = E252,W504,W503 -max-line-length = 500 +max-line-length = 120 [mypy] disallow_incomplete_defs = True From 6a75d4e405c3a3e52a3c46c5d8c6d0f28aa9b8fe Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Tue, 16 May 2023 16:32:35 -0700 Subject: [PATCH 12/20] update toc --- fork_choice/confirmation-rule.md | 56 +++++++++++++++++++++++--------- specs/phase0/fork-choice.md | 1 + 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index 625ed79cd1..f2037944e3 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -9,14 +9,24 @@ - [Usage](#usage) - [Confirmation Rule](#confirmation-rule) - [Helper Functions](#helper-functions) - - [`get_committee_weight_between_slots`](#get_committee_weight_between_slots) - - [Main Function](#main-function) -- [`get_safe_execution_payload_hash`](#get_safe_execution_payload_hash) - - [Helper](#helper) - - [Main Function](#main-function-1) -- [Confirmation Score](#confirmation-score) + - [`get_committee_weight_between_slots`](#get_committee_weight_between_slots) + - [`is_one_confirmed`](#is_one_confirmed) + - [`is_LMD_confirmed`](#is_lmd_confirmed) + - [`get_remaining_weight_in_epoch`](#get_remaining_weight_in_epoch) + - [`get_leaf_block_roots`](#get_leaf_block_roots) + - [`get_ffg_support`](#get_ffg_support) + - [`is_ffg_confirmed`](#is_ffg_confirmed) + - [`is_confirmed`](#is_confirmed) +- [Safe Block Hash](#safe-block-hash) - [Helper Functions](#helper-functions-1) - - [Main Function](#main-function-2) + - [`find_confirmed_block`](#find_confirmed_block) + - [`get_safe_execution_payload_hash`](#get_safe_execution_payload_hash) +- [Confirmation Score](#confirmation-score) + - [Helper Functions](#helper-functions-2) + - [`get_score_for_one_confirmation`](#get_score_for_one_confirmation) + - [`get_score_for_LMD_confirmation`](#get_score_for_lmd_confirmation) + - [`get_score_for_FFG_confirmation`](#get_score_for_ffg_confirmation) + - [`get_confirmation_score`](#get_confirmation_score) @@ -42,9 +52,7 @@ This section specifies an algorithm to determine whether a block is confirmed. T ### Helper Functions - -##### `get_committee_weight_between_slots` - +#### `get_committee_weight_between_slots` ```python def get_committee_weight_between_slots(store: Store, start_slot: Slot, end_slot: Slot) -> Gwei: @@ -73,6 +81,7 @@ def get_committee_weight_between_slots(store: Store, start_slot: Slot, end_slot: return num_committees * committee_weight ``` +#### `is_one_confirmed` ```python def is_one_confirmed(store: Store, confirmation_byzantine_threshold: int, block_root: Root) -> bool: @@ -87,6 +96,8 @@ def is_one_confirmed(store: Store, confirmation_byzantine_threshold: int, block_ 0.5 * (1 + proposer_score / maximum_support) + confirmation_byzantine_threshold / 100 ``` +#### `is_LMD_confirmed` + ```python def is_LMD_confirmed(store: Store, confirmation_byzantine_threshold: int, block_root: Root) -> bool: if block_root == store.finalized_checkpoint.root: @@ -105,6 +116,8 @@ def is_LMD_confirmed(store: Store, confirmation_byzantine_threshold: int, block_ ) ``` +#### `get_remaining_weight_in_epoch` + ```python def get_remaining_weight_in_epoch(store: Store, current_slot: Slot) -> Gwei: # Returns the total weight of votes for this epoch from future committees after the current slot @@ -112,6 +125,7 @@ def get_remaining_weight_in_epoch(store: Store, current_slot: Slot) -> Gwei: return get_committee_weight_between_slots(store, Slot(current_slot + 1), Slot(first_slot_next_epoch - 1)) ``` +#### `get_leaf_block_roots` ```python def get_leaf_block_roots(store: Store, block_root: Root) -> Set[Root]: @@ -130,6 +144,8 @@ def get_leaf_block_roots(store: Store, block_root: Root) -> Set[Root]: ``` +#### `get_ffg_support` + ```python def get_ffg_support(store: Store, block_root: Root) -> Gwei: # Returns the total weight supporting the highest checkpoint in the block's chain @@ -150,6 +166,8 @@ def get_ffg_support(store: Store, block_root: Root) -> Gwei: return get_attesting_balance(checkpoint_state, list(support_for_checkpoint)) ``` +#### `is_ffg_confirmed` + ```python def is_ffg_confirmed( # Returns whether the branch will justify it's current epoch checkpoint at the end of this epoch @@ -185,7 +203,7 @@ def is_ffg_confirmed( (1 - confirmation_byzantine_threshold / 100) * remaining_ffg_weight ``` -### Main Function +### `is_confirmed` ```python def is_confirmed( @@ -210,12 +228,13 @@ def is_confirmed( ) ``` - -## `get_safe_execution_payload_hash` +## Safe Block Hash This function is used to compute the value of the `safeBlockHash` field which is passed from CL to EL in the `forkchoiceUpdated` Engine API call. -### Helper +### Helper Functions + +#### `find_confirmed_block` ```python def find_confirmed_block( @@ -239,7 +258,7 @@ def find_confirmed_block( ``` -### Main Function +### `get_safe_execution_payload_hash` ```python def get_safe_execution_payload_hash( @@ -270,6 +289,7 @@ prevent a block from being confirmed. ### Helper Functions +#### `get_score_for_one_confirmation` ```python def get_score_for_one_confirmation(store: Store, block_root: Root) -> int: @@ -286,6 +306,8 @@ def get_score_for_one_confirmation(store: Store, block_root: Root) -> int: return (100 * support - 50 * proposer_score - 1) // maximum_support - 50 ``` +#### `get_score_for_LMD_confirmation` + ```python def get_score_for_LMD_confirmation(store: Store, block_root: Root) -> int: if block_root == store.finalized_checkpoint.root: @@ -304,6 +326,8 @@ def get_score_for_LMD_confirmation(store: Store, block_root: Root) -> int: ) ``` +#### `get_score_for_FFG_confirmation` + ```python def get_score_for_FFG_confirmation(store: Store, block_root: Root) -> int: current_slot = get_current_slot(store) @@ -367,7 +391,7 @@ def get_score_for_FFG_confirmation(store: Store, block_root: Root) -> int: (3 * total_active_balance) ``` -### Main Function +### `get_confirmation_score` ```python def get_confirmation_score( diff --git a/specs/phase0/fork-choice.md b/specs/phase0/fork-choice.md index 246dcecabb..4c9ac00cef 100644 --- a/specs/phase0/fork-choice.md +++ b/specs/phase0/fork-choice.md @@ -20,6 +20,7 @@ - [`compute_slots_since_epoch_start`](#compute_slots_since_epoch_start) - [`get_ancestor`](#get_ancestor) - [`get_checkpoint_block`](#get_checkpoint_block) + - [`get_proposer_score_boost`](#get_proposer_score_boost) - [`get_weight`](#get_weight) - [`get_voting_source`](#get_voting_source) - [`filter_block_tree`](#filter_block_tree) From 03b194ed6722c530fe65168e95e209671682620d Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Tue, 16 May 2023 16:36:01 -0700 Subject: [PATCH 13/20] function renaming --- fork_choice/confirmation-rule.md | 44 ++++++++++++++++---------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index f2037944e3..71c907db61 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -14,8 +14,8 @@ - [`is_LMD_confirmed`](#is_lmd_confirmed) - [`get_remaining_weight_in_epoch`](#get_remaining_weight_in_epoch) - [`get_leaf_block_roots`](#get_leaf_block_roots) - - [`get_ffg_support`](#get_ffg_support) - - [`is_ffg_confirmed`](#is_ffg_confirmed) + - [`get_FFG_support`](#get_FFG_support) + - [`is_FFG_confirmed`](#is_FFG_confirmed) - [`is_confirmed`](#is_confirmed) - [Safe Block Hash](#safe-block-hash) - [Helper Functions](#helper-functions-1) @@ -23,9 +23,9 @@ - [`get_safe_execution_payload_hash`](#get_safe_execution_payload_hash) - [Confirmation Score](#confirmation-score) - [Helper Functions](#helper-functions-2) - - [`get_score_for_one_confirmation`](#get_score_for_one_confirmation) - - [`get_score_for_LMD_confirmation`](#get_score_for_lmd_confirmation) - - [`get_score_for_FFG_confirmation`](#get_score_for_ffg_confirmation) + - [`get_one_confirmation_score`](#get_one_confirmation_score) + - [`get_LMD_confirmation_score`](#get_LMD_confirmation_score) + - [`get_FFG_confirmation_score`](#get_FFG_confirmation_score) - [`get_confirmation_score`](#get_confirmation_score) @@ -144,10 +144,10 @@ def get_leaf_block_roots(store: Store, block_root: Root) -> Set[Root]: ``` -#### `get_ffg_support` +#### `get_FFG_support` ```python -def get_ffg_support(store: Store, block_root: Root) -> Gwei: +def get_FFG_support(store: Store, block_root: Root) -> Gwei: # Returns the total weight supporting the highest checkpoint in the block's chain block = store.blocks[block_root] @@ -166,10 +166,10 @@ def get_ffg_support(store: Store, block_root: Root) -> Gwei: return get_attesting_balance(checkpoint_state, list(support_for_checkpoint)) ``` -#### `is_ffg_confirmed` +#### `is_FFG_confirmed` ```python -def is_ffg_confirmed( +def is_FFG_confirmed( # Returns whether the branch will justify it's current epoch checkpoint at the end of this epoch store: Store, confirmation_byzantine_threshold: int, @@ -189,7 +189,7 @@ def is_ffg_confirmed( current_weight_in_epoch = total_active_balance - remaining_ffg_weight assert current_weight_in_epoch >= 0 - ffg_suport_for_checkpoint = int(get_ffg_support(store, block_root)) + ffg_suport_for_checkpoint = int(get_FFG_support(store, block_root)) max_adversarial_ffg_suport_for_checkpoint = int( min( (current_weight_in_epoch * confirmation_byzantine_threshold - 1) // 100 + 1, @@ -223,7 +223,7 @@ def is_confirmed( return ( is_LMD_confirmed(store, confirmation_byzantine_threshold, block_root) and - is_ffg_confirmed(store, confirmation_byzantine_threshold, confirmation_slashing_threshold, block_root) and + is_FFG_confirmed(store, confirmation_byzantine_threshold, confirmation_slashing_threshold, block_root) and block_justified_checkpoint + 1 == current_epoch ) ``` @@ -289,10 +289,10 @@ prevent a block from being confirmed. ### Helper Functions -#### `get_score_for_one_confirmation` +#### `get_one_confirmation_score` ```python -def get_score_for_one_confirmation(store: Store, block_root: Root) -> int: +def get_one_confirmation_score(store: Store, block_root: Root) -> int: current_slot = get_current_slot(store) block = store.blocks[block_root] parent_block = store.blocks[block.parent_root] @@ -306,10 +306,10 @@ def get_score_for_one_confirmation(store: Store, block_root: Root) -> int: return (100 * support - 50 * proposer_score - 1) // maximum_support - 50 ``` -#### `get_score_for_LMD_confirmation` +#### `get_LMD_confirmation_score` ```python -def get_score_for_LMD_confirmation(store: Store, block_root: Root) -> int: +def get_LMD_confirmation_score(store: Store, block_root: Root) -> int: if block_root == store.finalized_checkpoint.root: return 100 // 3 else: @@ -321,15 +321,15 @@ def get_score_for_LMD_confirmation(store: Store, block_root: Root) -> int: else: # Check one_confirmed score for this block and LMD_confirmed score for the preceding chain. return min( - get_score_for_one_confirmation(store, block_root), - get_score_for_LMD_confirmation(store, block.parent_root) + get_one_confirmation_score(store, block_root), + get_LMD_confirmation_score(store, block.parent_root) ) ``` -#### `get_score_for_FFG_confirmation` +#### `get_FFG_confirmation_score` ```python -def get_score_for_FFG_confirmation(store: Store, block_root: Root) -> int: +def get_FFG_confirmation_score(store: Store, block_root: Root) -> int: current_slot = get_current_slot(store) block = store.blocks[block_root] assert get_current_epoch(store) == compute_epoch_at_slot(block.slot) @@ -345,7 +345,7 @@ def get_score_for_FFG_confirmation(store: Store, block_root: Root) -> int: ffg_voting_weight_so_far = total_active_balance - remaining_ffg_weight assert ffg_voting_weight_so_far >= 0 - ffg_suport_for_checkpoint = int(get_ffg_support(store, block_root)) + ffg_suport_for_checkpoint = int(get_FFG_support(store, block_root)) # We assume confirmation_slashing_threshold = + infinity # So, we want to return a value confirmation_byzantine_threshold such that the following statement is true @@ -411,7 +411,7 @@ def get_confirmation_score( assert compute_epoch_at_slot(block.slot) == current_epoch return min( - get_score_for_LMD_confirmation(store, block_root), - get_score_for_FFG_confirmation(store, block_root) + get_LMD_confirmation_score(store, block_root), + get_FFG_confirmation_score(store, block_root) ) ``` From b843dadaabae81aaba9b732b5cbff92c8552b555 Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Tue, 16 May 2023 17:20:35 -0700 Subject: [PATCH 14/20] wip - update conf score comments --- fork_choice/confirmation-rule.md | 74 ++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index 71c907db61..826f064d47 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -189,17 +189,17 @@ def is_FFG_confirmed( current_weight_in_epoch = total_active_balance - remaining_ffg_weight assert current_weight_in_epoch >= 0 - ffg_suport_for_checkpoint = int(get_FFG_support(store, block_root)) - max_adversarial_ffg_suport_for_checkpoint = int( + ffg_support_for_checkpoint = int(get_FFG_support(store, block_root)) + max_adversarial_ffg_support_for_checkpoint = int( min( (current_weight_in_epoch * confirmation_byzantine_threshold - 1) // 100 + 1, confirmation_slashing_threshold, - ffg_suport_for_checkpoint + ffg_support_for_checkpoint ) ) return 2 / 3 * total_active_balance <= \ - ffg_suport_for_checkpoint - max_adversarial_ffg_suport_for_checkpoint + \ + ffg_support_for_checkpoint - max_adversarial_ffg_support_for_checkpoint + \ (1 - confirmation_byzantine_threshold / 100) * remaining_ffg_weight ``` @@ -300,10 +300,12 @@ def get_one_confirmation_score(store: Store, block_root: Root) -> int: maximum_support = int(get_committee_weight_between_slots(store, Slot(parent_block.slot + 1), current_slot)) proposer_score = int(get_proposer_score(store)) - # We need to return a value confirmation_byzantine_threshold such that the following inequality is true - # 100 * support > 50 * maximum_support + 50 * proposer_score + confirmation_byzantine_threshold * maximum_support - # the "-1" in the numerator is to return a "<=" rather than a "<" value - return (100 * support - 50 * proposer_score - 1) // maximum_support - 50 + """ + Return a confirmation_byzantine_threshold such that: + support / maximum_support > \ + 0.5 * (1 + proposer_score / maximum_support) + confirmation_byzantine_threshold / 100 + """ + return (100 * support - 50 * proposer_score - 1) / maximum_support - 50 ``` #### `get_LMD_confirmation_score` @@ -345,27 +347,37 @@ def get_FFG_confirmation_score(store: Store, block_root: Root) -> int: ffg_voting_weight_so_far = total_active_balance - remaining_ffg_weight assert ffg_voting_weight_so_far >= 0 - ffg_suport_for_checkpoint = int(get_FFG_support(store, block_root)) + ffg_support_for_checkpoint = int(get_FFG_support(store, block_root)) - # We assume confirmation_slashing_threshold = + infinity - # So, we want to return a value confirmation_byzantine_threshold such that the following statement is true + """ + Note: This function assumes confirmation_slashing_threshold = + infinity. - # ffg_suport_for_checkpoint - # - min(ffg_suport_for_checkpoint, ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100) - # + (1 - confirmation_byzantine_threshold/100) * remaining_ffg_weight - # >= 2/3 * total_active_balance + Return a value confirmation_byzantine_threshold such that: + 2 / 3 * total_active_balance <= \ + ffg_support_for_checkpoint - \ + min(ffg_support_for_checkpoint, ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100) + \ + (1 - confirmation_byzantine_threshold / 100) * remaining_ffg_weight + + First, we check whether confirmation_byzantine_threshold >= ffg_support_for_checkpoint / ffg_voting_weight_so_far * 100 + To do this we check whether in the case that confirmation_byzantine_threshold == ffg_support_for_checkpoint / ffg_voting_weight_so_far * 100 + our target statement is true + This amount to checking that + (1 - ffg_support_for_checkpoint / ffg_voting_weight_so_far) * remaining_ffg_weight >= 2/3 * total_active_balance + multiplying each side by 3 * ffg_voting_weight_so_far, we get (assuming ffg_voting_weight_so_far != 0): + """ - # First, we check whether confirmation_byzantine_threshold >= ffg_suport_for_checkpoint / ffg_voting_weight_so_far * 100 - # To do this we check whether in the case that confirmation_byzantine_threshold == ffg_suport_for_checkpoint / ffg_voting_weight_so_far * 100 - # our target statement is true - # This amount to checking that - # (1 - ffg_suport_for_checkpoint / ffg_voting_weight_so_far) * remaining_ffg_weight >= 2/3 * total_active_balance - # multiplying each side by 3 * ffg_voting_weight_so_far, we get (assuming ffg_voting_weight_so_far != 0): + """ + Case 1: ffg_support_for_checkpoint <= ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100 + 2 / 3 * total_active_balance <= \ + ffg_support_for_checkpoint - \ + ffg_support_for_checkpoint + \ + (1 - confirmation_byzantine_threshold / 100) * remaining_ffg_weight + """ if ffg_voting_weight_so_far > 0 and \ - 3 * (ffg_voting_weight_so_far - ffg_suport_for_checkpoint) * remaining_ffg_weight >= \ + 3 * (ffg_voting_weight_so_far - ffg_support_for_checkpoint) * remaining_ffg_weight >= \ 2 * total_active_balance * ffg_voting_weight_so_far: - # We know that confirmation_byzantine_threshold >= ffg_suport_for_checkpoint / ffg_voting_weight_so_far + # We know that confirmation_byzantine_threshold >= ffg_support_for_checkpoint / ffg_voting_weight_so_far # Then our target statement reduces to # (1 - confirmation_byzantine_threshold/100) * remaining_ffg_weight >= 2/3 * total_active_balance @@ -375,19 +387,27 @@ def get_FFG_confirmation_score(store: Store, block_root: Root) -> int: # (1 - (2/3 * total_active_balance / remaining_ffg_weight)) * 100 = # by bringing all to the denominator (3 * remaining_ffg_weight), we get return (300 * remaining_ffg_weight - 200 * total_active_balance) // (3 * remaining_ffg_weight) + """ + Case 2: ffg_support_for_checkpoint > ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100 + + 2 / 3 * total_active_balance <= \ + ffg_support_for_checkpoint - \ + ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100 + \ + (1 - confirmation_byzantine_threshold / 100) * remaining_ffg_weight + """ else: - # We know that confirmation_byzantine_threshold <= ffg_suport_for_checkpoint / ffg_voting_weight_so_far + # We know that confirmation_byzantine_threshold <= ffg_support_for_checkpoint / ffg_voting_weight_so_far # Then our target statement reduces to - # ffg_suport_for_checkpoint + # ffg_support_for_checkpoint # - ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100 # + (1 - confirmation_byzantine_threshold/100) * remaining_ffg_weight # >= 2/3 * total_active_balance # Therfore: - # confirmation_byzantine_threshold <= ((ffg_suport_for_checkpoint + remaining_ffg_weight)/total_active_balance - 2/3) * 100 + # confirmation_byzantine_threshold <= ((ffg_support_for_checkpoint + remaining_ffg_weight)/total_active_balance - 2/3) * 100 # by bringing all to the denominator (3 * total_active_balance), we get - return (300 * (ffg_suport_for_checkpoint + remaining_ffg_weight) - 200 * total_active_balance) // \ + return (300 * (ffg_support_for_checkpoint + remaining_ffg_weight) - 200 * total_active_balance) // \ (3 * total_active_balance) ``` From 185338c452e7655e93a34c94a449e0e93323a83c Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Tue, 16 May 2023 17:21:19 -0700 Subject: [PATCH 15/20] fix multiplier --- fork_choice/confirmation-rule.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index 826f064d47..a127434ff7 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -76,7 +76,7 @@ def get_committee_weight_between_slots(store: Store, start_slot: Slot, end_slot: num_committees += end_slot - epoch_boundary_slot + 1 # Next, calculate the weight from the previous epoch # Each committee from the previous epoch only contributes a pro-rated weight - multiplier = (SLOTS_PER_EPOCH - end_slot - 1) / SLOTS_PER_EPOCH + multiplier = (SLOTS_PER_EPOCH - end_slot % SLOTS_PER_EPOCH - 1) / SLOTS_PER_EPOCH num_committees += (epoch_boundary_slot - start_slot) * multiplier return num_committees * committee_weight ``` From 5c38f67a189bdb2d0de6816544a343791b4362f7 Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Tue, 16 May 2023 17:26:56 -0700 Subject: [PATCH 16/20] remove comments --- fork_choice/confirmation-rule.md | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index a127434ff7..d2d64e286a 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -357,35 +357,18 @@ def get_FFG_confirmation_score(store: Store, block_root: Root) -> int: ffg_support_for_checkpoint - \ min(ffg_support_for_checkpoint, ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100) + \ (1 - confirmation_byzantine_threshold / 100) * remaining_ffg_weight - - First, we check whether confirmation_byzantine_threshold >= ffg_support_for_checkpoint / ffg_voting_weight_so_far * 100 - To do this we check whether in the case that confirmation_byzantine_threshold == ffg_support_for_checkpoint / ffg_voting_weight_so_far * 100 - our target statement is true - This amount to checking that - (1 - ffg_support_for_checkpoint / ffg_voting_weight_so_far) * remaining_ffg_weight >= 2/3 * total_active_balance - multiplying each side by 3 * ffg_voting_weight_so_far, we get (assuming ffg_voting_weight_so_far != 0): """ """ Case 1: ffg_support_for_checkpoint <= ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100 2 / 3 * total_active_balance <= \ - ffg_support_for_checkpoint - \ - ffg_support_for_checkpoint + \ (1 - confirmation_byzantine_threshold / 100) * remaining_ffg_weight """ if ffg_voting_weight_so_far > 0 and \ 3 * (ffg_voting_weight_so_far - ffg_support_for_checkpoint) * remaining_ffg_weight >= \ 2 * total_active_balance * ffg_voting_weight_so_far: # We know that confirmation_byzantine_threshold >= ffg_support_for_checkpoint / ffg_voting_weight_so_far - - # Then our target statement reduces to - # (1 - confirmation_byzantine_threshold/100) * remaining_ffg_weight >= 2/3 * total_active_balance - - # Therefore - # confirmation_byzantine_threshold <= - # (1 - (2/3 * total_active_balance / remaining_ffg_weight)) * 100 = - # by bringing all to the denominator (3 * remaining_ffg_weight), we get return (300 * remaining_ffg_weight - 200 * total_active_balance) // (3 * remaining_ffg_weight) """ Case 2: ffg_support_for_checkpoint > ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100 @@ -397,16 +380,6 @@ def get_FFG_confirmation_score(store: Store, block_root: Root) -> int: """ else: # We know that confirmation_byzantine_threshold <= ffg_support_for_checkpoint / ffg_voting_weight_so_far - # Then our target statement reduces to - - # ffg_support_for_checkpoint - # - ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100 - # + (1 - confirmation_byzantine_threshold/100) * remaining_ffg_weight - # >= 2/3 * total_active_balance - - # Therfore: - # confirmation_byzantine_threshold <= ((ffg_support_for_checkpoint + remaining_ffg_weight)/total_active_balance - 2/3) * 100 - # by bringing all to the denominator (3 * total_active_balance), we get return (300 * (ffg_support_for_checkpoint + remaining_ffg_weight) - 200 * total_active_balance) // \ (3 * total_active_balance) ``` From ba7cb1f980a8f1157346fd9ed6032332a5a6e6b7 Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Tue, 16 May 2023 18:29:36 -0700 Subject: [PATCH 17/20] simplify confirmation score functions --- fork_choice/confirmation-rule.md | 42 +++++++++++++++++--------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index d2d64e286a..df85b87556 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -301,11 +301,11 @@ def get_one_confirmation_score(store: Store, block_root: Root) -> int: proposer_score = int(get_proposer_score(store)) """ - Return a confirmation_byzantine_threshold such that: + Return the max possible one_confirmation_score such that: support / maximum_support > \ - 0.5 * (1 + proposer_score / maximum_support) + confirmation_byzantine_threshold / 100 + 0.5 * (1 + proposer_score / maximum_support) + one_confirmation_score / 100 """ - return (100 * support - 50 * proposer_score - 1) / maximum_support - 50 + return (100 * support - 50 * proposer_score - 1) // maximum_support - 50 ``` #### `get_LMD_confirmation_score` @@ -352,36 +352,38 @@ def get_FFG_confirmation_score(store: Store, block_root: Root) -> int: """ Note: This function assumes confirmation_slashing_threshold = + infinity. - Return a value confirmation_byzantine_threshold such that: + Return the max possible ffg_confirmation_score such that: 2 / 3 * total_active_balance <= \ ffg_support_for_checkpoint - \ - min(ffg_support_for_checkpoint, ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100) + \ - (1 - confirmation_byzantine_threshold / 100) * remaining_ffg_weight + min(ffg_support_for_checkpoint, ffg_voting_weight_so_far * ffg_confirmation_score / 100) + \ + (1 - ffg_confirmation_score / 100) * remaining_ffg_weight """ """ - Case 1: ffg_support_for_checkpoint <= ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100 + Case 1: ffg_support_for_checkpoint <= ffg_voting_weight_so_far * ffg_confirmation_score / 100 2 / 3 * total_active_balance <= \ - (1 - confirmation_byzantine_threshold / 100) * remaining_ffg_weight + (1 - ffg_confirmation_score / 100) * remaining_ffg_weight """ - if ffg_voting_weight_so_far > 0 and \ - 3 * (ffg_voting_weight_so_far - ffg_support_for_checkpoint) * remaining_ffg_weight >= \ - 2 * total_active_balance * ffg_voting_weight_so_far: - # We know that confirmation_byzantine_threshold >= ffg_support_for_checkpoint / ffg_voting_weight_so_far - return (300 * remaining_ffg_weight - 200 * total_active_balance) // (3 * remaining_ffg_weight) + if ffg_voting_weight_so_far > 0: + ffg_confirmation_score = \ + 100 * (3 * remaining_ffg_weight - 2 * total_active_balance) // (3 * remaining_ffg_weight) + if ffg_confirmation_score / 100 >= ffg_support_for_checkpoint / ffg_voting_weight_so_far: + return ffg_confirmation_score + """ - Case 2: ffg_support_for_checkpoint > ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100 + Case 2: ffg_support_for_checkpoint > ffg_voting_weight_so_far * ffg_confirmation_score / 100 2 / 3 * total_active_balance <= \ ffg_support_for_checkpoint - \ - ffg_voting_weight_so_far * confirmation_byzantine_threshold / 100 + \ - (1 - confirmation_byzantine_threshold / 100) * remaining_ffg_weight + ffg_voting_weight_so_far * ffg_confirmation_score / 100 + \ + (1 - ffg_confirmation_score / 100) * remaining_ffg_weight """ - else: - # We know that confirmation_byzantine_threshold <= ffg_support_for_checkpoint / ffg_voting_weight_so_far - return (300 * (ffg_support_for_checkpoint + remaining_ffg_weight) - 200 * total_active_balance) // \ - (3 * total_active_balance) + ffg_confirmation_score = \ + 100 * (3 * (ffg_support_for_checkpoint + remaining_ffg_weight) - 2 * total_active_balance) // \ + (3 * total_active_balance) + assert ffg_confirmation_score / 100 < ffg_support_for_checkpoint / ffg_voting_weight_so_far + return ffg_confirmation_score ``` ### `get_confirmation_score` From c0efc70f88b8d96d926bf2c107342dd417447b74 Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Tue, 16 May 2023 19:08:24 -0700 Subject: [PATCH 18/20] attach paper --- fork_choice/confirmation-rule.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index df85b87556..8659ef5414 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -36,6 +36,8 @@ This document specifies a fast block confirmation rule for the Ethereum protocol. *Note:* Confirmation is not a substitute for finality! The safety of confirmations is weaker than that of finality. +The research paper for this rule is attached in this [ethresear.ch post](https://ethresear.ch/t/confirmation-rule-for-ethereum-pos/15454). + ### Usage This rule makes the following network synchrony assumption: starting from the current slot, attestations created by honest validators in any slot are received by the end of that slot. From 0041a005e784771f7e46bfe17bea5c0f8ed73431 Mon Sep 17 00:00:00 2001 From: Roberto Saltini Date: Wed, 17 May 2023 20:38:31 +1000 Subject: [PATCH 19/20] Fix calculation bug in remaining_slots_in_current_epoch --- fork_choice/confirmation-rule.md | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index 8659ef5414..165be11297 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -70,16 +70,22 @@ def get_committee_weight_between_slots(store: Store, start_slot: Slot, end_slot: if end_epoch > start_epoch + 1: return total_active_balance - # A range that does not span any full epoch needs pro-rata calculation - committee_weight = get_total_active_balance(justified_state) // SLOTS_PER_EPOCH - num_committees = 0 - # First, calculate the weight from the end epoch - epoch_boundary_slot = compute_start_slot_at_epoch(end_epoch) - num_committees += end_slot - epoch_boundary_slot + 1 - # Next, calculate the weight from the previous epoch - # Each committee from the previous epoch only contributes a pro-rated weight - multiplier = (SLOTS_PER_EPOCH - end_slot % SLOTS_PER_EPOCH - 1) / SLOTS_PER_EPOCH - num_committees += (epoch_boundary_slot - start_slot) * multiplier + if start_epoch == end_epoch: + num_committees = end_slot - start_slot + 1 + else: + # A range that spans an epoch boundary, but does not span any full epoch + # needs pro-rata calculation + committee_weight = get_total_active_balance(justified_state) // SLOTS_PER_EPOCH + # First, calculate the number of committees in the current epoch + num_slots_in_current_epoch = (end_slot % SLOTS_PER_EPOCH) + 1 + # Next, calculate the number of slots remaining in the current epoch + remaining_slots_in_current_epoch = SLOTS_PER_EPOCH - num_slots_in_current_epoch + # Then, calculate the number of slots in the previous epoch + num_slots_in_previous_epoch = SLOTS_PER_EPOCH - (start_slot % SLOTS_PER_EPOCH) + + # Each committee from the previous epoch only contributes a pro-rated weight + multiplier = remaining_slots_in_current_epoch / SLOTS_PER_EPOCH + num_committees = num_slots_in_current_epoch + num_slots_in_previous_epoch * multiplier return num_committees * committee_weight ``` From 569366b01e76e9819ae5743a9f93befc39ab4494 Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Wed, 17 May 2023 10:55:24 -0700 Subject: [PATCH 20/20] minor update --- fork_choice/confirmation-rule.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fork_choice/confirmation-rule.md b/fork_choice/confirmation-rule.md index 165be11297..99fd799d69 100644 --- a/fork_choice/confirmation-rule.md +++ b/fork_choice/confirmation-rule.md @@ -70,10 +70,10 @@ def get_committee_weight_between_slots(store: Store, start_slot: Slot, end_slot: if end_epoch > start_epoch + 1: return total_active_balance - if start_epoch == end_epoch: + if start_epoch == end_epoch: num_committees = end_slot - start_slot + 1 else: - # A range that spans an epoch boundary, but does not span any full epoch + # A range that spans an epoch boundary, but does not span any full epoch # needs pro-rata calculation committee_weight = get_total_active_balance(justified_state) // SLOTS_PER_EPOCH # First, calculate the number of committees in the current epoch @@ -313,7 +313,7 @@ def get_one_confirmation_score(store: Store, block_root: Root) -> int: support / maximum_support > \ 0.5 * (1 + proposer_score / maximum_support) + one_confirmation_score / 100 """ - return (100 * support - 50 * proposer_score - 1) // maximum_support - 50 + return 100 * (support - 0.5 * proposer_score - 1) // maximum_support - 50 ``` #### `get_LMD_confirmation_score`