Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions specs/gloas/fast-confirmation.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<!-- mdformat-toc start --slug=github --no-anchors --maxlevel=6 --minlevel=2 -->

- [Introduction](#introduction)
- [Helpers](#helpers)
- [Modified `get_node_for_root`](#modified-get_node_for_root)
- [Safe execution block](#safe-execution-block)
- [Modified `get_safe_execution_block_hash`](#modified-get_safe_execution_block_hash)

Expand All @@ -13,6 +15,19 @@
This is the modification of the fast confirmation rule specification
accompanying Gloas.

## Helpers

### Modified `get_node_for_root`

*Note*: This function is modified to return an extended `ForkChoiceNode`
structure with `PAYLOAD_STATUS_PENDING` payload status.

```python
def get_node_for_root(block_root: Root) -> ForkChoiceNode:
# [Modified in Gloas:EIP7732]
return ForkChoiceNode(root=block_root, payload_status=PAYLOAD_STATUS_PENDING)
```

## Safe execution block

### Modified `get_safe_execution_block_hash`
Expand Down
13 changes: 0 additions & 13 deletions specs/gloas/fork-choice.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
- [New `payload_data_availability`](#new-payload_data_availability)
- [New `get_parent_payload_status`](#new-get_parent_payload_status)
- [New `is_parent_node_full`](#new-is_parent_node_full)
- [Modified `get_block_root_node`](#modified-get_block_root_node)
- [Modified `get_ancestor`](#modified-get_ancestor)
- [Modified `is_ancestor`](#modified-is_ancestor)
- [Modified `get_checkpoint_block`](#modified-get_checkpoint_block)
Expand Down Expand Up @@ -331,18 +330,6 @@ def is_parent_node_full(store: Store, block: BeaconBlock) -> bool:
return get_parent_payload_status(store, block) == PAYLOAD_STATUS_FULL
```

### Modified `get_block_root_node`

*Note*: This function is modified to return an extended `ForkChoiceNode`
structure with `PAYLOAD_STATUS_PENDING` payload status as a common ancestor of
all nodes referring to a given `block_root`.

```python
def get_block_root_node(block_root: Root) -> ForkChoiceNode:
# [Modified in Gloas:EIP7732]
return ForkChoiceNode(root=block_root, payload_status=PAYLOAD_STATUS_PENDING)
```

### Modified `get_ancestor`

*Note*: `get_ancestor` is modified to return whether the chain is based on an
Expand Down
22 changes: 15 additions & 7 deletions specs/phase0/fast-confirmation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- [`FastConfirmationStore`](#fastconfirmationstore)
- [`get_fast_confirmation_store`](#get_fast_confirmation_store)
- [Misc helper functions](#misc-helper-functions)
- [`get_node_for_root`](#get_node_for_root)
- [`get_block_slot`](#get_block_slot)
- [`get_block_epoch`](#get_block_epoch)
- [`get_checkpoint_for_block`](#get_checkpoint_for_block)
Expand Down Expand Up @@ -136,6 +137,13 @@ def get_fast_confirmation_store(store: Store) -> FastConfirmationStore:

#### Misc helper functions

#### `get_node_for_root`

```python
def get_node_for_root(block_root: Root) -> ForkChoiceNode:
return ForkChoiceNode(root=block_root)
```

##### `get_block_slot`

```python
Expand Down Expand Up @@ -611,7 +619,7 @@ def is_one_confirmed(store: Store, balance_source: BeaconState, block_root: Root
"""
Return ``True`` if and only if the block is LMD-GHOST safe.
"""
support = get_attestation_score(store, get_block_root_node(block_root), balance_source)
support = get_attestation_score(store, get_node_for_root(block_root), balance_source)
safety_threshold = compute_safety_threshold(store, block_root, balance_source)
return support > safety_threshold
```
Expand Down Expand Up @@ -640,8 +648,8 @@ def is_confirmed_chain_safe(fcr_store: FastConfirmationStore, confirmed_root: Ro
# Check if the confirmed_root is descendant of current_epoch_observed_justified_checkpoint
if not is_ancestor(
store,
get_block_root_node(confirmed_root),
get_block_root_node(fcr_store.current_epoch_observed_justified_checkpoint.root),
get_node_for_root(confirmed_root),
get_node_for_root(fcr_store.current_epoch_observed_justified_checkpoint.root),
):
return False

Expand All @@ -655,7 +663,7 @@ def is_confirmed_chain_safe(fcr_store: FastConfirmationStore, confirmed_root: Ro
# as if it is successful, reconfirmation of the ancestors is implied.
ancestor_at_previous_epoch_start = get_ancestor(
store,
get_block_root_node(confirmed_root),
get_node_for_root(confirmed_root),
compute_start_slot_at_epoch(Epoch(current_epoch - 1)),
).root
if get_block_epoch(store, ancestor_at_previous_epoch_start) + 1 == current_epoch:
Expand Down Expand Up @@ -883,8 +891,8 @@ def find_latest_confirmed_descendant(
# if it is a descendant of the block that is attempted to be confirmed
if not is_ancestor(
store,
get_block_root_node(fcr_store.previous_slot_head),
get_block_root_node(block_root),
get_node_for_root(fcr_store.previous_slot_head),
get_node_for_root(block_root),
):
break

Expand Down Expand Up @@ -975,7 +983,7 @@ def get_latest_confirmed(fcr_store: FastConfirmationStore) -> Root:
head = get_head(store).root
if (
get_block_epoch(store, confirmed_root) + 1 < current_epoch
or not is_ancestor(store, get_block_root_node(head), get_block_root_node(confirmed_root))
or not is_ancestor(store, get_node_for_root(head), get_node_for_root(confirmed_root))
or (
is_start_slot_at_epoch(get_current_slot(store))
and not is_confirmed_chain_safe(fcr_store, confirmed_root)
Expand Down
15 changes: 0 additions & 15 deletions specs/phase0/fork-choice.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
- [`get_current_slot`](#get_current_slot)
- [`get_current_store_epoch`](#get_current_store_epoch)
- [`compute_slots_since_epoch_start`](#compute_slots_since_epoch_start)
- [`get_block_root_node`](#get_block_root_node)
- [`get_ancestor`](#get_ancestor)
- [`is_ancestor`](#is_ancestor)
- [`calculate_committee_fraction`](#calculate_committee_fraction)
Expand Down Expand Up @@ -263,20 +262,6 @@ def compute_slots_since_epoch_start(slot: Slot) -> int:
return slot - compute_start_slot_at_epoch(compute_epoch_at_slot(slot))
```

#### `get_block_root_node`

*Note*: The semantics of this function is to construct a `ForkChoiceNode` that
is a common ancestor for all fork choice nodes referring to the same beacon
block. Future protocol versions may introduce new types of nodes, but this
function assumes that the common ancestor node type will always exist. This
function is introduced for future compatibility, it allows the specification
code that operates over beacon blocks to be agnostic to the protocol version.

```python
def get_block_root_node(block_root: Root) -> ForkChoiceNode:
return ForkChoiceNode(root=block_root)
```

#### `get_ancestor`

```python
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ def apply_attester_slashing(

def compute_score_and_threshold(self, block_root) -> (int, int):
balance_source = self.spec.get_current_balance_source(self.fcr_store)
node = self.spec.get_block_root_node(block_root)
node = self.spec.get_node_for_root(block_root)
score = self.spec.get_attestation_score(self.store, node, balance_source)
safety_threshold = self.spec.compute_safety_threshold(
self.store, block_root, balance_source
Expand Down
15 changes: 12 additions & 3 deletions tests/core/pyspec/eth_consensus_specs/test/helpers/fork_choice.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,15 @@ def wrap(flag: AtomicBoolean):
assert is_called.value


def get_fork_choice_node(spec, root, payload_status=None):
if is_post_gloas(spec):
if payload_status is None:
payload_status = spec.PAYLOAD_STATUS_PENDING
return spec.ForkChoiceNode(root=root, payload_status=payload_status)
else:
return spec.ForkChoiceNode(root=root)


def get_anchor_root(spec, state):
anchor_block_header = state.latest_block_header.copy()
if anchor_block_header.state_root == spec.Bytes32():
Expand Down Expand Up @@ -583,7 +592,7 @@ def get_weighed_node_checks(spec, store, node):

def get_viable_for_head_checks(spec, store):
filtered_blocks = spec.get_filtered_block_tree(store)
root_node = spec.get_block_root_node(store.justified_checkpoint.root)
root_node = get_fork_choice_node(spec, store.justified_checkpoint.root)
pending_nodes = [root_node]
leaves_viable_for_head = []

Expand Down Expand Up @@ -689,11 +698,11 @@ def is_ancestor(spec, store, root_or_node, maybe_ancestor_root_or_node) -> bool:
if hasattr(root_or_node, "root"):
node = root_or_node
else:
node = spec.get_block_root_node(root_or_node)
node = get_fork_choice_node(spec, root_or_node)

if hasattr(maybe_ancestor_root_or_node, "root"):
maybe_ancestor = maybe_ancestor_root_or_node
else:
maybe_ancestor = spec.get_block_root_node(maybe_ancestor_root_or_node)
maybe_ancestor = get_fork_choice_node(spec, maybe_ancestor_root_or_node)

return spec.is_ancestor(store, node, maybe_ancestor)
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def test_is_one_confirmed_passes_with_full_participation(spec, state):

# Inspect the individual terms of the inequality
current_slot = spec.get_current_slot(store)
node_b = spec.get_block_root_node(block_b)
node_b = spec.get_node_for_root(block_b)
support = spec.get_attestation_score(store, node_b, balance_source)
proposer_score = spec.compute_proposer_score(balance_source)
total_active_balance = spec.get_total_active_balance(balance_source)
Expand Down Expand Up @@ -136,7 +136,7 @@ def test_is_one_confirmed_fails_with_low_participation(spec, state):

# Inspect the individual terms
current_slot = spec.get_current_slot(store)
node_b = spec.get_block_root_node(block_b)
node_b = spec.get_node_for_root(block_b)
support = spec.get_attestation_score(store, node_b, balance_source)
proposer_score = spec.compute_proposer_score(balance_source)
total_active_balance = spec.get_total_active_balance(balance_source)
Expand Down Expand Up @@ -258,7 +258,7 @@ def test_is_one_confirmed_slashing_non_supporters_helps(spec, state):

balance_source = spec.get_current_balance_source(fcr_store)

node_b = spec.get_block_root_node(block_b)
node_b = spec.get_node_for_root(block_b)
support_before = spec.get_attestation_score(store, node_b, balance_source)
adversarial_weight_before = spec.get_adversarial_weight(store, balance_source, block_b)

Expand All @@ -281,7 +281,7 @@ def test_is_one_confirmed_slashing_non_supporters_helps(spec, state):
fcr.apply_attester_slashing(slashing_indices=non_supporters, slot=fcr.current_slot())

# Support must be unchanged — we only slashed non-voters for B
node_b = spec.get_block_root_node(block_b)
node_b = spec.get_node_for_root(block_b)
support_after = spec.get_attestation_score(store, node_b, balance_source)
assert support_after == support_before, (
f"Support changed after slashing non-voters: {support_before} -> {support_after}"
Expand Down Expand Up @@ -413,7 +413,7 @@ def test_is_one_confirmed_empty_slot_discount(spec, state):

# Verify the discount is still contributing: without it, would it still pass?
current_slot = spec.get_current_slot(store)
node_b = spec.get_block_root_node(block_b)
node_b = spec.get_node_for_root(block_b)
support_b = int(spec.get_attestation_score(store, node_b, balance_source))
proposer_b = int(spec.compute_proposer_score(balance_source))
total_active_balance = spec.get_total_active_balance(balance_source)
Expand Down Expand Up @@ -656,8 +656,8 @@ def test_is_one_confirmed_fails_with_competing_branch(spec, state):
balance_source = spec.get_current_balance_source(fcr_store)

# Both must have some support
node_b1 = spec.get_block_root_node(block_b1)
node_b2 = spec.get_block_root_node(block_b2)
node_b1 = spec.get_node_for_root(block_b1)
node_b2 = spec.get_node_for_root(block_b2)
support_b1 = int(spec.get_attestation_score(store, node_b1, balance_source))
support_b2 = int(spec.get_attestation_score(store, node_b2, balance_source))
assert support_b1 > 0, "B1 should have some support"
Expand Down Expand Up @@ -864,7 +864,7 @@ def test_is_one_confirmed_epoch_crossing_adversarial_range_matters(spec, state):
)
)

node_b = spec.get_block_root_node(block_b)
node_b = spec.get_node_for_root(block_b)
support = int(spec.get_attestation_score(store, node_b, balance_source))
max_support = int(
spec.estimate_committee_weight_between_slots(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

@dataclass
class PreviousEpochTestSpecification:
prev_head_ancestor: bool # is_ancestor(store, spec.get_block_root_node(fcr_store.previous_slot_head), spec.get_block_root_node((block_root))
prev_head_ancestor: bool # is_ancestor(store, spec.get_node_for_root(fcr_store.previous_slot_head), spec.get_node_for_root((block_root))
first_slot_call: bool # is_start_slot_at_epoch(get_current_slot(store))
is_one_confirmed: bool # is_one_confirmed(store, get_current_balance_source(store), block_root)
no_conflicting_chkp: bool # will_no_conflicting_checkpoint_be_justified(store)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
add_attestation,
add_block,
check_head_against_root,
get_fork_choice_node,
get_genesis_forkchoice_store_and_block,
on_tick_and_append_step,
tick_and_add_block,
Expand Down Expand Up @@ -117,7 +118,7 @@ def _get_greater_than_proposer_boost_score(spec, store, state, proposer_boost_ro
block = store.blocks[root]
proposer_score = 0
if (
spec.get_ancestor(store, spec.get_block_root_node(root), block.slot).root
spec.get_ancestor(store, get_fork_choice_node(spec, root), block.slot).root
== proposer_boost_root
):
num_validators = len(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
apply_next_slots_with_attestations,
check_head_against_root,
find_next_justifying_slot,
get_fork_choice_node,
get_genesis_forkchoice_store_and_block,
is_ready_to_justify,
on_tick_and_append_step,
Expand Down Expand Up @@ -524,7 +525,7 @@ def test_proposer_boost(spec, state):
on_tick_and_append_step(spec, store, time, test_steps)
yield from add_block(spec, store, signed_block, test_steps)
assert store.proposer_boost_root == spec.hash_tree_root(block)
node = spec.get_block_root_node(spec.hash_tree_root(block))
node = get_fork_choice_node(spec, spec.hash_tree_root(block))
assert spec.get_weight(store, node) > 0

# Ensure that boost is removed after slot is over
Expand All @@ -535,7 +536,7 @@ def test_proposer_boost(spec, state):
)
on_tick_and_append_step(spec, store, time, test_steps)
assert store.proposer_boost_root == spec.Root()
node = spec.get_block_root_node(spec.hash_tree_root(block))
node = get_fork_choice_node(spec, spec.hash_tree_root(block))
assert spec.get_weight(store, node) == 0

next_slots(spec, state, 3)
Expand All @@ -547,7 +548,7 @@ def test_proposer_boost(spec, state):
on_tick_and_append_step(spec, store, time, test_steps)
yield from add_block(spec, store, signed_block, test_steps)
assert store.proposer_boost_root == spec.hash_tree_root(block)
node = spec.get_block_root_node(spec.hash_tree_root(block))
node = get_fork_choice_node(spec, spec.hash_tree_root(block))
assert spec.get_weight(store, node) > 0

# Ensure that boost is removed after slot is over
Expand All @@ -558,7 +559,7 @@ def test_proposer_boost(spec, state):
)
on_tick_and_append_step(spec, store, time, test_steps)
assert store.proposer_boost_root == spec.Root()
node = spec.get_block_root_node(spec.hash_tree_root(block))
node = get_fork_choice_node(spec, spec.hash_tree_root(block))
assert spec.get_weight(store, node) == 0

test_steps.append(
Expand Down Expand Up @@ -646,7 +647,7 @@ def test_proposer_boost_is_first_block(spec, state):
yield from add_block(spec, store, signed_block_a, test_steps)
# `proposer_boost_root` is now `block_a`
assert store.proposer_boost_root == spec.hash_tree_root(block_a)
node_a = spec.get_block_root_node(spec.hash_tree_root(block_a))
node_a = get_fork_choice_node(spec, spec.hash_tree_root(block_a))
assert spec.get_weight(store, node_a) > 0
test_steps.append(
{
Expand All @@ -664,7 +665,7 @@ def test_proposer_boost_is_first_block(spec, state):
yield from add_block(spec, store, signed_block_b, test_steps)
# `proposer_boost_root` is still `block_a`
assert store.proposer_boost_root == spec.hash_tree_root(block_a)
node_b = spec.get_block_root_node(spec.hash_tree_root(block_b))
node_b = get_fork_choice_node(spec, spec.hash_tree_root(block_b))
assert spec.get_weight(store, node_b) == 0
test_steps.append(
{
Expand Down Expand Up @@ -1220,7 +1221,7 @@ def test_justified_update_not_realized_finality(spec, state):
last_block = signed_blocks[-1]
last_block_root = last_block.message.hash_tree_root()
ancestor_at_finalized_slot = spec.get_ancestor(
store, spec.get_block_root_node(last_block_root), finalized_block.slot
store, get_fork_choice_node(spec, last_block_root), finalized_block.slot
).root

assert ancestor_at_finalized_slot == store.finalized_checkpoint.root
Expand Down Expand Up @@ -1301,7 +1302,7 @@ def test_justified_update_monotonic(spec, state):
last_block = signed_blocks[-1]
last_block_root = last_block.message.hash_tree_root()
ancestor_at_finalized_slot = spec.get_ancestor(
store, spec.get_block_root_node(last_block_root), finalized_block.slot
store, get_fork_choice_node(spec, last_block_root), finalized_block.slot
).root
assert ancestor_at_finalized_slot == finalized_root

Expand Down