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
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ def add_attestations_to_state(spec, state, attestations, slot):


def get_valid_attestations_at_slot(
state, spec, slot_to_attest, participation_fn=None, beacon_block_root=None
state, spec, slot_to_attest, participation_fn=None, beacon_block_root=None, payload_index=None
):
"""
Return attestations at slot `slot_to_attest`.
Expand All @@ -282,6 +282,7 @@ def participants_filter(comm, index=index):
signed=True,
filter_participant_set=participants_filter,
beacon_block_root=beacon_block_root,
payload_index=payload_index,
)

# Valid attestation must have at least one aggregation bit set
Expand All @@ -293,7 +294,7 @@ def participants_filter(comm, index=index):


def get_valid_attestation_at_slot(
state, spec, slot_to_attest, participation_fn=None, beacon_block_root=None
state, spec, slot_to_attest, participation_fn=None, beacon_block_root=None, payload_index=None
):
"""
Return the aggregate attestation post Electra.
Expand All @@ -306,6 +307,7 @@ def get_valid_attestation_at_slot(
slot_to_attest,
participation_fn=participation_fn,
beacon_block_root=beacon_block_root,
payload_index=payload_index,
)
if not attestations:
raise Exception("No valid attestations found")
Expand Down Expand Up @@ -335,7 +337,7 @@ def next_slots_with_attestations(


def get_valid_attestations_for_block_at_slot(
spec, state, slot_to_attest, beacon_block_root=None, participation_fn=None
spec, state, slot_to_attest, beacon_block_root=None, participation_fn=None, payload_index=None
):
if is_post_electra(spec):
return [
Expand All @@ -345,6 +347,7 @@ def get_valid_attestations_for_block_at_slot(
slot_to_attest,
participation_fn=participation_fn,
beacon_block_root=beacon_block_root,
payload_index=payload_index,
)
]
else:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ def build_signed_execution_payload_envelope(
payload.block_hash = state.latest_execution_payload_bid.block_hash
payload.gas_limit = state.latest_execution_payload_bid.gas_limit
payload.parent_hash = state.latest_block_hash
payload.withdrawals = state.payload_expected_withdrawals

if execution_requests is None:
execution_requests = spec.ExecutionRequests()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,20 @@
from eth_consensus_specs.test.helpers.block import (
build_empty_block,
)
from eth_consensus_specs.test.helpers.execution_payload import (
build_signed_execution_payload_envelope,
)
from eth_consensus_specs.test.helpers.fork_choice import (
add_attester_slashing,
add_block,
add_execution_payload,
get_attestation_file_name,
get_basic_store_checks,
get_genesis_forkchoice_store_and_block,
)
from eth_consensus_specs.test.helpers.forks import (
is_post_electra,
is_post_gloas,
)
from eth_consensus_specs.test.helpers.state import (
state_transition_and_sign_block,
Expand Down Expand Up @@ -190,6 +195,8 @@ def add_and_apply_block(
include_atts=True,
attestations=None,
include_att_fn: Callable[[object, object], bool] | None = None,
deposit_requests=None,
exits=None,
):
if parent_root is None:
parent_root = self.head_root()
Expand All @@ -209,9 +216,11 @@ def add_and_apply_block(
# build_empty_block will advance the state to current_slot if necessary
block = build_empty_block(self.spec, parent_state, current_slot)

# Graffiti
if graffiti is not None:
block.body.graffiti = str_to_graffiti(graffiti)

# Attestations
if attestations is not None:
for att in attestations[: self.max_attestations()]:
block.body.attestations.append(att)
Expand Down Expand Up @@ -241,14 +250,60 @@ def add_and_apply_block(
att for att in self.attestation_pool if att not in included_atts
]

# Beacon operations
if exits is not None:
assert len(exits) <= self.spec.MAX_VOLUNTARY_EXITS
for e in exits:
block.body.voluntary_exits.append(e)

# Execution requests
if is_post_electra(self.spec):
execution_requests = self.spec.ExecutionRequests()
if deposit_requests is not None:
assert is_post_electra(self.spec)
assert len(deposit_requests) <= self.spec.MAX_DEPOSIT_REQUESTS_PER_PAYLOAD
for d in deposit_requests:
execution_requests.deposits.append(d)
if is_post_gloas(self.spec):
bid = block.body.signed_execution_payload_bid.message
bid.execution_requests_root = self.spec.hash_tree_root(execution_requests)
else:
block.body.execution_requests = execution_requests

# Always build on full block post-Gloas
if is_post_gloas(self.spec) and self.spec.is_payload_verified(self.store, parent_root):
bid = block.body.signed_execution_payload_bid.message
parent_envelope = self.store.payloads[parent_root]
bid.parent_block_hash = parent_envelope.payload.block_hash
# Set parent execution requests
block.body.parent_execution_requests = parent_envelope.execution_requests

# Sign block and add it to the Store
signed_block = state_transition_and_sign_block(self.spec, parent_state, block)
block_root = self.spec.hash_tree_root(block)
for artefact in add_block(self.spec, self.store, signed_block, self.test_steps):
self.blockchain_artefacts.append(artefact)

return self.spec.Root(block.hash_tree_root())
# Build and reveal execution payload post-Gloas
if is_post_gloas(self.spec):
signed_envelope = build_signed_execution_payload_envelope(
self.spec, parent_state, block_root, signed_block, execution_requests
)
for artefact in add_execution_payload(
self.spec, self.store, signed_envelope, self.test_steps
):
self.blockchain_artefacts.append(artefact)

return block_root

def attest(self, block_root=None, slot=None, participation_rate=100, pool_and_disseminate=True):
def attest(
self,
block_root=None,
slot=None,
participation_rate=100,
pool_and_disseminate=True,
attester_indices=None,
):
assert 0 <= participation_rate <= 100

# Do not attest if participation is zero
Expand All @@ -274,11 +329,23 @@ def attest(self, block_root=None, slot=None, participation_rate=100, pool_and_di
assert self.spec.get_current_epoch(att_state) == self.spec.compute_epoch_at_slot(slot)

# Sample active validators
if participation_rate < 100:
if attester_indices is not None:
active_set = set(attester_indices)
elif participation_rate < 100:
active_set = self.sample_fraction_of_participants(att_state, slot, participation_rate)
else:
active_set = None

# Compute payload index post-Gloas
if is_post_gloas(self.spec):
block = self.store.blocks[block_root]
if slot > block.slot and self.spec.is_payload_verified(self.store, block_root):
payload_index = 1
else:
payload_index = 0
else:
payload_index = None

# Attest and add attestations to the pool
attestations = get_valid_attestations_for_block_at_slot(
self.spec,
Expand All @@ -290,6 +357,7 @@ def attest(self, block_root=None, slot=None, participation_rate=100, pool_and_di
committee & active_set if active_set is not None else committee
)
),
payload_index=payload_index,
)
if pool_and_disseminate:
self.attestation_pool.extend(attestations)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
from eth_consensus_specs.test.context import (
default_activation_threshold,
default_balances,
MINIMAL,
never_bls,
only_generator,
single_phase,
spec_test,
spec_state_test,
with_altair_and_later,
with_custom_state,
with_presets,
)
from eth_consensus_specs.test.helpers.fast_confirmation import (
Expand All @@ -17,20 +13,65 @@

@only_generator("too slow")
@with_altair_and_later
@spec_state_test
@with_presets([MINIMAL], reason="too slow")
@with_custom_state(
balances_fn=(lambda spec: default_balances(spec, num_validators=128)),
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
@never_bls
def test_fast_confirm_an_epoch(spec, state):
fcr_test = FCRTest(spec, seed=1)
_store, fcr_store = fcr_test.initialize(state)
_, fcr_store = fcr_test.initialize(state)
for _ in range(spec.SLOTS_PER_EPOCH):
fcr_test.next_slot_with_block_and_fast_confirmation()
fcr_test.next_slot_with_block_and_fast_confirmation(participation_rate=100)
# Ensure head is confirmed
assert fcr_store.confirmed_root == fcr_test.head_root()

yield from fcr_test.get_test_artefacts()


@only_generator("too slow")
@with_altair_and_later
@spec_state_test
@with_presets([MINIMAL], reason="too slow")
@never_bls
def test_fast_confirm_with_low_participation(spec, state):
fcr_test = FCRTest(spec, seed=1)
store, fcr_store = fcr_test.initialize(state)
for _ in range(3 * spec.SLOTS_PER_EPOCH):
fcr_test.next_slot_with_block_and_fast_confirmation(participation_rate=90)
# Ensure confirmed block is being advanced
assert spec.get_block_epoch(store, fcr_store.confirmed_root) + 1 >= fcr_test.current_epoch()

yield from fcr_test.get_test_artefacts()


@only_generator("too slow")
@with_altair_and_later
@spec_state_test
@with_presets([MINIMAL], reason="too slow")
@never_bls
def test_fast_confirm_with_asynchrony_at_epoch_boundary(spec, state):
fcr_test = FCRTest(spec, seed=1)
store, fcr_store = fcr_test.initialize(state)

# Move closer to epoch boundary
while fcr_test.current_slot() < spec.SLOTS_PER_EPOCH - 2:
fcr_test.next_slot_with_block_and_fast_confirmation(participation_rate=100)
# Ensure confirmed block is being advanced
assert fcr_store.confirmed_root == fcr_test.head_root()

# Run for a few slots with asynchrony
while fcr_test.current_slot() < spec.SLOTS_PER_EPOCH + 2:
fcr_test.next_slot_with_block_and_fast_confirmation(participation_rate=75)
# Confirmed block is behind the head
assert fcr_store.confirmed_root != fcr_test.head_root()

# Restore participation and run for an epoch
for _ in range(spec.SLOTS_PER_EPOCH):
fcr_test.next_slot_with_block_and_fast_confirmation(participation_rate=100)
# Ensure confirmed block is being advanced
assert spec.get_block_epoch(store, fcr_store.confirmed_root) + 1 >= fcr_test.current_epoch()

# After asynchrony followed by a period of good participation FCR provides 1-slot delay
fcr_test.next_slot_with_block_and_fast_confirmation(participation_rate=100)
assert fcr_store.confirmed_root == fcr_test.head_root()

yield from fcr_test.get_test_artefacts()
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
from eth_consensus_specs.test.context import (
default_activation_threshold,
default_balances,
MINIMAL,
never_bls,
only_generator,
single_phase,
spec_test,
spec_state_test,
with_altair_and_later,
with_custom_state,
with_presets,
)
from eth_consensus_specs.test.helpers.fast_confirmation import (
Expand All @@ -21,13 +17,8 @@

@only_generator("too slow")
@with_altair_and_later
@spec_state_test
@with_presets([MINIMAL], reason="too slow")
@with_custom_state(
balances_fn=(lambda spec: default_balances(spec, num_validators=64)),
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
@never_bls
def test_fcr_handles_single_empty_slot(spec, state):
"""
Expand Down Expand Up @@ -94,13 +85,8 @@ def test_fcr_handles_single_empty_slot(spec, state):

@only_generator("too slow")
@with_altair_and_later
@spec_state_test
@with_presets([MINIMAL], reason="too slow")
@with_custom_state(
balances_fn=(lambda spec: default_balances(spec, num_validators=64)),
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
@never_bls
# Note: The exact timing of when confirmation catches up after empty slots
# depends on the validator set size due to how attestation weight accumulates
Expand Down Expand Up @@ -164,13 +150,8 @@ def test_fcr_handles_multiple_consecutive_empty_slots(spec, state):

@only_generator("too slow")
@with_altair_and_later
@spec_state_test
@with_presets([MINIMAL], reason="too slow")
@with_custom_state(
balances_fn=(lambda spec: default_balances(spec, num_validators=64)),
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
@never_bls
def test_fcr_empty_slot_at_epoch_boundary(spec, state):
"""
Expand Down Expand Up @@ -298,13 +279,8 @@ def test_fcr_empty_slot_at_epoch_boundary(spec, state):

@only_generator("too slow")
@with_altair_and_later
@spec_state_test
@with_presets([MINIMAL], reason="too slow")
@with_custom_state(
balances_fn=(lambda spec: default_balances(spec, num_validators=64)),
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
@never_bls
def test_fcr_empty_slots_at_epoch_boundary_both_sides(spec, state):
"""
Expand Down Expand Up @@ -434,13 +410,8 @@ def test_fcr_empty_slots_at_epoch_boundary_both_sides(spec, state):

@only_generator("too slow")
@with_altair_and_later
@spec_state_test
@with_presets([MINIMAL], reason="too slow")
@with_custom_state(
balances_fn=(lambda spec: default_balances(spec, num_validators=64)),
threshold_fn=default_activation_threshold,
)
@spec_test
@single_phase
@never_bls
def test_fcr_slot_head_tracking_during_empty_slots(spec, state):
"""
Expand Down
Loading