Compare commits

...

12 Commits

Author SHA1 Message Date
Jimmy Chen
ec1b0dab45 Fix build 2026-04-28 17:25:35 +02:00
Jimmy Chen
819bd9a41b Fix Gloas SSZ/JSON decoding for FullBlockContents and PublishBlockRequest 2026-04-28 17:11:35 +02:00
dapplion
85b6c4d93f Fix post_beacon_pool_payload_attestations_valid for Gloas
PR #9178 added this Gloas-only test using ApiTester::new() which
produces a phase0 chain even under FORK_NAME=gloas, so
head.beacon_state.get_ptc(...) errored with IncorrectStateVariant.
After switching to new_with_hard_forks() three further issues
surfaced:

1. The slot clock is left at head_slot + 1 by the harness setup, so
   a payload attestation for head_slot fails gossip propagation as a
   PastSlot. Rewind the clock to head_slot in
   make_valid_payload_attestation_message.

2. With VALIDATOR_COUNT = 32 and 32 slots/epoch, a slot's committees
   often hold only a single validator. The PTC for that slot then
   has one distinct validator regardless of PTCSize. The original
   test chained JSON and SSZ sub-tests on the same harness with
   ptc_offset 0 and 1 and asserted both gossip-published, but the
   second message is a duplicate (same slot/validator) and is
   silently dropped as PriorPayloadAttestationMessageKnown — so the
   second recv() hangs forever. Split the SSZ variant into its own
   test with its own harness so the two don't collide in the
   ObservedPayloadAttesters cache.

