Skip to content

Commit 4bd2f92

Browse files
committed
custody-based fork-choice
1 parent 572ca9e commit 4bd2f92

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

pysetup/spec_builders/eip7594.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ def imports(cls, preset_name: str):
1212
return f'''
1313
from eth2spec.deneb import {preset_name} as deneb
1414
'''
15+
16+
17+
@classmethod
18+
def sundry_functions(cls) -> str:
19+
return """
20+
def retrieve_column_sidecars(beacon_block_root: Root) -> Sequence[DataColumnSidecar]:
21+
return []
22+
"""
1523

1624
@classmethod
1725
def hardcoded_custom_type_dep_constants(cls, spec_object) -> str:
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# EIP-7594 -- Fork Choice
2+
3+
## Table of contents
4+
<!-- TOC -->
5+
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
6+
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
7+
8+
- [Introduction](#introduction)
9+
- [Helpers](#helpers)
10+
- [Modified `is_data_available`](#modified-is_data_available)
11+
- [New `is_chain_available`](#new-is_chain_available)
12+
- [Modified `get_head`](#modified-get_head)
13+
- [New `is_peer_sampling_required`](#new-is_peer_sampling_required)
14+
- [Updated fork-choice handlers](#updated-fork-choice-handlers)
15+
- [Modified `on_block`](#modified-on_block)
16+
- [Pull-up tip helpers](#pull-up-tip-helpers)
17+
- [Modified `compute_pulled_up_tip`](#modified-compute_pulled_up_tip)
18+
19+
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
20+
<!-- /TOC -->
21+
22+
## Introduction
23+
24+
This is the modification of the fork choice accompanying EIP-7594.
25+
26+
### Helpers
27+
28+
#### Modified `is_data_available`
29+
30+
```python
31+
def is_data_available(beacon_block_root: Root) -> bool:
32+
# `retrieve_column_sidecars` is implementation and context dependent, replacing `retrieve_blobs_and_proofs`.
33+
# For the given block root, it returns all column sidecars to custody, or raises an exception if they are not available. # The p2p network does not guarantee sidecar retrieval outside of `MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS` epochs.
34+
column_sidecars = retrieve_column_sidecars(beacon_block_root)
35+
return all(
36+
verify_data_column_sidecar_kzg_proofs(column_sidecar)
37+
for column_sidecar in column_sidecars
38+
)
39+
```
40+
41+
#### Modified `get_head`
42+
43+
*Note*: children of the current `head` are required to be available in order to be considered by the fork-choice.
44+
45+
```python
46+
def get_head(store: Store) -> Root:
47+
# Get filtered block tree that only includes viable branches
48+
blocks = get_filtered_block_tree(store)
49+
# Execute the LMD-GHOST fork choice
50+
head = store.justified_checkpoint.root
51+
while True:
52+
# Get available children for the current slot
53+
children = [
54+
root for (root, block) in blocks.items()
55+
if (
56+
block.parent_root == head
57+
and is_data_available(root)
58+
)
59+
]
60+
if len(children) == 0:
61+
return head
62+
# Sort by latest attesting balance with ties broken lexicographically
63+
# Ties broken by favoring block with lexicographically higher root
64+
head = max(children, key=lambda root: (get_weight(store, root), root))
65+
```
66+
67+
## Updated fork-choice handlers
68+
69+
### Modified `on_block`
70+
71+
*Note*: The blob data availability check is removed.
72+
73+
```python
74+
def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
75+
"""
76+
Run ``on_block`` upon receiving a new block.
77+
"""
78+
block = signed_block.message
79+
# Parent block must be known
80+
assert block.parent_root in store.block_states
81+
# Make a copy of the state to avoid mutability issues
82+
state = copy(store.block_states[block.parent_root])
83+
# Blocks cannot be in the future. If they are, their consideration must be delayed until they are in the past.
84+
assert get_current_slot(store) >= block.slot
85+
86+
# Check that block is later than the finalized epoch slot (optimization to reduce calls to get_ancestor)
87+
finalized_slot = compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)
88+
assert block.slot > finalized_slot
89+
# Check block is a descendant of the finalized block at the checkpoint finalized slot
90+
finalized_checkpoint_block = get_checkpoint_block(
91+
store,
92+
block.parent_root,
93+
store.finalized_checkpoint.epoch,
94+
)
95+
assert store.finalized_checkpoint.root == finalized_checkpoint_block
96+
97+
# Check the block is valid and compute the post-state
98+
block_root = hash_tree_root(block)
99+
state_transition(state, signed_block, True)
100+
101+
# Add new block to the store
102+
store.blocks[block_root] = block
103+
# Add new state for this block to the store
104+
store.block_states[block_root] = state
105+
106+
# Add block timeliness to the store
107+
time_into_slot = (store.time - store.genesis_time) % SECONDS_PER_SLOT
108+
is_before_attesting_interval = time_into_slot < SECONDS_PER_SLOT // INTERVALS_PER_SLOT
109+
is_timely = get_current_slot(store) == block.slot and is_before_attesting_interval
110+
store.block_timeliness[hash_tree_root(block)] = is_timely
111+
112+
# Add proposer score boost if the block is timely and not conflicting with an existing block
113+
is_first_block = store.proposer_boost_root == Root()
114+
if is_timely and is_first_block:
115+
store.proposer_boost_root = hash_tree_root(block)
116+
117+
# Update checkpoints in store if necessary
118+
update_checkpoints(store, state.current_justified_checkpoint, state.finalized_checkpoint)
119+
120+
# Eagerly compute unrealized justification and finality.
121+
compute_pulled_up_tip(store, block_root)
122+
```

0 commit comments

Comments
 (0)