diff --git a/beacon_node/beacon_chain/tests/block_verification.rs b/beacon_node/beacon_chain/tests/block_verification.rs index deadafac367..74a521a79ce 100644 --- a/beacon_node/beacon_chain/tests/block_verification.rs +++ b/beacon_node/beacon_chain/tests/block_verification.rs @@ -35,20 +35,30 @@ type E = MainnetEthSpec; // Gloas requires >= 1 validator per slot for PTC committee computation, so >= 32 for MainnetEthSpec. const VALIDATOR_COUNT: usize = 32; -const CHAIN_SEGMENT_LENGTH: usize = 64 * 5; -const BLOCK_INDICES: &[usize] = &[0, 1, 32, 64, 68 + 1, 129, CHAIN_SEGMENT_LENGTH - 1]; +const CHAIN_SEGMENT_LENGTH: usize = 32 * 6; +const BLOCK_INDICES: &[usize] = &[1, 32, 64]; /// A cached set of keys. static KEYPAIRS: LazyLock> = LazyLock::new(|| types::test_utils::generate_deterministic_keypairs(VALIDATOR_COUNT)); // TODO(#8633): Delete this unnecessary enum and refactor this file to use `AvailableBlockData` instead. +#[derive(Clone)] enum DataSidecars { Blobs(BlobSidecarList), DataColumns(Vec>), } -async fn get_chain_segment() -> (Vec>, Vec>>) { +type ChainSegmentData = (Vec>, Vec>>); + +static CHAIN_SEGMENT: LazyLock> = + LazyLock::new(tokio::sync::OnceCell::new); + +async fn get_chain_segment() -> &'static ChainSegmentData { + CHAIN_SEGMENT.get_or_init(build_chain_segment).await +} + +async fn build_chain_segment() -> ChainSegmentData { // The assumption that you can re-import a block based on what you have in your DB // is no longer true, as fullnodes stores less than what they sample. // We use a supernode here to build a chain segment. @@ -365,9 +375,9 @@ async fn chain_segment_full_segment() { } let harness = get_harness(VALIDATOR_COUNT, NodeCustodyType::Fullnode); let (chain_segment, chain_segment_blobs) = get_chain_segment().await; - store_envelopes_for_chain_segment(&chain_segment, &harness); + store_envelopes_for_chain_segment(chain_segment, &harness); let blocks: Vec> = - chain_segment_blocks(&chain_segment, &chain_segment_blobs, harness.chain.clone()) + chain_segment_blocks(chain_segment, chain_segment_blobs, harness.chain.clone()) .into_iter() .collect(); @@ -391,7 +401,7 @@ async fn chain_segment_full_segment() { .into_block_error() .expect("should import chain segment"); - update_fork_choice_with_envelopes(&chain_segment, &harness); + update_fork_choice_with_envelopes(chain_segment, &harness); harness.chain.recompute_head_at_current_slot().await; assert_eq!( @@ -410,13 +420,13 @@ async fn chain_segment_varying_chunk_size() { let (chain_segment, chain_segment_blobs) = get_chain_segment().await; let harness = get_harness(VALIDATOR_COUNT, NodeCustodyType::Fullnode); let blocks: Vec> = - chain_segment_blocks(&chain_segment, &chain_segment_blobs, harness.chain.clone()) + chain_segment_blocks(chain_segment, chain_segment_blobs, harness.chain.clone()) .into_iter() .collect(); - for chunk_size in &[1, 2, 31, 32, 33] { + for chunk_size in &[1, 32, 33] { let harness = get_harness(VALIDATOR_COUNT, NodeCustodyType::Fullnode); - store_envelopes_for_chain_segment(&chain_segment, &harness); + store_envelopes_for_chain_segment(chain_segment, &harness); harness .chain @@ -432,7 +442,7 @@ async fn chain_segment_varying_chunk_size() { .unwrap_or_else(|_| panic!("should import chain segment of len {}", chunk_size)); } - update_fork_choice_with_envelopes(&chain_segment, &harness); + update_fork_choice_with_envelopes(chain_segment, &harness); harness.chain.recompute_head_at_current_slot().await; assert_eq!( @@ -457,7 +467,7 @@ async fn chain_segment_non_linear_parent_roots() { * Test with a block removed. */ let mut blocks: Vec> = - chain_segment_blocks(&chain_segment, &chain_segment_blobs, harness.chain.clone()) + chain_segment_blocks(chain_segment, chain_segment_blobs, harness.chain.clone()) .into_iter() .collect(); blocks.remove(2); @@ -478,7 +488,7 @@ async fn chain_segment_non_linear_parent_roots() { * Test with a modified parent root. */ let mut blocks: Vec> = - chain_segment_blocks(&chain_segment, &chain_segment_blobs, harness.chain.clone()) + chain_segment_blocks(chain_segment, chain_segment_blobs, harness.chain.clone()) .into_iter() .collect(); @@ -520,7 +530,7 @@ async fn chain_segment_non_linear_slots() { */ let mut blocks: Vec> = - chain_segment_blocks(&chain_segment, &chain_segment_blobs, harness.chain.clone()) + chain_segment_blocks(chain_segment, chain_segment_blobs, harness.chain.clone()) .into_iter() .collect(); let (mut block, signature) = blocks[3].as_block().clone().deconstruct(); @@ -550,7 +560,7 @@ async fn chain_segment_non_linear_slots() { */ let mut blocks: Vec> = - chain_segment_blocks(&chain_segment, &chain_segment_blobs, harness.chain.clone()) + chain_segment_blocks(chain_segment, chain_segment_blobs, harness.chain.clone()) .into_iter() .collect(); let (mut block, signature) = blocks[3].as_block().clone().deconstruct(); @@ -694,7 +704,7 @@ async fn invalid_signature_gossip_block() { let (chain_segment, chain_segment_blobs) = get_chain_segment().await; for &block_index in BLOCK_INDICES { // Ensure the block will be rejected if imported on its own (without gossip checking). - let harness = get_invalid_sigs_harness(&chain_segment).await; + let harness = get_invalid_sigs_harness(chain_segment).await; let mut snapshots = chain_segment.clone(); let (block, _) = snapshots[block_index] .beacon_block @@ -753,7 +763,7 @@ async fn invalid_signature_block_proposal() { } let (chain_segment, chain_segment_blobs) = get_chain_segment().await; for &block_index in BLOCK_INDICES { - let harness = get_invalid_sigs_harness(&chain_segment).await; + let harness = get_invalid_sigs_harness(chain_segment).await; let mut snapshots = chain_segment.clone(); let (block, _) = snapshots[block_index] .beacon_block @@ -794,9 +804,10 @@ async fn invalid_signature_randao_reveal() { if fork_name_from_env().is_some_and(|f| f.gloas_enabled()) { return; } - let (chain_segment, mut chain_segment_blobs) = get_chain_segment().await; + let (chain_segment, ref_blobs) = get_chain_segment().await; + let mut chain_segment_blobs = ref_blobs.clone(); for &block_index in BLOCK_INDICES { - let harness = get_invalid_sigs_harness(&chain_segment).await; + let harness = get_invalid_sigs_harness(chain_segment).await; let mut snapshots = chain_segment.clone(); let (mut block, signature) = snapshots[block_index] .beacon_block @@ -809,7 +820,7 @@ async fn invalid_signature_randao_reveal() { update_parent_roots(&mut snapshots, &mut chain_segment_blobs); update_proposal_signatures(&mut snapshots, &harness); assert_invalid_signature( - &chain_segment, + chain_segment, &chain_segment_blobs, &harness, block_index, @@ -826,9 +837,10 @@ async fn invalid_signature_proposer_slashing() { if fork_name_from_env().is_some_and(|f| f.gloas_enabled()) { return; } - let (chain_segment, mut chain_segment_blobs) = get_chain_segment().await; + let (chain_segment, ref_blobs) = get_chain_segment().await; + let mut chain_segment_blobs = ref_blobs.clone(); for &block_index in BLOCK_INDICES { - let harness = get_invalid_sigs_harness(&chain_segment).await; + let harness = get_invalid_sigs_harness(chain_segment).await; let mut snapshots = chain_segment.clone(); let (mut block, signature) = snapshots[block_index] .beacon_block @@ -855,7 +867,7 @@ async fn invalid_signature_proposer_slashing() { update_parent_roots(&mut snapshots, &mut chain_segment_blobs); update_proposal_signatures(&mut snapshots, &harness); assert_invalid_signature( - &chain_segment, + chain_segment, &chain_segment_blobs, &harness, block_index, @@ -872,9 +884,10 @@ async fn invalid_signature_attester_slashing() { if fork_name_from_env().is_some_and(|f| f.gloas_enabled()) { return; } - let (chain_segment, mut chain_segment_blobs) = get_chain_segment().await; + let (chain_segment, ref_blobs) = get_chain_segment().await; + let mut chain_segment_blobs = ref_blobs.clone(); for &block_index in BLOCK_INDICES { - let harness = get_invalid_sigs_harness(&chain_segment).await; + let harness = get_invalid_sigs_harness(chain_segment).await; let mut snapshots = chain_segment.clone(); let fork_name = harness.chain.spec.fork_name_at_slot::(Slot::new(0)); @@ -980,7 +993,7 @@ async fn invalid_signature_attester_slashing() { update_parent_roots(&mut snapshots, &mut chain_segment_blobs); update_proposal_signatures(&mut snapshots, &harness); assert_invalid_signature( - &chain_segment, + chain_segment, &chain_segment_blobs, &harness, block_index, @@ -997,11 +1010,12 @@ async fn invalid_signature_attestation() { if fork_name_from_env().is_some_and(|f| f.gloas_enabled()) { return; } - let (chain_segment, mut chain_segment_blobs) = get_chain_segment().await; + let (chain_segment, ref_blobs) = get_chain_segment().await; + let mut chain_segment_blobs = ref_blobs.clone(); let mut checked_attestation = false; for &block_index in BLOCK_INDICES { - let harness = get_invalid_sigs_harness(&chain_segment).await; + let harness = get_invalid_sigs_harness(chain_segment).await; let mut snapshots = chain_segment.clone(); let (mut block, signature) = snapshots[block_index] .beacon_block @@ -1049,7 +1063,7 @@ async fn invalid_signature_attestation() { update_parent_roots(&mut snapshots, &mut chain_segment_blobs); update_proposal_signatures(&mut snapshots, &harness); assert_invalid_signature( - &chain_segment, + chain_segment, &chain_segment_blobs, &harness, block_index, @@ -1069,10 +1083,11 @@ async fn invalid_signature_attestation() { #[tokio::test] async fn invalid_signature_deposit() { - let (chain_segment, mut chain_segment_blobs) = get_chain_segment().await; + let (chain_segment, ref_blobs) = get_chain_segment().await; + let mut chain_segment_blobs = ref_blobs.clone(); for &block_index in BLOCK_INDICES { // Note: an invalid deposit signature is permitted! - let harness = get_invalid_sigs_harness(&chain_segment).await; + let harness = get_invalid_sigs_harness(chain_segment).await; let mut snapshots = chain_segment.clone(); let deposit = Deposit { proof: vec![Hash256::zero(); DEPOSIT_TREE_DEPTH + 1] @@ -1126,9 +1141,10 @@ async fn invalid_signature_exit() { if fork_name_from_env().is_some_and(|f| f.gloas_enabled()) { return; } - let (chain_segment, mut chain_segment_blobs) = get_chain_segment().await; + let (chain_segment, ref_blobs) = get_chain_segment().await; + let mut chain_segment_blobs = ref_blobs.clone(); for &block_index in BLOCK_INDICES { - let harness = get_invalid_sigs_harness(&chain_segment).await; + let harness = get_invalid_sigs_harness(chain_segment).await; let mut snapshots = chain_segment.clone(); let epoch = snapshots[block_index].beacon_state.current_epoch(); let (mut block, signature) = snapshots[block_index] @@ -1152,7 +1168,7 @@ async fn invalid_signature_exit() { update_parent_roots(&mut snapshots, &mut chain_segment_blobs); update_proposal_signatures(&mut snapshots, &harness); assert_invalid_signature( - &chain_segment, + chain_segment, &chain_segment_blobs, &harness, block_index, @@ -1173,7 +1189,8 @@ fn unwrap_err(result: Result) -> U { #[tokio::test] async fn block_gossip_verification() { let harness = get_harness(VALIDATOR_COUNT, NodeCustodyType::Fullnode); - let (chain_segment, chain_segment_blobs) = get_chain_segment().await; + let (chain_segment, ref_blobs) = get_chain_segment().await; + let chain_segment_blobs = ref_blobs.clone(); let block_index = CHAIN_SEGMENT_LENGTH - 2; diff --git a/beacon_node/http_api/src/beacon/execution_payload_bid.rs b/beacon_node/http_api/src/beacon/execution_payload_bids.rs similarity index 91% rename from beacon_node/http_api/src/beacon/execution_payload_bid.rs rename to beacon_node/http_api/src/beacon/execution_payload_bids.rs index f6041b55c8a..856670aa944 100644 --- a/beacon_node/http_api/src/beacon/execution_payload_bid.rs +++ b/beacon_node/http_api/src/beacon/execution_payload_bids.rs @@ -14,8 +14,8 @@ use tracing::{debug, warn}; use types::SignedExecutionPayloadBid; use warp::{Filter, Rejection, Reply, hyper::Body, hyper::Response}; -// POST /eth/v1/beacon/execution_payload_bid (SSZ) -pub(crate) fn post_beacon_execution_payload_bid_ssz( +// POST /eth/v1/beacon/execution_payload_bids (SSZ) +pub(crate) fn post_beacon_execution_payload_bids_ssz( eth_v1: EthV1Filter, task_spawner_filter: TaskSpawnerFilter, chain_filter: ChainFilter, @@ -23,7 +23,7 @@ pub(crate) fn post_beacon_execution_payload_bid_ssz( ) -> ResponseFilter { eth_v1 .and(warp::path("beacon")) - .and(warp::path("execution_payload_bid")) + .and(warp::path("execution_payload_bids")) .and(warp::path::end()) .and(warp::body::bytes()) .and(task_spawner_filter) @@ -46,8 +46,8 @@ pub(crate) fn post_beacon_execution_payload_bid_ssz( .boxed() } -// POST /eth/v1/beacon/execution_payload_bid -pub(crate) fn post_beacon_execution_payload_bid( +// POST /eth/v1/beacon/execution_payload_bids +pub(crate) fn post_beacon_execution_payload_bids( eth_v1: EthV1Filter, task_spawner_filter: TaskSpawnerFilter, chain_filter: ChainFilter, @@ -55,7 +55,7 @@ pub(crate) fn post_beacon_execution_payload_bid( ) -> ResponseFilter { eth_v1 .and(warp::path("beacon")) - .and(warp::path("execution_payload_bid")) + .and(warp::path("execution_payload_bids")) .and(warp::path::end()) .and(warp::body::json()) .and(task_spawner_filter) diff --git a/beacon_node/http_api/src/beacon/execution_payload_envelope.rs b/beacon_node/http_api/src/beacon/execution_payload_envelopes.rs similarity index 95% rename from beacon_node/http_api/src/beacon/execution_payload_envelope.rs rename to beacon_node/http_api/src/beacon/execution_payload_envelopes.rs index d12e4371a41..f8ab8cddc8a 100644 --- a/beacon_node/http_api/src/beacon/execution_payload_envelope.rs +++ b/beacon_node/http_api/src/beacon/execution_payload_envelopes.rs @@ -24,8 +24,8 @@ use warp::{ hyper::{Body, Response}, }; -// POST beacon/execution_payload_envelope (SSZ) -pub(crate) fn post_beacon_execution_payload_envelope_ssz( +// POST beacon/execution_payload_envelopes (SSZ) +pub(crate) fn post_beacon_execution_payload_envelopes_ssz( eth_v1: EthV1Filter, task_spawner_filter: TaskSpawnerFilter, chain_filter: ChainFilter, @@ -33,7 +33,7 @@ pub(crate) fn post_beacon_execution_payload_envelope_ssz( ) -> ResponseFilter { eth_v1 .and(warp::path("beacon")) - .and(warp::path("execution_payload_envelope")) + .and(warp::path("execution_payload_envelopes")) .and(warp::path::end()) .and(warp::body::bytes()) .and(task_spawner_filter) @@ -57,8 +57,8 @@ pub(crate) fn post_beacon_execution_payload_envelope_ssz( .boxed() } -// POST beacon/execution_payload_envelope -pub(crate) fn post_beacon_execution_payload_envelope( +// POST beacon/execution_payload_envelopes +pub(crate) fn post_beacon_execution_payload_envelopes( eth_v1: EthV1Filter, task_spawner_filter: TaskSpawnerFilter, chain_filter: ChainFilter, @@ -66,7 +66,7 @@ pub(crate) fn post_beacon_execution_payload_envelope( ) -> ResponseFilter { eth_v1 .and(warp::path("beacon")) - .and(warp::path("execution_payload_envelope")) + .and(warp::path("execution_payload_envelopes")) .and(warp::path::end()) .and(warp::body::json()) .and(task_spawner_filter.clone()) @@ -85,7 +85,7 @@ pub(crate) fn post_beacon_execution_payload_envelope( .boxed() } /// Publishes a signed execution payload envelope to the network. Implements -/// `POST /eth/v1/beacon/execution_payload_envelope` per the in-flight beacon-APIs PR +/// `POST /eth/v1/beacon/execution_payload_envelopes` per the in-flight beacon-APIs PR /// . pub async fn publish_execution_payload_envelope( envelope: SignedExecutionPayloadEnvelope, @@ -292,8 +292,8 @@ fn build_gloas_data_columns( } // TODO(gloas): add tests for this endpoint once we support importing payloads into the db -// GET beacon/execution_payload_envelope/{block_id} -pub(crate) fn get_beacon_execution_payload_envelope( +// GET beacon/execution_payload_envelopes/{block_id} +pub(crate) fn get_beacon_execution_payload_envelopes( eth_v1: EthV1Filter, block_id_or_err: impl Filter + Clone @@ -305,7 +305,7 @@ pub(crate) fn get_beacon_execution_payload_envelope( ) -> ResponseFilter { eth_v1 .and(warp::path("beacon")) - .and(warp::path("execution_payload_envelope")) + .and(warp::path("execution_payload_envelopes")) .and(block_id_or_err) .and(warp::path::end()) .and(task_spawner_filter) diff --git a/beacon_node/http_api/src/beacon/mod.rs b/beacon_node/http_api/src/beacon/mod.rs index db0062c14f7..31c40775401 100644 --- a/beacon_node/http_api/src/beacon/mod.rs +++ b/beacon_node/http_api/src/beacon/mod.rs @@ -1,4 +1,4 @@ -pub mod execution_payload_bid; -pub mod execution_payload_envelope; +pub mod execution_payload_bids; +pub mod execution_payload_envelopes; pub mod pool; pub mod states; diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index ff88c12925b..94f2e3f1df0 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -36,12 +36,12 @@ mod validator_inclusion; mod validators; mod version; -use crate::beacon::execution_payload_bid::{ - post_beacon_execution_payload_bid, post_beacon_execution_payload_bid_ssz, +use crate::beacon::execution_payload_bids::{ + post_beacon_execution_payload_bids, post_beacon_execution_payload_bids_ssz, }; -use crate::beacon::execution_payload_envelope::{ - get_beacon_execution_payload_envelope, post_beacon_execution_payload_envelope, - post_beacon_execution_payload_envelope_ssz, +use crate::beacon::execution_payload_envelopes::{ + get_beacon_execution_payload_envelopes, post_beacon_execution_payload_envelopes, + post_beacon_execution_payload_envelopes_ssz, }; use crate::beacon::pool::*; use crate::caches::DEFAULT_HISTORICAL_COMMITTEE_CACHE_SIZE; @@ -101,7 +101,7 @@ use types::{ BeaconStateError, Checkpoint, ConfigAndPreset, Epoch, EthSpec, ForkName, Hash256, SignedBlindedBeaconBlock, }; -use validator::execution_payload_envelope::get_validator_execution_payload_envelope; +use validator::execution_payload_envelopes::get_validator_execution_payload_envelopes; use version::{ ResponseIncludesVersion, V1, V2, add_consensus_version_header, add_ssz_content_type_header, execution_optimistic_finalized_beacon_response, inconsistent_fork_rejection, @@ -1542,40 +1542,40 @@ pub fn serve( network_tx_filter.clone(), ); - // POST beacon/execution_payload_envelope - let post_beacon_execution_payload_envelope = post_beacon_execution_payload_envelope( + // POST beacon/execution_payload_envelopes + let post_beacon_execution_payload_envelopes = post_beacon_execution_payload_envelopes( eth_v1.clone(), task_spawner_filter.clone(), chain_filter.clone(), network_tx_filter.clone(), ); - // POST beacon/execution_payload_envelope (SSZ) - let post_beacon_execution_payload_envelope_ssz = post_beacon_execution_payload_envelope_ssz( + // POST beacon/execution_payload_envelopes (SSZ) + let post_beacon_execution_payload_envelopes_ssz = post_beacon_execution_payload_envelopes_ssz( eth_v1.clone(), task_spawner_filter.clone(), chain_filter.clone(), network_tx_filter.clone(), ); - // POST beacon/execution_payload_bid - let post_beacon_execution_payload_bid = post_beacon_execution_payload_bid( + // POST beacon/execution_payload_bids + let post_beacon_execution_payload_bids = post_beacon_execution_payload_bids( eth_v1.clone(), task_spawner_filter.clone(), chain_filter.clone(), network_tx_filter.clone(), ); - // POST beacon/execution_payload_bid (SSZ) - let post_beacon_execution_payload_bid_ssz = post_beacon_execution_payload_bid_ssz( + // POST beacon/execution_payload_bids (SSZ) + let post_beacon_execution_payload_bids_ssz = post_beacon_execution_payload_bids_ssz( eth_v1.clone(), task_spawner_filter.clone(), chain_filter.clone(), network_tx_filter.clone(), ); - // GET beacon/execution_payload_envelope/{block_id} - let get_beacon_execution_payload_envelope = get_beacon_execution_payload_envelope( + // GET beacon/execution_payload_envelopes/{block_id} + let get_beacon_execution_payload_envelopes = get_beacon_execution_payload_envelopes( eth_v1.clone(), block_id_or_err, task_spawner_filter.clone(), @@ -2584,8 +2584,8 @@ pub fn serve( task_spawner_filter.clone(), ); - // GET validator/execution_payload_envelope/{slot}/{builder_index} - let get_validator_execution_payload_envelope = get_validator_execution_payload_envelope( + // GET validator/execution_payload_envelopes/{slot}/{builder_index} + let get_validator_execution_payload_envelopes = get_validator_execution_payload_envelopes( eth_v1.clone(), chain_filter.clone(), not_while_syncing_filter.clone(), @@ -3401,7 +3401,7 @@ pub fn serve( .uor(get_beacon_block_root) .uor(get_blob_sidecars) .uor(get_blobs) - .uor(get_beacon_execution_payload_envelope) + .uor(get_beacon_execution_payload_envelopes) .uor(get_beacon_pool_attestations) .uor(get_beacon_pool_attester_slashings) .uor(get_beacon_pool_proposer_slashings) @@ -3425,7 +3425,7 @@ pub fn serve( .uor(get_validator_duties_proposer) .uor(get_validator_blocks) .uor(get_validator_blinded_blocks) - .uor(get_validator_execution_payload_envelope) + .uor(get_validator_execution_payload_envelopes) .uor(get_validator_attestation_data) .uor(get_validator_payload_attestation_data) .uor(get_validator_aggregate_attestation) @@ -3463,8 +3463,8 @@ pub fn serve( .uor(post_beacon_blocks_v2_ssz) .uor(post_beacon_blinded_blocks_ssz) .uor(post_beacon_blinded_blocks_v2_ssz) - .uor(post_beacon_execution_payload_envelope_ssz) - .uor(post_beacon_execution_payload_bid_ssz) + .uor(post_beacon_execution_payload_envelopes_ssz) + .uor(post_beacon_execution_payload_bids_ssz) .uor(post_beacon_pool_payload_attestations_ssz) .uor(post_validator_proposer_preferences_ssz), ) @@ -3480,8 +3480,8 @@ pub fn serve( .uor(post_beacon_pool_payload_attestations) .uor(post_beacon_pool_bls_to_execution_changes) .uor(post_validator_proposer_preferences) - .uor(post_beacon_execution_payload_envelope) - .uor(post_beacon_execution_payload_bid) + .uor(post_beacon_execution_payload_envelopes) + .uor(post_beacon_execution_payload_bids) .uor(post_beacon_state_validators) .uor(post_beacon_state_validator_balances) .uor(post_beacon_state_validator_identities) diff --git a/beacon_node/http_api/src/validator/execution_payload_envelope.rs b/beacon_node/http_api/src/validator/execution_payload_envelopes.rs similarity index 95% rename from beacon_node/http_api/src/validator/execution_payload_envelope.rs rename to beacon_node/http_api/src/validator/execution_payload_envelopes.rs index 7a7a430414c..3a20b37c9b3 100644 --- a/beacon_node/http_api/src/validator/execution_payload_envelope.rs +++ b/beacon_node/http_api/src/validator/execution_payload_envelopes.rs @@ -12,8 +12,8 @@ use types::Slot; use warp::http::Response; use warp::{Filter, Rejection}; -// GET validator/execution_payload_envelope/{slot} -pub fn get_validator_execution_payload_envelope( +// GET validator/execution_payload_envelopes/{slot} +pub fn get_validator_execution_payload_envelopes( eth_v1: EthV1Filter, chain_filter: ChainFilter, not_while_syncing_filter: NotWhileSyncingFilter, @@ -21,7 +21,7 @@ pub fn get_validator_execution_payload_envelope( ) -> ResponseFilter { eth_v1 .and(warp::path("validator")) - .and(warp::path("execution_payload_envelope")) + .and(warp::path("execution_payload_envelopes")) .and(warp::path::param::().or_else(|_| async { Err(warp_utils::reject::custom_bad_request( "Invalid slot".to_string(), diff --git a/beacon_node/http_api/src/validator/mod.rs b/beacon_node/http_api/src/validator/mod.rs index 77df94bc363..86399147746 100644 --- a/beacon_node/http_api/src/validator/mod.rs +++ b/beacon_node/http_api/src/validator/mod.rs @@ -36,7 +36,7 @@ use types::{ use warp::{Filter, Rejection, Reply}; use warp_utils::reject::convert_rejection; -pub mod execution_payload_envelope; +pub mod execution_payload_envelopes; /// Uses the `chain.validator_pubkey_cache` to resolve a pubkey to a validator /// index and then ensures that the validator exists in the given `state`. diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 319229d5f17..455a957337f 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -3085,12 +3085,12 @@ impl ApiTester { } /// JSON bid with a valid structure reaches gossip verification and is rejected with 400. - pub async fn test_post_beacon_execution_payload_bid_json(self) -> Self { + pub async fn test_post_beacon_execution_payload_bids_json(self) -> Self { let (bid, fork_name) = self.make_signed_execution_payload_bid(); let result = self .client - .post_beacon_execution_payload_bid(&bid, fork_name) + .post_beacon_execution_payload_bids(&bid, fork_name) .await; assert!( @@ -3102,12 +3102,12 @@ impl ApiTester { } /// SSZ bid with a valid structure reaches gossip verification and is rejected with 400. - pub async fn test_post_beacon_execution_payload_bid_ssz(self) -> Self { + pub async fn test_post_beacon_execution_payload_bids_ssz(self) -> Self { let (bid, fork_name) = self.make_signed_execution_payload_bid(); let result = self .client - .post_beacon_execution_payload_bid_ssz(&bid, fork_name) + .post_beacon_execution_payload_bids_ssz(&bid, fork_name) .await; assert!( @@ -4433,7 +4433,7 @@ impl ApiTester { let envelope = self .client - .get_validator_execution_payload_envelope::(slot) + .get_validator_execution_payload_envelopes::(slot) .await .unwrap() .data; @@ -4452,7 +4452,7 @@ impl ApiTester { let signed_envelope = self.sign_envelope(envelope, &sk, epoch, &fork, genesis_validators_root); self.client - .post_beacon_execution_payload_envelope(&signed_envelope, fork_name) + .post_beacon_execution_payload_envelopes(&signed_envelope, fork_name) .await .unwrap(); @@ -4495,7 +4495,7 @@ impl ApiTester { let envelope = self .client - .get_validator_execution_payload_envelope_ssz::(slot) + .get_validator_execution_payload_envelopes_ssz::(slot) .await .unwrap(); @@ -4513,7 +4513,7 @@ impl ApiTester { let signed_envelope = self.sign_envelope(envelope, &sk, epoch, &fork, genesis_validators_root); self.client - .post_beacon_execution_payload_envelope_ssz(&signed_envelope, fork_name) + .post_beacon_execution_payload_envelopes_ssz(&signed_envelope, fork_name) .await .unwrap(); @@ -4942,7 +4942,7 @@ impl ApiTester { // Retrieve and publish the envelope. let envelope = self .client - .get_validator_execution_payload_envelope::(slot) + .get_validator_execution_payload_envelopes::(slot) .await .unwrap() .data; @@ -4950,7 +4950,7 @@ impl ApiTester { let signed_envelope = self.sign_envelope(envelope, &sk, epoch, &fork, genesis_validators_root); self.client - .post_beacon_execution_payload_envelope(&signed_envelope, fork_name) + .post_beacon_execution_payload_envelopes(&signed_envelope, fork_name) .await .unwrap(); @@ -9558,14 +9558,14 @@ async fn post_validator_proposer_preferences() { } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] -async fn post_beacon_execution_payload_bid() { +async fn post_beacon_execution_payload_bids() { if !fork_name_from_env().is_some_and(|f| f.gloas_enabled()) { return; } ApiTester::new_with_hard_forks() .await - .test_post_beacon_execution_payload_bid_json() + .test_post_beacon_execution_payload_bids_json() .await - .test_post_beacon_execution_payload_bid_ssz() + .test_post_beacon_execution_payload_bids_ssz() .await; } diff --git a/beacon_node/lighthouse_network/src/discovery/enr.rs b/beacon_node/lighthouse_network/src/discovery/enr.rs index 01a01d55ab5..0735cbb37a8 100644 --- a/beacon_node/lighthouse_network/src/discovery/enr.rs +++ b/beacon_node/lighthouse_network/src/discovery/enr.rs @@ -320,11 +320,12 @@ fn compare_enr(local_enr: &Enr, disk_enr: &Enr) -> bool { && (local_enr.udp4().is_none() || local_enr.udp4() == disk_enr.udp4()) && (local_enr.udp6().is_none() || local_enr.udp6() == disk_enr.udp6()) // we need the ATTESTATION_BITFIELD_ENR_KEY and SYNC_COMMITTEE_BITFIELD_ENR_KEY and - // PEERDAS_CUSTODY_GROUP_COUNT_ENR_KEY key to match, otherwise we use a new ENR. This will - // likely only be true for non-validating nodes. + // PEERDAS_CUSTODY_GROUP_COUNT_ENR_KEY and NEXT_FORK_DIGEST_ENR_KEY keys to match, + // otherwise we use a new ENR. This will likely only be true for non-validating nodes. && local_enr.get_decodable::(ATTESTATION_BITFIELD_ENR_KEY) == disk_enr.get_decodable(ATTESTATION_BITFIELD_ENR_KEY) && local_enr.get_decodable::(SYNC_COMMITTEE_BITFIELD_ENR_KEY) == disk_enr.get_decodable(SYNC_COMMITTEE_BITFIELD_ENR_KEY) && local_enr.get_decodable::(PEERDAS_CUSTODY_GROUP_COUNT_ENR_KEY) == disk_enr.get_decodable(PEERDAS_CUSTODY_GROUP_COUNT_ENR_KEY) + && local_enr.get_decodable::(NEXT_FORK_DIGEST_ENR_KEY) == disk_enr.get_decodable(NEXT_FORK_DIGEST_ENR_KEY) } /// Loads enr from the given directory diff --git a/beacon_node/lighthouse_network/src/service/mod.rs b/beacon_node/lighthouse_network/src/service/mod.rs index 097736a0108..93c84104902 100644 --- a/beacon_node/lighthouse_network/src/service/mod.rs +++ b/beacon_node/lighthouse_network/src/service/mod.rs @@ -201,9 +201,7 @@ impl Network { // set up a collection of variables accessible outside of the network crate // Create an ENR or load from disk if appropriate - // Per [spec](https://github.com/ethereum/consensus-specs/blob/1baa05e71148b0975e28918ac6022d2256b56f4a/specs/fulu/p2p-interface.md?plain=1#L636-L637) - // `nfd` must be zero-valued when no next fork is scheduled. - let next_fork_digest = ctx.fork_context.next_fork_digest().unwrap_or_default(); + let next_fork_digest = ctx.fork_context.next_fork_digest(); let advertised_cgc = config .advertise_false_custody_group_count diff --git a/beacon_node/network/src/service.rs b/beacon_node/network/src/service.rs index ce54ffc38fd..c2e79fe9e87 100644 --- a/beacon_node/network/src/service.rs +++ b/beacon_node/network/src/service.rs @@ -883,10 +883,7 @@ impl NetworkService { fork_context.update_current_fork(*new_fork_name, new_fork_digest, current_epoch); if self.beacon_chain.spec.is_peer_das_scheduled() { - let next_fork_digest = fork_context - .next_fork_digest() - .unwrap_or_else(|| fork_context.current_fork_digest()); - self.libp2p.update_nfd(next_fork_digest); + self.libp2p.update_nfd(fork_context.next_fork_digest()); } self.libp2p.update_fork_version(new_enr_fork_id); diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index e5c66ab5ff7..572f9522eee 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -2755,8 +2755,8 @@ impl BeaconNodeHttpClient { opt_response.ok_or(Error::StatusCode(StatusCode::NOT_FOUND)) } - /// `GET v1/validator/execution_payload_envelope/{slot}` - pub async fn get_validator_execution_payload_envelope( + /// `GET v1/validator/execution_payload_envelopes/{slot}` + pub async fn get_validator_execution_payload_envelopes( &self, slot: Slot, ) -> Result>, Error> { @@ -2765,14 +2765,14 @@ impl BeaconNodeHttpClient { path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("validator") - .push("execution_payload_envelope") + .push("execution_payload_envelopes") .push(&slot.to_string()); self.get(path).await } - /// `GET v1/validator/execution_payload_envelope/{slot}` in SSZ format - pub async fn get_validator_execution_payload_envelope_ssz( + /// `GET v1/validator/execution_payload_envelopes/{slot}` in SSZ format + pub async fn get_validator_execution_payload_envelopes_ssz( &self, slot: Slot, ) -> Result, Error> { @@ -2781,7 +2781,7 @@ impl BeaconNodeHttpClient { path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("validator") - .push("execution_payload_envelope") + .push("execution_payload_envelopes") .push(&slot.to_string()); let opt_response = self @@ -2793,8 +2793,8 @@ impl BeaconNodeHttpClient { ExecutionPayloadEnvelope::from_ssz_bytes(&response_bytes).map_err(Error::InvalidSsz) } - /// `POST v1/beacon/execution_payload_envelope` - pub async fn post_beacon_execution_payload_envelope( + /// `POST v1/beacon/execution_payload_envelopes` + pub async fn post_beacon_execution_payload_envelopes( &self, envelope: &SignedExecutionPayloadEnvelope, fork_name: ForkName, @@ -2804,7 +2804,7 @@ impl BeaconNodeHttpClient { path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("beacon") - .push("execution_payload_envelope"); + .push("execution_payload_envelopes"); self.post_generic_with_consensus_version( path, @@ -2817,8 +2817,8 @@ impl BeaconNodeHttpClient { Ok(()) } - /// `POST v1/beacon/execution_payload_envelope` in SSZ format - pub async fn post_beacon_execution_payload_envelope_ssz( + /// `POST v1/beacon/execution_payload_envelopes` in SSZ format + pub async fn post_beacon_execution_payload_envelopes_ssz( &self, envelope: &SignedExecutionPayloadEnvelope, fork_name: ForkName, @@ -2828,7 +2828,7 @@ impl BeaconNodeHttpClient { path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("beacon") - .push("execution_payload_envelope"); + .push("execution_payload_envelopes"); self.post_generic_with_consensus_version_and_ssz_body( path, @@ -2841,8 +2841,8 @@ impl BeaconNodeHttpClient { Ok(()) } - /// `POST v1/beacon/execution_payload_bid` - pub async fn post_beacon_execution_payload_bid( + /// `POST v1/beacon/execution_payload_bids` + pub async fn post_beacon_execution_payload_bids( &self, bid: &SignedExecutionPayloadBid, fork_name: ForkName, @@ -2852,7 +2852,7 @@ impl BeaconNodeHttpClient { path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("beacon") - .push("execution_payload_bid"); + .push("execution_payload_bids"); self.post_generic_with_consensus_version( path, @@ -2865,8 +2865,8 @@ impl BeaconNodeHttpClient { Ok(()) } - /// `POST v1/beacon/execution_payload_bid` in SSZ format - pub async fn post_beacon_execution_payload_bid_ssz( + /// `POST v1/beacon/execution_payload_bids` in SSZ format + pub async fn post_beacon_execution_payload_bids_ssz( &self, bid: &SignedExecutionPayloadBid, fork_name: ForkName, @@ -2876,7 +2876,7 @@ impl BeaconNodeHttpClient { path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("beacon") - .push("execution_payload_bid"); + .push("execution_payload_bids"); self.post_generic_with_consensus_version_and_ssz_body( path, @@ -2889,8 +2889,8 @@ impl BeaconNodeHttpClient { Ok(()) } - /// Path for `v1/beacon/execution_payload_envelope/{block_id}` - pub fn get_beacon_execution_payload_envelope_path( + /// Path for `v1/beacon/execution_payload_envelopes/{block_id}` + pub fn get_beacon_execution_payload_envelopes_path( &self, block_id: BlockId, ) -> Result { @@ -2898,35 +2898,35 @@ impl BeaconNodeHttpClient { path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("beacon") - .push("execution_payload_envelope") + .push("execution_payload_envelopes") .push(&block_id.to_string()); Ok(path) } - /// `GET v1/beacon/execution_payload_envelope/{block_id}` + /// `GET v1/beacon/execution_payload_envelopes/{block_id}` /// /// Returns `Ok(None)` on a 404 error. - pub async fn get_beacon_execution_payload_envelope( + pub async fn get_beacon_execution_payload_envelopes( &self, block_id: BlockId, ) -> Result< Option>>, Error, > { - let path = self.get_beacon_execution_payload_envelope_path(block_id)?; + let path = self.get_beacon_execution_payload_envelopes_path(block_id)?; self.get_opt(path) .await .map(|opt| opt.map(BeaconResponse::ForkVersioned)) } - /// `GET v1/beacon/execution_payload_envelope/{block_id}` in SSZ format + /// `GET v1/beacon/execution_payload_envelopes/{block_id}` in SSZ format /// /// Returns `Ok(None)` on a 404 error. - pub async fn get_beacon_execution_payload_envelope_ssz( + pub async fn get_beacon_execution_payload_envelopes_ssz( &self, block_id: BlockId, ) -> Result>, Error> { - let path = self.get_beacon_execution_payload_envelope_path(block_id)?; + let path = self.get_beacon_execution_payload_envelopes_path(block_id)?; let opt_response = self .get_bytes_opt_accept_header(path, Accept::Ssz, self.timeouts.get_beacon_blocks_ssz) .await?; diff --git a/consensus/types/src/fork/fork_context.rs b/consensus/types/src/fork/fork_context.rs index 3407689e790..f5635782373 100644 --- a/consensus/types/src/fork/fork_context.rs +++ b/consensus/types/src/fork/fork_context.rs @@ -93,14 +93,16 @@ impl ForkContext { pub fn current_fork_digest(&self) -> [u8; 4] { self.current_fork.read().fork_digest } - - /// Returns the next fork digest. If there's no future fork, returns the current fork digest. - pub fn next_fork_digest(&self) -> Option<[u8; 4]> { + /// Per [spec](https://github.com/ethereum/consensus-specs/blob/1baa05e71148b0975e28918ac6022d2256b56f4a/specs/fulu/p2p-interface.md?plain=1#L636-L637) + /// `nfd` must be zero-valued when no next fork is scheduled. + /// Returns the next fork digest. If there's no future fork, returns zero-valued bytes. + pub fn next_fork_digest(&self) -> [u8; 4] { let current_fork_epoch = self.current_fork_epoch(); self.epoch_to_forks .range(current_fork_epoch..) .nth(1) .map(|(_, fork)| fork.fork_digest) + .unwrap_or_default() } /// Updates the `digest_epoch` field to a new digest epoch. @@ -222,11 +224,46 @@ mod tests { let context = ForkContext::new::(electra_slot, genesis_root, &spec); - let next_digest = context.next_fork_digest().unwrap(); + let next_digest = context.next_fork_digest(); let expected_digest = spec.compute_fork_digest(genesis_root, spec.fulu_fork_epoch.unwrap()); assert_eq!(next_digest, expected_digest); } + #[test] + fn test_next_fork_digest_returns_zero_when_no_next_fork() { + let spec = make_chain_spec(); + let genesis_root = Hash256::ZERO; + // Epoch 100 is the last BPO fork in make_chain_spec + let last_bpo_slot = Epoch::new(100).end_slot(E::slots_per_epoch()); + + let context = ForkContext::new::(last_bpo_slot, genesis_root, &spec); + + // No next fork after the last BPO epoch — must return zero bytes per spec + assert_eq!(context.next_fork_digest(), [0u8; 4]); + } + + #[test] + fn test_next_fork_digest_zero_after_runtime_transition_to_last_fork() { + let spec = make_chain_spec(); + let genesis_root = Hash256::ZERO; + // Start at Gloas (epoch 7) + let gloas_epoch = spec.gloas_fork_epoch.unwrap(); + let gloas_slot = gloas_epoch.end_slot(E::slots_per_epoch()); + + let context = ForkContext::new::(gloas_slot, genesis_root, &spec); + + // Before: next fork exists (BPO at epoch 50) + let bpo_50_digest = spec.compute_fork_digest(genesis_root, Epoch::new(50)); + assert_eq!(context.next_fork_digest(), bpo_50_digest); + + // Simulate runtime transition to the last BPO fork (epoch 100) + let last_digest = spec.compute_fork_digest(genesis_root, Epoch::new(100)); + context.update_current_fork(ForkName::Gloas, last_digest, Epoch::new(100)); + + // After: no next fork — must return zero bytes per spec + assert_eq!(context.next_fork_digest(), [0u8; 4]); + } + #[test] fn test_get_fork_from_context_bytes() { let spec = make_chain_spec(); diff --git a/validator_client/validator_services/src/block_service.rs b/validator_client/validator_services/src/block_service.rs index 1dd1878f4c3..06fd14360a1 100644 --- a/validator_client/validator_services/src/block_service.rs +++ b/validator_client/validator_services/src/block_service.rs @@ -659,7 +659,7 @@ impl BlockService { .beacon_nodes .first_success(|beacon_node| async move { beacon_node - .get_validator_execution_payload_envelope_ssz::(slot) + .get_validator_execution_payload_envelopes_ssz::(slot) .await .map_err(|e| { BlockError::Recoverable(format!( @@ -702,7 +702,7 @@ impl BlockService { let signed_envelope = signed_envelope.clone(); async move { beacon_node - .post_beacon_execution_payload_envelope_ssz(&signed_envelope, fork_name) + .post_beacon_execution_payload_envelopes_ssz(&signed_envelope, fork_name) .await .map_err(|e| { BlockError::Recoverable(format!(