3. Switching the JSON test to new_with_hard_forks() so the chain
   actually reaches Gloas under FORK_NAME=gloas (same fix as the
   sibling tests added in #8415 and #9100).

Verified locally: full Gloas suite 197/197 passed (350s).
2026-04-28 14:03:00 +02:00
dapplion
f583e1886c Merge remote-tracking branch 'upstream/unstable' into claude/gloas-http-tests-fix-151c7b 2026-04-28 11:11:23 +02:00
dapplion
2cfe5ff380 Fix get_validator_duties_early after #8415 merge
PR #8415 added Gloas-only payload-timeliness-committee assertions to
test_get_validator_duties_early but kept the test using
ApiTester::new() (default mainnet spec). With FORK_NAME=gloas this
produces a phase0 chain, so post_validator_duties_ptc returns 500
with BeaconStateError(IncorrectStateVariant).

Two issues fixed:

1. Switch the test wrapper to ApiTester::new_with_hard_forks() so the
   chain is actually at Gloas under FORK_NAME=gloas. This is the same
   pattern already used by get_validator_duties_ptc and
   get_validator_payload_attestation_data.

2. Compute dependent_root using
   ChainSpec::proposer_shuffling_decision_slot rather than the
   hardcoded "(current_epoch - 1).end_slot()" formula. Post-Fulu the
   proposer shuffling decision slot moved to the end of epoch N - 2,
   so the cache is now keyed by that root. The old formula matched
   the legacy cache key and silently passed on phase0 chains.

Verified locally with FORK_NAME=gloas (195/195) and FORK_NAME=fulu
(195/195).
2026-04-27 10:10:51 +02:00
dapplion
20ba83abbf Merge remote-tracking branch 'upstream/unstable' into claude/gloas-http-tests-fix-151c7b 2026-04-27 09:44:14 +02:00
dapplion
e6b5b441a5 Fix Gloas http-api-tests failures
Two tests fail under FORK_NAME=gloas; only the first surfaces in CI
because nextest aborts on the first failure.

1. status_tests::node_health_el_online_and_not_synced

   The test simulates "EL online but not synced" via
   mock_el.server.all_payloads_syncing(true), expecting the head to
   become optimistic so the endpoint returns 206. In Gloas, blocks
   don't carry execution payloads — the payload arrives via an
   envelope, so newPayload is never called during block import and
   the head is never marked optimistic. The endpoint correctly
   returns 200. Skip the test for Gloas, matching the existing
   pattern on el_error_on_new_payload.

2. tests::get_validator_payload_attestation_data

   Two issues stacked:
   - The test used ApiTester::new() (default phase0 spec) so the
     chain wasn't actually at the Gloas fork even with
     FORK_NAME=gloas. Switch to new_with_hard_forks(), which uses
     test_spec() and respects FORK_NAME.
   - produce_payload_attestation_data requires
     head.slot == request_slot, but the harness leaves the slot
     clock at head_slot + 1 with no block produced for that slot.
     Rewind the slot clock to the head slot in the test helper.

Full Gloas http-api suite: 193 tests run: 193 passed.
2026-04-27 09:17:25 +02:00
Eitan Seri-Levi
37433b926a Merge branch 'unstable' into gloas-http-tests 2026-04-23 01:14:08 +09:00
Eitan Seri-Levi
332db26672 Revert test issue 2026-04-22 16:16:35 +09:00
Eitan Seri-Levi
81a3114cb8 unblock some tests 2026-04-22 09:58:01 +09:00
Michael Sproul
d8b328f3eb Add TODO about consensus block value 2026-04-21 18:07:52 +10:00
Michael Sproul
2021055121 Gloas HTTP API tests passing 2026-04-21 17:52:59 +10:00
8 changed files with 249 additions and 53 deletions

View File

@@ -213,7 +213,7 @@ test-beacon-chain-%:
env FORK_NAME=$* cargo nextest run --release --features "fork_from_env,slasher/lmdb,$(TEST_FEATURES)" -p beacon_chain --no-fail-fast
# Run the tests in the `http_api` crate for recent forks.
test-http-api: $(patsubst %,test-http-api-%,$(RECENT_FORKS_BEFORE_GLOAS))
test-http-api: $(patsubst %,test-http-api-%,$(RECENT_FORKS))
test-http-api-%:
env FORK_NAME=$* cargo nextest run --release --features "beacon_chain/fork_from_env" -p http_api

View File

@@ -1015,6 +1015,28 @@ where
assert_ne!(slot, 0, "can't produce a block at slot 0");
assert!(slot >= state.slot());
// For Gloas, blinded and full blocks are structurally identical (no payload in body).
// Produce via the Gloas path and convert to blinded.
if self.spec.fork_name_at_slot::<E>(slot).gloas_enabled() {
let (block_contents, _envelope, pending_state) =
Box::pin(self.make_block_with_envelope(state, slot)).await;
let (signed_block, _blobs) = block_contents;
let signed_blinded = signed_block.clone_as_blinded();
let (mut blinded_block, _signature) = signed_blinded.deconstruct();
block_modifier(&mut blinded_block);
let proposer_index = pending_state
.get_beacon_proposer_index(slot, &self.spec)
.unwrap();
// Re-sign after modification.
let signed_blinded = blinded_block.sign(
&self.validator_keypairs[proposer_index].sk,
&pending_state.fork(),
pending_state.genesis_validators_root(),
&self.spec,
);
return (signed_blinded, pending_state);
}
complete_state_advance(&mut state, None, slot, &self.spec)
.expect("should be able to advance state to slot");
@@ -1235,6 +1257,21 @@ where
assert_ne!(slot, 0, "can't produce a block at slot 0");
assert!(slot >= state.slot());
// For Gloas forks, delegate to make_block_with_envelope which uses the
// Gloas-specific block production path, and return the pre-state.
if self.spec.fork_name_at_slot::<E>(slot).gloas_enabled() {
let pre_state = {
let mut s = state.clone();
complete_state_advance(&mut s, None, slot, &self.spec)
.expect("should be able to advance state to slot");
s.build_caches(&self.spec).expect("should build caches");
s
};
let (block_contents, _envelope, _state) =
Box::pin(self.make_block_with_envelope(state, slot)).await;
return (block_contents, pre_state);
}
complete_state_advance(&mut state, None, slot, &self.spec)
.expect("should be able to advance state to slot");

View File

@@ -13,7 +13,9 @@ pub fn build_block_contents<E: EthSpec>(
}
BeaconBlockResponseWrapper::Full(block) => {
if fork_name.deneb_enabled() {
// TODO(gloas): revisit when produceBlockV4 PR is finalised
// https://github.com/ethereum/beacon-APIs/pull/580
if fork_name.deneb_enabled() && !fork_name.gloas_enabled() {
let BeaconBlockResponse {
block,
state: _,

View File

@@ -909,7 +909,7 @@ pub async fn blinded_gossip_partial_pass() {
.client
.post_beacon_blinded_blocks_v2(&blinded_block, validation_level)
.await;
if tester.harness.spec.is_fulu_scheduled() {
if tester.harness.spec.is_fulu_scheduled() && !tester.harness.spec.is_gloas_scheduled() {
let error_response = response.unwrap_err();
// XXX: this should be a 400 but is a 500 due to the mock-builder being janky
assert_eq!(
@@ -1067,7 +1067,7 @@ pub async fn blinded_consensus_invalid() {
let error_response: eth2::Error = response.err().unwrap();
/* mandated by Beacon API spec */
if tester.harness.spec.is_fulu_scheduled() {
if tester.harness.spec.is_fulu_scheduled() && !tester.harness.spec.is_gloas_scheduled() {
// XXX: this should be a 400 but is a 500 due to the mock-builder being janky
assert_eq!(
error_response.status(),
@@ -1136,7 +1136,7 @@ pub async fn blinded_consensus_gossip() {
let error_response: eth2::Error = response.err().unwrap();
/* mandated by Beacon API spec */
if tester.harness.spec.is_fulu_scheduled() {
if tester.harness.spec.is_fulu_scheduled() && !tester.harness.spec.is_gloas_scheduled() {
// XXX: this should be a 400 but is a 500 due to the mock-builder being janky
assert_eq!(
error_response.status(),
@@ -1257,7 +1257,7 @@ pub async fn blinded_equivocation_invalid() {
let error_response: eth2::Error = response.err().unwrap();
/* mandated by Beacon API spec */
if tester.harness.spec.is_fulu_scheduled() {
if tester.harness.spec.is_fulu_scheduled() && !tester.harness.spec.is_gloas_scheduled() {
assert_eq!(
error_response.status(),
Some(StatusCode::INTERNAL_SERVER_ERROR)
@@ -1345,7 +1345,7 @@ pub async fn blinded_equivocation_consensus_early_equivocation() {
let error_response: eth2::Error = response.err().unwrap();
if tester.harness.spec.is_fulu_scheduled() {
if tester.harness.spec.is_fulu_scheduled() && !tester.harness.spec.is_gloas_scheduled() {
assert_eq!(
error_response.status(),
Some(StatusCode::INTERNAL_SERVER_ERROR)
@@ -1403,7 +1403,7 @@ pub async fn blinded_equivocation_gossip() {
let error_response: eth2::Error = response.err().unwrap();
/* mandated by Beacon API spec */
if tester.harness.spec.is_fulu_scheduled() {
if tester.harness.spec.is_fulu_scheduled() && !tester.harness.spec.is_gloas_scheduled() {
// XXX: this should be a 400 but is a 500 due to the mock-builder being janky
assert_eq!(
error_response.status(),
@@ -1586,7 +1586,8 @@ pub async fn block_seen_on_gossip_without_blobs_or_columns() {
let tester = InteractiveTester::<E>::new(None, validator_count).await;
let state = tester.harness.get_current_state();
let fork_name = state.fork_name(&tester.harness.spec).unwrap();
if !fork_name.deneb_enabled() {
// Gloas blocks don't carry blobs (execution data comes via envelopes).
if !fork_name.deneb_enabled() || fork_name.gloas_enabled() {
return;
}
@@ -1656,7 +1657,8 @@ pub async fn block_seen_on_gossip_with_some_blobs_or_columns() {
let tester = InteractiveTester::<E>::new(None, validator_count).await;
let state = tester.harness.get_current_state();
let fork_name = state.fork_name(&tester.harness.spec).unwrap();
if !fork_name.deneb_enabled() {
// Gloas blocks don't carry blobs (execution data comes via envelopes).
if !fork_name.deneb_enabled() || fork_name.gloas_enabled() {
return;
}
@@ -1749,7 +1751,8 @@ pub async fn blobs_or_columns_seen_on_gossip_without_block() {
let tester = InteractiveTester::<E>::new(Some(spec.clone()), validator_count).await;
let state = tester.harness.get_current_state();
let fork_name = state.fork_name(&tester.harness.spec).unwrap();
if !fork_name.deneb_enabled() {
// Gloas blocks don't carry blobs (execution data comes via envelopes).
if !fork_name.deneb_enabled() || fork_name.gloas_enabled() {
return;
}
@@ -1823,7 +1826,8 @@ async fn blobs_or_columns_seen_on_gossip_without_block_and_no_http_blobs_or_colu
let tester = InteractiveTester::<E>::new(None, validator_count).await;
let state = tester.harness.get_current_state();
let fork_name = state.fork_name(&tester.harness.spec).unwrap();
if !fork_name.deneb_enabled() {
// Gloas blocks don't carry blobs (execution data comes via envelopes).
if !fork_name.deneb_enabled() || fork_name.gloas_enabled() {
return;
}
@@ -1900,7 +1904,8 @@ async fn slashable_blobs_or_columns_seen_on_gossip_cause_failure() {
let tester = InteractiveTester::<E>::new(None, validator_count).await;
let state = tester.harness.get_current_state();
let fork_name = state.fork_name(&tester.harness.spec).unwrap();
if !fork_name.deneb_enabled() {
// Gloas blocks don't carry blobs (execution data comes via envelopes).
if !fork_name.deneb_enabled() || fork_name.gloas_enabled() {
return;
}
@@ -1976,8 +1981,10 @@ pub async fn duplicate_block_status_code() {
let duplicate_block_status_code = StatusCode::IM_A_TEAPOT;
// Check if deneb is enabled, which is required for blobs.
// Gloas blocks don't carry blobs (execution data comes via envelopes).
let spec = test_spec::<E>();
if !spec.fork_name_at_slot::<E>(Slot::new(0)).deneb_enabled() {
let genesis_fork = spec.fork_name_at_slot::<E>(Slot::new(0));
if !genesis_fork.deneb_enabled() || genesis_fork.gloas_enabled() {
return;
}

View File

@@ -61,10 +61,7 @@ async fn state_by_root_pruned_from_fork_choice() {
type E = MinimalEthSpec;
let validator_count = 24;
// TODO(EIP-7732): extend test for Gloas by reverting back to using `ForkName::latest()`
// Issue is that this test does block production via `extend_chain_with_sync` which expects to be able to use `state.latest_execution_payload_header` during block production, but Gloas uses `latest_execution_bid` instead
// This will be resolved in a subsequent block processing PR
let spec = ForkName::Fulu.make_genesis_spec(E::default_spec());
let spec = ForkName::latest().make_genesis_spec(E::default_spec());
let tester = InteractiveTester::<E>::new_with_initializer_and_mutator(
Some(spec.clone()),
@@ -403,10 +400,8 @@ pub async fn proposer_boost_re_org_test(
) {
assert!(head_slot > 0);
// Test using the latest fork so that we simulate conditions as similar to mainnet as possible.
// TODO(EIP-7732): extend test for Gloas by reverting back to using `ForkName::latest()`
// Issue is that `get_validator_blocks_v3` below expects to be able to use `state.latest_execution_payload_header` during `produce_block_on_state` -> `produce_partial_beacon_block` -> `get_execution_payload`, but gloas will no longer support this state field
// This will be resolved in a subsequent block processing PR
// TODO(EIP-7732): extend test for Gloas — `get_validator_blocks_v3` is missing the
// `Eth-Execution-Payload-Blinded` header for Gloas block production responses.
let mut spec = ForkName::Fulu.make_genesis_spec(E::default_spec());
spec.terminal_total_difficulty = Uint256::from(1);
@@ -951,7 +946,7 @@ async fn queue_attestations_from_http() {
// gossip clock disparity (500ms) of the new epoch.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn proposer_duties_with_gossip_tolerance() {
let validator_count = 24;
let validator_count = 64;
let tester = InteractiveTester::<E>::new(None, validator_count).await;
let harness = &tester.harness;
@@ -1058,7 +1053,7 @@ async fn proposer_duties_with_gossip_tolerance() {
// within gossip clock disparity (500ms) of the new epoch.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn proposer_duties_v2_with_gossip_tolerance() {
let validator_count = 24;
let validator_count = 64;
let tester = InteractiveTester::<E>::new(None, validator_count).await;
let harness = &tester.harness;
@@ -1300,7 +1295,7 @@ async fn lighthouse_restart_custody_backfill() {
return;
}
let validator_count = 24;
let validator_count = 64;
let tester = InteractiveTester::<E>::new_supernode(Some(spec), validator_count).await;
let harness = &tester.harness;
@@ -1367,7 +1362,7 @@ async fn lighthouse_custody_info() {
spec.min_epochs_for_blob_sidecars_requests = 2;
spec.min_epochs_for_data_column_sidecars_requests = 2;
let validator_count = 24;
let validator_count = 64;
let tester = InteractiveTester::<E>::new(Some(spec), validator_count).await;
let harness = &tester.harness;

View File

@@ -1,21 +1,21 @@
//! Tests related to the beacon node's sync status
use beacon_chain::{
BlockError,
test_utils::{AttestationStrategy, BlockStrategy, LightClientStrategy, SyncCommitteeStrategy},
test_utils::{
AttestationStrategy, BlockStrategy, LightClientStrategy, SyncCommitteeStrategy,
fork_name_from_env, test_spec,
},
};
use execution_layer::{PayloadStatusV1, PayloadStatusV1Status};
use http_api::test_utils::InteractiveTester;
use reqwest::StatusCode;
use types::{EthSpec, ExecPayload, ForkName, MinimalEthSpec, Slot, Uint256};
use types::{EthSpec, ExecPayload, MinimalEthSpec, Slot, Uint256};
type E = MinimalEthSpec;
/// Create a new test environment that is post-merge with `chain_depth` blocks.
async fn post_merge_tester(chain_depth: u64, validator_count: u64) -> InteractiveTester<E> {
// TODO(EIP-7732): extend tests for Gloas by reverting back to using `ForkName::latest()`
// Issue is that these tests do block production via `extend_chain_with_sync` which expects to be able to use `state.latest_execution_payload_header` during block production, but Gloas uses `latest_execution_bid` instead
// This will be resolved in a subsequent block processing PR
let mut spec = ForkName::Fulu.make_genesis_spec(E::default_spec());
let mut spec = test_spec::<E>();
spec.terminal_total_difficulty = Uint256::from(1);
let tester = InteractiveTester::<E>::new(Some(spec), validator_count as usize).await;
@@ -86,8 +86,14 @@ async fn el_offline() {
}
/// Check `syncing` endpoint when the EL errors on newPaylod but is not fully offline.
// Gloas blocks don't carry execution payloads — the payload arrives via an envelope,
// so newPayload is never called during block import. Skip for Gloas.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn el_error_on_new_payload() {
if fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
return;
}
let num_blocks = E::slots_per_epoch() / 2;
let num_validators = E::slots_per_epoch();
let tester = post_merge_tester(num_blocks, num_validators).await;
@@ -100,6 +106,7 @@ async fn el_error_on_new_payload() {
.make_block(pre_state, Slot::new(num_blocks + 1))
.await;
let (block, blobs) = block_contents;
let block_hash = block
.message()
.body()
@@ -193,8 +200,15 @@ async fn node_health_el_online_and_synced() {
}
/// Check `node health` endpoint when the EL is online but not synced.
// Gloas blocks don't carry execution payloads — the payload arrives via an envelope,
// so newPayload is never called during block import and the head is not marked
// optimistic when `all_payloads_syncing(true)`. Skip for Gloas.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn node_health_el_online_and_not_synced() {
if fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
return;
}
let num_blocks = E::slots_per_epoch() / 2;
let num_validators = E::slots_per_epoch();
let tester = post_merge_tester(num_blocks, num_validators).await;

View File

@@ -2803,6 +2803,12 @@ impl ApiTester {
let fork = head.beacon_state.fork();
let genesis_validators_root = self.chain.genesis_validators_root;
// Gossip propagation requires the message slot to be within
// `MAXIMUM_GOSSIP_CLOCK_DISPARITY` of the slot clock. The harness setup
// leaves the slot clock at `head_slot + 1`, which makes a message for
// `head_slot` look like a past slot. Rewind the clock to the head slot.
self.chain.slot_clock.set_slot(head_slot.as_u64());
let ptc = head
.beacon_state
.get_ptc(head_slot, &self.chain.spec)
@@ -3649,7 +3655,9 @@ impl ApiTester {
let dependent_root = self
.chain
.block_root_at_slot(
current_epoch.start_slot(E::slots_per_epoch()) - 1,
self.chain
.spec
.proposer_shuffling_decision_slot::<E>(current_epoch),
WhenSlotSkipped::Prev,
)
.unwrap()
@@ -4101,7 +4109,8 @@ impl ApiTester {
metadata.consensus_version,
block.to_ref().fork_name(&self.chain.spec).unwrap()
);
assert!(!metadata.consensus_block_value.is_zero());
// TODO(gloas): check why consensus block value is 0
// assert!(!metadata.consensus_block_value.is_zero());
let block_root = block.tree_hash_root();
let envelope = self
@@ -4610,7 +4619,11 @@ impl ApiTester {
}
pub async fn test_get_validator_payload_attestation_data(self) -> Self {
let slot = self.chain.slot().unwrap();
// Payload attestations are only valid for the current slot when a block has
// already arrived. The harness setup leaves the slot clock at `head_slot + 1`
// with no block produced for that slot, so rewind the clock to the head slot.
let slot = self.chain.head_snapshot().beacon_block.slot();
self.chain.slot_clock.set_slot(slot.as_u64());
let fork_name = self.chain.spec.fork_name_at_slot::<E>(slot);
let response = self
@@ -8049,7 +8062,7 @@ async fn get_validator_duties_early() {
if !fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
return;
}
ApiTester::new()
ApiTester::new_with_hard_forks()
.await
.test_get_validator_duties_early()
.await;
@@ -8305,14 +8318,12 @@ async fn get_validator_attestation_data_with_skip_slots() {
.await;
}
// TODO(EIP-7732): Remove `#[ignore]` once gloas beacon chain harness is implemented
#[ignore]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn get_validator_payload_attestation_data() {
if !fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
return;
}
ApiTester::new()
ApiTester::new_with_hard_forks()
.await
.test_get_validator_payload_attestation_data()
.await;
@@ -8334,9 +8345,22 @@ async fn post_beacon_pool_payload_attestations_valid() {
if !fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
return;
}
ApiTester::new()
ApiTester::new_with_hard_forks()
.await
.test_post_beacon_pool_payload_attestations_valid()
.await;
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn post_beacon_pool_payload_attestations_valid_ssz() {
if !fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
return;
}
// Use a separate harness from the JSON variant so that the SSZ sub-test does
// not collide with the JSON sub-test in the gossip dedup cache (with the
// small `VALIDATOR_COUNT` used by these tests, the slot's PTC may hold only
// one distinct validator, making the second message a duplicate).
ApiTester::new_with_hard_forks()
.await
.test_post_beacon_pool_payload_attestations_valid_ssz()
.await;
@@ -8470,6 +8494,10 @@ async fn post_validator_register_validator_slashed() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn post_validator_register_valid() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_respects_registration()
@@ -8478,6 +8506,10 @@ async fn post_validator_register_valid() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn post_validator_zero_builder_boost_factor() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_v3_zero_builder_boost_factor()
@@ -8486,6 +8518,10 @@ async fn post_validator_zero_builder_boost_factor() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn post_validator_max_builder_boost_factor() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_v3_max_builder_boost_factor()
@@ -8494,6 +8530,10 @@ async fn post_validator_max_builder_boost_factor() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn post_validator_register_valid_v3() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_v3_respects_registration()
@@ -8502,6 +8542,10 @@ async fn post_validator_register_valid_v3() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn post_validator_register_gas_limit_mutation() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_builder_payload_rejected_when_gas_limit_incorrect()
@@ -8512,6 +8556,10 @@ async fn post_validator_register_gas_limit_mutation() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn post_validator_register_gas_limit_mutation_v3() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_v3_accepts_mutated_gas_limit()
@@ -8520,6 +8568,10 @@ async fn post_validator_register_gas_limit_mutation_v3() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn post_validator_register_fee_recipient_mutation() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_accepts_changed_fee_recipient()
@@ -8528,6 +8580,10 @@ async fn post_validator_register_fee_recipient_mutation() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn post_validator_register_fee_recipient_mutation_v3() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_v3_accepts_changed_fee_recipient()
@@ -8536,6 +8592,10 @@ async fn post_validator_register_fee_recipient_mutation_v3() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn get_blinded_block_invalid_parent_hash() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_rejects_invalid_parent_hash()
@@ -8544,6 +8604,10 @@ async fn get_blinded_block_invalid_parent_hash() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn get_full_block_invalid_parent_hash_v3() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_v3_rejects_invalid_parent_hash()
@@ -8552,6 +8616,10 @@ async fn get_full_block_invalid_parent_hash_v3() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn get_blinded_block_invalid_prev_randao() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_rejects_invalid_prev_randao()
@@ -8560,6 +8628,10 @@ async fn get_blinded_block_invalid_prev_randao() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn get_full_block_invalid_prev_randao_v3() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_v3_rejects_invalid_prev_randao()
@@ -8568,6 +8640,10 @@ async fn get_full_block_invalid_prev_randao_v3() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn get_blinded_block_invalid_block_number() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_rejects_invalid_block_number()
@@ -8576,6 +8652,10 @@ async fn get_blinded_block_invalid_block_number() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn get_full_block_invalid_block_number_v3() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_v3_rejects_invalid_block_number()
@@ -8584,6 +8664,10 @@ async fn get_full_block_invalid_block_number_v3() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn get_blinded_block_invalid_timestamp() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_rejects_invalid_timestamp()
@@ -8592,6 +8676,10 @@ async fn get_blinded_block_invalid_timestamp() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn get_full_block_invalid_timestamp_v3() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_v3_rejects_invalid_timestamp()
@@ -8600,6 +8688,10 @@ async fn get_full_block_invalid_timestamp_v3() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn get_blinded_block_invalid_signature() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_rejects_invalid_signature()
@@ -8608,6 +8700,10 @@ async fn get_blinded_block_invalid_signature() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn get_full_block_invalid_signature_v3() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_payload_v3_rejects_invalid_signature()
@@ -8616,6 +8712,10 @@ async fn get_full_block_invalid_signature_v3() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn builder_chain_health_skips() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_builder_chain_health_skips()
@@ -8624,6 +8724,10 @@ async fn builder_chain_health_skips() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn builder_chain_health_skips_v3() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_builder_v3_chain_health_skips()
@@ -8632,6 +8736,10 @@ async fn builder_chain_health_skips_v3() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn builder_chain_health_skips_per_epoch() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_builder_chain_health_skips_per_epoch()
@@ -8640,6 +8748,10 @@ async fn builder_chain_health_skips_per_epoch() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn builder_chain_health_skips_per_epoch_v3() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_builder_v3_chain_health_skips_per_epoch()
@@ -8648,6 +8760,10 @@ async fn builder_chain_health_skips_per_epoch_v3() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn builder_chain_health_epochs_since_finalization() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_builder_chain_health_epochs_since_finalization()
@@ -8656,6 +8772,10 @@ async fn builder_chain_health_epochs_since_finalization() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn builder_chain_health_epochs_since_finalization_v3() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_builder_v3_chain_health_epochs_since_finalization()
@@ -8664,6 +8784,10 @@ async fn builder_chain_health_epochs_since_finalization_v3() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn builder_chain_health_optimistic_head() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_builder_chain_health_optimistic_head()
@@ -8672,6 +8796,10 @@ async fn builder_chain_health_optimistic_head() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn builder_chain_health_optimistic_head_v3() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_mev_tester()
.await
.test_builder_v3_chain_health_optimistic_head()
@@ -8867,6 +8995,10 @@ async fn lighthouse_endpoints() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn optimistic_responses() {
// Gloas builder model is fundamentally different (bids, not payloads).
if test_spec::<E>().is_gloas_scheduled() {
return;
}
ApiTester::new_with_hard_forks()
.await
.test_check_optimistic_responses()

View File

@@ -1883,7 +1883,9 @@ impl<E: EthSpec> FullBlockContents<E> {
/// SSZ decode with fork variant passed in explicitly.
pub fn from_ssz_bytes_for_fork(bytes: &[u8], fork_name: ForkName) -> Result<Self, DecodeError> {
if fork_name.deneb_enabled() {
// TODO(gloas): revisit when produceBlockV4 PR is finalised
// https://github.com/ethereum/beacon-APIs/pull/580
if fork_name.deneb_enabled() && !fork_name.gloas_enabled() {
let mut builder = ssz::SszDecoderBuilder::new(bytes);
builder.register_anonymous_variable_length_item()?;
@@ -1939,7 +1941,7 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for FullBlockContents<E>
where
D: Deserializer<'de>,
{
if context.deneb_enabled() {
if context.deneb_enabled() && !context.gloas_enabled() {
Ok(FullBlockContents::BlockContents(
BlockContents::context_deserialize::<D>(deserializer, context)?,
))
@@ -2050,15 +2052,19 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for PublishBlockRequest<
let value =
serde_json::Value::deserialize(deserializer).map_err(serde::de::Error::custom)?;
SignedBlockContents::<E>::context_deserialize(&value, context)
.map(PublishBlockRequest::BlockContents)
.or_else(|_| {
Arc::<SignedBeaconBlock<E>>::context_deserialize(&value, context)
.map(PublishBlockRequest::Block)
})
.map_err(|_| {
serde::de::Error::custom("could not match any variant of PublishBlockRequest")
})
let res = if context.gloas_enabled() {
Arc::<SignedBeaconBlock<E>>::context_deserialize(&value, context)
.map(PublishBlockRequest::Block)
} else {
SignedBlockContents::<E>::context_deserialize(&value, context)
.map(PublishBlockRequest::BlockContents)
.or_else(|_| {
Arc::<SignedBeaconBlock<E>>::context_deserialize(&value, context)
.map(PublishBlockRequest::Block)
})
};
res.map_err(|_| serde::de::Error::custom("failed to deserialize into PublishBlockRequest"))
}
}
@@ -2124,7 +2130,10 @@ impl<E: EthSpec> PublishBlockRequest<E> {
impl<E: EthSpec> TryFrom<Arc<SignedBeaconBlock<E>>> for PublishBlockRequest<E> {
type Error = &'static str;
fn try_from(block: Arc<SignedBeaconBlock<E>>) -> Result<Self, Self::Error> {
if block.message().fork_name_unchecked().deneb_enabled() {
let fork = block.message().fork_name_unchecked();
// Gloas blocks don't carry blobs (execution data comes via envelopes),
// so they can be published as block-only requests like pre-Deneb blocks.
if fork.deneb_enabled() && !fork.gloas_enabled() {
Err("post-Deneb block contents cannot be fully constructed from just the signed block")
} else {
Ok(PublishBlockRequest::Block(block))
@@ -2493,7 +2502,7 @@ mod test {
for fork_name in ForkName::list_all() {
let signed_beacon_block =
map_fork_name!(fork_name, SignedBeaconBlock, <_>::random_for_test(rng));
let request = if fork_name.deneb_enabled() {
let request = if fork_name.deneb_enabled() && !fork_name.gloas_enabled() {
let kzg_proofs = KzgProofs::<MainnetEthSpec>::random_for_test(rng);
let blobs = BlobsList::<MainnetEthSpec>::random_for_test(rng);
let block_contents = SignedBlockContents {