diff --git a/tests/core/pyspec/eth_consensus_specs/test/gloas/networking/test_gossip_execution_payload_bid.py b/tests/core/pyspec/eth_consensus_specs/test/gloas/networking/test_gossip_execution_payload_bid.py index 826b9a8a30..a284d5ebae 100644 --- a/tests/core/pyspec/eth_consensus_specs/test/gloas/networking/test_gossip_execution_payload_bid.py +++ b/tests/core/pyspec/eth_consensus_specs/test/gloas/networking/test_gossip_execution_payload_bid.py @@ -10,6 +10,7 @@ activate_builders, build_signed_bid, get_blocks_meta, + record_block_in_store, record_head_payload, setup_store_advanced_for_bid, setup_store_finalized_with_pending_payment, @@ -2464,6 +2465,147 @@ def _run_bid_gas_limit_scenario( yield "messages", "meta", messages +@with_gloas_and_later +@spec_state_test_with_matching_config +def test_gossip_execution_payload_bid__valid_gas_limit_after_empty_parent(spec, state): + """A bid after an empty head uses its execution parent's gas limit.""" + anchor_state = state.copy() + yield "topic", "meta", "execution_payload_bid" + + store, blocks, _ = setup_store_advanced_for_bid(spec, state) + + parent_gas_limit = spec.Uint64(30_000_000) + payload_block_hash = spec.Hash32(b"\xaa" * 32) + payload_block = build_empty_block_for_next_slot(spec, state) + payload_bid = payload_block.body.signed_execution_payload_bid.message + payload_bid.block_hash = payload_block_hash + payload_bid.gas_limit = parent_gas_limit + signed_payload_block = state_transition_and_sign_block(spec, state, payload_block) + payload_root = record_block_in_store(spec, store, signed_payload_block, state.copy()) + blocks.append(signed_payload_block) + + payload_state = state.copy() + signed_envelope = build_signed_execution_payload_envelope( + spec, payload_state, payload_root, signed_payload_block + ) + store.payloads[payload_root] = signed_envelope.message + + # Leave the next block's payload unreceived so it becomes the empty head. + empty_parent_gas_limit = spec.Uint64(30_029_295) + empty_parent = build_empty_block_for_next_slot(spec, state) + empty_parent_bid = empty_parent.body.signed_execution_payload_bid.message + empty_parent_bid.parent_block_hash = payload_block_hash + empty_parent_bid.gas_limit = empty_parent_gas_limit + signed_empty_parent = state_transition_and_sign_block(spec, state, empty_parent) + parent_root = record_block_in_store(spec, store, signed_empty_parent, state.copy()) + blocks.append(signed_empty_parent) + + finalized_checkpoint_meta = activate_builders(spec, state, store, blocks) + yield "state", anchor_state + for signed in blocks: + yield get_filename(signed), signed + yield get_filename(signed_envelope), signed_envelope + blocks_meta = get_blocks_meta(blocks) + blocks_meta[-2]["payload"] = get_filename(signed_envelope) + yield "blocks", "meta", blocks_meta + yield "finalized_checkpoint", "meta", finalized_checkpoint_meta + + seen = get_seen(spec) + time_ms = spec.compute_time_at_slot_ms(store, state.slot) + yield "current_time_ms", "meta", int(time_ms) + messages = [] + common_fee = spec.ExecutionAddress(b"\x11" * 20) + + proposal_slot, validator_index = find_upcoming_proposal_slot(spec, state) + time_ms += 50 + signed_prefs = build_signed_proposer_preferences( + spec, + state, + proposal_slot=proposal_slot, + validator_index=validator_index, + fee_recipient=common_fee, + target_gas_limit=spec.Uint64(60_000_000), + ) + yield get_filename(signed_prefs), signed_prefs + result, reason = run_validate_gossip( + spec, + seen=seen, + store=store, + signed_proposer_preferences=signed_prefs, + current_time_ms=time_ms, + ) + assert result == "valid" + assert reason is None + messages.append( + { + "current_time_ms": int(time_ms), + "message": get_filename(signed_prefs), + "expected": result, + } + ) + + time_ms += 10 + result, reason = run_validate_gossip( + spec, + seen=seen, + store=store, + signed_execution_payload_envelope=signed_envelope, + ) + assert result == "valid" + assert reason is None + messages.append( + { + "current_time_ms": int(time_ms), + "message": get_filename(signed_envelope), + "expected": result, + } + ) + + signed_bid = build_signed_bid( + spec, + state, + builder_index=spec.BuilderIndex(0), + slot=proposal_slot, + parent_block_hash=payload_block_hash, + parent_block_root=parent_root, + fee_recipient=common_fee, + gas_limit=empty_parent_gas_limit, + value=spec.Gwei(1), + ) + yield get_filename(signed_bid), signed_bid + + assert spec.is_gas_limit_target_compatible( + parent_gas_limit, + signed_bid.message.gas_limit, + signed_prefs.message.target_gas_limit, + ) + assert not spec.is_gas_limit_target_compatible( + empty_parent_gas_limit, + signed_bid.message.gas_limit, + signed_prefs.message.target_gas_limit, + ) + + time_ms += 40 + result, reason = run_validate_gossip( + spec, + seen=seen, + store=store, + signed_execution_payload_bid=signed_bid, + current_time_ms=time_ms, + ) + assert result == "valid" + assert reason is None + messages.append( + { + "current_time_ms": int(time_ms), + "message": get_filename(signed_bid), + "expected": result, + } + ) + + yield "messages", "meta", messages + + @with_gloas_and_later @spec_state_test_with_matching_config def test_gossip_execution_payload_bid__valid_gas_limit_increase_within_limit(spec, state):