Skip to content

Commit e1647fe

Browse files
authored
Fix payload_timeliness and payload_data_availability comparisons (#5331)
1 parent e17a138 commit e1647fe

5 files changed

Lines changed: 265 additions & 2 deletions

File tree

specs/gloas/fork-choice.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ def payload_timeliness(store: Store, root: Root, timely: bool) -> bool:
291291
return not timely
292292

293293
votes = store.payload_timeliness_vote[root]
294-
return sum(vote is timely for vote in votes) > PAYLOAD_TIMELY_THRESHOLD
294+
return sum(vote == timely for vote in votes) > PAYLOAD_TIMELY_THRESHOLD
295295
```
296296

297297
### New `payload_data_availability`
@@ -312,7 +312,7 @@ def payload_data_availability(store: Store, root: Root, available: bool) -> bool
312312
return not available
313313

314314
votes = store.payload_data_availability_vote[root]
315-
return sum(vote is available for vote in votes) > DATA_AVAILABILITY_TIMELY_THRESHOLD
315+
return sum(vote == available for vote in votes) > DATA_AVAILABILITY_TIMELY_THRESHOLD
316316
```
317317

318318
### New `get_parent_payload_status`

tests/core/pyspec/eth_consensus_specs/test/gloas/block_processing/test_process_payload_attestation.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def prepare_signed_payload_attestation(
4242
slot=None,
4343
beacon_block_root=None,
4444
payload_present=True,
45+
blob_data_available=False,
4546
attesting_indices=None,
4647
valid_signature=True,
4748
domain_epoch=None,
@@ -82,6 +83,7 @@ def prepare_signed_payload_attestation(
8283
beacon_block_root=beacon_block_root,
8384
slot=slot,
8485
payload_present=payload_present,
86+
blob_data_available=blob_data_available,
8587
)
8688

8789
# Create payload attestation
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from eth_consensus_specs.test.context import (
2+
default_activation_threshold,
3+
single_phase,
4+
spec_test,
5+
with_custom_state,
6+
with_gloas_and_later,
7+
)
8+
from eth_consensus_specs.test.helpers.fork_choice import (
9+
add_payload_vote_checks,
10+
output_head_check,
11+
)
12+
from eth_consensus_specs.test.helpers.payload_attestation import (
13+
ptc_size_balances,
14+
setup_verified_parent_with_distinct_ptc,
15+
vote_via_child_block,
16+
)
17+
18+
19+
@with_gloas_and_later
20+
@spec_test
21+
@with_custom_state(balances_fn=ptc_size_balances, threshold_fn=default_activation_threshold)
22+
@single_phase
23+
def test_payload_data_availability_at_threshold_returns_false(spec, state):
24+
"""
25+
Test that DATA_AVAILABILITY_TIMELY_THRESHOLD available votes return False.
26+
"""
27+
store, block_root, block_state, test_steps = yield from setup_verified_parent_with_distinct_ptc(
28+
spec, state
29+
)
30+
31+
yield from vote_via_child_block(
32+
spec,
33+
store,
34+
block_root,
35+
block_state,
36+
positions=range(spec.DATA_AVAILABILITY_TIMELY_THRESHOLD),
37+
test_steps=test_steps,
38+
)
39+
40+
assert not spec.payload_data_availability(store, block_root, available=True)
41+
add_payload_vote_checks(store, block_root, test_steps)
42+
output_head_check(spec, store, test_steps)
43+
yield "steps", test_steps
44+
45+
46+
@with_gloas_and_later
47+
@spec_test
48+
@with_custom_state(balances_fn=ptc_size_balances, threshold_fn=default_activation_threshold)
49+
@single_phase
50+
def test_payload_data_availability_above_threshold_returns_true(spec, state):
51+
"""
52+
Test that DATA_AVAILABILITY_TIMELY_THRESHOLD + 1 available votes return True.
53+
"""
54+
store, block_root, block_state, test_steps = yield from setup_verified_parent_with_distinct_ptc(
55+
spec, state
56+
)
57+
58+
yield from vote_via_child_block(
59+
spec,
60+
store,
61+
block_root,
62+
block_state,
63+
positions=range(spec.DATA_AVAILABILITY_TIMELY_THRESHOLD + 1),
64+
test_steps=test_steps,
65+
)
66+
67+
assert spec.payload_data_availability(store, block_root, available=True)
68+
add_payload_vote_checks(store, block_root, test_steps)
69+
output_head_check(spec, store, test_steps)
70+
yield "steps", test_steps
71+
72+
73+
@with_gloas_and_later
74+
@spec_test
75+
@with_custom_state(balances_fn=ptc_size_balances, threshold_fn=default_activation_threshold)
76+
@single_phase
77+
def test_payload_data_availability_single_vote_returns_false(spec, state):
78+
"""
79+
Test that None votes are not counted as available.
80+
"""
81+
store, block_root, block_state, test_steps = yield from setup_verified_parent_with_distinct_ptc(
82+
spec, state
83+
)
84+
85+
yield from vote_via_child_block(
86+
spec,
87+
store,
88+
block_root,
89+
block_state,
90+
positions=[0],
91+
test_steps=test_steps,
92+
)
93+
94+
assert not spec.payload_data_availability(store, block_root, available=True)
95+
add_payload_vote_checks(store, block_root, test_steps)
96+
output_head_check(spec, store, test_steps)
97+
yield "steps", test_steps
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from eth_consensus_specs.test.context import (
2+
default_activation_threshold,
3+
single_phase,
4+
spec_test,
5+
with_custom_state,
6+
with_gloas_and_later,
7+
)
8+
from eth_consensus_specs.test.helpers.fork_choice import (
9+
add_payload_vote_checks,
10+
output_head_check,
11+
)
12+
from eth_consensus_specs.test.helpers.payload_attestation import (
13+
ptc_size_balances,
14+
setup_verified_parent_with_distinct_ptc,
15+
vote_via_child_block,
16+
)
17+
18+
19+
@with_gloas_and_later
20+
@spec_test
21+
@with_custom_state(balances_fn=ptc_size_balances, threshold_fn=default_activation_threshold)
22+
@single_phase
23+
def test_payload_timeliness_at_threshold_returns_false(spec, state):
24+
"""
25+
Test that PAYLOAD_TIMELY_THRESHOLD timely votes return False.
26+
"""
27+
store, block_root, block_state, test_steps = yield from setup_verified_parent_with_distinct_ptc(
28+
spec, state
29+
)
30+
31+
yield from vote_via_child_block(
32+
spec,
33+
store,
34+
block_root,
35+
block_state,
36+
positions=range(spec.PAYLOAD_TIMELY_THRESHOLD),
37+
test_steps=test_steps,
38+
)
39+
40+
assert not spec.payload_timeliness(store, block_root, timely=True)
41+
add_payload_vote_checks(store, block_root, test_steps)
42+
output_head_check(spec, store, test_steps)
43+
yield "steps", test_steps
44+
45+
46+
@with_gloas_and_later
47+
@spec_test
48+
@with_custom_state(balances_fn=ptc_size_balances, threshold_fn=default_activation_threshold)
49+
@single_phase
50+
def test_payload_timeliness_above_threshold_returns_true(spec, state):
51+
"""
52+
Test that PAYLOAD_TIMELY_THRESHOLD + 1 timely votes return True.
53+
"""
54+
store, block_root, block_state, test_steps = yield from setup_verified_parent_with_distinct_ptc(
55+
spec, state
56+
)
57+
58+
yield from vote_via_child_block(
59+
spec,
60+
store,
61+
block_root,
62+
block_state,
63+
positions=range(spec.PAYLOAD_TIMELY_THRESHOLD + 1),
64+
test_steps=test_steps,
65+
)
66+
67+
assert spec.payload_timeliness(store, block_root, timely=True)
68+
add_payload_vote_checks(store, block_root, test_steps)
69+
output_head_check(spec, store, test_steps)
70+
yield "steps", test_steps
71+
72+
73+
@with_gloas_and_later
74+
@spec_test
75+
@with_custom_state(balances_fn=ptc_size_balances, threshold_fn=default_activation_threshold)
76+
@single_phase
77+
def test_payload_timeliness_single_vote_returns_false(spec, state):
78+
"""
79+
Test that None votes are not counted as timely.
80+
"""
81+
store, block_root, block_state, test_steps = yield from setup_verified_parent_with_distinct_ptc(
82+
spec, state
83+
)
84+
85+
yield from vote_via_child_block(
86+
spec,
87+
store,
88+
block_root,
89+
block_state,
90+
positions=[0],
91+
test_steps=test_steps,
92+
)
93+
94+
assert not spec.payload_timeliness(store, block_root, timely=True)
95+
add_payload_vote_checks(store, block_root, test_steps)
96+
output_head_check(spec, store, test_steps)
97+
yield "steps", test_steps

tests/core/pyspec/eth_consensus_specs/test/helpers/payload_attestation.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
from eth_consensus_specs.test.gloas.block_processing.test_process_payload_attestation import (
22
prepare_signed_payload_attestation,
33
)
4+
from eth_consensus_specs.test.helpers.block import build_empty_block_for_next_slot
5+
from eth_consensus_specs.test.helpers.execution_payload import (
6+
build_signed_execution_payload_envelope,
7+
)
8+
from eth_consensus_specs.test.helpers.fork_choice import (
9+
add_execution_payload,
10+
setup_one_block_store,
11+
tick_and_add_block,
12+
)
13+
from eth_consensus_specs.test.helpers.state import state_transition_and_sign_block
414

515

616
def get_random_payload_attestations(spec, state, rng):
@@ -36,3 +46,60 @@ def get_random_payload_attestations(spec, state, rng):
3646
)
3747

3848
return [payload_attestation]
49+
50+
51+
def ptc_size_balances(spec):
52+
"""
53+
Return a balances list sized to PTC_SIZE so each PTC seat can be pinned to a unique validator.
54+
"""
55+
return [spec.MAX_EFFECTIVE_BALANCE] * spec.PTC_SIZE
56+
57+
58+
def setup_verified_parent_with_distinct_ptc(spec, state):
59+
"""
60+
Build a Gloas store with one block at state.slot+1 whose envelope has been delivered,
61+
and pin each PTC seat for that slot to a distinct validator so each cast vote later
62+
lands on exactly one position.
63+
"""
64+
block_slot = state.slot + 1
65+
window_idx = spec.SLOTS_PER_EPOCH + block_slot % spec.SLOTS_PER_EPOCH
66+
for i in range(spec.PTC_SIZE):
67+
state.ptc_window[window_idx][i] = spec.ValidatorIndex(i)
68+
69+
store, block_root, block_state, signed_block, test_steps = yield from setup_one_block_store(
70+
spec, state
71+
)
72+
envelope = build_signed_execution_payload_envelope(spec, block_state, block_root, signed_block)
73+
yield from add_execution_payload(spec, store, envelope, test_steps)
74+
return store, block_root, block_state, test_steps
75+
76+
77+
def vote_via_child_block(
78+
spec,
79+
store,
80+
parent_root,
81+
parent_state,
82+
positions,
83+
test_steps,
84+
payload_present=True,
85+
blob_data_available=True,
86+
):
87+
"""
88+
Deliver PTC votes for parent_root through a child block at parent_state.slot + 1
89+
that carries a PayloadAttestation aggregate.
90+
"""
91+
aggregate = prepare_signed_payload_attestation(
92+
spec,
93+
parent_state,
94+
slot=parent_state.slot,
95+
beacon_block_root=parent_root,
96+
payload_present=payload_present,
97+
blob_data_available=blob_data_available,
98+
attesting_indices=[spec.ValidatorIndex(p) for p in positions],
99+
)
100+
101+
child_state = parent_state.copy()
102+
child_block = build_empty_block_for_next_slot(spec, child_state)
103+
child_block.body.payload_attestations.append(aggregate)
104+
signed_child = state_transition_and_sign_block(spec, child_state, child_block)
105+
yield from tick_and_add_block(spec, store, signed_child, test_steps)

0 commit comments

Comments
 (0)