diff --git a/.github/workflows/local-testnet.yml b/.github/workflows/local-testnet.yml index c129c0ec95..9992273e0a 100644 --- a/.github/workflows/local-testnet.yml +++ b/.github/workflows/local-testnet.yml @@ -179,7 +179,7 @@ jobs: continue-on-error: true strategy: matrix: - network: [sepolia, devnet] + network: [sepolia] steps: - uses: actions/checkout@v5 diff --git a/Cargo.lock b/Cargo.lock index e78cd10205..901ff65952 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3125,6 +3125,7 @@ dependencies = [ name = "eth2" version = "0.1.0" dependencies = [ + "context_deserialize", "educe", "eip_3076", "either", @@ -8335,6 +8336,7 @@ dependencies = [ "reqwest", "serde", "task_executor", + "tracing", "types", "url", "validator_metrics", @@ -8545,6 +8547,7 @@ checksum = "1fc20a89bab2dabeee65e9c9eb96892dc222c23254b401e1319b85efd852fa31" dependencies = [ "arbitrary", "context_deserialize", + "educe", "ethereum_serde_utils", "ethereum_ssz", "itertools 0.14.0", diff --git a/Cargo.toml b/Cargo.toml index 4fb72987bf..9e388fed84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -227,7 +227,7 @@ slashing_protection = { path = "validator_client/slashing_protection" } slot_clock = { path = "common/slot_clock" } smallvec = { version = "1.11.2", features = ["arbitrary"] } snap = "1" -ssz_types = { version = "0.14.0", features = ["context_deserialize"] } +ssz_types = { version = "0.14.0", features = ["context_deserialize", "runtime_types"] } state_processing = { path = "consensus/state_processing" } store = { path = "beacon_node/store" } strum = { version = "0.24", features = ["derive"] } diff --git a/beacon_node/beacon_chain/src/attester_cache.rs b/beacon_node/beacon_chain/src/attester_cache.rs index f879adfb49..beaa1e581c 100644 --- a/beacon_node/beacon_chain/src/attester_cache.rs +++ b/beacon_node/beacon_chain/src/attester_cache.rs @@ -17,7 +17,7 @@ use std::ops::Range; use types::{ BeaconState, BeaconStateError, ChainSpec, Checkpoint, Epoch, EthSpec, FixedBytesExtended, Hash256, RelativeEpoch, Slot, - attestation::Error as AttestationError, + attestation::AttestationError, beacon_state::{ compute_committee_index_in_epoch, compute_committee_range_in_epoch, epoch_committee_count, }, diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 494346e7ff..adc400b1c1 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -74,6 +74,7 @@ use crate::{ AvailabilityPendingExecutedBlock, BeaconChainError, BeaconForkChoiceStore, BeaconSnapshot, CachedHead, metrics, }; +use eth2::beacon_response::ForkVersionedResponse; use eth2::types::{ EventKind, SseBlobSidecar, SseBlock, SseDataColumnSidecar, SseExtendedPayloadAttributes, }; @@ -1248,7 +1249,7 @@ impl BeaconChain { let num_required_columns = T::EthSpec::number_of_columns() / 2; let reconstruction_possible = columns.len() >= num_required_columns; if reconstruction_possible { - reconstruct_blobs(&self.kzg, &columns, None, &block, &self.spec) + reconstruct_blobs(&self.kzg, columns, None, &block, &self.spec) .map(Some) .map_err(Error::FailedToReconstructBlobs) } else { diff --git a/beacon_node/beacon_chain/src/data_availability_checker/overflow_lru_cache.rs b/beacon_node/beacon_chain/src/data_availability_checker/overflow_lru_cache.rs index c383c20f9f..e7c536c0d8 100644 --- a/beacon_node/beacon_chain/src/data_availability_checker/overflow_lru_cache.rs +++ b/beacon_node/beacon_chain/src/data_availability_checker/overflow_lru_cache.rs @@ -12,6 +12,7 @@ use crate::{BeaconChainTypes, BlockProcessStatus}; use lighthouse_tracing::SPAN_PENDING_COMPONENTS; use lru::LruCache; use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}; +use ssz_types::{RuntimeFixedVector, RuntimeVariableList}; use std::cmp::Ordering; use std::num::NonZeroUsize; use std::sync::Arc; @@ -20,8 +21,7 @@ use types::beacon_block_body::KzgCommitments; use types::blob_sidecar::BlobIdentifier; use types::{ BlobSidecar, BlockImportSource, ChainSpec, ColumnIndex, DataColumnSidecar, - DataColumnSidecarList, Epoch, EthSpec, Hash256, RuntimeFixedVector, RuntimeVariableList, - SignedBeaconBlock, + DataColumnSidecarList, Epoch, EthSpec, Hash256, SignedBeaconBlock, }; #[derive(Clone)] diff --git a/beacon_node/beacon_chain/src/kzg_utils.rs b/beacon_node/beacon_chain/src/kzg_utils.rs index 200774ebe4..334124419b 100644 --- a/beacon_node/beacon_chain/src/kzg_utils.rs +++ b/beacon_node/beacon_chain/src/kzg_utils.rs @@ -308,12 +308,14 @@ pub(crate) fn build_data_column_sidecars( /// and it will be slow if the node needs to reconstruct the blobs pub fn reconstruct_blobs( kzg: &Kzg, - data_columns: &[Arc>], + mut data_columns: Vec>>, blob_indices_opt: Option>, signed_block: &SignedBlindedBeaconBlock, spec: &ChainSpec, ) -> Result, String> { - // The data columns are from the database, so we assume their correctness. + // Sort data columns by index to ensure ascending order for KZG operations + data_columns.sort_unstable_by_key(|dc| dc.index); + let first_data_column = data_columns .first() .ok_or("data_columns should have at least one element".to_string())?; @@ -331,7 +333,7 @@ pub fn reconstruct_blobs( .map(|row_index| { let mut cells: Vec = vec![]; let mut cell_ids: Vec = vec![]; - for data_column in data_columns { + for data_column in &data_columns { let cell = data_column .column .get(row_index) @@ -463,6 +465,7 @@ mod test { test_reconstruct_data_columns(&kzg, &spec); test_reconstruct_data_columns_unordered(&kzg, &spec); test_reconstruct_blobs_from_data_columns(&kzg, &spec); + test_reconstruct_blobs_from_data_columns_unordered(&kzg, &spec); test_validate_data_columns(&kzg, &spec); } @@ -595,7 +598,7 @@ mod test { let blob_indices = vec![1, 2]; let reconstructed_blobs = reconstruct_blobs( kzg, - &column_sidecars.iter().as_slice()[0..column_sidecars.len() / 2], + column_sidecars[0..column_sidecars.len() / 2].to_vec(), Some(blob_indices.clone()), &signed_blinded_block, spec, @@ -613,6 +616,31 @@ mod test { } } + #[track_caller] + fn test_reconstruct_blobs_from_data_columns_unordered(kzg: &Kzg, spec: &ChainSpec) { + let num_of_blobs = 2; + let (signed_block, blobs, proofs) = + create_test_fulu_block_and_blobs::(num_of_blobs, spec); + let blob_refs = blobs.iter().collect::>(); + let column_sidecars = + blobs_to_data_column_sidecars(&blob_refs, proofs.to_vec(), &signed_block, kzg, spec) + .unwrap(); + + // Test reconstruction with columns in reverse order (non-ascending) + let mut subset_columns: Vec<_> = + column_sidecars.iter().as_slice()[0..column_sidecars.len() / 2].to_vec(); + subset_columns.reverse(); // This would fail without proper sorting in reconstruct_blobs + + let signed_blinded_block = signed_block.into(); + let reconstructed_blobs = + reconstruct_blobs(kzg, subset_columns, None, &signed_blinded_block, spec).unwrap(); + + for (i, original_blob) in blobs.iter().enumerate() { + let reconstructed_blob = &reconstructed_blobs.get(i).unwrap().blob; + assert_eq!(reconstructed_blob, original_blob, "{i}"); + } + } + fn get_kzg() -> Kzg { Kzg::new_from_trusted_setup(&get_trusted_setup()).expect("should create kzg") } diff --git a/beacon_node/beacon_chain/src/sync_committee_verification.rs b/beacon_node/beacon_chain/src/sync_committee_verification.rs index e72e9a6b21..88b040a6e5 100644 --- a/beacon_node/beacon_chain/src/sync_committee_verification.rs +++ b/beacon_node/beacon_chain/src/sync_committee_verification.rs @@ -49,7 +49,7 @@ use tree_hash_derive::TreeHash; use types::ChainSpec; use types::consts::altair::SYNC_COMMITTEE_SUBNET_COUNT; use types::slot_data::SlotData; -use types::sync_committee::Error as SyncCommitteeError; +use types::sync_committee::SyncCommitteeError; use types::{ AggregateSignature, BeaconStateError, EthSpec, Hash256, SignedContributionAndProof, Slot, SyncCommitteeContribution, SyncCommitteeMessage, SyncSelectionProof, SyncSubnetId, diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 05d67e4504..759b7e9bd7 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -46,6 +46,7 @@ use rand::seq::SliceRandom; use rayon::prelude::*; use sensitive_url::SensitiveUrl; use slot_clock::{SlotClock, TestingSlotClock}; +use ssz_types::RuntimeVariableList; use state_processing::per_block_processing::compute_timestamp_at_slot; use state_processing::state_advance::complete_state_advance; use std::borrow::Cow; diff --git a/beacon_node/execution_layer/src/test_utils/mock_builder.rs b/beacon_node/execution_layer/src/test_utils/mock_builder.rs index 9add136919..589b29193c 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_builder.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_builder.rs @@ -1,6 +1,7 @@ use crate::test_utils::{DEFAULT_BUILDER_PAYLOAD_VALUE_WEI, DEFAULT_JWT_SECRET}; use crate::{Config, ExecutionLayer, PayloadAttributes, PayloadParameters}; use bytes::Bytes; +use eth2::beacon_response::ForkVersionedResponse; use eth2::types::PublishBlockRequest; use eth2::types::{ BlobsBundle, BlockId, BroadcastValidation, EndpointVersion, EventKind, EventTopic, @@ -31,9 +32,9 @@ use types::builder_bid::{ }; use types::{ Address, BeaconState, ChainSpec, Epoch, EthSpec, ExecPayload, ExecutionPayload, - ExecutionPayloadHeaderRefMut, ExecutionRequests, ForkName, ForkVersionDecode, - ForkVersionedResponse, Hash256, PublicKeyBytes, Signature, SignedBlindedBeaconBlock, - SignedRoot, SignedValidatorRegistrationData, Slot, Uint256, + ExecutionPayloadHeaderRefMut, ExecutionRequests, ForkName, ForkVersionDecode, Hash256, + PublicKeyBytes, Signature, SignedBlindedBeaconBlock, SignedRoot, + SignedValidatorRegistrationData, Slot, Uint256, }; use types::{ExecutionBlockHash, SecretKey}; use warp::reply::{self, Reply}; diff --git a/beacon_node/http_api/src/beacon/mod.rs b/beacon_node/http_api/src/beacon/mod.rs new file mode 100644 index 0000000000..df5e6eee5c --- /dev/null +++ b/beacon_node/http_api/src/beacon/mod.rs @@ -0,0 +1,2 @@ +pub mod pool; +pub mod states; diff --git a/beacon_node/http_api/src/beacon/pool.rs b/beacon_node/http_api/src/beacon/pool.rs new file mode 100644 index 0000000000..059573c317 --- /dev/null +++ b/beacon_node/http_api/src/beacon/pool.rs @@ -0,0 +1,522 @@ +use crate::task_spawner::{Priority, TaskSpawner}; +use crate::utils::{NetworkTxFilter, OptionalConsensusVersionHeaderFilter, ResponseFilter}; +use crate::version::{ + ResponseIncludesVersion, V1, V2, add_consensus_version_header, beacon_response, + unsupported_version_rejection, +}; +use crate::{sync_committees, utils}; +use beacon_chain::observed_operations::ObservationOutcome; +use beacon_chain::{BeaconChain, BeaconChainTypes}; +use eth2::types::{AttestationPoolQuery, EndpointVersion, Failure, GenericResponse}; +use lighthouse_network::PubsubMessage; +use network::NetworkMessage; +use operation_pool::ReceivedPreCapella; +use slot_clock::SlotClock; +use std::collections::HashSet; +use std::sync::Arc; +use tokio::sync::mpsc::UnboundedSender; +use tracing::{debug, info, warn}; +use types::{ + Attestation, AttestationData, AttesterSlashing, ForkName, ProposerSlashing, + SignedBlsToExecutionChange, SignedVoluntaryExit, SingleAttestation, SyncCommitteeMessage, +}; +use warp::filters::BoxedFilter; +use warp::{Filter, Reply}; +use warp_utils::reject::convert_rejection; + +pub type BeaconPoolPathFilter = BoxedFilter<( + TaskSpawner<::EthSpec>, + Arc>, +)>; +pub type BeaconPoolPathV2Filter = BoxedFilter<( + TaskSpawner<::EthSpec>, + Arc>, +)>; +pub type BeaconPoolPathAnyFilter = BoxedFilter<( + EndpointVersion, + TaskSpawner<::EthSpec>, + Arc>, +)>; + +/// POST beacon/pool/bls_to_execution_changes +pub fn post_beacon_pool_bls_to_execution_changes( + network_tx_filter: &NetworkTxFilter, + beacon_pool_path: &BeaconPoolPathFilter, +) -> ResponseFilter { + beacon_pool_path + .clone() + .and(warp::path("bls_to_execution_changes")) + .and(warp::path::end()) + .and(warp_utils::json::json()) + .and(network_tx_filter.clone()) + .then( + |task_spawner: TaskSpawner, + chain: Arc>, + address_changes: Vec, + network_tx: UnboundedSender>| { + task_spawner.blocking_json_task(Priority::P0, move || { + let mut failures = vec![]; + + for (index, address_change) in address_changes.into_iter().enumerate() { + let validator_index = address_change.message.validator_index; + + match chain.verify_bls_to_execution_change_for_http_api(address_change) { + Ok(ObservationOutcome::New(verified_address_change)) => { + let validator_index = + verified_address_change.as_inner().message.validator_index; + let address = verified_address_change + .as_inner() + .message + .to_execution_address; + + // New to P2P *and* op pool, gossip immediately if post-Capella. + let received_pre_capella = + if chain.current_slot_is_post_capella().unwrap_or(false) { + ReceivedPreCapella::No + } else { + ReceivedPreCapella::Yes + }; + if matches!(received_pre_capella, ReceivedPreCapella::No) { + utils::publish_pubsub_message( + &network_tx, + PubsubMessage::BlsToExecutionChange(Box::new( + verified_address_change.as_inner().clone(), + )), + )?; + } + + // Import to op pool (may return `false` if there's a race). + let imported = chain.import_bls_to_execution_change( + verified_address_change, + received_pre_capella, + ); + + info!( + %validator_index, + ?address, + published = + matches!(received_pre_capella, ReceivedPreCapella::No), + imported, + "Processed BLS to execution change" + ); + } + Ok(ObservationOutcome::AlreadyKnown) => { + debug!(%validator_index, "BLS to execution change already known"); + } + Err(e) => { + warn!( + validator_index, + reason = ?e, + source = "HTTP", + "Invalid BLS to execution change" + ); + failures.push(Failure::new(index, format!("invalid: {e:?}"))); + } + } + } + + if failures.is_empty() { + Ok(()) + } else { + Err(warp_utils::reject::indexed_bad_request( + "some BLS to execution changes failed to verify".into(), + failures, + )) + } + }) + }, + ) + .boxed() +} + +/// GET beacon/pool/bls_to_execution_changes +pub fn get_beacon_pool_bls_to_execution_changes( + beacon_pool_path: &BeaconPoolPathFilter, +) -> ResponseFilter { + beacon_pool_path + .clone() + .and(warp::path("bls_to_execution_changes")) + .and(warp::path::end()) + .then( + |task_spawner: TaskSpawner, chain: Arc>| { + task_spawner.blocking_json_task(Priority::P1, move || { + let address_changes = chain.op_pool.get_all_bls_to_execution_changes(); + Ok(GenericResponse::from(address_changes)) + }) + }, + ) + .boxed() +} + +/// POST beacon/pool/sync_committees +pub fn post_beacon_pool_sync_committees( + network_tx_filter: &NetworkTxFilter, + beacon_pool_path: &BeaconPoolPathFilter, +) -> ResponseFilter { + beacon_pool_path + .clone() + .and(warp::path("sync_committees")) + .and(warp::path::end()) + .and(warp_utils::json::json()) + .and(network_tx_filter.clone()) + .then( + |task_spawner: TaskSpawner, + chain: Arc>, + signatures: Vec, + network_tx: UnboundedSender>| { + task_spawner.blocking_json_task(Priority::P0, move || { + sync_committees::process_sync_committee_signatures( + signatures, network_tx, &chain, + )?; + Ok(GenericResponse::from(())) + }) + }, + ) + .boxed() +} + +/// GET beacon/pool/voluntary_exits +pub fn get_beacon_pool_voluntary_exits( + beacon_pool_path: &BeaconPoolPathFilter, +) -> ResponseFilter { + beacon_pool_path + .clone() + .and(warp::path("voluntary_exits")) + .and(warp::path::end()) + .then( + |task_spawner: TaskSpawner, chain: Arc>| { + task_spawner.blocking_json_task(Priority::P1, move || { + let attestations = chain.op_pool.get_all_voluntary_exits(); + Ok(GenericResponse::from(attestations)) + }) + }, + ) + .boxed() +} + +/// POST beacon/pool/voluntary_exits +pub fn post_beacon_pool_voluntary_exits( + network_tx_filter: &NetworkTxFilter, + beacon_pool_path: &BeaconPoolPathFilter, +) -> ResponseFilter { + beacon_pool_path + .clone() + .and(warp::path("voluntary_exits")) + .and(warp::path::end()) + .and(warp_utils::json::json()) + .and(network_tx_filter.clone()) + .then( + |task_spawner: TaskSpawner, + chain: Arc>, + exit: SignedVoluntaryExit, + network_tx: UnboundedSender>| { + task_spawner.blocking_json_task(Priority::P0, move || { + let outcome = chain + .verify_voluntary_exit_for_gossip(exit.clone()) + .map_err(|e| { + warp_utils::reject::object_invalid(format!( + "gossip verification failed: {:?}", + e + )) + })?; + + // Notify the validator monitor. + chain + .validator_monitor + .read() + .register_api_voluntary_exit(&exit.message); + + if let ObservationOutcome::New(exit) = outcome { + utils::publish_pubsub_message( + &network_tx, + PubsubMessage::VoluntaryExit(Box::new(exit.clone().into_inner())), + )?; + + chain.import_voluntary_exit(exit); + } + + Ok(()) + }) + }, + ) + .boxed() +} + +/// GET beacon/pool/proposer_slashings +pub fn get_beacon_pool_proposer_slashings( + beacon_pool_path: &BeaconPoolPathFilter, +) -> ResponseFilter { + beacon_pool_path + .clone() + .and(warp::path("proposer_slashings")) + .and(warp::path::end()) + .then( + |task_spawner: TaskSpawner, chain: Arc>| { + task_spawner.blocking_json_task(Priority::P1, move || { + let attestations = chain.op_pool.get_all_proposer_slashings(); + Ok(GenericResponse::from(attestations)) + }) + }, + ) + .boxed() +} + +/// POST beacon/pool/proposer_slashings +pub fn post_beacon_pool_proposer_slashings( + network_tx_filter: &NetworkTxFilter, + beacon_pool_path: &BeaconPoolPathFilter, +) -> ResponseFilter { + beacon_pool_path + .clone() + .and(warp::path("proposer_slashings")) + .and(warp::path::end()) + .and(warp_utils::json::json()) + .and(network_tx_filter.clone()) + .then( + |task_spawner: TaskSpawner, + chain: Arc>, + slashing: ProposerSlashing, + network_tx: UnboundedSender>| { + task_spawner.blocking_json_task(Priority::P0, move || { + let outcome = chain + .verify_proposer_slashing_for_gossip(slashing.clone()) + .map_err(|e| { + warp_utils::reject::object_invalid(format!( + "gossip verification failed: {:?}", + e + )) + })?; + + // Notify the validator monitor. + chain + .validator_monitor + .read() + .register_api_proposer_slashing(&slashing); + + if let ObservationOutcome::New(slashing) = outcome { + utils::publish_pubsub_message( + &network_tx, + PubsubMessage::ProposerSlashing(Box::new( + slashing.clone().into_inner(), + )), + )?; + + chain.import_proposer_slashing(slashing); + } + + Ok(()) + }) + }, + ) + .boxed() +} + +/// GET beacon/pool/attester_slashings +pub fn get_beacon_pool_attester_slashings( + beacon_pool_path_any: &BeaconPoolPathAnyFilter, +) -> ResponseFilter { + beacon_pool_path_any + .clone() + .and(warp::path("attester_slashings")) + .and(warp::path::end()) + .then( + |endpoint_version: EndpointVersion, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.blocking_response_task(Priority::P1, move || { + let slashings = chain.op_pool.get_all_attester_slashings(); + + // Use the current slot to find the fork version, and convert all messages to the + // current fork's format. This is to ensure consistent message types matching + // `Eth-Consensus-Version`. + let current_slot = + chain + .slot_clock + .now() + .ok_or(warp_utils::reject::custom_server_error( + "unable to read slot clock".to_string(), + ))?; + let fork_name = chain.spec.fork_name_at_slot::(current_slot); + let slashings = slashings + .into_iter() + .filter(|slashing| { + (fork_name.electra_enabled() + && matches!(slashing, AttesterSlashing::Electra(_))) + || (!fork_name.electra_enabled() + && matches!(slashing, AttesterSlashing::Base(_))) + }) + .collect::>(); + + let require_version = match endpoint_version { + V1 => ResponseIncludesVersion::No, + V2 => ResponseIncludesVersion::Yes(fork_name), + _ => return Err(unsupported_version_rejection(endpoint_version)), + }; + + let res = beacon_response(require_version, &slashings); + Ok(add_consensus_version_header( + warp::reply::json(&res).into_response(), + fork_name, + )) + }) + }, + ) + .boxed() +} + +// POST beacon/pool/attester_slashings +pub fn post_beacon_pool_attester_slashings( + network_tx_filter: &NetworkTxFilter, + beacon_pool_path_any: &BeaconPoolPathAnyFilter, +) -> ResponseFilter { + beacon_pool_path_any + .clone() + .and(warp::path("attester_slashings")) + .and(warp::path::end()) + .and(warp_utils::json::json()) + .and(network_tx_filter.clone()) + .then( + // V1 and V2 are identical except V2 has a consensus version header in the request. + // We only require this header for SSZ deserialization, which isn't supported for + // this endpoint presently. + |_endpoint_version: EndpointVersion, + task_spawner: TaskSpawner, + chain: Arc>, + slashing: AttesterSlashing, + network_tx: UnboundedSender>| { + task_spawner.blocking_json_task(Priority::P0, move || { + let outcome = chain + .verify_attester_slashing_for_gossip(slashing.clone()) + .map_err(|e| { + warp_utils::reject::object_invalid(format!( + "gossip verification failed: {:?}", + e + )) + })?; + + // Notify the validator monitor. + chain + .validator_monitor + .read() + .register_api_attester_slashing(slashing.to_ref()); + + if let ObservationOutcome::New(slashing) = outcome { + utils::publish_pubsub_message( + &network_tx, + PubsubMessage::AttesterSlashing(Box::new( + slashing.clone().into_inner(), + )), + )?; + + chain.import_attester_slashing(slashing); + } + + Ok(()) + }) + }, + ) + .boxed() +} + +/// GET beacon/pool/attestations?committee_index,slot +pub fn get_beacon_pool_attestations( + beacon_pool_path_any: &BeaconPoolPathAnyFilter, +) -> ResponseFilter { + beacon_pool_path_any + .clone() + .and(warp::path("attestations")) + .and(warp::path::end()) + .and(warp::query::()) + .then( + |endpoint_version: EndpointVersion, + task_spawner: TaskSpawner, + chain: Arc>, + query: AttestationPoolQuery| { + task_spawner.blocking_response_task(Priority::P1, move || { + let query_filter = |data: &AttestationData, committee_indices: HashSet| { + query.slot.is_none_or(|slot| slot == data.slot) + && query + .committee_index + .is_none_or(|index| committee_indices.contains(&index)) + }; + + let mut attestations = chain.op_pool.get_filtered_attestations(query_filter); + attestations.extend( + chain + .naive_aggregation_pool + .read() + .iter() + .filter(|&att| { + query_filter(att.data(), att.get_committee_indices_map()) + }) + .cloned(), + ); + // Use the current slot to find the fork version, and convert all messages to the + // current fork's format. This is to ensure consistent message types matching + // `Eth-Consensus-Version`. + let current_slot = + chain + .slot_clock + .now() + .ok_or(warp_utils::reject::custom_server_error( + "unable to read slot clock".to_string(), + ))?; + let fork_name = chain.spec.fork_name_at_slot::(current_slot); + let attestations = attestations + .into_iter() + .filter(|att| { + (fork_name.electra_enabled() && matches!(att, Attestation::Electra(_))) + || (!fork_name.electra_enabled() + && matches!(att, Attestation::Base(_))) + }) + .collect::>(); + + let require_version = match endpoint_version { + V1 => ResponseIncludesVersion::No, + V2 => ResponseIncludesVersion::Yes(fork_name), + _ => return Err(unsupported_version_rejection(endpoint_version)), + }; + + let res = beacon_response(require_version, &attestations); + Ok(add_consensus_version_header( + warp::reply::json(&res).into_response(), + fork_name, + )) + }) + }, + ) + .boxed() +} + +pub fn post_beacon_pool_attestations_v2( + network_tx_filter: &NetworkTxFilter, + optional_consensus_version_header_filter: OptionalConsensusVersionHeaderFilter, + beacon_pool_path_v2: &BeaconPoolPathV2Filter, +) -> ResponseFilter { + beacon_pool_path_v2 + .clone() + .and(warp::path("attestations")) + .and(warp::path::end()) + .and(warp_utils::json::json::>()) + .and(optional_consensus_version_header_filter) + .and(network_tx_filter.clone()) + .then( + |task_spawner: TaskSpawner, + chain: Arc>, + attestations: Vec, + _fork_name: Option, + network_tx: UnboundedSender>| async move { + let result = crate::publish_attestations::publish_attestations( + task_spawner, + chain, + attestations, + network_tx, + true, + ) + .await + .map(|()| warp::reply::json(&())); + convert_rejection(result).await + }, + ) + .boxed() +} diff --git a/beacon_node/http_api/src/beacon/states.rs b/beacon_node/http_api/src/beacon/states.rs new file mode 100644 index 0000000000..6d06bcc77d --- /dev/null +++ b/beacon_node/http_api/src/beacon/states.rs @@ -0,0 +1,787 @@ +use crate::StateId; +use crate::task_spawner::{Priority, TaskSpawner}; +use crate::utils::ResponseFilter; +use crate::validator::pubkey_to_validator_index; +use crate::version::{ + ResponseIncludesVersion, add_consensus_version_header, + execution_optimistic_finalized_beacon_response, +}; +use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes, WhenSlotSkipped}; +use eth2::types::{ + ValidatorBalancesRequestBody, ValidatorId, ValidatorIdentitiesRequestBody, + ValidatorsRequestBody, +}; +use std::sync::Arc; +use types::{ + AttestationShufflingId, CommitteeCache, Error as BeaconStateError, EthSpec, RelativeEpoch, +}; +use warp::filters::BoxedFilter; +use warp::{Filter, Reply}; +use warp_utils::query::multi_key_query; + +type BeaconStatesPath = BoxedFilter<( + StateId, + TaskSpawner<::EthSpec>, + Arc>, +)>; + +// GET beacon/states/{state_id}/pending_consolidations +pub fn get_beacon_state_pending_consolidations( + beacon_states_path: BeaconStatesPath, +) -> ResponseFilter { + beacon_states_path + .clone() + .and(warp::path("pending_consolidations")) + .and(warp::path::end()) + .then( + |state_id: StateId, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.blocking_response_task(Priority::P1, move || { + let (data, execution_optimistic, finalized, fork_name) = state_id + .map_state_and_execution_optimistic_and_finalized( + &chain, + |state, execution_optimistic, finalized| { + let Ok(consolidations) = state.pending_consolidations() else { + return Err(warp_utils::reject::custom_bad_request( + "Pending consolidations not found".to_string(), + )); + }; + + Ok(( + consolidations.clone(), + execution_optimistic, + finalized, + state.fork_name_unchecked(), + )) + }, + )?; + + execution_optimistic_finalized_beacon_response( + ResponseIncludesVersion::Yes(fork_name), + execution_optimistic, + finalized, + data, + ) + .map(|res| warp::reply::json(&res).into_response()) + .map(|resp| add_consensus_version_header(resp, fork_name)) + }) + }, + ) + .boxed() +} + +// GET beacon/states/{state_id}/pending_partial_withdrawals +pub fn get_beacon_state_pending_partial_withdrawals( + beacon_states_path: BeaconStatesPath, +) -> ResponseFilter { + beacon_states_path + .clone() + .and(warp::path("pending_partial_withdrawals")) + .and(warp::path::end()) + .then( + |state_id: StateId, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.blocking_response_task(Priority::P1, move || { + let (data, execution_optimistic, finalized, fork_name) = state_id + .map_state_and_execution_optimistic_and_finalized( + &chain, + |state, execution_optimistic, finalized| { + let Ok(withdrawals) = state.pending_partial_withdrawals() else { + return Err(warp_utils::reject::custom_bad_request( + "Pending withdrawals not found".to_string(), + )); + }; + + Ok(( + withdrawals.clone(), + execution_optimistic, + finalized, + state.fork_name_unchecked(), + )) + }, + )?; + + execution_optimistic_finalized_beacon_response( + ResponseIncludesVersion::Yes(fork_name), + execution_optimistic, + finalized, + data, + ) + .map(|res| warp::reply::json(&res).into_response()) + .map(|resp| add_consensus_version_header(resp, fork_name)) + }) + }, + ) + .boxed() +} + +// GET beacon/states/{state_id}/pending_deposits +pub fn get_beacon_state_pending_deposits( + beacon_states_path: BeaconStatesPath, +) -> ResponseFilter { + beacon_states_path + .clone() + .and(warp::path("pending_deposits")) + .and(warp::path::end()) + .then( + |state_id: StateId, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.blocking_response_task(Priority::P1, move || { + let (data, execution_optimistic, finalized, fork_name) = state_id + .map_state_and_execution_optimistic_and_finalized( + &chain, + |state, execution_optimistic, finalized| { + let Ok(deposits) = state.pending_deposits() else { + return Err(warp_utils::reject::custom_bad_request( + "Pending deposits not found".to_string(), + )); + }; + + Ok(( + deposits.clone(), + execution_optimistic, + finalized, + state.fork_name_unchecked(), + )) + }, + )?; + + execution_optimistic_finalized_beacon_response( + ResponseIncludesVersion::Yes(fork_name), + execution_optimistic, + finalized, + data, + ) + .map(|res| warp::reply::json(&res).into_response()) + .map(|resp| add_consensus_version_header(resp, fork_name)) + }) + }, + ) + .boxed() +} + +// GET beacon/states/{state_id}/randao?epoch +pub fn get_beacon_state_randao( + beacon_states_path: BeaconStatesPath, +) -> ResponseFilter { + beacon_states_path + .clone() + .and(warp::path("randao")) + .and(warp::query::()) + .and(warp::path::end()) + .then( + |state_id: StateId, + task_spawner: TaskSpawner, + chain: Arc>, + query: eth2::types::RandaoQuery| { + task_spawner.blocking_json_task(Priority::P1, move || { + let (randao, execution_optimistic, finalized) = state_id + .map_state_and_execution_optimistic_and_finalized( + &chain, + |state, execution_optimistic, finalized| { + let epoch = query.epoch.unwrap_or_else(|| state.current_epoch()); + let randao = *state.get_randao_mix(epoch).map_err(|e| { + warp_utils::reject::custom_bad_request(format!( + "epoch out of range: {e:?}" + )) + })?; + Ok((randao, execution_optimistic, finalized)) + }, + )?; + + Ok( + eth2::types::GenericResponse::from(eth2::types::RandaoMix { randao }) + .add_execution_optimistic_finalized(execution_optimistic, finalized), + ) + }) + }, + ) + .boxed() +} + +// GET beacon/states/{state_id}/sync_committees?epoch +pub fn get_beacon_state_sync_committees( + beacon_states_path: BeaconStatesPath, +) -> ResponseFilter { + beacon_states_path + .clone() + .and(warp::path("sync_committees")) + .and(warp::query::()) + .and(warp::path::end()) + .then( + |state_id: StateId, + task_spawner: TaskSpawner, + chain: Arc>, + query: eth2::types::SyncCommitteesQuery| { + task_spawner.blocking_json_task(Priority::P1, move || { + let (sync_committee, execution_optimistic, finalized) = state_id + .map_state_and_execution_optimistic_and_finalized( + &chain, + |state, execution_optimistic, finalized| { + let current_epoch = state.current_epoch(); + let epoch = query.epoch.unwrap_or(current_epoch); + Ok(( + state + .get_built_sync_committee(epoch, &chain.spec) + .cloned() + .map_err(|e| match e { + BeaconStateError::SyncCommitteeNotKnown { .. } => { + warp_utils::reject::custom_bad_request(format!( + "state at epoch {} has no \ + sync committee for epoch {}", + current_epoch, epoch + )) + } + BeaconStateError::IncorrectStateVariant => { + warp_utils::reject::custom_bad_request(format!( + "state at epoch {} is not activated for Altair", + current_epoch, + )) + } + e => warp_utils::reject::beacon_state_error(e), + })?, + execution_optimistic, + finalized, + )) + }, + )?; + + let validators = chain + .validator_indices(sync_committee.pubkeys.iter()) + .map_err(warp_utils::reject::unhandled_error)?; + + let validator_aggregates = validators + .chunks_exact(T::EthSpec::sync_subcommittee_size()) + .map(|indices| eth2::types::SyncSubcommittee { + indices: indices.to_vec(), + }) + .collect(); + + let response = eth2::types::SyncCommitteeByValidatorIndices { + validators, + validator_aggregates, + }; + + Ok(eth2::types::GenericResponse::from(response) + .add_execution_optimistic_finalized(execution_optimistic, finalized)) + }) + }, + ) + .boxed() +} + +// GET beacon/states/{state_id}/committees?slot,index,epoch +pub fn get_beacon_state_committees( + beacon_states_path: BeaconStatesPath, +) -> ResponseFilter { + beacon_states_path + .clone() + .and(warp::path("committees")) + .and(warp::query::()) + .and(warp::path::end()) + .then( + |state_id: StateId, + task_spawner: TaskSpawner, + chain: Arc>, + query: eth2::types::CommitteesQuery| { + task_spawner.blocking_json_task(Priority::P1, move || { + let (data, execution_optimistic, finalized) = state_id + .map_state_and_execution_optimistic_and_finalized( + &chain, + |state, execution_optimistic, finalized| { + let current_epoch = state.current_epoch(); + let epoch = query.epoch.unwrap_or(current_epoch); + + // Attempt to obtain the committee_cache from the beacon chain + let decision_slot = (epoch.saturating_sub(2u64)) + .end_slot(T::EthSpec::slots_per_epoch()); + // Find the decision block and skip to another method on any kind + // of failure + let shuffling_id = if let Ok(Some(shuffling_decision_block)) = + chain.block_root_at_slot(decision_slot, WhenSlotSkipped::Prev) + { + Some(AttestationShufflingId { + shuffling_epoch: epoch, + shuffling_decision_block, + }) + } else { + None + }; + + // Attempt to read from the chain cache if there exists a + // shuffling_id + let maybe_cached_shuffling = if let Some(shuffling_id) = + shuffling_id.as_ref() + { + chain + .shuffling_cache + .try_write_for(std::time::Duration::from_secs(1)) + .and_then(|mut cache_write| cache_write.get(shuffling_id)) + .and_then(|cache_item| cache_item.wait().ok()) + } else { + None + }; + + let committee_cache = + if let Some(shuffling) = maybe_cached_shuffling { + shuffling + } else { + let possibly_built_cache = + match RelativeEpoch::from_epoch(current_epoch, epoch) { + Ok(relative_epoch) + if state.committee_cache_is_initialized( + relative_epoch, + ) => + { + state.committee_cache(relative_epoch).cloned() + } + _ => CommitteeCache::initialized( + state, + epoch, + &chain.spec, + ), + } + .map_err( + |e| match e { + BeaconStateError::EpochOutOfBounds => { + let max_sprp = + T::EthSpec::slots_per_historical_root() + as u64; + let first_subsequent_restore_point_slot = + ((epoch.start_slot( + T::EthSpec::slots_per_epoch(), + ) / max_sprp) + + 1) + * max_sprp; + if epoch < current_epoch { + warp_utils::reject::custom_bad_request( + format!( + "epoch out of bounds, \ + try state at slot {}", + first_subsequent_restore_point_slot, + ), + ) + } else { + warp_utils::reject::custom_bad_request( + "epoch out of bounds, \ + too far in future" + .into(), + ) + } + } + _ => warp_utils::reject::unhandled_error( + BeaconChainError::from(e), + ), + }, + )?; + + // Attempt to write to the beacon cache (only if the cache + // size is not the default value). + if chain.config.shuffling_cache_size + != beacon_chain::shuffling_cache::DEFAULT_CACHE_SIZE + && let Some(shuffling_id) = shuffling_id + && let Some(mut cache_write) = chain + .shuffling_cache + .try_write_for(std::time::Duration::from_secs(1)) + { + cache_write.insert_committee_cache( + shuffling_id, + &possibly_built_cache, + ); + } + + possibly_built_cache + }; + + // Use either the supplied slot or all slots in the epoch. + let slots = + query.slot.map(|slot| vec![slot]).unwrap_or_else(|| { + epoch.slot_iter(T::EthSpec::slots_per_epoch()).collect() + }); + + // Use either the supplied committee index or all available indices. + let indices = + query.index.map(|index| vec![index]).unwrap_or_else(|| { + (0..committee_cache.committees_per_slot()).collect() + }); + + let mut response = Vec::with_capacity(slots.len() * indices.len()); + + for slot in slots { + // It is not acceptable to query with a slot that is not within the + // specified epoch. + if slot.epoch(T::EthSpec::slots_per_epoch()) != epoch { + return Err(warp_utils::reject::custom_bad_request( + format!("{} is not in epoch {}", slot, epoch), + )); + } + + for &index in &indices { + let committee = committee_cache + .get_beacon_committee(slot, index) + .ok_or_else(|| { + warp_utils::reject::custom_bad_request(format!( + "committee index {} does not exist in epoch {}", + index, epoch + )) + })?; + + response.push(eth2::types::CommitteeData { + index, + slot, + validators: committee + .committee + .iter() + .map(|i| *i as u64) + .collect(), + }); + } + } + + Ok((response, execution_optimistic, finalized)) + }, + )?; + Ok(eth2::types::ExecutionOptimisticFinalizedResponse { + data, + execution_optimistic: Some(execution_optimistic), + finalized: Some(finalized), + }) + }) + }, + ) + .boxed() +} + +// GET beacon/states/{state_id}/validators/{validator_id} +pub fn get_beacon_state_validators_id( + beacon_states_path: BeaconStatesPath, +) -> ResponseFilter { + beacon_states_path + .clone() + .and(warp::path("validators")) + .and(warp::path::param::().or_else(|_| async { + Err(warp_utils::reject::custom_bad_request( + "Invalid validator ID".to_string(), + )) + })) + .and(warp::path::end()) + .then( + |state_id: StateId, + task_spawner: TaskSpawner, + chain: Arc>, + validator_id: ValidatorId| { + // Prioritise requests for validators at the head. These should be fast to service + // and could be required by the validator client. + let priority = if let StateId(eth2::types::StateId::Head) = state_id { + Priority::P0 + } else { + Priority::P1 + }; + task_spawner.blocking_json_task(priority, move || { + let (data, execution_optimistic, finalized) = state_id + .map_state_and_execution_optimistic_and_finalized( + &chain, + |state, execution_optimistic, finalized| { + let index_opt = match &validator_id { + ValidatorId::PublicKey(pubkey) => pubkey_to_validator_index( + &chain, state, pubkey, + ) + .map_err(|e| { + warp_utils::reject::custom_not_found(format!( + "unable to access pubkey cache: {e:?}", + )) + })?, + ValidatorId::Index(index) => Some(*index as usize), + }; + + Ok(( + index_opt + .and_then(|index| { + let validator = state.validators().get(index)?; + let balance = *state.balances().get(index)?; + let epoch = state.current_epoch(); + let far_future_epoch = chain.spec.far_future_epoch; + + Some(eth2::types::ValidatorData { + index: index as u64, + balance, + status: + eth2::types::ValidatorStatus::from_validator( + validator, + epoch, + far_future_epoch, + ), + validator: validator.clone(), + }) + }) + .ok_or_else(|| { + warp_utils::reject::custom_not_found(format!( + "unknown validator: {}", + validator_id + )) + })?, + execution_optimistic, + finalized, + )) + }, + )?; + + Ok(eth2::types::ExecutionOptimisticFinalizedResponse { + data, + execution_optimistic: Some(execution_optimistic), + finalized: Some(finalized), + }) + }) + }, + ) + .boxed() +} + +// POST beacon/states/{state_id}/validators +pub fn post_beacon_state_validators( + beacon_states_path: BeaconStatesPath, +) -> ResponseFilter { + beacon_states_path + .clone() + .and(warp::path("validators")) + .and(warp::path::end()) + .and(warp_utils::json::json()) + .then( + |state_id: StateId, + task_spawner: TaskSpawner, + chain: Arc>, + query: ValidatorsRequestBody| { + // Prioritise requests for validators at the head. These should be fast to service + // and could be required by the validator client. + let priority = if let StateId(eth2::types::StateId::Head) = state_id { + Priority::P0 + } else { + Priority::P1 + }; + task_spawner.blocking_json_task(priority, move || { + crate::validators::get_beacon_state_validators( + state_id, + chain, + &query.ids, + &query.statuses, + ) + }) + }, + ) + .boxed() +} + +// GET beacon/states/{state_id}/validators?id,status +pub fn get_beacon_state_validators( + beacon_states_path: BeaconStatesPath, +) -> ResponseFilter { + beacon_states_path + .clone() + .and(warp::path("validators")) + .and(warp::path::end()) + .and(multi_key_query::()) + .then( + |state_id: StateId, + task_spawner: TaskSpawner, + chain: Arc>, + query_res: Result| { + // Prioritise requests for validators at the head. These should be fast to service + // and could be required by the validator client. + let priority = if let StateId(eth2::types::StateId::Head) = state_id { + Priority::P0 + } else { + Priority::P1 + }; + task_spawner.blocking_json_task(priority, move || { + let query = query_res?; + crate::validators::get_beacon_state_validators( + state_id, + chain, + &query.id, + &query.status, + ) + }) + }, + ) + .boxed() +} + +// POST beacon/states/{state_id}/validator_identities +pub fn post_beacon_state_validator_identities( + beacon_states_path: BeaconStatesPath, +) -> ResponseFilter { + beacon_states_path + .clone() + .and(warp::path("validator_identities")) + .and(warp::path::end()) + .and(warp_utils::json::json_no_body()) + .then( + |state_id: StateId, + task_spawner: TaskSpawner, + chain: Arc>, + query: ValidatorIdentitiesRequestBody| { + // Prioritise requests for validators at the head. These should be fast to service + // and could be required by the validator client. + let priority = if let StateId(eth2::types::StateId::Head) = state_id { + Priority::P0 + } else { + Priority::P1 + }; + task_spawner.blocking_json_task(priority, move || { + crate::validators::get_beacon_state_validator_identities( + state_id, + chain, + Some(&query.ids), + ) + }) + }, + ) + .boxed() +} + +// POST beacon/states/{state_id}/validator_balances +pub fn post_beacon_state_validator_balances( + beacon_states_path: BeaconStatesPath, +) -> ResponseFilter { + beacon_states_path + .clone() + .and(warp::path("validator_balances")) + .and(warp::path::end()) + .and(warp_utils::json::json_no_body()) + .then( + |state_id: StateId, + task_spawner: TaskSpawner, + chain: Arc>, + query: ValidatorBalancesRequestBody| { + task_spawner.blocking_json_task(Priority::P1, move || { + crate::validators::get_beacon_state_validator_balances( + state_id, + chain, + Some(&query.ids), + ) + }) + }, + ) + .boxed() +} + +// GET beacon/states/{state_id}/validator_balances?id +pub fn get_beacon_state_validator_balances( + beacon_states_path: BeaconStatesPath, +) -> ResponseFilter { + beacon_states_path + .clone() + .and(warp::path("validator_balances")) + .and(warp::path::end()) + .and(multi_key_query::()) + .then( + |state_id: StateId, + task_spawner: TaskSpawner, + chain: Arc>, + query_res: Result| { + task_spawner.blocking_json_task(Priority::P1, move || { + let query = query_res?; + crate::validators::get_beacon_state_validator_balances( + state_id, + chain, + query.id.as_deref(), + ) + }) + }, + ) + .boxed() +} + +// GET beacon/states/{state_id}/finality_checkpoints +pub fn get_beacon_state_finality_checkpoints( + beacon_states_path: BeaconStatesPath, +) -> ResponseFilter { + beacon_states_path + .clone() + .and(warp::path("finality_checkpoints")) + .and(warp::path::end()) + .then( + |state_id: StateId, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.blocking_json_task(Priority::P1, move || { + let (data, execution_optimistic, finalized) = state_id + .map_state_and_execution_optimistic_and_finalized( + &chain, + |state, execution_optimistic, finalized| { + Ok(( + eth2::types::FinalityCheckpointsData { + previous_justified: state.previous_justified_checkpoint(), + current_justified: state.current_justified_checkpoint(), + finalized: state.finalized_checkpoint(), + }, + execution_optimistic, + finalized, + )) + }, + )?; + + Ok(eth2::types::ExecutionOptimisticFinalizedResponse { + data, + execution_optimistic: Some(execution_optimistic), + finalized: Some(finalized), + }) + }) + }, + ) + .boxed() +} + +// GET beacon/states/{state_id}/fork +pub fn get_beacon_state_fork( + beacon_states_path: BeaconStatesPath, +) -> ResponseFilter { + beacon_states_path + .clone() + .and(warp::path("fork")) + .and(warp::path::end()) + .then( + |state_id: StateId, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.blocking_json_task(Priority::P1, move || { + let (fork, execution_optimistic, finalized) = + state_id.fork_and_execution_optimistic_and_finalized(&chain)?; + Ok(eth2::types::ExecutionOptimisticFinalizedResponse { + data: fork, + execution_optimistic: Some(execution_optimistic), + finalized: Some(finalized), + }) + }) + }, + ) + .boxed() +} + +// GET beacon/states/{state_id}/root +pub fn get_beacon_state_root( + beacon_states_path: BeaconStatesPath, +) -> ResponseFilter { + beacon_states_path + .and(warp::path("root")) + .and(warp::path::end()) + .then( + |state_id: StateId, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.blocking_json_task(Priority::P1, move || { + let (root, execution_optimistic, finalized) = state_id.root(&chain)?; + Ok(eth2::types::GenericResponse::from( + eth2::types::RootData::from(root), + )) + .map(|resp| { + resp.add_execution_optimistic_finalized(execution_optimistic, finalized) + }) + }) + }, + ) + .boxed() +} diff --git a/beacon_node/http_api/src/block_id.rs b/beacon_node/http_api/src/block_id.rs index 778067c32b..64f5451560 100644 --- a/beacon_node/http_api/src/block_id.rs +++ b/beacon_node/http_api/src/block_id.rs @@ -2,6 +2,7 @@ use crate::version::inconsistent_fork_rejection; use crate::{ExecutionOptimistic, state_id::checkpoint_slot_and_execution_optimistic}; use beacon_chain::kzg_utils::reconstruct_blobs; use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes, WhenSlotSkipped}; +use eth2::beacon_response::{ExecutionOptimisticFinalizedMetadata, UnversionedResponse}; use eth2::types::BlockId as CoreBlockId; use eth2::types::DataColumnIndicesQuery; use eth2::types::{BlobIndicesQuery, BlobWrapper, BlobsVersionedHashesQuery}; @@ -10,8 +11,7 @@ use std::str::FromStr; use std::sync::Arc; use types::{ BlobSidecarList, DataColumnSidecarList, EthSpec, FixedBytesExtended, ForkName, Hash256, - SignedBeaconBlock, SignedBlindedBeaconBlock, Slot, UnversionedResponse, - beacon_response::ExecutionOptimisticFinalizedMetadata, + SignedBeaconBlock, SignedBlindedBeaconBlock, Slot, }; use warp::Rejection; @@ -474,7 +474,7 @@ impl BlockId { ) .collect::, _>>()?; - reconstruct_blobs(&chain.kzg, &data_columns, blob_indices, block, &chain.spec).map_err( + reconstruct_blobs(&chain.kzg, data_columns, blob_indices, block, &chain.spec).map_err( |e| { warp_utils::reject::custom_server_error(format!( "Error reconstructing data columns: {e:?}" diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 6389b34961..628b94a2a7 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -8,6 +8,7 @@ mod aggregate_attestation; mod attestation_performance; mod attester_duties; +mod beacon; mod block_id; mod block_packing_efficiency; mod block_rewards; @@ -29,39 +30,40 @@ mod sync_committees; mod task_spawner; pub mod test_utils; mod ui; +mod utils; mod validator; mod validator_inclusion; mod validators; mod version; + +use crate::beacon::pool::*; use crate::light_client::{get_light_client_bootstrap, get_light_client_updates}; -use crate::produce_block::{produce_blinded_block_v2, produce_block_v2, produce_block_v3}; +use crate::utils::{AnyVersionFilter, EthV1Filter}; +use crate::validator::post_validator_liveness_epoch; +use crate::validator::*; use crate::version::beacon_response; -use beacon_chain::{ - AttestationError as AttnError, BeaconChain, BeaconChainError, BeaconChainTypes, - WhenSlotSkipped, attestation_verification::VerifiedAttestation, - observed_operations::ObservationOutcome, validator_monitor::timestamp_now, -}; +use beacon::states; +use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes, WhenSlotSkipped}; use beacon_processor::BeaconProcessorSend; pub use block_id::BlockId; use builder_states::get_next_withdrawals; use bytes::Bytes; use directory::DEFAULT_ROOT_DIR; use eth2::StatusCode; +use eth2::lighthouse::sync_state::SyncState; use eth2::types::{ self as api_types, BroadcastValidation, ContextDeserialize, EndpointVersion, ForkChoice, - ForkChoiceExtraData, ForkChoiceNode, LightClientUpdatesQuery, PublishBlockRequest, - StateId as CoreStateId, ValidatorBalancesRequestBody, ValidatorId, - ValidatorIdentitiesRequestBody, ValidatorStatus, ValidatorsRequestBody, + ForkChoiceExtraData, ForkChoiceNode, LightClientUpdatesQuery, PublishBlockRequest, ValidatorId, }; use eth2::{CONSENSUS_VERSION_HEADER, CONTENT_TYPE_HEADER, SSZ_CONTENT_TYPE_HEADER}; use health_metrics::observe::Observe; -use lighthouse_network::rpc::methods::MetaData; -use lighthouse_network::{Enr, NetworkGlobals, PeerId, PubsubMessage, types::SyncState}; +use lighthouse_network::Enr; +use lighthouse_network::NetworkGlobals; +use lighthouse_network::PeerId; use lighthouse_version::version_with_platform; use logging::{SSELoggingComponents, crit}; -use network::{NetworkMessage, NetworkSenders, ValidatorSubscriptionMessage}; +use network::{NetworkMessage, NetworkSenders}; use network_utils::enr_ext::EnrExt; -use operation_pool::ReceivedPreCapella; use parking_lot::RwLock; pub use publish_blocks::{ ProvenancedBlock, publish_blinded_block, publish_block, reconstruct_block, @@ -70,7 +72,6 @@ use serde::{Deserialize, Serialize}; use slot_clock::SlotClock; use ssz::Encode; pub use state_id::StateId; -use std::collections::HashSet; use std::future::Future; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::path::PathBuf; @@ -80,26 +81,18 @@ use std::sync::Arc; use sysinfo::{System, SystemExt}; use system_health::{observe_nat, observe_system_health_bn}; use task_spawner::{Priority, TaskSpawner}; -use tokio::sync::{ - mpsc::{Sender, UnboundedSender}, - oneshot, -}; +use tokio::sync::mpsc::UnboundedSender; use tokio_stream::{ StreamExt, wrappers::{BroadcastStream, errors::BroadcastStreamRecvError}, }; -use tracing::{debug, error, info, warn}; +use tracing::{debug, info, warn}; use types::{ - Attestation, AttestationData, AttestationShufflingId, AttesterSlashing, BeaconStateError, - ChainSpec, Checkpoint, CommitteeCache, ConfigAndPreset, Epoch, EthSpec, ForkName, Hash256, - ProposerPreparationData, ProposerSlashing, RelativeEpoch, SignedAggregateAndProof, - SignedBlindedBeaconBlock, SignedBlsToExecutionChange, SignedContributionAndProof, - SignedValidatorRegistrationData, SignedVoluntaryExit, SingleAttestation, Slot, - SyncCommitteeMessage, SyncContributionData, + BeaconStateError, Checkpoint, ConfigAndPreset, Epoch, EthSpec, ForkName, Hash256, + SignedBlindedBeaconBlock, Slot, }; -use validator::pubkey_to_validator_index; use version::{ - ResponseIncludesVersion, V1, V2, V3, add_consensus_version_header, add_ssz_content_type_header, + ResponseIncludesVersion, V1, V2, add_consensus_version_header, add_ssz_content_type_header, execution_optimistic_finalized_beacon_response, inconsistent_fork_rejection, unsupported_version_rejection, }; @@ -107,7 +100,7 @@ use warp::Reply; use warp::hyper::Body; use warp::sse::Event; use warp::{Filter, Rejection, http::Response}; -use warp_utils::{query::multi_key_query, reject::convert_rejection, uor::UnifyingOrFilter}; +use warp_utils::{query::multi_key_query, uor::UnifyingOrFilter}; const API_PREFIX: &str = "eth"; @@ -359,16 +352,18 @@ pub fn serve( } // Create a filter that extracts the endpoint version. - let any_version = warp::path(API_PREFIX).and(warp::path::param::().or_else( - |_| async move { - Err(warp_utils::reject::custom_bad_request( - "Invalid version identifier".to_string(), - )) - }, - )); + let any_version = warp::path(API_PREFIX) + .and( + warp::path::param::().or_else(|_| async move { + Err(warp_utils::reject::custom_bad_request( + "Invalid version identifier".to_string(), + )) + }), + ) + .boxed(); // Filter that enforces a single endpoint version and then discards the `EndpointVersion`. - let single_version = |reqd: EndpointVersion| { + fn single_version(any_version: AnyVersionFilter, reqd: EndpointVersion) -> EthV1Filter { any_version .and_then(move |version| async move { if version == reqd { @@ -378,10 +373,11 @@ pub fn serve( } }) .untuple_one() - }; + .boxed() + } - let eth_v1 = single_version(V1); - let eth_v2 = single_version(V2); + let eth_v1 = single_version(any_version.clone(), V1); + let eth_v2 = single_version(any_version.clone(), V2); // Create a `warp` filter that provides access to the network globals. let inner_network_globals = ctx.network_globals.clone(); @@ -402,34 +398,34 @@ pub fn serve( // Create a `warp` filter that provides access to the beacon chain. let inner_ctx = ctx.clone(); - let chain_filter = - warp::any() - .map(move || inner_ctx.chain.clone()) - .and_then(|chain| async move { - match chain { - Some(chain) => Ok(chain), - None => Err(warp_utils::reject::custom_not_found( - "Beacon chain genesis has not yet been observed.".to_string(), - )), - } - }); + let chain_filter = warp::any() + .map(move || inner_ctx.chain.clone()) + .and_then(|chain| async move { + match chain { + Some(chain) => Ok(chain), + None => Err(warp_utils::reject::custom_not_found( + "Beacon chain genesis has not yet been observed.".to_string(), + )), + } + }) + .boxed(); // Create a `warp` filter that provides access to the network sender channel. let network_tx = ctx .network_senders .as_ref() .map(|senders| senders.network_send()); - let network_tx_filter = - warp::any() - .map(move || network_tx.clone()) - .and_then(|network_tx| async move { - match network_tx { - Some(network_tx) => Ok(network_tx), - None => Err(warp_utils::reject::custom_not_found( - "The networking stack has not yet started (network_tx).".to_string(), - )), - } - }); + let network_tx_filter = warp::any() + .map(move || network_tx.clone()) + .and_then(|network_tx| async move { + match network_tx { + Some(network_tx) => Ok(network_tx), + None => Err(warp_utils::reject::custom_not_found( + "The networking stack has not yet started (network_tx).".to_string(), + )), + } + }) + .boxed(); // Create a `warp` filter that provides access to the network attestation subscription channel. let validator_subscriptions_tx = ctx @@ -446,7 +442,8 @@ pub fn serve( .to_string(), )), } - }); + }) + .boxed(); // Create a `warp` filter that rejects requests whilst the node is syncing. let not_while_syncing_filter = @@ -486,7 +483,8 @@ pub fn serve( SyncState::Stalled => Ok(()), } }, - ); + ) + .boxed(); // Create a `warp` filter that returns 404s if the light client server is disabled. let light_client_server_filter = @@ -539,8 +537,9 @@ pub fn serve( .beacon_processor_send .clone() .filter(|_| config.enable_beacon_processor); - let task_spawner_filter = - warp::any().map(move || TaskSpawner::new(beacon_processor_send.clone())); + let task_spawner_filter = warp::any() + .map(move || TaskSpawner::new(beacon_processor_send.clone())) + .boxed(); let duplicate_block_status_code = ctx.config.duplicate_block_status_code; @@ -552,6 +551,7 @@ pub fn serve( // GET beacon/genesis let get_beacon_genesis = eth_v1 + .clone() .and(warp::path("beacon")) .and(warp::path("genesis")) .and(warp::path::end()) @@ -575,6 +575,7 @@ pub fn serve( */ let beacon_states_path = eth_v1 + .clone() .and(warp::path("beacon")) .and(warp::path("states")) .and(warp::path::param::().or_else(|_| async { @@ -583,693 +584,65 @@ pub fn serve( )) })) .and(task_spawner_filter.clone()) - .and(chain_filter.clone()); + .and(chain_filter.clone()) + .boxed(); // GET beacon/states/{state_id}/root - let get_beacon_state_root = beacon_states_path - .clone() - .and(warp::path("root")) - .and(warp::path::end()) - .then( - |state_id: StateId, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.blocking_json_task(Priority::P1, move || { - let (root, execution_optimistic, finalized) = state_id.root(&chain)?; - Ok(api_types::GenericResponse::from(api_types::RootData::from( - root, - ))) - .map(|resp| { - resp.add_execution_optimistic_finalized(execution_optimistic, finalized) - }) - }) - }, - ); + let get_beacon_state_root = states::get_beacon_state_root(beacon_states_path.clone()); // GET beacon/states/{state_id}/fork - let get_beacon_state_fork = beacon_states_path - .clone() - .and(warp::path("fork")) - .and(warp::path::end()) - .then( - |state_id: StateId, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.blocking_json_task(Priority::P1, move || { - let (fork, execution_optimistic, finalized) = - state_id.fork_and_execution_optimistic_and_finalized(&chain)?; - Ok(api_types::ExecutionOptimisticFinalizedResponse { - data: fork, - execution_optimistic: Some(execution_optimistic), - finalized: Some(finalized), - }) - }) - }, - ); + let get_beacon_state_fork = states::get_beacon_state_fork(beacon_states_path.clone()); // GET beacon/states/{state_id}/finality_checkpoints - let get_beacon_state_finality_checkpoints = beacon_states_path - .clone() - .and(warp::path("finality_checkpoints")) - .and(warp::path::end()) - .then( - |state_id: StateId, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.blocking_json_task(Priority::P1, move || { - let (data, execution_optimistic, finalized) = state_id - .map_state_and_execution_optimistic_and_finalized( - &chain, - |state, execution_optimistic, finalized| { - Ok(( - api_types::FinalityCheckpointsData { - previous_justified: state.previous_justified_checkpoint(), - current_justified: state.current_justified_checkpoint(), - finalized: state.finalized_checkpoint(), - }, - execution_optimistic, - finalized, - )) - }, - )?; - - Ok(api_types::ExecutionOptimisticFinalizedResponse { - data, - execution_optimistic: Some(execution_optimistic), - finalized: Some(finalized), - }) - }) - }, - ); + let get_beacon_state_finality_checkpoints = + states::get_beacon_state_finality_checkpoints(beacon_states_path.clone()); // GET beacon/states/{state_id}/validator_balances?id - let get_beacon_state_validator_balances = beacon_states_path - .clone() - .and(warp::path("validator_balances")) - .and(warp::path::end()) - .and(multi_key_query::()) - .then( - |state_id: StateId, - task_spawner: TaskSpawner, - chain: Arc>, - query_res: Result| { - task_spawner.blocking_json_task(Priority::P1, move || { - let query = query_res?; - crate::validators::get_beacon_state_validator_balances( - state_id, - chain, - query.id.as_deref(), - ) - }) - }, - ); + let get_beacon_state_validator_balances = + states::get_beacon_state_validator_balances(beacon_states_path.clone()); // POST beacon/states/{state_id}/validator_balances - let post_beacon_state_validator_balances = beacon_states_path - .clone() - .and(warp::path("validator_balances")) - .and(warp::path::end()) - .and(warp_utils::json::json_no_body()) - .then( - |state_id: StateId, - task_spawner: TaskSpawner, - chain: Arc>, - query: ValidatorBalancesRequestBody| { - task_spawner.blocking_json_task(Priority::P1, move || { - crate::validators::get_beacon_state_validator_balances( - state_id, - chain, - Some(&query.ids), - ) - }) - }, - ); + let post_beacon_state_validator_balances = + states::post_beacon_state_validator_balances(beacon_states_path.clone()); // POST beacon/states/{state_id}/validator_identities - let post_beacon_state_validator_identities = beacon_states_path - .clone() - .and(warp::path("validator_identities")) - .and(warp::path::end()) - .and(warp_utils::json::json_no_body()) - .then( - |state_id: StateId, - task_spawner: TaskSpawner, - chain: Arc>, - query: ValidatorIdentitiesRequestBody| { - // Prioritise requests for validators at the head. These should be fast to service - // and could be required by the validator client. - let priority = if let StateId(eth2::types::StateId::Head) = state_id { - Priority::P0 - } else { - Priority::P1 - }; - task_spawner.blocking_json_task(priority, move || { - crate::validators::get_beacon_state_validator_identities( - state_id, - chain, - Some(&query.ids), - ) - }) - }, - ); + let post_beacon_state_validator_identities = + states::post_beacon_state_validator_identities(beacon_states_path.clone()); // GET beacon/states/{state_id}/validators?id,status - let get_beacon_state_validators = beacon_states_path - .clone() - .and(warp::path("validators")) - .and(warp::path::end()) - .and(multi_key_query::()) - .then( - |state_id: StateId, - task_spawner: TaskSpawner, - chain: Arc>, - query_res: Result| { - // Prioritise requests for validators at the head. These should be fast to service - // and could be required by the validator client. - let priority = if let StateId(eth2::types::StateId::Head) = state_id { - Priority::P0 - } else { - Priority::P1 - }; - task_spawner.blocking_json_task(priority, move || { - let query = query_res?; - crate::validators::get_beacon_state_validators( - state_id, - chain, - &query.id, - &query.status, - ) - }) - }, - ); + let get_beacon_state_validators = + states::get_beacon_state_validators(beacon_states_path.clone()); // POST beacon/states/{state_id}/validators - let post_beacon_state_validators = beacon_states_path - .clone() - .and(warp::path("validators")) - .and(warp::path::end()) - .and(warp_utils::json::json()) - .then( - |state_id: StateId, - task_spawner: TaskSpawner, - chain: Arc>, - query: ValidatorsRequestBody| { - // Prioritise requests for validators at the head. These should be fast to service - // and could be required by the validator client. - let priority = if let StateId(eth2::types::StateId::Head) = state_id { - Priority::P0 - } else { - Priority::P1 - }; - task_spawner.blocking_json_task(priority, move || { - crate::validators::get_beacon_state_validators( - state_id, - chain, - &query.ids, - &query.statuses, - ) - }) - }, - ); + let post_beacon_state_validators = + states::post_beacon_state_validators(beacon_states_path.clone()); // GET beacon/states/{state_id}/validators/{validator_id} - let get_beacon_state_validators_id = beacon_states_path - .clone() - .and(warp::path("validators")) - .and(warp::path::param::().or_else(|_| async { - Err(warp_utils::reject::custom_bad_request( - "Invalid validator ID".to_string(), - )) - })) - .and(warp::path::end()) - .then( - |state_id: StateId, - task_spawner: TaskSpawner, - chain: Arc>, - validator_id: ValidatorId| { - // Prioritise requests for validators at the head. These should be fast to service - // and could be required by the validator client. - let priority = if let StateId(eth2::types::StateId::Head) = state_id { - Priority::P0 - } else { - Priority::P1 - }; - task_spawner.blocking_json_task(priority, move || { - let (data, execution_optimistic, finalized) = state_id - .map_state_and_execution_optimistic_and_finalized( - &chain, - |state, execution_optimistic, finalized| { - let index_opt = match &validator_id { - ValidatorId::PublicKey(pubkey) => pubkey_to_validator_index( - &chain, state, pubkey, - ) - .map_err(|e| { - warp_utils::reject::custom_not_found(format!( - "unable to access pubkey cache: {e:?}", - )) - })?, - ValidatorId::Index(index) => Some(*index as usize), - }; - - Ok(( - index_opt - .and_then(|index| { - let validator = state.validators().get(index)?; - let balance = *state.balances().get(index)?; - let epoch = state.current_epoch(); - let far_future_epoch = chain.spec.far_future_epoch; - - Some(api_types::ValidatorData { - index: index as u64, - balance, - status: api_types::ValidatorStatus::from_validator( - validator, - epoch, - far_future_epoch, - ), - validator: validator.clone(), - }) - }) - .ok_or_else(|| { - warp_utils::reject::custom_not_found(format!( - "unknown validator: {}", - validator_id - )) - })?, - execution_optimistic, - finalized, - )) - }, - )?; - - Ok(api_types::ExecutionOptimisticFinalizedResponse { - data, - execution_optimistic: Some(execution_optimistic), - finalized: Some(finalized), - }) - }) - }, - ); + let get_beacon_state_validators_id = + states::get_beacon_state_validators_id(beacon_states_path.clone()); // GET beacon/states/{state_id}/committees?slot,index,epoch - let get_beacon_state_committees = beacon_states_path - .clone() - .and(warp::path("committees")) - .and(warp::query::()) - .and(warp::path::end()) - .then( - |state_id: StateId, - task_spawner: TaskSpawner, - chain: Arc>, - query: api_types::CommitteesQuery| { - task_spawner.blocking_json_task(Priority::P1, move || { - let (data, execution_optimistic, finalized) = state_id - .map_state_and_execution_optimistic_and_finalized( - &chain, - |state, execution_optimistic, finalized| { - let current_epoch = state.current_epoch(); - let epoch = query.epoch.unwrap_or(current_epoch); - - // Attempt to obtain the committee_cache from the beacon chain - let decision_slot = (epoch.saturating_sub(2u64)) - .end_slot(T::EthSpec::slots_per_epoch()); - // Find the decision block and skip to another method on any kind - // of failure - let shuffling_id = if let Ok(Some(shuffling_decision_block)) = - chain.block_root_at_slot(decision_slot, WhenSlotSkipped::Prev) - { - Some(AttestationShufflingId { - shuffling_epoch: epoch, - shuffling_decision_block, - }) - } else { - None - }; - - // Attempt to read from the chain cache if there exists a - // shuffling_id - let maybe_cached_shuffling = if let Some(shuffling_id) = - shuffling_id.as_ref() - { - chain - .shuffling_cache - .try_write_for(std::time::Duration::from_secs(1)) - .and_then(|mut cache_write| cache_write.get(shuffling_id)) - .and_then(|cache_item| cache_item.wait().ok()) - } else { - None - }; - - let committee_cache = - if let Some(shuffling) = maybe_cached_shuffling { - shuffling - } else { - let possibly_built_cache = - match RelativeEpoch::from_epoch(current_epoch, epoch) { - Ok(relative_epoch) - if state.committee_cache_is_initialized( - relative_epoch, - ) => - { - state.committee_cache(relative_epoch).cloned() - } - _ => CommitteeCache::initialized( - state, - epoch, - &chain.spec, - ), - } - .map_err( - |e| match e { - BeaconStateError::EpochOutOfBounds => { - let max_sprp = - T::EthSpec::slots_per_historical_root() - as u64; - let first_subsequent_restore_point_slot = - ((epoch.start_slot( - T::EthSpec::slots_per_epoch(), - ) / max_sprp) - + 1) - * max_sprp; - if epoch < current_epoch { - warp_utils::reject::custom_bad_request( - format!( - "epoch out of bounds, \ - try state at slot {}", - first_subsequent_restore_point_slot, - ), - ) - } else { - warp_utils::reject::custom_bad_request( - "epoch out of bounds, \ - too far in future" - .into(), - ) - } - } - _ => warp_utils::reject::unhandled_error( - BeaconChainError::from(e), - ), - }, - )?; - - // Attempt to write to the beacon cache (only if the cache - // size is not the default value). - if chain.config.shuffling_cache_size - != beacon_chain::shuffling_cache::DEFAULT_CACHE_SIZE - && let Some(shuffling_id) = shuffling_id - && let Some(mut cache_write) = chain - .shuffling_cache - .try_write_for(std::time::Duration::from_secs(1)) - { - cache_write.insert_committee_cache( - shuffling_id, - &possibly_built_cache, - ); - } - - possibly_built_cache - }; - - // Use either the supplied slot or all slots in the epoch. - let slots = - query.slot.map(|slot| vec![slot]).unwrap_or_else(|| { - epoch.slot_iter(T::EthSpec::slots_per_epoch()).collect() - }); - - // Use either the supplied committee index or all available indices. - let indices = - query.index.map(|index| vec![index]).unwrap_or_else(|| { - (0..committee_cache.committees_per_slot()).collect() - }); - - let mut response = Vec::with_capacity(slots.len() * indices.len()); - - for slot in slots { - // It is not acceptable to query with a slot that is not within the - // specified epoch. - if slot.epoch(T::EthSpec::slots_per_epoch()) != epoch { - return Err(warp_utils::reject::custom_bad_request( - format!("{} is not in epoch {}", slot, epoch), - )); - } - - for &index in &indices { - let committee = committee_cache - .get_beacon_committee(slot, index) - .ok_or_else(|| { - warp_utils::reject::custom_bad_request(format!( - "committee index {} does not exist in epoch {}", - index, epoch - )) - })?; - - response.push(api_types::CommitteeData { - index, - slot, - validators: committee - .committee - .iter() - .map(|i| *i as u64) - .collect(), - }); - } - } - - Ok((response, execution_optimistic, finalized)) - }, - )?; - Ok(api_types::ExecutionOptimisticFinalizedResponse { - data, - execution_optimistic: Some(execution_optimistic), - finalized: Some(finalized), - }) - }) - }, - ); + let get_beacon_state_committees = + states::get_beacon_state_committees(beacon_states_path.clone()); // GET beacon/states/{state_id}/sync_committees?epoch - let get_beacon_state_sync_committees = beacon_states_path - .clone() - .and(warp::path("sync_committees")) - .and(warp::query::()) - .and(warp::path::end()) - .then( - |state_id: StateId, - task_spawner: TaskSpawner, - chain: Arc>, - query: api_types::SyncCommitteesQuery| { - task_spawner.blocking_json_task(Priority::P1, move || { - let (sync_committee, execution_optimistic, finalized) = state_id - .map_state_and_execution_optimistic_and_finalized( - &chain, - |state, execution_optimistic, finalized| { - let current_epoch = state.current_epoch(); - let epoch = query.epoch.unwrap_or(current_epoch); - Ok(( - state - .get_built_sync_committee(epoch, &chain.spec) - .cloned() - .map_err(|e| match e { - BeaconStateError::SyncCommitteeNotKnown { .. } => { - warp_utils::reject::custom_bad_request(format!( - "state at epoch {} has no \ - sync committee for epoch {}", - current_epoch, epoch - )) - } - BeaconStateError::IncorrectStateVariant => { - warp_utils::reject::custom_bad_request(format!( - "state at epoch {} is not activated for Altair", - current_epoch, - )) - } - e => warp_utils::reject::beacon_state_error(e), - })?, - execution_optimistic, - finalized, - )) - }, - )?; - - let validators = chain - .validator_indices(sync_committee.pubkeys.iter()) - .map_err(warp_utils::reject::unhandled_error)?; - - let validator_aggregates = validators - .chunks_exact(T::EthSpec::sync_subcommittee_size()) - .map(|indices| api_types::SyncSubcommittee { - indices: indices.to_vec(), - }) - .collect(); - - let response = api_types::SyncCommitteeByValidatorIndices { - validators, - validator_aggregates, - }; - - Ok(api_types::GenericResponse::from(response) - .add_execution_optimistic_finalized(execution_optimistic, finalized)) - }) - }, - ); + let get_beacon_state_sync_committees = + states::get_beacon_state_sync_committees(beacon_states_path.clone()); // GET beacon/states/{state_id}/randao?epoch - let get_beacon_state_randao = beacon_states_path - .clone() - .and(warp::path("randao")) - .and(warp::query::()) - .and(warp::path::end()) - .then( - |state_id: StateId, - task_spawner: TaskSpawner, - chain: Arc>, - query: api_types::RandaoQuery| { - task_spawner.blocking_json_task(Priority::P1, move || { - let (randao, execution_optimistic, finalized) = state_id - .map_state_and_execution_optimistic_and_finalized( - &chain, - |state, execution_optimistic, finalized| { - let epoch = query.epoch.unwrap_or_else(|| state.current_epoch()); - let randao = *state.get_randao_mix(epoch).map_err(|e| { - warp_utils::reject::custom_bad_request(format!( - "epoch out of range: {e:?}" - )) - })?; - Ok((randao, execution_optimistic, finalized)) - }, - )?; - - Ok( - api_types::GenericResponse::from(api_types::RandaoMix { randao }) - .add_execution_optimistic_finalized(execution_optimistic, finalized), - ) - }) - }, - ); + let get_beacon_state_randao = states::get_beacon_state_randao(beacon_states_path.clone()); // GET beacon/states/{state_id}/pending_deposits - let get_beacon_state_pending_deposits = beacon_states_path - .clone() - .and(warp::path("pending_deposits")) - .and(warp::path::end()) - .then( - |state_id: StateId, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.blocking_response_task(Priority::P1, move || { - let (data, execution_optimistic, finalized, fork_name) = state_id - .map_state_and_execution_optimistic_and_finalized( - &chain, - |state, execution_optimistic, finalized| { - let Ok(deposits) = state.pending_deposits() else { - return Err(warp_utils::reject::custom_bad_request( - "Pending deposits not found".to_string(), - )); - }; - - Ok(( - deposits.clone(), - execution_optimistic, - finalized, - state.fork_name_unchecked(), - )) - }, - )?; - - execution_optimistic_finalized_beacon_response( - ResponseIncludesVersion::Yes(fork_name), - execution_optimistic, - finalized, - data, - ) - .map(|res| warp::reply::json(&res).into_response()) - .map(|resp| add_consensus_version_header(resp, fork_name)) - }) - }, - ); + let get_beacon_state_pending_deposits = + states::get_beacon_state_pending_deposits(beacon_states_path.clone()); // GET beacon/states/{state_id}/pending_partial_withdrawals - let get_beacon_state_pending_partial_withdrawals = beacon_states_path - .clone() - .and(warp::path("pending_partial_withdrawals")) - .and(warp::path::end()) - .then( - |state_id: StateId, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.blocking_response_task(Priority::P1, move || { - let (data, execution_optimistic, finalized, fork_name) = state_id - .map_state_and_execution_optimistic_and_finalized( - &chain, - |state, execution_optimistic, finalized| { - let Ok(withdrawals) = state.pending_partial_withdrawals() else { - return Err(warp_utils::reject::custom_bad_request( - "Pending withdrawals not found".to_string(), - )); - }; - - Ok(( - withdrawals.clone(), - execution_optimistic, - finalized, - state.fork_name_unchecked(), - )) - }, - )?; - - execution_optimistic_finalized_beacon_response( - ResponseIncludesVersion::Yes(fork_name), - execution_optimistic, - finalized, - data, - ) - .map(|res| warp::reply::json(&res).into_response()) - .map(|resp| add_consensus_version_header(resp, fork_name)) - }) - }, - ); + let get_beacon_state_pending_partial_withdrawals = + states::get_beacon_state_pending_partial_withdrawals(beacon_states_path.clone()); // GET beacon/states/{state_id}/pending_consolidations - let get_beacon_state_pending_consolidations = beacon_states_path - .clone() - .and(warp::path("pending_consolidations")) - .and(warp::path::end()) - .then( - |state_id: StateId, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.blocking_response_task(Priority::P1, move || { - let (data, execution_optimistic, finalized, fork_name) = state_id - .map_state_and_execution_optimistic_and_finalized( - &chain, - |state, execution_optimistic, finalized| { - let Ok(consolidations) = state.pending_consolidations() else { - return Err(warp_utils::reject::custom_bad_request( - "Pending consolidations not found".to_string(), - )); - }; - - Ok(( - consolidations.clone(), - execution_optimistic, - finalized, - state.fork_name_unchecked(), - )) - }, - )?; - - execution_optimistic_finalized_beacon_response( - ResponseIncludesVersion::Yes(fork_name), - execution_optimistic, - finalized, - data, - ) - .map(|res| warp::reply::json(&res).into_response()) - .map(|resp| add_consensus_version_header(resp, fork_name)) - }) - }, - ); + let get_beacon_state_pending_consolidations = + states::get_beacon_state_pending_consolidations(beacon_states_path.clone()); // GET beacon/headers // @@ -1279,6 +652,7 @@ pub fn serve( // mechanism for arbitrary forwards block iteration, we only support iterating forwards along // the canonical chain. let get_beacon_headers = eth_v1 + .clone() .and(warp::path("beacon")) .and(warp::path("headers")) .and(warp::query::()) @@ -1375,6 +749,7 @@ pub fn serve( // GET beacon/headers/{block_id} let get_beacon_headers_block_id = eth_v1 + .clone() .and(warp::path("beacon")) .and(warp::path("headers")) .and(warp::path::param::().or_else(|_| async { @@ -1423,18 +798,19 @@ pub fn serve( * beacon/blocks */ let consensus_version_header_filter = - warp::header::header::(CONSENSUS_VERSION_HEADER); + warp::header::header::(CONSENSUS_VERSION_HEADER).boxed(); let optional_consensus_version_header_filter = - warp::header::optional::(CONSENSUS_VERSION_HEADER); + warp::header::optional::(CONSENSUS_VERSION_HEADER).boxed(); // POST beacon/blocks let post_beacon_blocks = eth_v1 + .clone() .and(warp::path("beacon")) .and(warp::path("blocks")) .and(warp::path::end()) .and(warp::body::json()) - .and(consensus_version_header_filter) + .and(consensus_version_header_filter.clone()) .and(task_spawner_filter.clone()) .and(chain_filter.clone()) .and(network_tx_filter.clone()) @@ -1466,11 +842,12 @@ pub fn serve( ); let post_beacon_blocks_ssz = eth_v1 + .clone() .and(warp::path("beacon")) .and(warp::path("blocks")) .and(warp::path::end()) .and(warp::body::bytes()) - .and(consensus_version_header_filter) + .and(consensus_version_header_filter.clone()) .and(task_spawner_filter.clone()) .and(chain_filter.clone()) .and(network_tx_filter.clone()) @@ -1502,12 +879,13 @@ pub fn serve( ); let post_beacon_blocks_v2 = eth_v2 + .clone() .and(warp::path("beacon")) .and(warp::path("blocks")) .and(warp::query::()) .and(warp::path::end()) .and(warp::body::json()) - .and(consensus_version_header_filter) + .and(consensus_version_header_filter.clone()) .and(task_spawner_filter.clone()) .and(chain_filter.clone()) .and(network_tx_filter.clone()) @@ -1541,12 +919,13 @@ pub fn serve( ); let post_beacon_blocks_v2_ssz = eth_v2 + .clone() .and(warp::path("beacon")) .and(warp::path("blocks")) .and(warp::query::()) .and(warp::path::end()) .and(warp::body::bytes()) - .and(consensus_version_header_filter) + .and(consensus_version_header_filter.clone()) .and(task_spawner_filter.clone()) .and(chain_filter.clone()) .and(network_tx_filter.clone()) @@ -1584,6 +963,7 @@ pub fn serve( // POST beacon/blinded_blocks let post_beacon_blinded_blocks = eth_v1 + .clone() .and(warp::path("beacon")) .and(warp::path("blinded_blocks")) .and(warp::path::end()) @@ -1611,6 +991,7 @@ pub fn serve( // POST beacon/blocks let post_beacon_blinded_blocks_ssz = eth_v1 + .clone() .and(warp::path("beacon")) .and(warp::path("blinded_blocks")) .and(warp::path::end()) @@ -1645,6 +1026,7 @@ pub fn serve( ); let post_beacon_blinded_blocks_v2 = eth_v2 + .clone() .and(warp::path("beacon")) .and(warp::path("blinded_blocks")) .and(warp::query::()) @@ -1684,6 +1066,7 @@ pub fn serve( ); let post_beacon_blinded_blocks_v2_ssz = eth_v2 + .clone() .and(warp::path("beacon")) .and(warp::path("blinded_blocks")) .and(warp::query::()) @@ -1726,6 +1109,7 @@ pub fn serve( }); let beacon_blocks_path_v1 = eth_v1 + .clone() .and(warp::path("beacon")) .and(warp::path("blocks")) .and(block_id_or_err) @@ -1733,6 +1117,7 @@ pub fn serve( .and(chain_filter.clone()); let beacon_blocks_path_any = any_version + .clone() .and(warp::path("beacon")) .and(warp::path("blocks")) .and(block_id_or_err) @@ -1858,6 +1243,7 @@ pub fn serve( // GET beacon/blinded_blocks/{block_id} let get_beacon_blinded_block = eth_v1 + .clone() .and(warp::path("beacon")) .and(warp::path("blinded_blocks")) .and(block_id_or_err) @@ -1910,6 +1296,7 @@ pub fn serve( // GET beacon/blob_sidecars/{block_id} let get_blob_sidecars = eth_v1 + .clone() .and(warp::path("beacon")) .and(warp::path("blob_sidecars")) .and(block_id_or_err) @@ -1961,6 +1348,7 @@ pub fn serve( // GET beacon/blobs/{block_id} let get_blobs = eth_v1 + .clone() .and(warp::path("beacon")) .and(warp::path("blobs")) .and(block_id_or_err) @@ -2010,447 +1398,74 @@ pub fn serve( */ let beacon_pool_path = eth_v1 + .clone() .and(warp::path("beacon")) .and(warp::path("pool")) .and(task_spawner_filter.clone()) - .and(chain_filter.clone()); + .and(chain_filter.clone()) + .boxed(); let beacon_pool_path_v2 = eth_v2 + .clone() .and(warp::path("beacon")) .and(warp::path("pool")) .and(task_spawner_filter.clone()) - .and(chain_filter.clone()); + .and(chain_filter.clone()) + .boxed(); let beacon_pool_path_any = any_version + .clone() .and(warp::path("beacon")) .and(warp::path("pool")) .and(task_spawner_filter.clone()) - .and(chain_filter.clone()); + .and(chain_filter.clone()) + .boxed(); - let post_beacon_pool_attestations_v2 = beacon_pool_path_v2 - .clone() - .and(warp::path("attestations")) - .and(warp::path::end()) - .and(warp_utils::json::json::>()) - .and(optional_consensus_version_header_filter) - .and(network_tx_filter.clone()) - .then( - |task_spawner: TaskSpawner, - chain: Arc>, - attestations: Vec, - _fork_name: Option, - network_tx: UnboundedSender>| async move { - let result = crate::publish_attestations::publish_attestations( - task_spawner, - chain, - attestations, - network_tx, - true, - ) - .await - .map(|()| warp::reply::json(&())); - convert_rejection(result).await - }, - ); + let post_beacon_pool_attestations_v2 = post_beacon_pool_attestations_v2( + &network_tx_filter, + optional_consensus_version_header_filter, + &beacon_pool_path_v2, + ); // GET beacon/pool/attestations?committee_index,slot - let get_beacon_pool_attestations = beacon_pool_path_any - .clone() - .and(warp::path("attestations")) - .and(warp::path::end()) - .and(warp::query::()) - .then( - |endpoint_version: EndpointVersion, - task_spawner: TaskSpawner, - chain: Arc>, - query: api_types::AttestationPoolQuery| { - task_spawner.blocking_response_task(Priority::P1, move || { - let query_filter = |data: &AttestationData, committee_indices: HashSet| { - query.slot.is_none_or(|slot| slot == data.slot) - && query - .committee_index - .is_none_or(|index| committee_indices.contains(&index)) - }; - - let mut attestations = chain.op_pool.get_filtered_attestations(query_filter); - attestations.extend( - chain - .naive_aggregation_pool - .read() - .iter() - .filter(|&att| { - query_filter(att.data(), att.get_committee_indices_map()) - }) - .cloned(), - ); - // Use the current slot to find the fork version, and convert all messages to the - // current fork's format. This is to ensure consistent message types matching - // `Eth-Consensus-Version`. - let current_slot = - chain - .slot_clock - .now() - .ok_or(warp_utils::reject::custom_server_error( - "unable to read slot clock".to_string(), - ))?; - let fork_name = chain.spec.fork_name_at_slot::(current_slot); - let attestations = attestations - .into_iter() - .filter(|att| { - (fork_name.electra_enabled() && matches!(att, Attestation::Electra(_))) - || (!fork_name.electra_enabled() - && matches!(att, Attestation::Base(_))) - }) - .collect::>(); - - let require_version = match endpoint_version { - V1 => ResponseIncludesVersion::No, - V2 => ResponseIncludesVersion::Yes(fork_name), - _ => return Err(unsupported_version_rejection(endpoint_version)), - }; - - let res = beacon_response(require_version, &attestations); - Ok(add_consensus_version_header( - warp::reply::json(&res).into_response(), - fork_name, - )) - }) - }, - ); + let get_beacon_pool_attestations = get_beacon_pool_attestations(&beacon_pool_path_any); // POST beacon/pool/attester_slashings - let post_beacon_pool_attester_slashings = beacon_pool_path_any - .clone() - .and(warp::path("attester_slashings")) - .and(warp::path::end()) - .and(warp_utils::json::json()) - .and(network_tx_filter.clone()) - .then( - // V1 and V2 are identical except V2 has a consensus version header in the request. - // We only require this header for SSZ deserialization, which isn't supported for - // this endpoint presently. - |_endpoint_version: EndpointVersion, - task_spawner: TaskSpawner, - chain: Arc>, - slashing: AttesterSlashing, - network_tx: UnboundedSender>| { - task_spawner.blocking_json_task(Priority::P0, move || { - let outcome = chain - .verify_attester_slashing_for_gossip(slashing.clone()) - .map_err(|e| { - warp_utils::reject::object_invalid(format!( - "gossip verification failed: {:?}", - e - )) - })?; - - // Notify the validator monitor. - chain - .validator_monitor - .read() - .register_api_attester_slashing(slashing.to_ref()); - - if let ObservationOutcome::New(slashing) = outcome { - publish_pubsub_message( - &network_tx, - PubsubMessage::AttesterSlashing(Box::new( - slashing.clone().into_inner(), - )), - )?; - - chain.import_attester_slashing(slashing); - } - - Ok(()) - }) - }, - ); + let post_beacon_pool_attester_slashings = + post_beacon_pool_attester_slashings(&network_tx_filter, &beacon_pool_path_any); // GET beacon/pool/attester_slashings let get_beacon_pool_attester_slashings = - beacon_pool_path_any - .clone() - .and(warp::path("attester_slashings")) - .and(warp::path::end()) - .then( - |endpoint_version: EndpointVersion, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.blocking_response_task(Priority::P1, move || { - let slashings = chain.op_pool.get_all_attester_slashings(); - - // Use the current slot to find the fork version, and convert all messages to the - // current fork's format. This is to ensure consistent message types matching - // `Eth-Consensus-Version`. - let current_slot = chain.slot_clock.now().ok_or( - warp_utils::reject::custom_server_error( - "unable to read slot clock".to_string(), - ), - )?; - let fork_name = chain.spec.fork_name_at_slot::(current_slot); - let slashings = slashings - .into_iter() - .filter(|slashing| { - (fork_name.electra_enabled() - && matches!(slashing, AttesterSlashing::Electra(_))) - || (!fork_name.electra_enabled() - && matches!(slashing, AttesterSlashing::Base(_))) - }) - .collect::>(); - - let require_version = match endpoint_version { - V1 => ResponseIncludesVersion::No, - V2 => ResponseIncludesVersion::Yes(fork_name), - _ => return Err(unsupported_version_rejection(endpoint_version)), - }; - - let res = beacon_response(require_version, &slashings); - Ok(add_consensus_version_header( - warp::reply::json(&res).into_response(), - fork_name, - )) - }) - }, - ); + get_beacon_pool_attester_slashings(&beacon_pool_path_any); // POST beacon/pool/proposer_slashings - let post_beacon_pool_proposer_slashings = beacon_pool_path - .clone() - .and(warp::path("proposer_slashings")) - .and(warp::path::end()) - .and(warp_utils::json::json()) - .and(network_tx_filter.clone()) - .then( - |task_spawner: TaskSpawner, - chain: Arc>, - slashing: ProposerSlashing, - network_tx: UnboundedSender>| { - task_spawner.blocking_json_task(Priority::P0, move || { - let outcome = chain - .verify_proposer_slashing_for_gossip(slashing.clone()) - .map_err(|e| { - warp_utils::reject::object_invalid(format!( - "gossip verification failed: {:?}", - e - )) - })?; - - // Notify the validator monitor. - chain - .validator_monitor - .read() - .register_api_proposer_slashing(&slashing); - - if let ObservationOutcome::New(slashing) = outcome { - publish_pubsub_message( - &network_tx, - PubsubMessage::ProposerSlashing(Box::new( - slashing.clone().into_inner(), - )), - )?; - - chain.import_proposer_slashing(slashing); - } - - Ok(()) - }) - }, - ); + let post_beacon_pool_proposer_slashings = + post_beacon_pool_proposer_slashings(&network_tx_filter, &beacon_pool_path); // GET beacon/pool/proposer_slashings - let get_beacon_pool_proposer_slashings = beacon_pool_path - .clone() - .and(warp::path("proposer_slashings")) - .and(warp::path::end()) - .then( - |task_spawner: TaskSpawner, chain: Arc>| { - task_spawner.blocking_json_task(Priority::P1, move || { - let attestations = chain.op_pool.get_all_proposer_slashings(); - Ok(api_types::GenericResponse::from(attestations)) - }) - }, - ); + let get_beacon_pool_proposer_slashings = get_beacon_pool_proposer_slashings(&beacon_pool_path); // POST beacon/pool/voluntary_exits - let post_beacon_pool_voluntary_exits = beacon_pool_path - .clone() - .and(warp::path("voluntary_exits")) - .and(warp::path::end()) - .and(warp_utils::json::json()) - .and(network_tx_filter.clone()) - .then( - |task_spawner: TaskSpawner, - chain: Arc>, - exit: SignedVoluntaryExit, - network_tx: UnboundedSender>| { - task_spawner.blocking_json_task(Priority::P0, move || { - let outcome = chain - .verify_voluntary_exit_for_gossip(exit.clone()) - .map_err(|e| { - warp_utils::reject::object_invalid(format!( - "gossip verification failed: {:?}", - e - )) - })?; - - // Notify the validator monitor. - chain - .validator_monitor - .read() - .register_api_voluntary_exit(&exit.message); - - if let ObservationOutcome::New(exit) = outcome { - publish_pubsub_message( - &network_tx, - PubsubMessage::VoluntaryExit(Box::new(exit.clone().into_inner())), - )?; - - chain.import_voluntary_exit(exit); - } - - Ok(()) - }) - }, - ); + let post_beacon_pool_voluntary_exits = + post_beacon_pool_voluntary_exits(&network_tx_filter, &beacon_pool_path); // GET beacon/pool/voluntary_exits - let get_beacon_pool_voluntary_exits = beacon_pool_path - .clone() - .and(warp::path("voluntary_exits")) - .and(warp::path::end()) - .then( - |task_spawner: TaskSpawner, chain: Arc>| { - task_spawner.blocking_json_task(Priority::P1, move || { - let attestations = chain.op_pool.get_all_voluntary_exits(); - Ok(api_types::GenericResponse::from(attestations)) - }) - }, - ); + let get_beacon_pool_voluntary_exits = get_beacon_pool_voluntary_exits(&beacon_pool_path); // POST beacon/pool/sync_committees - let post_beacon_pool_sync_committees = beacon_pool_path - .clone() - .and(warp::path("sync_committees")) - .and(warp::path::end()) - .and(warp_utils::json::json()) - .and(network_tx_filter.clone()) - .then( - |task_spawner: TaskSpawner, - chain: Arc>, - signatures: Vec, - network_tx: UnboundedSender>| { - task_spawner.blocking_json_task(Priority::P0, move || { - sync_committees::process_sync_committee_signatures( - signatures, network_tx, &chain, - )?; - Ok(api_types::GenericResponse::from(())) - }) - }, - ); + let post_beacon_pool_sync_committees = + post_beacon_pool_sync_committees(&network_tx_filter, &beacon_pool_path); // GET beacon/pool/bls_to_execution_changes - let get_beacon_pool_bls_to_execution_changes = beacon_pool_path - .clone() - .and(warp::path("bls_to_execution_changes")) - .and(warp::path::end()) - .then( - |task_spawner: TaskSpawner, chain: Arc>| { - task_spawner.blocking_json_task(Priority::P1, move || { - let address_changes = chain.op_pool.get_all_bls_to_execution_changes(); - Ok(api_types::GenericResponse::from(address_changes)) - }) - }, - ); + let get_beacon_pool_bls_to_execution_changes = + get_beacon_pool_bls_to_execution_changes(&beacon_pool_path); // POST beacon/pool/bls_to_execution_changes - let post_beacon_pool_bls_to_execution_changes = beacon_pool_path - .clone() - .and(warp::path("bls_to_execution_changes")) - .and(warp::path::end()) - .and(warp_utils::json::json()) - .and(network_tx_filter.clone()) - .then( - |task_spawner: TaskSpawner, - chain: Arc>, - address_changes: Vec, - network_tx: UnboundedSender>| { - task_spawner.blocking_json_task(Priority::P0, move || { - let mut failures = vec![]; - - for (index, address_change) in address_changes.into_iter().enumerate() { - let validator_index = address_change.message.validator_index; - - match chain.verify_bls_to_execution_change_for_http_api(address_change) { - Ok(ObservationOutcome::New(verified_address_change)) => { - let validator_index = - verified_address_change.as_inner().message.validator_index; - let address = verified_address_change - .as_inner() - .message - .to_execution_address; - - // New to P2P *and* op pool, gossip immediately if post-Capella. - let received_pre_capella = - if chain.current_slot_is_post_capella().unwrap_or(false) { - ReceivedPreCapella::No - } else { - ReceivedPreCapella::Yes - }; - if matches!(received_pre_capella, ReceivedPreCapella::No) { - publish_pubsub_message( - &network_tx, - PubsubMessage::BlsToExecutionChange(Box::new( - verified_address_change.as_inner().clone(), - )), - )?; - } - - // Import to op pool (may return `false` if there's a race). - let imported = chain.import_bls_to_execution_change( - verified_address_change, - received_pre_capella, - ); - - info!( - %validator_index, - ?address, - published = - matches!(received_pre_capella, ReceivedPreCapella::No), - imported, - "Processed BLS to execution change" - ); - } - Ok(ObservationOutcome::AlreadyKnown) => { - debug!(%validator_index, "BLS to execution change already known"); - } - Err(e) => { - warn!( - validator_index, - reason = ?e, - source = "HTTP", - "Invalid BLS to execution change" - ); - failures.push(api_types::Failure::new( - index, - format!("invalid: {e:?}"), - )); - } - } - } - - if failures.is_empty() { - Ok(()) - } else { - Err(warp_utils::reject::indexed_bad_request( - "some BLS to execution changes failed to verify".into(), - failures, - )) - } - }) - }, - ); + let post_beacon_pool_bls_to_execution_changes = + post_beacon_pool_bls_to_execution_changes(&network_tx_filter, &beacon_pool_path); let beacon_rewards_path = eth_v1 + .clone() .and(warp::path("beacon")) .and(warp::path("rewards")) .and(task_spawner_filter.clone()) @@ -2481,6 +1496,7 @@ pub fn serve( */ let builder_states_path = eth_v1 + .clone() .and(warp::path("builder")) .and(warp::path("states")) .and(chain_filter.clone()); @@ -2535,6 +1551,7 @@ pub fn serve( */ let beacon_light_client_path = eth_v1 + .clone() .and(warp::path("beacon")) .and(warp::path("light_client")) .and(light_client_server_filter) @@ -2687,6 +1704,7 @@ pub fn serve( */ let beacon_rewards_path = eth_v1 + .clone() .and(warp::path("beacon")) .and(warp::path("rewards")) .and(task_spawner_filter.clone()) @@ -2771,10 +1789,11 @@ pub fn serve( * config */ - let config_path = eth_v1.and(warp::path("config")); + let config_path = eth_v1.clone().and(warp::path("config")); // GET config/fork_schedule let get_config_fork_schedule = config_path + .clone() .and(warp::path("fork_schedule")) .and(warp::path::end()) .and(task_spawner_filter.clone()) @@ -2793,6 +1812,7 @@ pub fn serve( // GET config/spec let get_config_spec = config_path + .clone() .and(warp::path("spec")) .and(warp::path::end()) .and(task_spawner_filter.clone()) @@ -2832,6 +1852,7 @@ pub fn serve( // GET debug/beacon/data_column_sidecars/{block_id} let get_debug_data_column_sidecars = eth_v1 + .clone() .and(warp::path("debug")) .and(warp::path("beacon")) .and(warp::path("data_column_sidecars")) @@ -2881,6 +1902,7 @@ pub fn serve( // GET debug/beacon/states/{state_id} let get_debug_beacon_states = any_version + .clone() .and(warp::path("debug")) .and(warp::path("beacon")) .and(warp::path("states")) @@ -2956,6 +1978,7 @@ pub fn serve( // GET debug/beacon/heads let get_debug_beacon_heads = any_version + .clone() .and(warp::path("debug")) .and(warp::path("beacon")) .and(warp::path("heads")) @@ -2996,6 +2019,7 @@ pub fn serve( // GET debug/fork_choice let get_debug_fork_choice = eth_v1 + .clone() .and(warp::path("debug")) .and(warp::path("fork_choice")) .and(warp::path::end()) @@ -3077,6 +2101,7 @@ pub fn serve( // GET node/identity let get_node_identity = eth_v1 + .clone() .and(warp::path("node")) .and(warp::path("identity")) .and(warp::path::end()) @@ -3096,7 +2121,7 @@ pub fn serve( enr, p2p_addresses, discovery_addresses, - metadata: from_meta_data::( + metadata: utils::from_meta_data::( &network_globals.local_metadata, &chain.spec, ), @@ -3107,6 +2132,7 @@ pub fn serve( // GET node/version let get_node_version = eth_v1 + .clone() .and(warp::path("node")) .and(warp::path("version")) .and(warp::path::end()) @@ -3120,6 +2146,7 @@ pub fn serve( // GET node/syncing let get_node_syncing = eth_v1 + .clone() .and(warp::path("node")) .and(warp::path("syncing")) .and(warp::path::end()) @@ -3181,6 +2208,7 @@ pub fn serve( // GET node/health let get_node_health = eth_v1 + .clone() .and(warp::path("node")) .and(warp::path("health")) .and(warp::path::end()) @@ -3229,6 +2257,7 @@ pub fn serve( // GET node/peers/{peer_id} let get_node_peers_by_id = eth_v1 + .clone() .and(warp::path("node")) .and(warp::path("peers")) .and(warp::path::param::()) @@ -3283,6 +2312,7 @@ pub fn serve( // GET node/peers let get_node_peers = eth_v1 + .clone() .and(warp::path("node")) .and(warp::path("peers")) .and(warp::path::end()) @@ -3347,6 +2377,7 @@ pub fn serve( // GET node/peer_count let get_node_peer_count = eth_v1 + .clone() .and(warp::path("node")) .and(warp::path("peer_count")) .and(warp::path::end()) @@ -3390,804 +2421,124 @@ pub fn serve( */ // GET validator/duties/proposer/{epoch} - let get_validator_duties_proposer = eth_v1 - .and(warp::path("validator")) - .and(warp::path("duties")) - .and(warp::path("proposer")) - .and(warp::path::param::().or_else(|_| async { - Err(warp_utils::reject::custom_bad_request( - "Invalid epoch".to_string(), - )) - })) - .and(warp::path::end()) - .and(not_while_syncing_filter.clone()) - .and(task_spawner_filter.clone()) - .and(chain_filter.clone()) - .then( - |epoch: Epoch, - not_synced_filter: Result<(), Rejection>, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.blocking_json_task(Priority::P0, move || { - not_synced_filter?; - proposer_duties::proposer_duties(epoch, &chain) - }) - }, - ); + let get_validator_duties_proposer = get_validator_duties_proposer( + eth_v1.clone().clone(), + chain_filter.clone(), + not_while_syncing_filter.clone(), + task_spawner_filter.clone(), + ); // GET validator/blocks/{slot} - let get_validator_blocks = any_version - .and(warp::path("validator")) - .and(warp::path("blocks")) - .and(warp::path::param::().or_else(|_| async { - Err(warp_utils::reject::custom_bad_request( - "Invalid slot".to_string(), - )) - })) - .and(warp::path::end()) - .and(warp::header::optional::("accept")) - .and(not_while_syncing_filter.clone()) - .and(warp::query::()) - .and(task_spawner_filter.clone()) - .and(chain_filter.clone()) - .then( - |endpoint_version: EndpointVersion, - slot: Slot, - accept_header: Option, - not_synced_filter: Result<(), Rejection>, - query: api_types::ValidatorBlocksQuery, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.spawn_async_with_rejection(Priority::P0, async move { - debug!(?slot, "Block production request from HTTP API"); - - not_synced_filter?; - - if endpoint_version == V3 { - produce_block_v3(accept_header, chain, slot, query).await - } else { - produce_block_v2(accept_header, chain, slot, query).await - } - }) - }, - ); + let get_validator_blocks = get_validator_blocks( + any_version.clone().clone(), + chain_filter.clone(), + not_while_syncing_filter.clone(), + task_spawner_filter.clone(), + ); // GET validator/blinded_blocks/{slot} - let get_validator_blinded_blocks = eth_v1 - .and(warp::path("validator")) - .and(warp::path("blinded_blocks")) - .and(warp::path::param::().or_else(|_| async { - Err(warp_utils::reject::custom_bad_request( - "Invalid slot".to_string(), - )) - })) - .and(warp::path::end()) - .and(not_while_syncing_filter.clone()) - .and(warp::query::()) - .and(warp::header::optional::("accept")) - .and(task_spawner_filter.clone()) - .and(chain_filter.clone()) - .then( - |slot: Slot, - not_synced_filter: Result<(), Rejection>, - query: api_types::ValidatorBlocksQuery, - accept_header: Option, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.spawn_async_with_rejection(Priority::P0, async move { - not_synced_filter?; - produce_blinded_block_v2(accept_header, chain, slot, query).await - }) - }, - ); + let get_validator_blinded_blocks = get_validator_blinded_blocks( + eth_v1.clone().clone(), + chain_filter.clone(), + not_while_syncing_filter.clone(), + task_spawner_filter.clone(), + ); // GET validator/attestation_data?slot,committee_index - let get_validator_attestation_data = eth_v1 - .and(warp::path("validator")) - .and(warp::path("attestation_data")) - .and(warp::path::end()) - .and(warp::query::()) - .and(not_while_syncing_filter.clone()) - .and(task_spawner_filter.clone()) - .and(chain_filter.clone()) - .then( - |query: api_types::ValidatorAttestationDataQuery, - not_synced_filter: Result<(), Rejection>, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.blocking_json_task(Priority::P0, move || { - not_synced_filter?; - - let current_slot = chain.slot().map_err(warp_utils::reject::unhandled_error)?; - - // allow a tolerance of one slot to account for clock skew - if query.slot > current_slot + 1 { - return Err(warp_utils::reject::custom_bad_request(format!( - "request slot {} is more than one slot past the current slot {}", - query.slot, current_slot - ))); - } - - chain - .produce_unaggregated_attestation(query.slot, query.committee_index) - .map(|attestation| attestation.data().clone()) - .map(api_types::GenericResponse::from) - .map_err(warp_utils::reject::unhandled_error) - }) - }, - ); + let get_validator_attestation_data = get_validator_attestation_data( + eth_v1.clone().clone(), + chain_filter.clone(), + not_while_syncing_filter.clone(), + task_spawner_filter.clone(), + ); // GET validator/aggregate_attestation?attestation_data_root,slot - let get_validator_aggregate_attestation = any_version - .and(warp::path("validator")) - .and(warp::path("aggregate_attestation")) - .and(warp::path::end()) - .and(warp::query::()) - .and(not_while_syncing_filter.clone()) - .and(task_spawner_filter.clone()) - .and(chain_filter.clone()) - .then( - |endpoint_version: EndpointVersion, - query: api_types::ValidatorAggregateAttestationQuery, - not_synced_filter: Result<(), Rejection>, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.blocking_response_task(Priority::P0, move || { - not_synced_filter?; - crate::aggregate_attestation::get_aggregate_attestation( - query.slot, - &query.attestation_data_root, - query.committee_index, - endpoint_version, - chain, - ) - }) - }, - ); + let get_validator_aggregate_attestation = get_validator_aggregate_attestation( + any_version.clone().clone(), + chain_filter.clone(), + not_while_syncing_filter.clone(), + task_spawner_filter.clone(), + ); // POST validator/duties/attester/{epoch} - let post_validator_duties_attester = eth_v1 - .and(warp::path("validator")) - .and(warp::path("duties")) - .and(warp::path("attester")) - .and(warp::path::param::().or_else(|_| async { - Err(warp_utils::reject::custom_bad_request( - "Invalid epoch".to_string(), - )) - })) - .and(warp::path::end()) - .and(not_while_syncing_filter.clone()) - .and(warp_utils::json::json()) - .and(task_spawner_filter.clone()) - .and(chain_filter.clone()) - .then( - |epoch: Epoch, - not_synced_filter: Result<(), Rejection>, - indices: api_types::ValidatorIndexData, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.blocking_json_task(Priority::P0, move || { - not_synced_filter?; - attester_duties::attester_duties(epoch, &indices.0, &chain) - }) - }, - ); + let post_validator_duties_attester = post_validator_duties_attester( + eth_v1.clone().clone(), + chain_filter.clone(), + not_while_syncing_filter.clone(), + task_spawner_filter.clone(), + ); // POST validator/duties/sync/{epoch} - let post_validator_duties_sync = eth_v1 - .and(warp::path("validator")) - .and(warp::path("duties")) - .and(warp::path("sync")) - .and(warp::path::param::().or_else(|_| async { - Err(warp_utils::reject::custom_bad_request( - "Invalid epoch".to_string(), - )) - })) - .and(warp::path::end()) - .and(not_while_syncing_filter.clone()) - .and(warp_utils::json::json()) - .and(task_spawner_filter.clone()) - .and(chain_filter.clone()) - .then( - |epoch: Epoch, - not_synced_filter: Result<(), Rejection>, - indices: api_types::ValidatorIndexData, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.blocking_json_task(Priority::P0, move || { - not_synced_filter?; - sync_committees::sync_committee_duties(epoch, &indices.0, &chain) - }) - }, - ); + let post_validator_duties_sync = post_validator_duties_sync( + eth_v1.clone().clone(), + chain_filter.clone(), + not_while_syncing_filter.clone(), + task_spawner_filter.clone(), + ); // GET validator/sync_committee_contribution - let get_validator_sync_committee_contribution = eth_v1 - .and(warp::path("validator")) - .and(warp::path("sync_committee_contribution")) - .and(warp::path::end()) - .and(warp::query::()) - .and(not_while_syncing_filter.clone()) - .and(task_spawner_filter.clone()) - .and(chain_filter.clone()) - .then( - |sync_committee_data: SyncContributionData, - not_synced_filter: Result<(), Rejection>, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.blocking_json_task(Priority::P0, move || { - not_synced_filter?; - chain - .get_aggregated_sync_committee_contribution(&sync_committee_data) - .map_err(|e| { - warp_utils::reject::custom_bad_request(format!( - "unable to fetch sync contribution: {:?}", - e - )) - })? - .map(api_types::GenericResponse::from) - .ok_or_else(|| { - warp_utils::reject::custom_not_found( - "no matching sync contribution found".to_string(), - ) - }) - }) - }, - ); + let get_validator_sync_committee_contribution = get_validator_sync_committee_contribution( + eth_v1.clone().clone(), + chain_filter.clone(), + not_while_syncing_filter.clone(), + task_spawner_filter.clone(), + ); // POST validator/aggregate_and_proofs - let post_validator_aggregate_and_proofs = any_version - .and(warp::path("validator")) - .and(warp::path("aggregate_and_proofs")) - .and(warp::path::end()) - .and(not_while_syncing_filter.clone()) - .and(task_spawner_filter.clone()) - .and(chain_filter.clone()) - .and(warp_utils::json::json()) - .and(network_tx_filter.clone()) - .then( - // V1 and V2 are identical except V2 has a consensus version header in the request. - // We only require this header for SSZ deserialization, which isn't supported for - // this endpoint presently. - |_endpoint_version: EndpointVersion, - not_synced_filter: Result<(), Rejection>, - task_spawner: TaskSpawner, - chain: Arc>, - aggregates: Vec>, - network_tx: UnboundedSender>| { - task_spawner.blocking_json_task(Priority::P0, move || { - not_synced_filter?; - let seen_timestamp = timestamp_now(); - let mut verified_aggregates = Vec::with_capacity(aggregates.len()); - let mut messages = Vec::with_capacity(aggregates.len()); - let mut failures = Vec::new(); + let post_validator_aggregate_and_proofs = post_validator_aggregate_and_proofs( + any_version.clone().clone(), + chain_filter.clone(), + network_tx_filter.clone(), + not_while_syncing_filter.clone(), + task_spawner_filter.clone(), + ); - // Verify that all messages in the post are valid before processing further - for (index, aggregate) in aggregates.iter().enumerate() { - match chain.verify_aggregated_attestation_for_gossip(aggregate) { - Ok(verified_aggregate) => { - messages.push(PubsubMessage::AggregateAndProofAttestation(Box::new( - verified_aggregate.aggregate().clone(), - ))); - - // Notify the validator monitor. - chain - .validator_monitor - .read() - .register_api_aggregated_attestation( - seen_timestamp, - verified_aggregate.aggregate(), - verified_aggregate.indexed_attestation(), - &chain.slot_clock, - ); - - verified_aggregates.push((index, verified_aggregate)); - } - // If we already know the attestation, don't broadcast it or attempt to - // further verify it. Return success. - // - // It's reasonably likely that two different validators produce - // identical aggregates, especially if they're using the same beacon - // node. - Err(AttnError::AttestationSupersetKnown(_)) => continue, - // If we've already seen this aggregator produce an aggregate, just - // skip this one. - // - // We're likely to see this with VCs that use fallback BNs. The first - // BN might time-out *after* publishing the aggregate and then the - // second BN will indicate it's already seen the aggregate. - // - // There's no actual error for the user or the network since the - // aggregate has been successfully published by some other node. - Err(AttnError::AggregatorAlreadyKnown(_)) => continue, - Err(e) => { - error!( - error = ?e, - request_index = index, - aggregator_index = aggregate.message().aggregator_index(), - attestation_index = aggregate.message().aggregate().committee_index(), - attestation_slot = %aggregate.message().aggregate().data().slot, - "Failure verifying aggregate and proofs" - ); - failures.push(api_types::Failure::new(index, format!("Verification: {:?}", e))); - } - } - } - - // Publish aggregate attestations to the libp2p network - if !messages.is_empty() { - publish_network_message(&network_tx, NetworkMessage::Publish { messages })?; - } - - // Import aggregate attestations - for (index, verified_aggregate) in verified_aggregates { - if let Err(e) = chain.apply_attestation_to_fork_choice(&verified_aggregate) { - error!( - error = ?e, - request_index = index, - aggregator_index = verified_aggregate.aggregate().message().aggregator_index(), - attestation_index = verified_aggregate.attestation().committee_index(), - attestation_slot = %verified_aggregate.attestation().data().slot, - "Failure applying verified aggregate attestation to fork choice" - ); - failures.push(api_types::Failure::new(index, format!("Fork choice: {:?}", e))); - } - if let Err(e) = chain.add_to_block_inclusion_pool(verified_aggregate) { - warn!( - error = ?e, - request_index = index, - "Could not add verified aggregate attestation to the inclusion pool" - ); - failures.push(api_types::Failure::new(index, format!("Op pool: {:?}", e))); - } - } - - if !failures.is_empty() { - Err(warp_utils::reject::indexed_bad_request("error processing aggregate and proofs".to_string(), - failures, - )) - } else { - Ok(()) - } - }) - }, - ); - - let post_validator_contribution_and_proofs = eth_v1 - .and(warp::path("validator")) - .and(warp::path("contribution_and_proofs")) - .and(warp::path::end()) - .and(not_while_syncing_filter.clone()) - .and(task_spawner_filter.clone()) - .and(chain_filter.clone()) - .and(warp_utils::json::json()) - .and(network_tx_filter.clone()) - .then( - |not_synced_filter: Result<(), Rejection>, - task_spawner: TaskSpawner, - chain: Arc>, - contributions: Vec>, - network_tx: UnboundedSender>| { - task_spawner.blocking_json_task(Priority::P0, move || { - not_synced_filter?; - sync_committees::process_signed_contribution_and_proofs( - contributions, - network_tx, - &chain, - )?; - Ok(api_types::GenericResponse::from(())) - }) - }, - ); + let post_validator_contribution_and_proofs = post_validator_contribution_and_proofs( + eth_v1.clone().clone(), + chain_filter.clone(), + network_tx_filter.clone(), + not_while_syncing_filter.clone(), + task_spawner_filter.clone(), + ); // POST validator/beacon_committee_subscriptions - let post_validator_beacon_committee_subscriptions = eth_v1 - .and(warp::path("validator")) - .and(warp::path("beacon_committee_subscriptions")) - .and(warp::path::end()) - .and(warp_utils::json::json()) - .and(validator_subscription_tx_filter.clone()) - .and(task_spawner_filter.clone()) - .and(chain_filter.clone()) - .then( - |committee_subscriptions: Vec, - validator_subscription_tx: Sender, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.blocking_json_task(Priority::P0, move || { - let subscriptions: std::collections::BTreeSet<_> = committee_subscriptions - .iter() - .map(|subscription| { - chain - .validator_monitor - .write() - .auto_register_local_validator(subscription.validator_index); - api_types::ValidatorSubscription { - attestation_committee_index: subscription.committee_index, - slot: subscription.slot, - committee_count_at_slot: subscription.committees_at_slot, - is_aggregator: subscription.is_aggregator, - } - }) - .collect(); - - let message = - ValidatorSubscriptionMessage::AttestationSubscribe { subscriptions }; - if let Err(e) = validator_subscription_tx.try_send(message) { - warn!( - info = "the host may be overloaded or resource-constrained", - error = ?e, - "Unable to process committee subscriptions" - ); - return Err(warp_utils::reject::custom_server_error( - "unable to queue subscription, host may be overloaded or shutting down" - .to_string(), - )); - } - Ok(()) - }) - }, + let post_validator_beacon_committee_subscriptions = + post_validator_beacon_committee_subscriptions( + eth_v1.clone().clone(), + chain_filter.clone(), + validator_subscription_tx_filter.clone(), + task_spawner_filter.clone(), ); // POST validator/prepare_beacon_proposer - let post_validator_prepare_beacon_proposer = eth_v1 - .and(warp::path("validator")) - .and(warp::path("prepare_beacon_proposer")) - .and(warp::path::end()) - .and(not_while_syncing_filter.clone()) - .and(network_tx_filter.clone()) - .and(task_spawner_filter.clone()) - .and(chain_filter.clone()) - .and(warp_utils::json::json()) - .then( - |not_synced_filter: Result<(), Rejection>, - network_tx: UnboundedSender>, - task_spawner: TaskSpawner, - chain: Arc>, - preparation_data: Vec| { - task_spawner.spawn_async_with_rejection(Priority::P0, async move { - not_synced_filter?; - let execution_layer = chain - .execution_layer - .as_ref() - .ok_or(BeaconChainError::ExecutionLayerMissing) - .map_err(warp_utils::reject::unhandled_error)?; - - let current_slot = chain - .slot_clock - .now_or_genesis() - .ok_or(BeaconChainError::UnableToReadSlot) - .map_err(warp_utils::reject::unhandled_error)?; - let current_epoch = current_slot.epoch(T::EthSpec::slots_per_epoch()); - - debug!( - count = preparation_data.len(), - "Received proposer preparation data" - ); - - execution_layer - .update_proposer_preparation( - current_epoch, - preparation_data.iter().map(|data| (data, &None)), - ) - .await; - - chain - .prepare_beacon_proposer(current_slot) - .await - .map_err(|e| { - warp_utils::reject::custom_bad_request(format!( - "error updating proposer preparations: {:?}", - e - )) - })?; - - if chain.spec.is_peer_das_scheduled() { - let (finalized_beacon_state, _, _) = - StateId(CoreStateId::Finalized).state(&chain)?; - let validators_and_balances = preparation_data - .iter() - .filter_map(|preparation| { - if let Ok(effective_balance) = finalized_beacon_state - .get_effective_balance(preparation.validator_index as usize) - { - Some((preparation.validator_index as usize, effective_balance)) - } else { - None - } - }) - .collect::>(); - - let current_slot = - chain.slot().map_err(warp_utils::reject::unhandled_error)?; - if let Some(cgc_change) = chain - .data_availability_checker - .custody_context() - .register_validators(validators_and_balances, current_slot, &chain.spec) - { - chain.update_data_column_custody_info(Some( - cgc_change - .effective_epoch - .start_slot(T::EthSpec::slots_per_epoch()), - )); - - network_tx.send(NetworkMessage::CustodyCountChanged { - new_custody_group_count: cgc_change.new_custody_group_count, - sampling_count: cgc_change.sampling_count, - }).unwrap_or_else(|e| { - debug!(error = %e, "Could not send message to the network service. \ - Likely shutdown") - }); - } - } - - Ok::<_, warp::reject::Rejection>(warp::reply::json(&()).into_response()) - }) - }, - ); + let post_validator_prepare_beacon_proposer = post_validator_prepare_beacon_proposer( + eth_v1.clone().clone(), + chain_filter.clone(), + network_tx_filter.clone(), + not_while_syncing_filter.clone(), + task_spawner_filter.clone(), + ); // POST validator/register_validator - let post_validator_register_validator = eth_v1 - .and(warp::path("validator")) - .and(warp::path("register_validator")) - .and(warp::path::end()) - .and(task_spawner_filter.clone()) - .and(chain_filter.clone()) - .and(warp_utils::json::json()) - .then( - |task_spawner: TaskSpawner, - chain: Arc>, - register_val_data: Vec| async { - let (tx, rx) = oneshot::channel(); - - let initial_result = task_spawner - .spawn_async_with_rejection_no_conversion(Priority::P0, async move { - let execution_layer = chain - .execution_layer - .as_ref() - .ok_or(BeaconChainError::ExecutionLayerMissing) - .map_err(warp_utils::reject::unhandled_error)?; - let current_slot = chain - .slot_clock - .now_or_genesis() - .ok_or(BeaconChainError::UnableToReadSlot) - .map_err(warp_utils::reject::unhandled_error)?; - let current_epoch = current_slot.epoch(T::EthSpec::slots_per_epoch()); - - debug!( - count = register_val_data.len(), - "Received register validator request" - ); - - let head_snapshot = chain.head_snapshot(); - let spec = &chain.spec; - - let (preparation_data, filtered_registration_data): ( - Vec<(ProposerPreparationData, Option)>, - Vec, - ) = register_val_data - .into_iter() - .filter_map(|register_data| { - chain - .validator_index(®ister_data.message.pubkey) - .ok() - .flatten() - .and_then(|validator_index| { - let validator = head_snapshot - .beacon_state - .get_validator(validator_index) - .ok()?; - let validator_status = ValidatorStatus::from_validator( - validator, - current_epoch, - spec.far_future_epoch, - ) - .superstatus(); - let is_active_or_pending = - matches!(validator_status, ValidatorStatus::Pending) - || matches!( - validator_status, - ValidatorStatus::Active - ); - - // Filter out validators who are not 'active' or 'pending'. - is_active_or_pending.then_some({ - ( - ( - ProposerPreparationData { - validator_index: validator_index as u64, - fee_recipient: register_data - .message - .fee_recipient, - }, - Some(register_data.message.gas_limit), - ), - register_data, - ) - }) - }) - }) - .unzip(); - - // Update the prepare beacon proposer cache based on this request. - execution_layer - .update_proposer_preparation( - current_epoch, - preparation_data.iter().map(|(data, limit)| (data, limit)), - ) - .await; - - // Call prepare beacon proposer blocking with the latest update in order to make - // sure we have a local payload to fall back to in the event of the blinded block - // flow failing. - chain - .prepare_beacon_proposer(current_slot) - .await - .map_err(|e| { - warp_utils::reject::custom_bad_request(format!( - "error updating proposer preparations: {:?}", - e - )) - })?; - - info!( - count = filtered_registration_data.len(), - "Forwarding register validator request to connected builder" - ); - - // It's a waste of a `BeaconProcessor` worker to just - // wait on a response from the builder (especially since - // they have frequent timeouts). Spawn a new task and - // send the response back to our original HTTP request - // task via a channel. - let builder_future = async move { - let arc_builder = chain - .execution_layer - .as_ref() - .ok_or(BeaconChainError::ExecutionLayerMissing) - .map_err(warp_utils::reject::unhandled_error)? - .builder(); - let builder = arc_builder - .as_ref() - .ok_or(BeaconChainError::BuilderMissing) - .map_err(warp_utils::reject::unhandled_error)?; - builder - .post_builder_validators(&filtered_registration_data) - .await - .map(|resp| warp::reply::json(&resp).into_response()) - .map_err(|e| { - warn!( - num_registrations = filtered_registration_data.len(), - error = ?e, - "Relay error when registering validator(s)" - ); - // Forward the HTTP status code if we are able to, otherwise fall back - // to a server error. - if let eth2::Error::ServerMessage(message) = e { - if message.code == StatusCode::BAD_REQUEST.as_u16() { - return warp_utils::reject::custom_bad_request( - message.message, - ); - } else { - // According to the spec this response should only be a 400 or 500, - // so we fall back to a 500 here. - return warp_utils::reject::custom_server_error( - message.message, - ); - } - } - warp_utils::reject::custom_server_error(format!("{e:?}")) - }) - }; - tokio::task::spawn(async move { tx.send(builder_future.await) }); - - // Just send a generic 200 OK from this closure. We'll - // ignore the `Ok` variant and form a proper response - // from what is sent back down the channel. - Ok(warp::reply::reply().into_response()) - }) - .await; - - if initial_result.is_err() { - return convert_rejection(initial_result).await; - } - - // Await a response from the builder without blocking a - // `BeaconProcessor` worker. - convert_rejection(rx.await.unwrap_or_else(|_| { - Ok(warp::reply::with_status( - warp::reply::json(&"No response from channel"), - warp::http::StatusCode::INTERNAL_SERVER_ERROR, - ) - .into_response()) - })) - .await - }, - ); + let post_validator_register_validator = post_validator_register_validator( + eth_v1.clone().clone(), + chain_filter.clone(), + task_spawner_filter.clone(), + ); // POST validator/sync_committee_subscriptions - let post_validator_sync_committee_subscriptions = eth_v1 - .and(warp::path("validator")) - .and(warp::path("sync_committee_subscriptions")) - .and(warp::path::end()) - .and(warp_utils::json::json()) - .and(validator_subscription_tx_filter) - .and(task_spawner_filter.clone()) - .and(chain_filter.clone()) - .then( - |subscriptions: Vec, - validator_subscription_tx: Sender, - task_spawner: TaskSpawner, - chain: Arc>, - | { - task_spawner.blocking_json_task(Priority::P0, move || { - for subscription in subscriptions { - chain - .validator_monitor - .write() - .auto_register_local_validator(subscription.validator_index); - - let message = ValidatorSubscriptionMessage::SyncCommitteeSubscribe { - subscriptions: vec![subscription], - }; - if let Err(e) = validator_subscription_tx.try_send(message) { - warn!( - info = "the host may be overloaded or resource-constrained", - error = ?e, - "Unable to process sync subscriptions" - ); - return Err(warp_utils::reject::custom_server_error( - "unable to queue subscription, host may be overloaded or shutting down".to_string(), - )); - } - } - - Ok(()) - }) - }, - ); + let post_validator_sync_committee_subscriptions = post_validator_sync_committee_subscriptions( + eth_v1.clone().clone(), + chain_filter.clone(), + validator_subscription_tx_filter.clone(), + task_spawner_filter.clone(), + ); // POST validator/liveness/{epoch} - let post_validator_liveness_epoch = eth_v1 - .and(warp::path("validator")) - .and(warp::path("liveness")) - .and(warp::path::param::()) - .and(warp::path::end()) - .and(warp_utils::json::json()) - .and(task_spawner_filter.clone()) - .and(chain_filter.clone()) - .then( - |epoch: Epoch, - indices: api_types::ValidatorIndexData, - task_spawner: TaskSpawner, - chain: Arc>| { - task_spawner.blocking_json_task(Priority::P0, move || { - // Ensure the request is for either the current, previous or next epoch. - let current_epoch = - chain.epoch().map_err(warp_utils::reject::unhandled_error)?; - let prev_epoch = current_epoch.saturating_sub(Epoch::new(1)); - let next_epoch = current_epoch.saturating_add(Epoch::new(1)); - - if epoch < prev_epoch || epoch > next_epoch { - return Err(warp_utils::reject::custom_bad_request(format!( - "request epoch {} is more than one epoch from the current epoch {}", - epoch, current_epoch - ))); - } - - let liveness: Vec = indices - .0 - .iter() - .cloned() - .map(|index| { - let is_live = chain.validator_seen_at_epoch(index as usize, epoch); - api_types::StandardLivenessResponseData { index, is_live } - }) - .collect(); - - Ok(api_types::GenericResponse::from(liveness)) - }) - }, - ); + let post_validator_liveness_epoch = post_validator_liveness_epoch( + eth_v1.clone().clone(), + chain_filter.clone(), + task_spawner_filter.clone(), + ); // POST lighthouse/finalize let post_lighthouse_finalize = warp::path("lighthouse") @@ -4259,7 +2610,10 @@ pub fn serve( ); network_globals.add_trusted_peer(enr.clone()); - publish_network_message(&network_tx, NetworkMessage::ConnectTrustedPeer(enr))?; + utils::publish_network_message( + &network_tx, + NetworkMessage::ConnectTrustedPeer(enr), + )?; Ok(()) }) @@ -4290,7 +2644,7 @@ pub fn serve( ); network_globals.remove_trusted_peer(enr.clone()); - publish_network_message( + utils::publish_network_message( &network_tx, NetworkMessage::DisconnectTrustedPeer(enr), )?; @@ -4742,6 +3096,7 @@ pub fn serve( ); let get_events = eth_v1 + .clone() .and(warp::path("events")) .and(warp::path::end()) .and(multi_key_query::()) @@ -5063,70 +3418,3 @@ pub fn serve( Ok(http_server) } - -fn from_meta_data( - meta_data: &RwLock>, - spec: &ChainSpec, -) -> api_types::MetaData { - let meta_data = meta_data.read(); - let format_hex = |bytes: &[u8]| format!("0x{}", hex::encode(bytes)); - - let seq_number = *meta_data.seq_number(); - let attnets = format_hex(&meta_data.attnets().clone().into_bytes()); - let syncnets = format_hex( - &meta_data - .syncnets() - .cloned() - .unwrap_or_default() - .into_bytes(), - ); - - if spec.is_peer_das_scheduled() { - api_types::MetaData::V3(api_types::MetaDataV3 { - seq_number, - attnets, - syncnets, - custody_group_count: meta_data.custody_group_count().cloned().unwrap_or_default(), - }) - } else { - api_types::MetaData::V2(api_types::MetaDataV2 { - seq_number, - attnets, - syncnets, - }) - } -} - -/// Publish a message to the libp2p pubsub network. -fn publish_pubsub_message( - network_tx: &UnboundedSender>, - message: PubsubMessage, -) -> Result<(), warp::Rejection> { - publish_network_message( - network_tx, - NetworkMessage::Publish { - messages: vec![message], - }, - ) -} - -/// Publish a message to the libp2p pubsub network. -fn publish_pubsub_messages( - network_tx: &UnboundedSender>, - messages: Vec>, -) -> Result<(), warp::Rejection> { - publish_network_message(network_tx, NetworkMessage::Publish { messages }) -} - -/// Publish a message to the libp2p network. -fn publish_network_message( - network_tx: &UnboundedSender>, - message: NetworkMessage, -) -> Result<(), warp::Rejection> { - network_tx.send(message).map_err(|e| { - warp_utils::reject::custom_server_error(format!( - "unable to publish to network channel: {}", - e - )) - }) -} diff --git a/beacon_node/http_api/src/light_client.rs b/beacon_node/http_api/src/light_client.rs index ca9b86990c..86eef03218 100644 --- a/beacon_node/http_api/src/light_client.rs +++ b/beacon_node/http_api/src/light_client.rs @@ -3,13 +3,14 @@ use crate::version::{ beacon_response, }; use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes}; +use eth2::beacon_response::BeaconResponse; use eth2::types::{ self as api_types, LightClientUpdate, LightClientUpdateResponseChunk, LightClientUpdateResponseChunkInner, LightClientUpdatesQuery, }; use ssz::Encode; use std::sync::Arc; -use types::{BeaconResponse, EthSpec, ForkName, Hash256, LightClientBootstrap}; +use types::{EthSpec, ForkName, Hash256, LightClientBootstrap}; use warp::{ Rejection, hyper::{Body, Response}, diff --git a/beacon_node/http_api/src/produce_block.rs b/beacon_node/http_api/src/produce_block.rs index 367e09969b..472ec0b65e 100644 --- a/beacon_node/http_api/src/produce_block.rs +++ b/beacon_node/http_api/src/produce_block.rs @@ -9,6 +9,7 @@ use crate::{ use beacon_chain::{ BeaconBlockResponseWrapper, BeaconChain, BeaconChainTypes, ProduceBlockVerification, }; +use eth2::beacon_response::ForkVersionedResponse; use eth2::types::{self as api_types, ProduceBlockV3Metadata, SkipRandaoVerification}; use lighthouse_tracing::{SPAN_PRODUCE_BLOCK_V2, SPAN_PRODUCE_BLOCK_V3}; use ssz::Encode; diff --git a/beacon_node/http_api/src/publish_blocks.rs b/beacon_node/http_api/src/publish_blocks.rs index 9671a72da2..b54c071eb8 100644 --- a/beacon_node/http_api/src/publish_blocks.rs +++ b/beacon_node/http_api/src/publish_blocks.rs @@ -138,9 +138,10 @@ pub async fn publish_block>( "Signed block published to network via HTTP API" ); - crate::publish_pubsub_message(&sender, PubsubMessage::BeaconBlock(block.clone())).map_err( - |_| BlockError::BeaconChainError(Box::new(BeaconChainError::UnableToPublish)), - )?; + crate::utils::publish_pubsub_message(&sender, PubsubMessage::BeaconBlock(block.clone())) + .map_err(|_| { + BlockError::BeaconChainError(Box::new(BeaconChainError::UnableToPublish)) + })?; Ok(()) }; @@ -492,7 +493,7 @@ fn publish_blob_sidecars( blob: &GossipVerifiedBlob, ) -> Result<(), BlockError> { let pubsub_message = PubsubMessage::BlobSidecar(Box::new((blob.index(), blob.clone_blob()))); - crate::publish_pubsub_message(sender_clone, pubsub_message) + crate::utils::publish_pubsub_message(sender_clone, pubsub_message) .map_err(|_| BlockError::BeaconChainError(Box::new(BeaconChainError::UnableToPublish))) } @@ -525,7 +526,7 @@ fn publish_column_sidecars( PubsubMessage::DataColumnSidecar(Box::new((subnet, data_col))) }) .collect::>(); - crate::publish_pubsub_messages(sender_clone, pubsub_messages) + crate::utils::publish_pubsub_messages(sender_clone, pubsub_messages) .map_err(|_| BlockError::BeaconChainError(Box::new(BeaconChainError::UnableToPublish))) } diff --git a/beacon_node/http_api/src/sync_committees.rs b/beacon_node/http_api/src/sync_committees.rs index edda0e60a6..b9fa24ad6a 100644 --- a/beacon_node/http_api/src/sync_committees.rs +++ b/beacon_node/http_api/src/sync_committees.rs @@ -1,6 +1,6 @@ //! Handlers for sync committee endpoints. -use crate::publish_pubsub_message; +use crate::utils::publish_pubsub_message; use beacon_chain::sync_committee_verification::{ Error as SyncVerificationError, VerifiedSyncCommitteeMessage, }; diff --git a/beacon_node/http_api/src/utils.rs b/beacon_node/http_api/src/utils.rs new file mode 100644 index 0000000000..f2b859ebe5 --- /dev/null +++ b/beacon_node/http_api/src/utils.rs @@ -0,0 +1,90 @@ +use crate::task_spawner::TaskSpawner; +use beacon_chain::{BeaconChain, BeaconChainTypes}; +use eth2::types::EndpointVersion; +use lighthouse_network::PubsubMessage; +use lighthouse_network::rpc::methods::MetaData; +use network::{NetworkMessage, ValidatorSubscriptionMessage}; +use parking_lot::RwLock; +use std::sync::Arc; +use tokio::sync::mpsc::{Sender, UnboundedSender}; +use types::{ChainSpec, EthSpec, ForkName}; +use warp::Rejection; +use warp::filters::BoxedFilter; + +pub type ResponseFilter = BoxedFilter<(warp::reply::Response,)>; +pub type AnyVersionFilter = BoxedFilter<(EndpointVersion,)>; +pub type EthV1Filter = BoxedFilter<()>; +pub type ChainFilter = BoxedFilter<(Arc>,)>; +pub type NotWhileSyncingFilter = BoxedFilter<(Result<(), Rejection>,)>; +pub type TaskSpawnerFilter = BoxedFilter<(TaskSpawner<::EthSpec>,)>; +pub type ValidatorSubscriptionTxFilter = BoxedFilter<(Sender,)>; +pub type NetworkTxFilter = + BoxedFilter<(UnboundedSender::EthSpec>>,)>; +pub type OptionalConsensusVersionHeaderFilter = BoxedFilter<(Option,)>; + +pub fn from_meta_data( + meta_data: &RwLock>, + spec: &ChainSpec, +) -> eth2::types::MetaData { + let meta_data = meta_data.read(); + let format_hex = |bytes: &[u8]| format!("0x{}", hex::encode(bytes)); + + let seq_number = *meta_data.seq_number(); + let attnets = format_hex(&meta_data.attnets().clone().into_bytes()); + let syncnets = format_hex( + &meta_data + .syncnets() + .cloned() + .unwrap_or_default() + .into_bytes(), + ); + + if spec.is_peer_das_scheduled() { + eth2::types::MetaData::V3(eth2::types::MetaDataV3 { + seq_number, + attnets, + syncnets, + custody_group_count: meta_data.custody_group_count().cloned().unwrap_or_default(), + }) + } else { + eth2::types::MetaData::V2(eth2::types::MetaDataV2 { + seq_number, + attnets, + syncnets, + }) + } +} + +/// Publish a message to the libp2p pubsub network. +pub fn publish_pubsub_message( + network_tx: &UnboundedSender>, + message: PubsubMessage, +) -> Result<(), warp::Rejection> { + publish_network_message( + network_tx, + NetworkMessage::Publish { + messages: vec![message], + }, + ) +} + +/// Publish a message to the libp2p pubsub network. +pub fn publish_pubsub_messages( + network_tx: &UnboundedSender>, + messages: Vec>, +) -> Result<(), warp::Rejection> { + publish_network_message(network_tx, NetworkMessage::Publish { messages }) +} + +/// Publish a message to the libp2p network. +pub fn publish_network_message( + network_tx: &UnboundedSender>, + message: NetworkMessage, +) -> Result<(), warp::Rejection> { + network_tx.send(message).map_err(|e| { + warp_utils::reject::custom_server_error(format!( + "unable to publish to network channel: {}", + e + )) + }) +} diff --git a/beacon_node/http_api/src/validator.rs b/beacon_node/http_api/src/validator.rs deleted file mode 100644 index 25b0feb99e..0000000000 --- a/beacon_node/http_api/src/validator.rs +++ /dev/null @@ -1,22 +0,0 @@ -use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes}; -use types::{BeaconState, PublicKeyBytes}; - -/// 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`. -pub fn pubkey_to_validator_index( - chain: &BeaconChain, - state: &BeaconState, - pubkey: &PublicKeyBytes, -) -> Result, Box> { - chain - .validator_index(pubkey) - .map_err(Box::new)? - .filter(|&index| { - state - .validators() - .get(index) - .is_some_and(|v| v.pubkey == *pubkey) - }) - .map(Result::Ok) - .transpose() -} diff --git a/beacon_node/http_api/src/validator/mod.rs b/beacon_node/http_api/src/validator/mod.rs new file mode 100644 index 0000000000..9cf1f1a33d --- /dev/null +++ b/beacon_node/http_api/src/validator/mod.rs @@ -0,0 +1,971 @@ +use crate::produce_block::{produce_blinded_block_v2, produce_block_v2, produce_block_v3}; +use crate::task_spawner::{Priority, TaskSpawner}; +use crate::utils::{ + AnyVersionFilter, ChainFilter, EthV1Filter, NetworkTxFilter, NotWhileSyncingFilter, + ResponseFilter, TaskSpawnerFilter, ValidatorSubscriptionTxFilter, publish_network_message, +}; +use crate::version::V3; +use crate::{StateId, attester_duties, proposer_duties, sync_committees}; +use beacon_chain::attestation_verification::VerifiedAttestation; +use beacon_chain::validator_monitor::timestamp_now; +use beacon_chain::{AttestationError, BeaconChain, BeaconChainError, BeaconChainTypes}; +use eth2::StatusCode; +use eth2::types::{ + Accept, BeaconCommitteeSubscription, EndpointVersion, Failure, GenericResponse, + StandardLivenessResponseData, StateId as CoreStateId, ValidatorAggregateAttestationQuery, + ValidatorAttestationDataQuery, ValidatorBlocksQuery, ValidatorIndexData, ValidatorStatus, +}; +use lighthouse_network::PubsubMessage; +use network::{NetworkMessage, ValidatorSubscriptionMessage}; +use slot_clock::SlotClock; +use std::sync::Arc; +use tokio::sync::mpsc::{Sender, UnboundedSender}; +use tokio::sync::oneshot; +use tracing::{debug, error, info, warn}; +use types::{ + BeaconState, Epoch, EthSpec, ProposerPreparationData, PublicKeyBytes, SignedAggregateAndProof, + SignedContributionAndProof, SignedValidatorRegistrationData, Slot, SyncContributionData, + ValidatorSubscription, +}; +use warp::{Filter, Rejection, Reply}; +use warp_utils::reject::convert_rejection; + +/// 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`. +pub fn pubkey_to_validator_index( + chain: &BeaconChain, + state: &BeaconState, + pubkey: &PublicKeyBytes, +) -> Result, Box> { + chain + .validator_index(pubkey) + .map_err(Box::new)? + .filter(|&index| { + state + .validators() + .get(index) + .is_some_and(|v| v.pubkey == *pubkey) + }) + .map(Result::Ok) + .transpose() +} + +// GET validator/sync_committee_contribution +pub fn get_validator_sync_committee_contribution( + eth_v1: EthV1Filter, + chain_filter: ChainFilter, + not_while_syncing_filter: NotWhileSyncingFilter, + task_spawner_filter: TaskSpawnerFilter, +) -> ResponseFilter { + eth_v1 + .and(warp::path("validator")) + .and(warp::path("sync_committee_contribution")) + .and(warp::path::end()) + .and(warp::query::()) + .and(not_while_syncing_filter.clone()) + .and(task_spawner_filter.clone()) + .and(chain_filter.clone()) + .then( + |sync_committee_data: SyncContributionData, + not_synced_filter: Result<(), Rejection>, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.blocking_json_task(Priority::P0, move || { + not_synced_filter?; + chain + .get_aggregated_sync_committee_contribution(&sync_committee_data) + .map_err(|e| { + warp_utils::reject::custom_bad_request(format!( + "unable to fetch sync contribution: {:?}", + e + )) + })? + .map(GenericResponse::from) + .ok_or_else(|| { + warp_utils::reject::custom_not_found( + "no matching sync contribution found".to_string(), + ) + }) + }) + }, + ) + .boxed() +} + +// POST validator/duties/sync/{epoch} +pub fn post_validator_duties_sync( + eth_v1: EthV1Filter, + chain_filter: ChainFilter, + not_while_syncing_filter: NotWhileSyncingFilter, + task_spawner_filter: TaskSpawnerFilter, +) -> ResponseFilter { + eth_v1 + .and(warp::path("validator")) + .and(warp::path("duties")) + .and(warp::path("sync")) + .and(warp::path::param::().or_else(|_| async { + Err(warp_utils::reject::custom_bad_request( + "Invalid epoch".to_string(), + )) + })) + .and(warp::path::end()) + .and(not_while_syncing_filter.clone()) + .and(warp_utils::json::json()) + .and(task_spawner_filter.clone()) + .and(chain_filter.clone()) + .then( + |epoch: Epoch, + not_synced_filter: Result<(), Rejection>, + indices: ValidatorIndexData, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.blocking_json_task(Priority::P0, move || { + not_synced_filter?; + sync_committees::sync_committee_duties(epoch, &indices.0, &chain) + }) + }, + ) + .boxed() +} + +// POST validator/duties/attester/{epoch} +pub fn post_validator_duties_attester( + eth_v1: EthV1Filter, + chain_filter: ChainFilter, + not_while_syncing_filter: NotWhileSyncingFilter, + task_spawner_filter: TaskSpawnerFilter, +) -> ResponseFilter { + eth_v1 + .and(warp::path("validator")) + .and(warp::path("duties")) + .and(warp::path("attester")) + .and(warp::path::param::().or_else(|_| async { + Err(warp_utils::reject::custom_bad_request( + "Invalid epoch".to_string(), + )) + })) + .and(warp::path::end()) + .and(not_while_syncing_filter.clone()) + .and(warp_utils::json::json()) + .and(task_spawner_filter.clone()) + .and(chain_filter.clone()) + .then( + |epoch: Epoch, + not_synced_filter: Result<(), Rejection>, + indices: ValidatorIndexData, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.blocking_json_task(Priority::P0, move || { + not_synced_filter?; + attester_duties::attester_duties(epoch, &indices.0, &chain) + }) + }, + ) + .boxed() +} + +// GET validator/aggregate_attestation?attestation_data_root,slot +pub fn get_validator_aggregate_attestation( + any_version: AnyVersionFilter, + chain_filter: ChainFilter, + not_while_syncing_filter: NotWhileSyncingFilter, + task_spawner_filter: TaskSpawnerFilter, +) -> ResponseFilter { + any_version + .and(warp::path("validator")) + .and(warp::path("aggregate_attestation")) + .and(warp::path::end()) + .and(warp::query::()) + .and(not_while_syncing_filter.clone()) + .and(task_spawner_filter.clone()) + .and(chain_filter.clone()) + .then( + |endpoint_version: EndpointVersion, + query: ValidatorAggregateAttestationQuery, + not_synced_filter: Result<(), Rejection>, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.blocking_response_task(Priority::P0, move || { + not_synced_filter?; + crate::aggregate_attestation::get_aggregate_attestation( + query.slot, + &query.attestation_data_root, + query.committee_index, + endpoint_version, + chain, + ) + }) + }, + ) + .boxed() +} + +// GET validator/attestation_data?slot,committee_index +pub fn get_validator_attestation_data( + eth_v1: EthV1Filter, + chain_filter: ChainFilter, + not_while_syncing_filter: NotWhileSyncingFilter, + task_spawner_filter: TaskSpawnerFilter, +) -> ResponseFilter { + eth_v1 + .and(warp::path("validator")) + .and(warp::path("attestation_data")) + .and(warp::path::end()) + .and(warp::query::()) + .and(not_while_syncing_filter.clone()) + .and(task_spawner_filter.clone()) + .and(chain_filter.clone()) + .then( + |query: ValidatorAttestationDataQuery, + not_synced_filter: Result<(), Rejection>, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.blocking_json_task(Priority::P0, move || { + not_synced_filter?; + + let current_slot = chain.slot().map_err(warp_utils::reject::unhandled_error)?; + + // allow a tolerance of one slot to account for clock skew + if query.slot > current_slot + 1 { + return Err(warp_utils::reject::custom_bad_request(format!( + "request slot {} is more than one slot past the current slot {}", + query.slot, current_slot + ))); + } + + chain + .produce_unaggregated_attestation(query.slot, query.committee_index) + .map(|attestation| attestation.data().clone()) + .map(GenericResponse::from) + .map_err(warp_utils::reject::unhandled_error) + }) + }, + ) + .boxed() +} + +// GET validator/blinded_blocks/{slot} +pub fn get_validator_blinded_blocks( + eth_v1: EthV1Filter, + chain_filter: ChainFilter, + not_while_syncing_filter: NotWhileSyncingFilter, + task_spawner_filter: TaskSpawnerFilter, +) -> ResponseFilter { + eth_v1 + .and(warp::path("validator")) + .and(warp::path("blinded_blocks")) + .and(warp::path::param::().or_else(|_| async { + Err(warp_utils::reject::custom_bad_request( + "Invalid slot".to_string(), + )) + })) + .and(warp::path::end()) + .and(not_while_syncing_filter.clone()) + .and(warp::query::()) + .and(warp::header::optional::("accept")) + .and(task_spawner_filter.clone()) + .and(chain_filter.clone()) + .then( + |slot: Slot, + not_synced_filter: Result<(), Rejection>, + query: ValidatorBlocksQuery, + accept_header: Option, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.spawn_async_with_rejection(Priority::P0, async move { + not_synced_filter?; + produce_blinded_block_v2(accept_header, chain, slot, query).await + }) + }, + ) + .boxed() +} + +// GET validator/blocks/{slot} +pub fn get_validator_blocks( + any_version: AnyVersionFilter, + chain_filter: ChainFilter, + not_while_syncing_filter: NotWhileSyncingFilter, + task_spawner_filter: TaskSpawnerFilter, +) -> ResponseFilter { + any_version + .and(warp::path("validator")) + .and(warp::path("blocks")) + .and(warp::path::param::().or_else(|_| async { + Err(warp_utils::reject::custom_bad_request( + "Invalid slot".to_string(), + )) + })) + .and(warp::path::end()) + .and(warp::header::optional::("accept")) + .and(not_while_syncing_filter) + .and(warp::query::()) + .and(task_spawner_filter) + .and(chain_filter) + .then( + |endpoint_version: EndpointVersion, + slot: Slot, + accept_header: Option, + not_synced_filter: Result<(), Rejection>, + query: ValidatorBlocksQuery, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.spawn_async_with_rejection(Priority::P0, async move { + debug!(?slot, "Block production request from HTTP API"); + + not_synced_filter?; + + if endpoint_version == V3 { + produce_block_v3(accept_header, chain, slot, query).await + } else { + produce_block_v2(accept_header, chain, slot, query).await + } + }) + }, + ) + .boxed() +} + +// POST validator/liveness/{epoch} +pub fn post_validator_liveness_epoch( + eth_v1: EthV1Filter, + chain_filter: ChainFilter, + task_spawner_filter: TaskSpawnerFilter, +) -> ResponseFilter { + eth_v1 + .and(warp::path("validator")) + .and(warp::path("liveness")) + .and(warp::path::param::()) + .and(warp::path::end()) + .and(warp_utils::json::json()) + .and(task_spawner_filter.clone()) + .and(chain_filter.clone()) + .then( + |epoch: Epoch, + indices: ValidatorIndexData, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.blocking_json_task(Priority::P0, move || { + // Ensure the request is for either the current, previous or next epoch. + let current_epoch = + chain.epoch().map_err(warp_utils::reject::unhandled_error)?; + let prev_epoch = current_epoch.saturating_sub(Epoch::new(1)); + let next_epoch = current_epoch.saturating_add(Epoch::new(1)); + + if epoch < prev_epoch || epoch > next_epoch { + return Err(warp_utils::reject::custom_bad_request(format!( + "request epoch {} is more than one epoch from the current epoch {}", + epoch, current_epoch + ))); + } + + let liveness: Vec = indices + .0 + .iter() + .cloned() + .map(|index| { + let is_live = chain.validator_seen_at_epoch(index as usize, epoch); + StandardLivenessResponseData { index, is_live } + }) + .collect(); + + Ok(GenericResponse::from(liveness)) + }) + }, + ) + .boxed() +} + +// POST validator/sync_committee_subscriptions +pub fn post_validator_sync_committee_subscriptions( + eth_v1: EthV1Filter, + chain_filter: ChainFilter, + validator_subscription_tx_filter: ValidatorSubscriptionTxFilter, + task_spawner_filter: TaskSpawnerFilter, +) -> ResponseFilter { + eth_v1 + .and(warp::path("validator")) + .and(warp::path("sync_committee_subscriptions")) + .and(warp::path::end()) + .and(warp_utils::json::json()) + .and(validator_subscription_tx_filter) + .and(task_spawner_filter.clone()) + .and(chain_filter.clone()) + .then( + |subscriptions: Vec, + validator_subscription_tx: Sender, + task_spawner: TaskSpawner, + chain: Arc>, + | { + task_spawner.blocking_json_task(Priority::P0, move || { + for subscription in subscriptions { + chain + .validator_monitor + .write() + .auto_register_local_validator(subscription.validator_index); + + let message = ValidatorSubscriptionMessage::SyncCommitteeSubscribe { + subscriptions: vec![subscription], + }; + if let Err(e) = validator_subscription_tx.try_send(message) { + warn!( + info = "the host may be overloaded or resource-constrained", + error = ?e, + "Unable to process sync subscriptions" + ); + return Err(warp_utils::reject::custom_server_error( + "unable to queue subscription, host may be overloaded or shutting down".to_string(), + )); + } + } + + Ok(()) + }) + }, + ).boxed() +} + +// POST validator/register_validator +pub fn post_validator_register_validator( + eth_v1: EthV1Filter, + chain_filter: ChainFilter, + task_spawner_filter: TaskSpawnerFilter, +) -> ResponseFilter { + eth_v1 + .and(warp::path("validator")) + .and(warp::path("register_validator")) + .and(warp::path::end()) + .and(task_spawner_filter.clone()) + .and(chain_filter.clone()) + .and(warp_utils::json::json()) + .then( + |task_spawner: TaskSpawner, + chain: Arc>, + register_val_data: Vec| async { + let (tx, rx) = oneshot::channel(); + + let initial_result = task_spawner + .spawn_async_with_rejection_no_conversion(Priority::P0, async move { + let execution_layer = chain + .execution_layer + .as_ref() + .ok_or(BeaconChainError::ExecutionLayerMissing) + .map_err(warp_utils::reject::unhandled_error)?; + let current_slot = chain + .slot_clock + .now_or_genesis() + .ok_or(BeaconChainError::UnableToReadSlot) + .map_err(warp_utils::reject::unhandled_error)?; + let current_epoch = current_slot.epoch(T::EthSpec::slots_per_epoch()); + + debug!( + count = register_val_data.len(), + "Received register validator request" + ); + + let head_snapshot = chain.head_snapshot(); + let spec = &chain.spec; + + let (preparation_data, filtered_registration_data): ( + Vec<(ProposerPreparationData, Option)>, + Vec, + ) = register_val_data + .into_iter() + .filter_map(|register_data| { + chain + .validator_index(®ister_data.message.pubkey) + .ok() + .flatten() + .and_then(|validator_index| { + let validator = head_snapshot + .beacon_state + .get_validator(validator_index) + .ok()?; + let validator_status = ValidatorStatus::from_validator( + validator, + current_epoch, + spec.far_future_epoch, + ) + .superstatus(); + let is_active_or_pending = + matches!(validator_status, ValidatorStatus::Pending) + || matches!( + validator_status, + ValidatorStatus::Active + ); + + // Filter out validators who are not 'active' or 'pending'. + is_active_or_pending.then_some({ + ( + ( + ProposerPreparationData { + validator_index: validator_index as u64, + fee_recipient: register_data + .message + .fee_recipient, + }, + Some(register_data.message.gas_limit), + ), + register_data, + ) + }) + }) + }) + .unzip(); + + // Update the prepare beacon proposer cache based on this request. + execution_layer + .update_proposer_preparation( + current_epoch, + preparation_data.iter().map(|(data, limit)| (data, limit)), + ) + .await; + + // Call prepare beacon proposer blocking with the latest update in order to make + // sure we have a local payload to fall back to in the event of the blinded block + // flow failing. + chain + .prepare_beacon_proposer(current_slot) + .await + .map_err(|e| { + warp_utils::reject::custom_bad_request(format!( + "error updating proposer preparations: {:?}", + e + )) + })?; + + info!( + count = filtered_registration_data.len(), + "Forwarding register validator request to connected builder" + ); + + // It's a waste of a `BeaconProcessor` worker to just + // wait on a response from the builder (especially since + // they have frequent timeouts). Spawn a new task and + // send the response back to our original HTTP request + // task via a channel. + let builder_future = async move { + let arc_builder = chain + .execution_layer + .as_ref() + .ok_or(BeaconChainError::ExecutionLayerMissing) + .map_err(warp_utils::reject::unhandled_error)? + .builder(); + let builder = arc_builder + .as_ref() + .ok_or(BeaconChainError::BuilderMissing) + .map_err(warp_utils::reject::unhandled_error)?; + builder + .post_builder_validators(&filtered_registration_data) + .await + .map(|resp| warp::reply::json(&resp).into_response()) + .map_err(|e| { + warn!( + num_registrations = filtered_registration_data.len(), + error = ?e, + "Relay error when registering validator(s)" + ); + // Forward the HTTP status code if we are able to, otherwise fall back + // to a server error. + if let eth2::Error::ServerMessage(message) = e { + if message.code == StatusCode::BAD_REQUEST.as_u16() { + return warp_utils::reject::custom_bad_request( + message.message, + ); + } else { + // According to the spec this response should only be a 400 or 500, + // so we fall back to a 500 here. + return warp_utils::reject::custom_server_error( + message.message, + ); + } + } + warp_utils::reject::custom_server_error(format!("{e:?}")) + }) + }; + tokio::task::spawn(async move { tx.send(builder_future.await) }); + + // Just send a generic 200 OK from this closure. We'll + // ignore the `Ok` variant and form a proper response + // from what is sent back down the channel. + Ok(warp::reply::reply().into_response()) + }) + .await; + + if initial_result.is_err() { + return convert_rejection(initial_result).await; + } + + // Await a response from the builder without blocking a + // `BeaconProcessor` worker. + convert_rejection(rx.await.unwrap_or_else(|_| { + Ok(warp::reply::with_status( + warp::reply::json(&"No response from channel"), + warp::http::StatusCode::INTERNAL_SERVER_ERROR, + ) + .into_response()) + })) + .await + }, + ) + .boxed() +} + +// POST validator/prepare_beacon_proposer +pub fn post_validator_prepare_beacon_proposer( + eth_v1: EthV1Filter, + chain_filter: ChainFilter, + network_tx_filter: NetworkTxFilter, + not_while_syncing_filter: NotWhileSyncingFilter, + task_spawner_filter: TaskSpawnerFilter, +) -> ResponseFilter { + eth_v1 + .and(warp::path("validator")) + .and(warp::path("prepare_beacon_proposer")) + .and(warp::path::end()) + .and(not_while_syncing_filter.clone()) + .and(network_tx_filter.clone()) + .and(task_spawner_filter.clone()) + .and(chain_filter.clone()) + .and(warp_utils::json::json()) + .then( + |not_synced_filter: Result<(), Rejection>, + network_tx: UnboundedSender>, + task_spawner: TaskSpawner, + chain: Arc>, + preparation_data: Vec| { + task_spawner.spawn_async_with_rejection(Priority::P0, async move { + not_synced_filter?; + let execution_layer = chain + .execution_layer + .as_ref() + .ok_or(BeaconChainError::ExecutionLayerMissing) + .map_err(warp_utils::reject::unhandled_error)?; + + let current_slot = chain + .slot_clock + .now_or_genesis() + .ok_or(BeaconChainError::UnableToReadSlot) + .map_err(warp_utils::reject::unhandled_error)?; + let current_epoch = current_slot.epoch(T::EthSpec::slots_per_epoch()); + + debug!( + count = preparation_data.len(), + "Received proposer preparation data" + ); + + execution_layer + .update_proposer_preparation( + current_epoch, + preparation_data.iter().map(|data| (data, &None)), + ) + .await; + + chain + .prepare_beacon_proposer(current_slot) + .await + .map_err(|e| { + warp_utils::reject::custom_bad_request(format!( + "error updating proposer preparations: {:?}", + e + )) + })?; + + if chain.spec.is_peer_das_scheduled() { + let (finalized_beacon_state, _, _) = + StateId(CoreStateId::Finalized).state(&chain)?; + let validators_and_balances = preparation_data + .iter() + .filter_map(|preparation| { + if let Ok(effective_balance) = finalized_beacon_state + .get_effective_balance(preparation.validator_index as usize) + { + Some((preparation.validator_index as usize, effective_balance)) + } else { + None + } + }) + .collect::>(); + + let current_slot = + chain.slot().map_err(warp_utils::reject::unhandled_error)?; + if let Some(cgc_change) = chain + .data_availability_checker + .custody_context() + .register_validators(validators_and_balances, current_slot, &chain.spec) + { + chain.update_data_column_custody_info(Some( + cgc_change + .effective_epoch + .start_slot(T::EthSpec::slots_per_epoch()), + )); + + network_tx.send(NetworkMessage::CustodyCountChanged { + new_custody_group_count: cgc_change.new_custody_group_count, + sampling_count: cgc_change.sampling_count, + }).unwrap_or_else(|e| { + debug!(error = %e, "Could not send message to the network service. \ + Likely shutdown") + }); + } + } + + Ok::<_, warp::reject::Rejection>(warp::reply::json(&()).into_response()) + }) + }, + ) + .boxed() +} + +// POST validator/beacon_committee_subscriptions +pub fn post_validator_beacon_committee_subscriptions( + eth_v1: EthV1Filter, + chain_filter: ChainFilter, + validator_subscription_tx_filter: ValidatorSubscriptionTxFilter, + task_spawner_filter: TaskSpawnerFilter, +) -> ResponseFilter { + eth_v1 + .and(warp::path("validator")) + .and(warp::path("beacon_committee_subscriptions")) + .and(warp::path::end()) + .and(warp_utils::json::json()) + .and(validator_subscription_tx_filter.clone()) + .and(task_spawner_filter.clone()) + .and(chain_filter.clone()) + .then( + |committee_subscriptions: Vec, + validator_subscription_tx: Sender, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.blocking_json_task(Priority::P0, move || { + let subscriptions: std::collections::BTreeSet<_> = committee_subscriptions + .iter() + .map(|subscription| { + chain + .validator_monitor + .write() + .auto_register_local_validator(subscription.validator_index); + ValidatorSubscription { + attestation_committee_index: subscription.committee_index, + slot: subscription.slot, + committee_count_at_slot: subscription.committees_at_slot, + is_aggregator: subscription.is_aggregator, + } + }) + .collect(); + + let message = + ValidatorSubscriptionMessage::AttestationSubscribe { subscriptions }; + if let Err(e) = validator_subscription_tx.try_send(message) { + warn!( + info = "the host may be overloaded or resource-constrained", + error = ?e, + "Unable to process committee subscriptions" + ); + return Err(warp_utils::reject::custom_server_error( + "unable to queue subscription, host may be overloaded or shutting down" + .to_string(), + )); + } + Ok(()) + }) + }, + ) + .boxed() +} + +pub fn post_validator_contribution_and_proofs( + eth_v1: EthV1Filter, + chain_filter: ChainFilter, + network_tx_filter: NetworkTxFilter, + not_while_syncing_filter: NotWhileSyncingFilter, + task_spawner_filter: TaskSpawnerFilter, +) -> ResponseFilter { + eth_v1 + .and(warp::path("validator")) + .and(warp::path("contribution_and_proofs")) + .and(warp::path::end()) + .and(not_while_syncing_filter.clone()) + .and(task_spawner_filter.clone()) + .and(chain_filter.clone()) + .and(warp_utils::json::json()) + .and(network_tx_filter.clone()) + .then( + |not_synced_filter: Result<(), Rejection>, + task_spawner: TaskSpawner, + chain: Arc>, + contributions: Vec>, + network_tx: UnboundedSender>| { + task_spawner.blocking_json_task(Priority::P0, move || { + not_synced_filter?; + sync_committees::process_signed_contribution_and_proofs( + contributions, + network_tx, + &chain, + )?; + Ok(GenericResponse::from(())) + }) + }, + ) + .boxed() +} + +// POST validator/aggregate_and_proofs +pub fn post_validator_aggregate_and_proofs( + any_version: AnyVersionFilter, + chain_filter: ChainFilter, + network_tx_filter: NetworkTxFilter, + not_while_syncing_filter: NotWhileSyncingFilter, + task_spawner_filter: TaskSpawnerFilter, +) -> ResponseFilter { + any_version + .and(warp::path("validator")) + .and(warp::path("aggregate_and_proofs")) + .and(warp::path::end()) + .and(not_while_syncing_filter.clone()) + .and(task_spawner_filter.clone()) + .and(chain_filter.clone()) + .and(warp_utils::json::json()) + .and(network_tx_filter.clone()) + .then( + // V1 and V2 are identical except V2 has a consensus version header in the request. + // We only require this header for SSZ deserialization, which isn't supported for + // this endpoint presently. + |_endpoint_version: EndpointVersion, + not_synced_filter: Result<(), Rejection>, + task_spawner: TaskSpawner, + chain: Arc>, + aggregates: Vec>, + network_tx: UnboundedSender>| { + task_spawner.blocking_json_task(Priority::P0, move || { + not_synced_filter?; + let seen_timestamp = timestamp_now(); + let mut verified_aggregates = Vec::with_capacity(aggregates.len()); + let mut messages = Vec::with_capacity(aggregates.len()); + let mut failures = Vec::new(); + + // Verify that all messages in the post are valid before processing further + for (index, aggregate) in aggregates.iter().enumerate() { + match chain.verify_aggregated_attestation_for_gossip(aggregate) { + Ok(verified_aggregate) => { + messages.push(PubsubMessage::AggregateAndProofAttestation(Box::new( + verified_aggregate.aggregate().clone(), + ))); + + // Notify the validator monitor. + chain + .validator_monitor + .read() + .register_api_aggregated_attestation( + seen_timestamp, + verified_aggregate.aggregate(), + verified_aggregate.indexed_attestation(), + &chain.slot_clock, + ); + + verified_aggregates.push((index, verified_aggregate)); + } + // If we already know the attestation, don't broadcast it or attempt to + // further verify it. Return success. + // + // It's reasonably likely that two different validators produce + // identical aggregates, especially if they're using the same beacon + // node. + Err(AttestationError::AttestationSupersetKnown(_)) => continue, + // If we've already seen this aggregator produce an aggregate, just + // skip this one. + // + // We're likely to see this with VCs that use fallback BNs. The first + // BN might time-out *after* publishing the aggregate and then the + // second BN will indicate it's already seen the aggregate. + // + // There's no actual error for the user or the network since the + // aggregate has been successfully published by some other node. + Err(AttestationError::AggregatorAlreadyKnown(_)) => continue, + Err(e) => { + error!( + error = ?e, + request_index = index, + aggregator_index = aggregate.message().aggregator_index(), + attestation_index = aggregate.message().aggregate().committee_index(), + attestation_slot = %aggregate.message().aggregate().data().slot, + "Failure verifying aggregate and proofs" + ); + failures.push(Failure::new(index, format!("Verification: {:?}", e))); + } + } + } + + // Publish aggregate attestations to the libp2p network + if !messages.is_empty() { + publish_network_message(&network_tx, NetworkMessage::Publish { messages })?; + } + + // Import aggregate attestations + for (index, verified_aggregate) in verified_aggregates { + if let Err(e) = chain.apply_attestation_to_fork_choice(&verified_aggregate) { + error!( + error = ?e, + request_index = index, + aggregator_index = verified_aggregate.aggregate().message().aggregator_index(), + attestation_index = verified_aggregate.attestation().committee_index(), + attestation_slot = %verified_aggregate.attestation().data().slot, + "Failure applying verified aggregate attestation to fork choice" + ); + failures.push(Failure::new(index, format!("Fork choice: {:?}", e))); + } + if let Err(e) = chain.add_to_block_inclusion_pool(verified_aggregate) { + warn!( + error = ?e, + request_index = index, + "Could not add verified aggregate attestation to the inclusion pool" + ); + failures.push(Failure::new(index, format!("Op pool: {:?}", e))); + } + } + + if !failures.is_empty() { + Err(warp_utils::reject::indexed_bad_request("error processing aggregate and proofs".to_string(), + failures, + )) + } else { + Ok(()) + } + }) + }, + ).boxed() +} + +// GET validator/duties/proposer/{epoch} +pub fn get_validator_duties_proposer( + eth_v1: EthV1Filter, + chain_filter: ChainFilter, + not_while_syncing_filter: NotWhileSyncingFilter, + task_spawner_filter: TaskSpawnerFilter, +) -> ResponseFilter { + eth_v1 + .and(warp::path("validator")) + .and(warp::path("duties")) + .and(warp::path("proposer")) + .and(warp::path::param::().or_else(|_| async { + Err(warp_utils::reject::custom_bad_request( + "Invalid epoch".to_string(), + )) + })) + .and(warp::path::end()) + .and(not_while_syncing_filter) + .and(task_spawner_filter) + .and(chain_filter) + .then( + |epoch: Epoch, + not_synced_filter: Result<(), Rejection>, + task_spawner: TaskSpawner, + chain: Arc>| { + task_spawner.blocking_json_task(Priority::P0, move || { + not_synced_filter?; + proposer_duties::proposer_duties(epoch, &chain) + }) + }, + ) + .boxed() +} diff --git a/beacon_node/http_api/src/version.rs b/beacon_node/http_api/src/version.rs index 871a10e7d4..371064c886 100644 --- a/beacon_node/http_api/src/version.rs +++ b/beacon_node/http_api/src/version.rs @@ -1,16 +1,14 @@ use crate::api_types::EndpointVersion; +use eth2::beacon_response::{ + BeaconResponse, ExecutionOptimisticFinalizedBeaconResponse, + ExecutionOptimisticFinalizedMetadata, ForkVersionedResponse, UnversionedResponse, +}; use eth2::{ CONSENSUS_BLOCK_VALUE_HEADER, CONSENSUS_VERSION_HEADER, CONTENT_TYPE_HEADER, EXECUTION_PAYLOAD_BLINDED_HEADER, EXECUTION_PAYLOAD_VALUE_HEADER, SSZ_CONTENT_TYPE_HEADER, }; use serde::Serialize; -use types::{ - BeaconResponse, ForkName, ForkVersionedResponse, InconsistentFork, Uint256, - UnversionedResponse, - beacon_response::{ - ExecutionOptimisticFinalizedBeaconResponse, ExecutionOptimisticFinalizedMetadata, - }, -}; +use types::{ForkName, InconsistentFork, Uint256}; use warp::reply::{self, Reply, Response}; pub const V1: EndpointVersion = EndpointVersion(1); diff --git a/beacon_node/lighthouse_network/src/rpc/codec.rs b/beacon_node/lighthouse_network/src/rpc/codec.rs index 77d2a34e16..5b3574d48a 100644 --- a/beacon_node/lighthouse_network/src/rpc/codec.rs +++ b/beacon_node/lighthouse_network/src/rpc/codec.rs @@ -8,7 +8,7 @@ use libp2p::bytes::BytesMut; use snap::read::FrameDecoder; use snap::write::FrameEncoder; use ssz::{Decode, Encode}; -use ssz_types::VariableList; +use ssz_types::{RuntimeVariableList, VariableList}; use std::io::Cursor; use std::io::ErrorKind; use std::io::{Read, Write}; @@ -18,10 +18,10 @@ use tokio_util::codec::{Decoder, Encoder}; use types::{ BlobSidecar, ChainSpec, DataColumnSidecar, DataColumnsByRootIdentifier, EthSpec, ForkContext, ForkName, Hash256, LightClientBootstrap, LightClientFinalityUpdate, - LightClientOptimisticUpdate, LightClientUpdate, RuntimeVariableList, SignedBeaconBlock, - SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockBellatrix, - SignedBeaconBlockCapella, SignedBeaconBlockDeneb, SignedBeaconBlockElectra, - SignedBeaconBlockFulu, SignedBeaconBlockGloas, + LightClientOptimisticUpdate, LightClientUpdate, SignedBeaconBlock, SignedBeaconBlockAltair, + SignedBeaconBlockBase, SignedBeaconBlockBellatrix, SignedBeaconBlockCapella, + SignedBeaconBlockDeneb, SignedBeaconBlockElectra, SignedBeaconBlockFulu, + SignedBeaconBlockGloas, }; use unsigned_varint::codec::Uvi; diff --git a/beacon_node/lighthouse_network/src/rpc/methods.rs b/beacon_node/lighthouse_network/src/rpc/methods.rs index 9aab079952..a9b4aa2fba 100644 --- a/beacon_node/lighthouse_network/src/rpc/methods.rs +++ b/beacon_node/lighthouse_network/src/rpc/methods.rs @@ -5,7 +5,7 @@ use regex::bytes::Regex; use serde::Serialize; use ssz::Encode; use ssz_derive::{Decode, Encode}; -use ssz_types::{VariableList, typenum::U256}; +use ssz_types::{RuntimeVariableList, VariableList, typenum::U256}; use std::fmt::Display; use std::marker::PhantomData; use std::ops::Deref; @@ -17,7 +17,7 @@ use types::light_client_update::MAX_REQUEST_LIGHT_CLIENT_UPDATES; use types::{ ChainSpec, ColumnIndex, DataColumnSidecar, DataColumnsByRootIdentifier, Epoch, EthSpec, ForkContext, Hash256, LightClientBootstrap, LightClientFinalityUpdate, - LightClientOptimisticUpdate, LightClientUpdate, RuntimeVariableList, SignedBeaconBlock, Slot, + LightClientOptimisticUpdate, LightClientUpdate, SignedBeaconBlock, Slot, blob_sidecar::BlobSidecar, }; diff --git a/beacon_node/lighthouse_network/tests/rpc_tests.rs b/beacon_node/lighthouse_network/tests/rpc_tests.rs index 60e3e3da97..8613edf5f5 100644 --- a/beacon_node/lighthouse_network/tests/rpc_tests.rs +++ b/beacon_node/lighthouse_network/tests/rpc_tests.rs @@ -7,7 +7,7 @@ use lighthouse_network::rpc::{RequestType, methods::*}; use lighthouse_network::service::api_types::AppRequestId; use lighthouse_network::{NetworkEvent, ReportSource, Response}; use ssz::Encode; -use ssz_types::VariableList; +use ssz_types::{RuntimeVariableList, VariableList}; use std::sync::Arc; use std::time::{Duration, Instant}; use tokio::runtime::Runtime; @@ -17,7 +17,7 @@ use types::{ BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockBellatrix, BeaconBlockHeader, BlobSidecar, ChainSpec, DataColumnSidecar, DataColumnsByRootIdentifier, EmptyBlock, Epoch, EthSpec, FixedBytesExtended, ForkName, Hash256, KzgCommitment, KzgProof, MinimalEthSpec, - RuntimeVariableList, Signature, SignedBeaconBlock, SignedBeaconBlockHeader, Slot, + Signature, SignedBeaconBlock, SignedBeaconBlockHeader, Slot, }; type E = MinimalEthSpec; diff --git a/beacon_node/network/src/network_beacon_processor/tests.rs b/beacon_node/network/src/network_beacon_processor/tests.rs index d83059ad27..841a8679cf 100644 --- a/beacon_node/network/src/network_beacon_processor/tests.rs +++ b/beacon_node/network/src/network_beacon_processor/tests.rs @@ -33,6 +33,7 @@ use lighthouse_network::{ }; use matches::assert_matches; use slot_clock::SlotClock; +use ssz_types::RuntimeVariableList; use std::collections::HashSet; use std::iter::Iterator; use std::sync::Arc; @@ -42,8 +43,8 @@ use types::blob_sidecar::{BlobIdentifier, FixedBlobSidecarList}; use types::{ AttesterSlashing, BlobSidecar, BlobSidecarList, ChainSpec, DataColumnSidecarList, DataColumnSubnetId, Epoch, EthSpec, Hash256, MainnetEthSpec, ProposerSlashing, - RuntimeVariableList, SignedAggregateAndProof, SignedBeaconBlock, SignedVoluntaryExit, - SingleAttestation, Slot, SubnetId, + SignedAggregateAndProof, SignedBeaconBlock, SignedVoluntaryExit, SingleAttestation, Slot, + SubnetId, }; type E = MainnetEthSpec; diff --git a/beacon_node/network/src/sync/block_sidecar_coupling.rs b/beacon_node/network/src/sync/block_sidecar_coupling.rs index 01929cbf90..ed9a11a03d 100644 --- a/beacon_node/network/src/sync/block_sidecar_coupling.rs +++ b/beacon_node/network/src/sync/block_sidecar_coupling.rs @@ -7,11 +7,12 @@ use lighthouse_network::{ BlobsByRangeRequestId, BlocksByRangeRequestId, DataColumnsByRangeRequestId, }, }; +use ssz_types::RuntimeVariableList; use std::{collections::HashMap, sync::Arc}; use tracing::{Span, debug}; use types::{ BlobSidecar, ChainSpec, ColumnIndex, DataColumnSidecar, DataColumnSidecarList, EthSpec, - Hash256, RuntimeVariableList, SignedBeaconBlock, + Hash256, SignedBeaconBlock, }; use crate::sync::network_context::MAX_COLUMN_RETRIES; diff --git a/beacon_node/network/src/sync/tests/lookups.rs b/beacon_node/network/src/sync/tests/lookups.rs index 63bcd176f5..ef52f89678 100644 --- a/beacon_node/network/src/sync/tests/lookups.rs +++ b/beacon_node/network/src/sync/tests/lookups.rs @@ -1929,8 +1929,8 @@ mod deneb_only { block_verification_types::{AsBlock, RpcBlock}, data_availability_checker::AvailabilityCheckError, }; + use ssz_types::RuntimeVariableList; use std::collections::VecDeque; - use types::RuntimeVariableList; struct DenebTester { rig: TestRig, diff --git a/common/deposit_contract/build.rs b/common/deposit_contract/build.rs index cae1d480c8..2061d13c24 100644 --- a/common/deposit_contract/build.rs +++ b/common/deposit_contract/build.rs @@ -153,14 +153,13 @@ fn verify_checksum(bytes: &[u8], expected_checksum: &str) { /// Returns the directory that will be used to store the deposit contract ABI. fn abi_dir() -> PathBuf { - let base = env::var("CARGO_MANIFEST_DIR") - .expect("should know manifest dir") + let base = env::var("OUT_DIR") + .expect("should know out dir") .parse::() - .expect("should parse manifest dir as path") - .join("contracts"); + .expect("should parse out dir as path"); std::fs::create_dir_all(base.clone()) - .expect("should be able to create abi directory in manifest"); + .expect("should be able to create abi directory in out dir"); base } diff --git a/common/deposit_contract/src/lib.rs b/common/deposit_contract/src/lib.rs index 12c3bdaa89..e5f11bb89c 100644 --- a/common/deposit_contract/src/lib.rs +++ b/common/deposit_contract/src/lib.rs @@ -44,15 +44,25 @@ impl From for Error { pub const CONTRACT_DEPLOY_GAS: usize = 4_000_000; pub const DEPOSIT_GAS: usize = 400_000; -pub const ABI: &[u8] = include_bytes!("../contracts/v0.12.1_validator_registration.json"); -pub const BYTECODE: &[u8] = include_bytes!("../contracts/v0.12.1_validator_registration.bytecode"); +pub const ABI: &[u8] = include_bytes!(concat!( + env!("OUT_DIR"), + "/v0.12.1_validator_registration.json" +)); +pub const BYTECODE: &[u8] = include_bytes!(concat!( + env!("OUT_DIR"), + "/v0.12.1_validator_registration.bytecode" +)); pub const DEPOSIT_DATA_LEN: usize = 420; // lol pub mod testnet { - pub const ABI: &[u8] = - include_bytes!("../contracts/v0.12.1_testnet_validator_registration.json"); - pub const BYTECODE: &[u8] = - include_bytes!("../contracts/v0.12.1_testnet_validator_registration.bytecode"); + pub const ABI: &[u8] = include_bytes!(concat!( + env!("OUT_DIR"), + "/v0.12.1_testnet_validator_registration.json" + )); + pub const BYTECODE: &[u8] = include_bytes!(concat!( + env!("OUT_DIR"), + "/v0.12.1_testnet_validator_registration.bytecode" + )); } pub fn encode_eth1_tx_data(deposit_data: &DepositData) -> Result, Error> { diff --git a/common/eth2/Cargo.toml b/common/eth2/Cargo.toml index 7a75bdc80a..f7e6cde210 100644 --- a/common/eth2/Cargo.toml +++ b/common/eth2/Cargo.toml @@ -9,6 +9,7 @@ default = ["lighthouse"] lighthouse = [] [dependencies] +context_deserialize = { workspace = true } educe = { workspace = true } eip_3076 = { workspace = true } either = { workspace = true } diff --git a/consensus/types/src/beacon_response.rs b/common/eth2/src/beacon_response.rs similarity index 97% rename from consensus/types/src/beacon_response.rs rename to common/eth2/src/beacon_response.rs index fc59fc9432..d58734997c 100644 --- a/consensus/types/src/beacon_response.rs +++ b/common/eth2/src/beacon_response.rs @@ -1,12 +1,8 @@ -use crate::{ContextDeserialize, ForkName}; +use context_deserialize::ContextDeserialize; use serde::de::DeserializeOwned; use serde::{Deserialize, Deserializer, Serialize}; use serde_json::value::Value; - -pub trait ForkVersionDecode: Sized { - /// SSZ decode with explicit fork variant. - fn from_ssz_bytes_by_fork(bytes: &[u8], fork_name: ForkName) -> Result; -} +use types::ForkName; /// The metadata of type M should be set to `EmptyMetadata` if you don't care about adding fields other than /// version. If you *do* care about adding other fields you can mix in any type that implements diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index bcd979daca..4e832a11df 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -7,6 +7,7 @@ //! Eventually it would be ideal to publish this crate on crates.io, however we have some local //! dependencies preventing this presently. +pub mod beacon_response; pub mod error; #[cfg(feature = "lighthouse")] pub mod lighthouse; @@ -15,10 +16,14 @@ pub mod lighthouse_vc; pub mod mixin; pub mod types; +pub use beacon_response::{ + BeaconResponse, EmptyMetadata, ExecutionOptimisticFinalizedBeaconResponse, + ExecutionOptimisticFinalizedMetadata, ForkVersionedResponse, UnversionedResponse, +}; + pub use self::error::{Error, ok_or_error, success_or_error}; use self::mixin::{RequestAccept, ResponseOptional}; use self::types::*; -use ::types::beacon_response::ExecutionOptimisticFinalizedBeaconResponse; use educe::Educe; use futures::Stream; use futures_util::StreamExt; diff --git a/common/eth2/src/lighthouse_vc/types.rs b/common/eth2/src/lighthouse_vc/types.rs index 4407e30e43..8e1d90f8f9 100644 --- a/common/eth2/src/lighthouse_vc/types.rs +++ b/common/eth2/src/lighthouse_vc/types.rs @@ -2,7 +2,6 @@ pub use crate::lighthouse::Health; pub use crate::lighthouse_vc::std_types::*; pub use crate::types::{GenericResponse, VersionData}; use eth2_keystore::Keystore; -use graffiti::GraffitiString; use serde::{Deserialize, Serialize}; use std::path::PathBuf; pub use types::*; diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index 6aad00301a..cbdaa004d0 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -19,10 +19,15 @@ use std::str::FromStr; use std::sync::Arc; use std::time::Duration; use test_random_derive::TestRandom; -use types::beacon_block_body::KzgCommitments; use types::test_utils::TestRandom; pub use types::*; +// TODO(mac): Temporary module and re-export hack to expose old `consensus/types` via `eth2/types`. +pub use crate::beacon_response::*; +pub mod beacon_response { + pub use crate::beacon_response::*; +} + #[cfg(feature = "lighthouse")] use crate::lighthouse::BlockReward; diff --git a/consensus/types/Cargo.toml b/consensus/types/Cargo.toml index 1f527c0de8..559a181948 100644 --- a/consensus/types/Cargo.toml +++ b/consensus/types/Cargo.toml @@ -73,6 +73,9 @@ paste = { workspace = true } state_processing = { workspace = true } tokio = { workspace = true } +[lints.clippy] +module_inception = "allow" + [[bench]] name = "benches" harness = false diff --git a/consensus/types/src/aggregate_and_proof.rs b/consensus/types/src/attestation/aggregate_and_proof.rs similarity index 93% rename from consensus/types/src/aggregate_and_proof.rs rename to consensus/types/src/attestation/aggregate_and_proof.rs index e76ba48bf4..4c6e775e56 100644 --- a/consensus/types/src/aggregate_and_proof.rs +++ b/consensus/types/src/attestation/aggregate_and_proof.rs @@ -1,17 +1,20 @@ -use super::{AttestationBase, AttestationElectra, AttestationRef}; -use super::{ - ChainSpec, Domain, EthSpec, Fork, ForkName, Hash256, PublicKey, SecretKey, SelectionProof, - Signature, SignedRoot, -}; -use crate::Attestation; -use crate::context_deserialize; -use crate::test_utils::TestRandom; +use bls::{PublicKey, SecretKey, Signature}; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use superstruct::superstruct; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + attestation::{ + Attestation, AttestationBase, AttestationElectra, AttestationRef, SelectionProof, + }, + core::{ChainSpec, Domain, EthSpec, Hash256, SignedRoot}, + fork::{Fork, ForkName}, + test_utils::TestRandom, +}; + #[superstruct( variants(Base, Electra), variant_attributes( diff --git a/consensus/types/src/attestation.rs b/consensus/types/src/attestation/attestation.rs similarity index 97% rename from consensus/types/src/attestation.rs rename to consensus/types/src/attestation/attestation.rs index 1430582658..693b5889f5 100644 --- a/consensus/types/src/attestation.rs +++ b/consensus/types/src/attestation/attestation.rs @@ -1,23 +1,28 @@ -use super::{ - AggregateSignature, AttestationData, BitList, ChainSpec, Domain, EthSpec, Fork, SecretKey, - Signature, SignedRoot, +use std::{ + collections::HashSet, + hash::{Hash, Hasher}, }; -use crate::slot_data::SlotData; -use crate::{ - Checkpoint, ContextDeserialize, ForkName, IndexedAttestationBase, IndexedAttestationElectra, -}; -use crate::{Hash256, Slot, test_utils::TestRandom}; -use crate::{IndexedAttestation, context_deserialize}; + +use bls::{AggregateSignature, SecretKey, Signature}; +use context_deserialize::{ContextDeserialize, context_deserialize}; use educe::Educe; use serde::{Deserialize, Deserializer, Serialize}; use ssz_derive::{Decode, Encode}; -use ssz_types::BitVector; -use std::collections::HashSet; -use std::hash::{Hash, Hasher}; +use ssz_types::{BitList, BitVector}; use superstruct::superstruct; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + attestation::{ + AttestationData, Checkpoint, IndexedAttestation, IndexedAttestationBase, + IndexedAttestationElectra, + }, + core::{ChainSpec, Domain, EthSpec, Hash256, SignedRoot, Slot, SlotData}, + fork::{Fork, ForkName}, + test_utils::TestRandom, +}; + #[derive(Debug, PartialEq, Clone)] pub enum Error { SszTypesError(ssz_types::Error), diff --git a/consensus/types/src/attestation_data.rs b/consensus/types/src/attestation/attestation_data.rs similarity index 87% rename from consensus/types/src/attestation_data.rs rename to consensus/types/src/attestation/attestation_data.rs index a4643e5474..f3fceb9b70 100644 --- a/consensus/types/src/attestation_data.rs +++ b/consensus/types/src/attestation/attestation_data.rs @@ -1,11 +1,16 @@ -use crate::slot_data::SlotData; -use crate::test_utils::TestRandom; -use crate::{Checkpoint, ForkName, Hash256, SignedRoot, Slot}; use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; + +use crate::{ + attestation::Checkpoint, + core::{Hash256, SignedRoot, Slot, SlotData}, + fork::ForkName, + test_utils::TestRandom, +}; + /// The data upon which an attestation is based. /// /// Spec v0.12.1 diff --git a/consensus/types/src/attestation_duty.rs b/consensus/types/src/attestation/attestation_duty.rs similarity index 92% rename from consensus/types/src/attestation_duty.rs rename to consensus/types/src/attestation/attestation_duty.rs index 70c7c5c170..fe3da79a2b 100644 --- a/consensus/types/src/attestation_duty.rs +++ b/consensus/types/src/attestation/attestation_duty.rs @@ -1,6 +1,7 @@ -use crate::*; use serde::{Deserialize, Serialize}; +use crate::{attestation::CommitteeIndex, core::Slot}; + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Debug, PartialEq, Clone, Copy, Default, Serialize, Deserialize)] pub struct AttestationDuty { diff --git a/consensus/types/src/beacon_committee.rs b/consensus/types/src/attestation/beacon_committee.rs similarity index 92% rename from consensus/types/src/beacon_committee.rs rename to consensus/types/src/attestation/beacon_committee.rs index 04fe763a11..2dba30bad3 100644 --- a/consensus/types/src/beacon_committee.rs +++ b/consensus/types/src/attestation/beacon_committee.rs @@ -1,4 +1,4 @@ -use crate::*; +use crate::{attestation::CommitteeIndex, core::Slot}; #[derive(Default, Clone, Debug, PartialEq)] pub struct BeaconCommittee<'a> { diff --git a/consensus/types/src/checkpoint.rs b/consensus/types/src/attestation/checkpoint.rs similarity index 88% rename from consensus/types/src/checkpoint.rs rename to consensus/types/src/attestation/checkpoint.rs index 545af59985..f5a95f0ad9 100644 --- a/consensus/types/src/checkpoint.rs +++ b/consensus/types/src/attestation/checkpoint.rs @@ -1,11 +1,15 @@ -use crate::test_utils::TestRandom; -use crate::{Epoch, ForkName, Hash256}; use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{Epoch, Hash256}, + fork::ForkName, + test_utils::TestRandom, +}; + /// Casper FFG checkpoint, used in attestations. /// /// Spec v0.12.1 diff --git a/consensus/types/src/indexed_attestation.rs b/consensus/types/src/attestation/indexed_attestation.rs similarity index 96% rename from consensus/types/src/indexed_attestation.rs rename to consensus/types/src/attestation/indexed_attestation.rs index dc32884217..272b015d90 100644 --- a/consensus/types/src/indexed_attestation.rs +++ b/consensus/types/src/attestation/indexed_attestation.rs @@ -1,17 +1,21 @@ -use crate::context_deserialize; -use crate::{ - AggregateSignature, AttestationData, EthSpec, ForkName, VariableList, test_utils::TestRandom, +use std::{ + hash::{Hash, Hasher}, + slice::Iter, }; -use core::slice::Iter; + +use bls::AggregateSignature; +use context_deserialize::context_deserialize; use educe::Educe; use serde::{Deserialize, Serialize}; use ssz::Encode; use ssz_derive::{Decode, Encode}; -use std::hash::{Hash, Hasher}; +use ssz_types::VariableList; use superstruct::superstruct; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{attestation::AttestationData, core::EthSpec, fork::ForkName, test_utils::TestRandom}; + /// Details an attestation that can be slashable. /// /// To be included in an `AttesterSlashing`. @@ -208,9 +212,10 @@ impl Hash for IndexedAttestation { #[cfg(test)] mod tests { use super::*; - use crate::MainnetEthSpec; - use crate::slot_epoch::Epoch; - use crate::test_utils::{SeedableRng, XorShiftRng}; + use crate::{ + core::{Epoch, MainnetEthSpec}, + test_utils::{SeedableRng, XorShiftRng}, + }; #[test] pub fn test_is_double_vote_true() { diff --git a/consensus/types/src/attestation/mod.rs b/consensus/types/src/attestation/mod.rs new file mode 100644 index 0000000000..2d2bf74e49 --- /dev/null +++ b/consensus/types/src/attestation/mod.rs @@ -0,0 +1,39 @@ +mod aggregate_and_proof; +mod attestation; +mod attestation_data; +mod attestation_duty; +mod beacon_committee; +mod checkpoint; +mod indexed_attestation; +mod participation_flags; +mod pending_attestation; +mod selection_proof; +mod shuffling_id; +mod signed_aggregate_and_proof; +mod subnet_id; + +pub use aggregate_and_proof::{ + AggregateAndProof, AggregateAndProofBase, AggregateAndProofElectra, AggregateAndProofRef, +}; +pub use attestation::{ + Attestation, AttestationBase, AttestationElectra, AttestationOnDisk, AttestationRef, + AttestationRefMut, AttestationRefOnDisk, Error as AttestationError, SingleAttestation, +}; +pub use attestation_data::AttestationData; +pub use attestation_duty::AttestationDuty; +pub use beacon_committee::{BeaconCommittee, OwnedBeaconCommittee}; +pub use checkpoint::Checkpoint; +pub use indexed_attestation::{ + IndexedAttestation, IndexedAttestationBase, IndexedAttestationElectra, IndexedAttestationRef, +}; +pub use participation_flags::ParticipationFlags; +pub use pending_attestation::PendingAttestation; +pub use selection_proof::SelectionProof; +pub use shuffling_id::AttestationShufflingId; +pub use signed_aggregate_and_proof::{ + SignedAggregateAndProof, SignedAggregateAndProofBase, SignedAggregateAndProofElectra, + SignedAggregateAndProofRefMut, +}; +pub use subnet_id::SubnetId; + +pub type CommitteeIndex = u64; diff --git a/consensus/types/src/participation_flags.rs b/consensus/types/src/attestation/participation_flags.rs similarity index 96% rename from consensus/types/src/participation_flags.rs rename to consensus/types/src/attestation/participation_flags.rs index e59efc5170..66831abfac 100644 --- a/consensus/types/src/participation_flags.rs +++ b/consensus/types/src/attestation/participation_flags.rs @@ -1,10 +1,14 @@ -use crate::{Hash256, consts::altair::NUM_FLAG_INDICES, test_utils::TestRandom}; use safe_arith::{ArithError, SafeArith}; use serde::{Deserialize, Serialize}; use ssz::{Decode, DecodeError, Encode}; use test_random_derive::TestRandom; use tree_hash::{PackedEncoding, TreeHash, TreeHashType}; +use crate::{ + core::{Hash256, consts::altair::NUM_FLAG_INDICES}, + test_utils::TestRandom, +}; + #[derive(Debug, Default, Clone, Copy, PartialEq, Deserialize, Serialize, TestRandom)] #[serde(transparent)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] diff --git a/consensus/types/src/pending_attestation.rs b/consensus/types/src/attestation/pending_attestation.rs similarity index 84% rename from consensus/types/src/pending_attestation.rs rename to consensus/types/src/attestation/pending_attestation.rs index 4a00a0495a..84353ac118 100644 --- a/consensus/types/src/pending_attestation.rs +++ b/consensus/types/src/attestation/pending_attestation.rs @@ -1,11 +1,12 @@ -use crate::context_deserialize; -use crate::test_utils::TestRandom; -use crate::{AttestationData, BitList, EthSpec, ForkName}; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; +use ssz_types::BitList; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{attestation::AttestationData, core::EthSpec, fork::ForkName, test_utils::TestRandom}; + /// An attestation that has been included in the state but not yet fully processed. /// /// Spec v0.12.1 diff --git a/consensus/types/src/selection_proof.rs b/consensus/types/src/attestation/selection_proof.rs similarity index 95% rename from consensus/types/src/selection_proof.rs rename to consensus/types/src/attestation/selection_proof.rs index aa8c0c5658..b4c48d0078 100644 --- a/consensus/types/src/selection_proof.rs +++ b/consensus/types/src/attestation/selection_proof.rs @@ -1,11 +1,15 @@ -use crate::{ - ChainSpec, Domain, EthSpec, Fork, Hash256, PublicKey, SecretKey, Signature, SignedRoot, Slot, -}; +use std::cmp; + +use bls::{PublicKey, SecretKey, Signature}; use ethereum_hashing::hash; use safe_arith::{ArithError, SafeArith}; use serde::{Deserialize, Serialize}; use ssz::Encode; -use std::cmp; + +use crate::{ + core::{ChainSpec, Domain, EthSpec, Hash256, SignedRoot, Slot}, + fork::Fork, +}; #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] diff --git a/consensus/types/src/shuffling_id.rs b/consensus/types/src/attestation/shuffling_id.rs similarity index 93% rename from consensus/types/src/shuffling_id.rs rename to consensus/types/src/attestation/shuffling_id.rs index df16f605ed..25217288f6 100644 --- a/consensus/types/src/shuffling_id.rs +++ b/consensus/types/src/attestation/shuffling_id.rs @@ -1,7 +1,12 @@ -use crate::*; +use std::hash::Hash; + use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; -use std::hash::Hash; + +use crate::{ + core::{Epoch, EthSpec, Hash256, RelativeEpoch}, + state::{BeaconState, BeaconStateError}, +}; /// Can be used to key (ID) the shuffling in some chain, in some epoch. /// diff --git a/consensus/types/src/signed_aggregate_and_proof.rs b/consensus/types/src/attestation/signed_aggregate_and_proof.rs similarity index 90% rename from consensus/types/src/signed_aggregate_and_proof.rs rename to consensus/types/src/attestation/signed_aggregate_and_proof.rs index 758ac2734b..48c3f4c567 100644 --- a/consensus/types/src/signed_aggregate_and_proof.rs +++ b/consensus/types/src/attestation/signed_aggregate_and_proof.rs @@ -1,18 +1,21 @@ -use super::{ - AggregateAndProof, AggregateAndProofBase, AggregateAndProofElectra, AggregateAndProofRef, -}; -use super::{ - Attestation, AttestationRef, ChainSpec, Domain, EthSpec, Fork, ForkName, Hash256, SecretKey, - SelectionProof, Signature, SignedRoot, -}; -use crate::context_deserialize; -use crate::test_utils::TestRandom; +use bls::{SecretKey, Signature}; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use superstruct::superstruct; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + attestation::{ + AggregateAndProof, AggregateAndProofBase, AggregateAndProofElectra, AggregateAndProofRef, + Attestation, AttestationRef, SelectionProof, + }, + core::{ChainSpec, Domain, EthSpec, Hash256, SignedRoot}, + fork::{Fork, ForkName}, + test_utils::TestRandom, +}; + /// A Validators signed aggregate proof to publish on the `beacon_aggregate_and_proof` /// gossipsub topic. /// diff --git a/consensus/types/src/subnet_id.rs b/consensus/types/src/attestation/subnet_id.rs similarity index 97% rename from consensus/types/src/subnet_id.rs rename to consensus/types/src/attestation/subnet_id.rs index 6ec8ca4a27..9585d077b5 100644 --- a/consensus/types/src/subnet_id.rs +++ b/consensus/types/src/attestation/subnet_id.rs @@ -1,11 +1,17 @@ //! Identifies each shard by an integer identifier. -use crate::SingleAttestation; -use crate::{AttestationRef, ChainSpec, CommitteeIndex, EthSpec, Slot}; +use std::{ + ops::{Deref, DerefMut}, + sync::LazyLock, +}; + use alloy_primitives::{U256, bytes::Buf}; use safe_arith::{ArithError, SafeArith}; use serde::{Deserialize, Serialize}; -use std::ops::{Deref, DerefMut}; -use std::sync::LazyLock; + +use crate::{ + attestation::{AttestationRef, CommitteeIndex, SingleAttestation}, + core::{ChainSpec, EthSpec, Slot}, +}; const MAX_SUBNET_ID: usize = 64; diff --git a/consensus/types/src/beacon_block.rs b/consensus/types/src/block/beacon_block.rs similarity index 97% rename from consensus/types/src/beacon_block.rs rename to consensus/types/src/block/beacon_block.rs index 060709d655..c2f361eb4b 100644 --- a/consensus/types/src/beacon_block.rs +++ b/consensus/types/src/block/beacon_block.rs @@ -1,18 +1,39 @@ -use crate::attestation::AttestationBase; -use crate::test_utils::TestRandom; -use crate::*; +use std::{fmt, marker::PhantomData}; + +use bls::{AggregateSignature, PublicKeyBytes, SecretKey, Signature, SignatureBytes}; +use context_deserialize::ContextDeserialize; use educe::Educe; +use fixed_bytes::FixedBytesExtended; use serde::{Deserialize, Deserializer, Serialize}; use ssz::{Decode, DecodeError}; use ssz_derive::{Decode, Encode}; -use std::fmt; -use std::marker::PhantomData; +use ssz_types::{BitList, BitVector, FixedVector, VariableList, typenum::Unsigned}; use superstruct::superstruct; use test_random_derive::TestRandom; use tree_hash::TreeHash; use tree_hash_derive::TreeHash; -use self::indexed_attestation::IndexedAttestationBase; +use crate::{ + attestation::{AttestationBase, AttestationData, IndexedAttestationBase}, + block::{ + BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyBellatrix, + BeaconBlockBodyCapella, BeaconBlockBodyDeneb, BeaconBlockBodyElectra, BeaconBlockBodyFulu, + BeaconBlockBodyGloas, BeaconBlockBodyRef, BeaconBlockBodyRefMut, BeaconBlockHeader, + SignedBeaconBlock, SignedBeaconBlockHeader, + }, + core::{ChainSpec, Domain, Epoch, EthSpec, Graffiti, Hash256, SignedRoot, Slot}, + deposit::{Deposit, DepositData}, + execution::{ + AbstractExecPayload, BlindedPayload, Eth1Data, ExecutionPayload, ExecutionRequests, + FullPayload, + }, + exit::{SignedVoluntaryExit, VoluntaryExit}, + fork::{Fork, ForkName, InconsistentFork, map_fork_name}, + slashing::{AttesterSlashingBase, ProposerSlashing}, + state::BeaconStateError, + sync_committee::SyncAggregate, + test_utils::TestRandom, +}; /// A block of the `BeaconChain`. #[superstruct( @@ -283,7 +304,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockRef<'a, E, Payl /// Extracts a reference to an execution payload from a block, returning an error if the block /// is pre-merge. - pub fn execution_payload(&self) -> Result, Error> { + pub fn execution_payload(&self) -> Result, BeaconStateError> { self.body().execution_payload() } } @@ -865,7 +886,10 @@ impl fmt::Display for BlockImportSource { #[cfg(test)] mod tests { use super::*; - use crate::test_utils::{SeedableRng, XorShiftRng, test_ssz_tree_hash_pair_with}; + use crate::{ + core::MainnetEthSpec, + test_utils::{SeedableRng, XorShiftRng, test_ssz_tree_hash_pair_with}, + }; use ssz::Encode; type BeaconBlock = super::BeaconBlock; diff --git a/consensus/types/src/beacon_block_body.rs b/consensus/types/src/block/beacon_block_body.rs similarity index 93% rename from consensus/types/src/beacon_block_body.rs rename to consensus/types/src/block/beacon_block_body.rs index ced8fea4a9..f85dd8909e 100644 --- a/consensus/types/src/beacon_block_body.rs +++ b/consensus/types/src/block/beacon_block_body.rs @@ -1,18 +1,42 @@ -use crate::test_utils::TestRandom; -use crate::*; +use std::marker::PhantomData; + +use bls::Signature; +use context_deserialize::{ContextDeserialize, context_deserialize}; use educe::Educe; use merkle_proof::{MerkleTree, MerkleTreeError}; use metastruct::metastruct; use serde::{Deserialize, Deserializer, Serialize}; use ssz_derive::{Decode, Encode}; -use std::marker::PhantomData; +use ssz_types::{FixedVector, VariableList}; use superstruct::superstruct; use test_random_derive::TestRandom; use tree_hash::{BYTES_PER_CHUNK, TreeHash}; use tree_hash_derive::TreeHash; -pub type KzgCommitments = - VariableList::MaxBlobCommitmentsPerBlock>; +use crate::{ + attestation::{AttestationBase, AttestationElectra, AttestationRef, AttestationRefMut}, + core::{EthSpec, Graffiti, Hash256}, + deposit::Deposit, + execution::{ + AbstractExecPayload, BlindedPayload, BlindedPayloadBellatrix, BlindedPayloadCapella, + BlindedPayloadDeneb, BlindedPayloadElectra, BlindedPayloadFulu, BlindedPayloadGloas, + Eth1Data, ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella, + ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu, + ExecutionPayloadGloas, ExecutionRequests, FullPayload, FullPayloadBellatrix, + FullPayloadCapella, FullPayloadDeneb, FullPayloadElectra, FullPayloadFulu, + FullPayloadGloas, SignedBlsToExecutionChange, + }, + exit::SignedVoluntaryExit, + fork::{ForkName, map_fork_name}, + kzg_ext::KzgCommitments, + light_client::consts::{EXECUTION_PAYLOAD_INDEX, EXECUTION_PAYLOAD_PROOF_LEN}, + slashing::{ + AttesterSlashingBase, AttesterSlashingElectra, AttesterSlashingRef, ProposerSlashing, + }, + state::BeaconStateError, + sync_committee::SyncAggregate, + test_utils::TestRandom, +}; /// The number of leaves (including padding) on the `BeaconBlockBody` Merkle tree. /// @@ -63,8 +87,14 @@ pub const BLOB_KZG_COMMITMENTS_INDEX: usize = 11; Fulu(metastruct(mappings(beacon_block_body_fulu_fields(groups(fields))))), Gloas(metastruct(mappings(beacon_block_body_gloas_fields(groups(fields))))), ), - cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"), - partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant") + cast_error( + ty = "BeaconStateError", + expr = "BeaconStateError::IncorrectStateVariant" + ), + partial_getter_error( + ty = "BeaconStateError", + expr = "BeaconStateError::IncorrectStateVariant" + ) )] #[cfg_attr( feature = "arbitrary", @@ -147,7 +177,7 @@ pub struct BeaconBlockBody = FullPay } impl> BeaconBlockBody { - pub fn execution_payload(&self) -> Result, Error> { + pub fn execution_payload(&self) -> Result, BeaconStateError> { self.to_ref().execution_payload() } @@ -158,9 +188,9 @@ impl> BeaconBlockBody { } impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, Payload> { - pub fn execution_payload(&self) -> Result, Error> { + pub fn execution_payload(&self) -> Result, BeaconStateError> { match self { - Self::Base(_) | Self::Altair(_) => Err(Error::IncorrectStateVariant), + Self::Base(_) | Self::Altair(_) => Err(BeaconStateError::IncorrectStateVariant), Self::Bellatrix(body) => Ok(Payload::Ref::from(&body.execution_payload)), Self::Capella(body) => Ok(Payload::Ref::from(&body.execution_payload)), Self::Deneb(body) => Ok(Payload::Ref::from(&body.execution_payload)), @@ -216,7 +246,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, pub fn kzg_commitment_merkle_proof( &self, index: usize, - ) -> Result, Error> { + ) -> Result, BeaconStateError> { let kzg_commitments_proof = self.kzg_commitments_merkle_proof()?; let proof = self.complete_kzg_commitment_merkle_proof(index, &kzg_commitments_proof)?; Ok(proof) @@ -228,10 +258,10 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, &self, index: usize, kzg_commitments_proof: &[Hash256], - ) -> Result, Error> { + ) -> Result, BeaconStateError> { match self { Self::Base(_) | Self::Altair(_) | Self::Bellatrix(_) | Self::Capella(_) => { - Err(Error::IncorrectStateVariant) + Err(BeaconStateError::IncorrectStateVariant) } Self::Deneb(_) | Self::Electra(_) | Self::Fulu(_) | Self::Gloas(_) => { // We compute the branches by generating 2 merkle trees: @@ -253,7 +283,7 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, let tree = MerkleTree::create(&blob_leaves, depth as usize); let (_, mut proof) = tree .generate_proof(index, depth as usize) - .map_err(Error::MerkleTreeError)?; + .map_err(BeaconStateError::MerkleTreeError)?; // Add the branch corresponding to the length mix-in. let length = blob_leaves.len(); @@ -261,7 +291,9 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, let mut length_bytes = [0; BYTES_PER_CHUNK]; length_bytes .get_mut(0..usize_len) - .ok_or(Error::MerkleTreeError(MerkleTreeError::PleaseNotifyTheDevs))? + .ok_or(BeaconStateError::MerkleTreeError( + MerkleTreeError::PleaseNotifyTheDevs, + ))? .copy_from_slice(&length.to_le_bytes()); let length_root = Hash256::from_slice(length_bytes.as_slice()); proof.push(length_root); @@ -279,32 +311,41 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload> BeaconBlockBodyRef<'a, E, /// Produces the proof of inclusion for `self.blob_kzg_commitments`. pub fn kzg_commitments_merkle_proof( &self, - ) -> Result, Error> { + ) -> Result, BeaconStateError> { let body_leaves = self.body_merkle_leaves(); let beacon_block_body_depth = body_leaves.len().next_power_of_two().ilog2() as usize; let tree = MerkleTree::create(&body_leaves, beacon_block_body_depth); let (_, proof) = tree .generate_proof(BLOB_KZG_COMMITMENTS_INDEX, beacon_block_body_depth) - .map_err(Error::MerkleTreeError)?; + .map_err(BeaconStateError::MerkleTreeError)?; Ok(FixedVector::new(proof)?) } - pub fn block_body_merkle_proof(&self, generalized_index: usize) -> Result, Error> { + pub fn block_body_merkle_proof( + &self, + generalized_index: usize, + ) -> Result, BeaconStateError> { let field_index = match generalized_index { - light_client_update::EXECUTION_PAYLOAD_INDEX => { + EXECUTION_PAYLOAD_INDEX => { // Execution payload is a top-level field, subtract off the generalized indices // for the internal nodes. Result should be 9, the field offset of the execution // payload in the `BeaconBlockBody`: // https://github.com/ethereum/consensus-specs/blob/dev/specs/deneb/beacon-chain.md#beaconblockbody generalized_index .checked_sub(NUM_BEACON_BLOCK_BODY_HASH_TREE_ROOT_LEAVES) - .ok_or(Error::GeneralizedIndexNotSupported(generalized_index))? + .ok_or(BeaconStateError::GeneralizedIndexNotSupported( + generalized_index, + ))? + } + _ => { + return Err(BeaconStateError::GeneralizedIndexNotSupported( + generalized_index, + )); } - _ => return Err(Error::GeneralizedIndexNotSupported(generalized_index)), }; let leaves = self.body_merkle_leaves(); - let depth = light_client_update::EXECUTION_PAYLOAD_PROOF_LEN; + let depth = EXECUTION_PAYLOAD_PROOF_LEN; let tree = merkle_proof::MerkleTree::create(&leaves, depth); let (_, proof) = tree.generate_proof(field_index, depth)?; @@ -1100,22 +1141,16 @@ impl<'de, E: EthSpec, Payload: AbstractExecPayload> ContextDeserialize<'de, F } } -/// Util method helpful for logging. -pub fn format_kzg_commitments(commitments: &[KzgCommitment]) -> String { - let commitment_strings: Vec = commitments.iter().map(|x| x.to_string()).collect(); - let commitments_joined = commitment_strings.join(", "); - let surrounded_commitments = format!("[{}]", commitments_joined); - surrounded_commitments -} - #[cfg(test)] mod tests { mod base { use super::super::*; + use crate::core::MainnetEthSpec; ssz_and_tree_hash_tests!(BeaconBlockBodyBase); } mod altair { use super::super::*; + use crate::core::MainnetEthSpec; ssz_and_tree_hash_tests!(BeaconBlockBodyAltair); } } diff --git a/consensus/types/src/beacon_block_header.rs b/consensus/types/src/block/beacon_block_header.rs similarity index 90% rename from consensus/types/src/beacon_block_header.rs rename to consensus/types/src/block/beacon_block_header.rs index e14a9fc8af..06e1023d91 100644 --- a/consensus/types/src/beacon_block_header.rs +++ b/consensus/types/src/block/beacon_block_header.rs @@ -1,6 +1,4 @@ -use crate::test_utils::TestRandom; -use crate::*; - +use bls::SecretKey; use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; @@ -8,6 +6,13 @@ use test_random_derive::TestRandom; use tree_hash::TreeHash; use tree_hash_derive::TreeHash; +use crate::{ + block::SignedBeaconBlockHeader, + core::{ChainSpec, Domain, EthSpec, Hash256, SignedRoot, Slot}, + fork::{Fork, ForkName}, + test_utils::TestRandom, +}; + /// A header of a `BeaconBlock`. /// /// Spec v0.12.1 diff --git a/consensus/types/src/block/mod.rs b/consensus/types/src/block/mod.rs new file mode 100644 index 0000000000..81c8ffbd63 --- /dev/null +++ b/consensus/types/src/block/mod.rs @@ -0,0 +1,26 @@ +mod beacon_block; +mod beacon_block_body; +mod beacon_block_header; +mod signed_beacon_block; +mod signed_beacon_block_header; + +pub use beacon_block::{ + BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockBellatrix, BeaconBlockCapella, + BeaconBlockDeneb, BeaconBlockElectra, BeaconBlockFulu, BeaconBlockGloas, BeaconBlockRef, + BeaconBlockRefMut, BlindedBeaconBlock, BlockImportSource, EmptyBlock, +}; +pub use beacon_block_body::{ + BLOB_KZG_COMMITMENTS_INDEX, BeaconBlockBody, BeaconBlockBodyAltair, BeaconBlockBodyBase, + BeaconBlockBodyBellatrix, BeaconBlockBodyCapella, BeaconBlockBodyDeneb, BeaconBlockBodyElectra, + BeaconBlockBodyFulu, BeaconBlockBodyGloas, BeaconBlockBodyRef, BeaconBlockBodyRefMut, + NUM_BEACON_BLOCK_BODY_HASH_TREE_ROOT_LEAVES, +}; +pub use beacon_block_header::BeaconBlockHeader; + +pub use signed_beacon_block::{ + SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockBellatrix, + SignedBeaconBlockCapella, SignedBeaconBlockDeneb, SignedBeaconBlockElectra, + SignedBeaconBlockFulu, SignedBeaconBlockGloas, SignedBeaconBlockHash, SignedBlindedBeaconBlock, + ssz_tagged_signed_beacon_block, ssz_tagged_signed_beacon_block_arc, +}; +pub use signed_beacon_block_header::SignedBeaconBlockHeader; diff --git a/consensus/types/src/signed_beacon_block.rs b/consensus/types/src/block/signed_beacon_block.rs similarity index 95% rename from consensus/types/src/signed_beacon_block.rs rename to consensus/types/src/block/signed_beacon_block.rs index 7b04cc5771..e8927ee765 100644 --- a/consensus/types/src/signed_beacon_block.rs +++ b/consensus/types/src/block/signed_beacon_block.rs @@ -1,17 +1,42 @@ -use crate::beacon_block_body::{BLOB_KZG_COMMITMENTS_INDEX, format_kzg_commitments}; -use crate::test_utils::TestRandom; -use crate::*; +use std::fmt; + +use bls::{PublicKey, Signature}; +use context_deserialize::ContextDeserialize; use educe::Educe; use merkle_proof::MerkleTree; use serde::{Deserialize, Deserializer, Serialize}; use ssz_derive::{Decode, Encode}; -use std::fmt; +use ssz_types::FixedVector; use superstruct::superstruct; use test_random_derive::TestRandom; use tracing::instrument; use tree_hash::TreeHash; use tree_hash_derive::TreeHash; +use crate::{ + block::{ + BLOB_KZG_COMMITMENTS_INDEX, BeaconBlock, BeaconBlockAltair, BeaconBlockBase, + BeaconBlockBellatrix, BeaconBlockBodyBellatrix, BeaconBlockBodyCapella, + BeaconBlockBodyDeneb, BeaconBlockBodyElectra, BeaconBlockBodyFulu, BeaconBlockBodyGloas, + BeaconBlockCapella, BeaconBlockDeneb, BeaconBlockElectra, BeaconBlockFulu, + BeaconBlockGloas, BeaconBlockHeader, BeaconBlockRef, BeaconBlockRefMut, + SignedBeaconBlockHeader, + }, + core::{ChainSpec, Domain, Epoch, EthSpec, Hash256, SignedRoot, SigningData, Slot}, + execution::{ + AbstractExecPayload, BlindedPayload, BlindedPayloadBellatrix, BlindedPayloadCapella, + BlindedPayloadDeneb, BlindedPayloadElectra, BlindedPayloadFulu, BlindedPayloadGloas, + ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella, + ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu, + ExecutionPayloadGloas, FullPayload, FullPayloadBellatrix, FullPayloadCapella, + FullPayloadDeneb, FullPayloadElectra, FullPayloadFulu, FullPayloadGloas, + }, + fork::{Fork, ForkName, ForkVersionDecode, InconsistentFork, map_fork_name}, + kzg_ext::format_kzg_commitments, + state::BeaconStateError, + test_utils::TestRandom, +}; + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(PartialEq, Eq, Hash, Clone, Copy)] pub struct SignedBeaconBlockHash(Hash256); @@ -272,7 +297,7 @@ impl> SignedBeaconBlock SignedBeaconBlockHeader, FixedVector, ), - Error, + BeaconStateError, > { // Create the block body merkle tree let body_leaves = self.message().body().body_merkle_leaves(); @@ -282,7 +307,7 @@ impl> SignedBeaconBlock // Compute the KZG commitments inclusion proof let (_, proof) = body_merkle_tree .generate_proof(BLOB_KZG_COMMITMENTS_INDEX, beacon_block_body_depth) - .map_err(Error::MerkleTreeError)?; + .map_err(BeaconStateError::MerkleTreeError)?; let kzg_commitments_inclusion_proof = FixedVector::new(proof)?; let block_header = BeaconBlockHeader { @@ -919,6 +944,7 @@ pub mod ssz_tagged_signed_beacon_block_arc { #[cfg(test)] mod test { use super::*; + use crate::{block::EmptyBlock, core::MainnetEthSpec}; #[test] fn add_remove_payload_roundtrip() { diff --git a/consensus/types/src/signed_beacon_block_header.rs b/consensus/types/src/block/signed_beacon_block_header.rs similarity index 84% rename from consensus/types/src/signed_beacon_block_header.rs rename to consensus/types/src/block/signed_beacon_block_header.rs index 4a5ff2ec1a..2fcd8a705f 100644 --- a/consensus/types/src/signed_beacon_block_header.rs +++ b/consensus/types/src/block/signed_beacon_block_header.rs @@ -1,13 +1,17 @@ -use crate::context_deserialize; -use crate::{ - BeaconBlockHeader, ChainSpec, Domain, EthSpec, Fork, ForkName, Hash256, PublicKey, Signature, - SignedRoot, test_utils::TestRandom, -}; +use bls::{PublicKey, Signature}; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + block::BeaconBlockHeader, + core::{ChainSpec, Domain, EthSpec, Hash256, SignedRoot}, + fork::{Fork, ForkName}, + test_utils::TestRandom, +}; + /// A signed header of a `BeaconBlock`. /// /// Spec v0.12.1 diff --git a/consensus/types/src/builder_bid.rs b/consensus/types/src/builder/builder_bid.rs similarity index 93% rename from consensus/types/src/builder_bid.rs rename to consensus/types/src/builder/builder_bid.rs index 3fb7af35ca..be9bb28155 100644 --- a/consensus/types/src/builder_bid.rs +++ b/consensus/types/src/builder/builder_bid.rs @@ -1,13 +1,6 @@ -use crate::beacon_block_body::KzgCommitments; -use crate::{ - ChainSpec, ContextDeserialize, EthSpec, ExecutionPayloadHeaderBellatrix, - ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, - ExecutionPayloadHeaderFulu, ExecutionPayloadHeaderGloas, ExecutionPayloadHeaderRef, - ExecutionPayloadHeaderRefMut, ExecutionRequests, ForkName, ForkVersionDecode, SignedRoot, - Uint256, test_utils::TestRandom, -}; use bls::PublicKeyBytes; use bls::Signature; +use context_deserialize::ContextDeserialize; use serde::{Deserialize, Deserializer, Serialize}; use ssz::Decode; use ssz_derive::{Decode, Encode}; @@ -15,6 +8,19 @@ use superstruct::superstruct; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{ChainSpec, EthSpec, SignedRoot, Uint256}, + execution::{ + ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella, + ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu, + ExecutionPayloadHeaderGloas, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, + ExecutionRequests, + }, + fork::{ForkName, ForkVersionDecode}, + kzg_ext::KzgCommitments, + test_utils::TestRandom, +}; + #[superstruct( variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( diff --git a/consensus/types/src/builder/mod.rs b/consensus/types/src/builder/mod.rs new file mode 100644 index 0000000000..88a8e6a01a --- /dev/null +++ b/consensus/types/src/builder/mod.rs @@ -0,0 +1,6 @@ +mod builder_bid; + +pub use builder_bid::{ + BuilderBid, BuilderBidBellatrix, BuilderBidCapella, BuilderBidDeneb, BuilderBidElectra, + BuilderBidFulu, BuilderBidGloas, SignedBuilderBid, +}; diff --git a/consensus/types/src/consolidation_request.rs b/consensus/types/src/consolidation/consolidation_request.rs similarity index 84% rename from consensus/types/src/consolidation_request.rs rename to consensus/types/src/consolidation/consolidation_request.rs index 2af3426b68..3f09517a90 100644 --- a/consensus/types/src/consolidation_request.rs +++ b/consensus/types/src/consolidation/consolidation_request.rs @@ -1,11 +1,17 @@ -use crate::context_deserialize; -use crate::{Address, ForkName, PublicKeyBytes, SignedRoot, test_utils::TestRandom}; +use bls::PublicKeyBytes; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz::Encode; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{Address, SignedRoot}, + fork::ForkName, + test_utils::TestRandom, +}; + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, diff --git a/consensus/types/src/consolidation/mod.rs b/consensus/types/src/consolidation/mod.rs new file mode 100644 index 0000000000..a6a2f4a331 --- /dev/null +++ b/consensus/types/src/consolidation/mod.rs @@ -0,0 +1,5 @@ +mod consolidation_request; +mod pending_consolidation; + +pub use consolidation_request::ConsolidationRequest; +pub use pending_consolidation::PendingConsolidation; diff --git a/consensus/types/src/pending_consolidation.rs b/consensus/types/src/consolidation/pending_consolidation.rs similarity index 86% rename from consensus/types/src/pending_consolidation.rs rename to consensus/types/src/consolidation/pending_consolidation.rs index 9fb8c3566d..fcd76e43b6 100644 --- a/consensus/types/src/pending_consolidation.rs +++ b/consensus/types/src/consolidation/pending_consolidation.rs @@ -1,11 +1,11 @@ -use crate::ForkName; -use crate::context_deserialize; -use crate::test_utils::TestRandom; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{fork::ForkName, test_utils::TestRandom}; + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, diff --git a/consensus/types/src/application_domain.rs b/consensus/types/src/core/application_domain.rs similarity index 100% rename from consensus/types/src/application_domain.rs rename to consensus/types/src/core/application_domain.rs diff --git a/consensus/types/src/chain_spec.rs b/consensus/types/src/core/chain_spec.rs similarity index 99% rename from consensus/types/src/chain_spec.rs rename to consensus/types/src/core/chain_spec.rs index a66080ada6..c8052b502b 100644 --- a/consensus/types/src/chain_spec.rs +++ b/consensus/types/src/core/chain_spec.rs @@ -1,19 +1,27 @@ -use crate::application_domain::{APPLICATION_DOMAIN_BUILDER, ApplicationDomain}; -use crate::blob_sidecar::BlobIdentifier; -use crate::data_column_sidecar::DataColumnsByRootIdentifier; -use crate::*; +use std::{fs::File, path::Path, time::Duration}; + use educe::Educe; use ethereum_hashing::hash; +use fixed_bytes::FixedBytesExtended; use int_to_bytes::int_to_bytes4; use safe_arith::{ArithError, SafeArith}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_utils::quoted_u64::MaybeQuoted; use ssz::Encode; -use std::fs::File; -use std::path::Path; -use std::time::Duration; +use ssz_types::{RuntimeVariableList, VariableList}; use tree_hash::TreeHash; +use crate::{ + core::{ + APPLICATION_DOMAIN_BUILDER, Address, ApplicationDomain, EnrForkId, Epoch, EthSpec, + EthSpecId, Hash256, MainnetEthSpec, Slot, Uint256, + }, + data::{BlobIdentifier, DataColumnSubnetId, DataColumnsByRootIdentifier}, + execution::ExecutionBlockHash, + fork::{Fork, ForkData, ForkName}, + state::BeaconState, +}; + /// Each of the BLS signature domains. #[derive(Debug, PartialEq, Clone, Copy)] pub enum Domain { @@ -2581,6 +2589,7 @@ mod tests { #[cfg(test)] mod yaml_tests { use super::*; + use crate::core::MinimalEthSpec; use paste::paste; use std::sync::Arc; use tempfile::NamedTempFile; diff --git a/consensus/types/src/config_and_preset.rs b/consensus/types/src/core/config_and_preset.rs similarity index 95% rename from consensus/types/src/config_and_preset.rs rename to consensus/types/src/core/config_and_preset.rs index 16b09c9c08..08141c7731 100644 --- a/consensus/types/src/config_and_preset.rs +++ b/consensus/types/src/core/config_and_preset.rs @@ -1,13 +1,14 @@ -use crate::{ - AltairPreset, BasePreset, BellatrixPreset, CapellaPreset, ChainSpec, Config, DenebPreset, - ElectraPreset, EthSpec, FuluPreset, GloasPreset, consts::altair, consts::deneb, -}; use maplit::hashmap; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; use superstruct::superstruct; +use crate::core::{ + AltairPreset, BasePreset, BellatrixPreset, CapellaPreset, ChainSpec, Config, DenebPreset, + ElectraPreset, EthSpec, FuluPreset, GloasPreset, consts, +}; + /// Fusion of a runtime-config with the compile-time preset values. /// /// Mostly useful for the API. @@ -131,11 +132,11 @@ pub fn get_extra_fields(spec: &ChainSpec) -> HashMap { "domain_sync_committee_selection_proof".to_uppercase() => u32_hex(spec.domain_sync_committee_selection_proof), "sync_committee_subnet_count".to_uppercase() => - altair::SYNC_COMMITTEE_SUBNET_COUNT.to_string().into(), + consts::altair::SYNC_COMMITTEE_SUBNET_COUNT.to_string().into(), "target_aggregators_per_sync_subcommittee".to_uppercase() => - altair::TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE.to_string().into(), + consts::altair::TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE.to_string().into(), // Deneb - "versioned_hash_version_kzg".to_uppercase() => deneb::VERSIONED_HASH_VERSION_KZG.to_string().into(), + "versioned_hash_version_kzg".to_uppercase() => consts::deneb::VERSIONED_HASH_VERSION_KZG.to_string().into(), // Electra "compounding_withdrawal_prefix".to_uppercase() => u8_hex(spec.compounding_withdrawal_prefix_byte), "unset_deposit_requests_start_index".to_uppercase() => spec.unset_deposit_requests_start_index.to_string().into(), diff --git a/consensus/types/src/consts.rs b/consensus/types/src/core/consts.rs similarity index 94% rename from consensus/types/src/consts.rs rename to consensus/types/src/core/consts.rs index c20d5fe8f3..b6d63c47a8 100644 --- a/consensus/types/src/consts.rs +++ b/consensus/types/src/core/consts.rs @@ -23,5 +23,5 @@ pub mod bellatrix { pub const INTERVALS_PER_SLOT: u64 = 3; } pub mod deneb { - pub use crate::VERSIONED_HASH_VERSION_KZG; + pub use kzg::VERSIONED_HASH_VERSION_KZG; } diff --git a/consensus/types/src/enr_fork_id.rs b/consensus/types/src/core/enr_fork_id.rs similarity index 95% rename from consensus/types/src/enr_fork_id.rs rename to consensus/types/src/core/enr_fork_id.rs index e22672aeb6..c3b400cd13 100644 --- a/consensus/types/src/enr_fork_id.rs +++ b/consensus/types/src/core/enr_fork_id.rs @@ -1,11 +1,10 @@ -use crate::Epoch; -use crate::test_utils::TestRandom; - use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{core::Epoch, test_utils::TestRandom}; + /// Specifies a fork which allows nodes to identify each other on the network. This fork is used in /// a nodes local ENR. /// diff --git a/consensus/types/src/eth_spec.rs b/consensus/types/src/core/eth_spec.rs similarity index 98% rename from consensus/types/src/eth_spec.rs rename to consensus/types/src/core/eth_spec.rs index 47d32ad9e4..11857e678c 100644 --- a/consensus/types/src/eth_spec.rs +++ b/consensus/types/src/core/eth_spec.rs @@ -1,16 +1,22 @@ -use crate::*; +use std::{ + fmt::{self, Debug}, + str::FromStr, +}; use safe_arith::SafeArith; use serde::{Deserialize, Serialize}; use ssz_types::typenum::{ U0, U1, U2, U4, U8, U16, U17, U32, U64, U128, U256, U512, U625, U1024, U2048, U4096, U8192, U65536, U131072, U262144, U1048576, U16777216, U33554432, U134217728, U1073741824, - U1099511627776, UInt, bit::B0, + U1099511627776, UInt, Unsigned, bit::B0, }; -use std::fmt::{self, Debug}; -use std::str::FromStr; -pub type U5000 = UInt, B0>, B0>; // 625 * 8 = 5000 +use crate::{ + core::{ChainSpec, Epoch}, + state::BeaconStateError, +}; + +type U5000 = UInt, B0>, B0>; // 625 * 8 = 5000 const MAINNET: &str = "mainnet"; const MINIMAL: &str = "minimal"; @@ -182,7 +188,7 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq + fn get_committee_count_per_slot( active_validator_count: usize, spec: &ChainSpec, - ) -> Result { + ) -> Result { Self::get_committee_count_per_slot_with( active_validator_count, spec.max_committees_per_slot, @@ -194,7 +200,7 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq + active_validator_count: usize, max_committees_per_slot: usize, target_committee_size: usize, - ) -> Result { + ) -> Result { let slots_per_epoch = Self::SlotsPerEpoch::to_usize(); Ok(std::cmp::max( diff --git a/consensus/types/src/graffiti.rs b/consensus/types/src/core/graffiti.rs similarity index 98% rename from consensus/types/src/graffiti.rs rename to consensus/types/src/core/graffiti.rs index 31cc4187a6..d0e0e1b1a8 100644 --- a/consensus/types/src/graffiti.rs +++ b/consensus/types/src/core/graffiti.rs @@ -1,14 +1,13 @@ -use crate::{ - Hash256, - test_utils::{RngCore, TestRandom}, -}; +use std::{fmt, str::FromStr}; + +use rand::RngCore; use regex::bytes::Regex; use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; use ssz::{Decode, DecodeError, Encode}; -use std::fmt; -use std::str::FromStr; use tree_hash::{PackedEncoding, TreeHash}; +use crate::{core::Hash256, test_utils::TestRandom}; + pub const GRAFFITI_BYTES_LEN: usize = 32; /// The 32-byte `graffiti` field on a beacon block. diff --git a/consensus/types/src/core/mod.rs b/consensus/types/src/core/mod.rs new file mode 100644 index 0000000000..bb50bb1856 --- /dev/null +++ b/consensus/types/src/core/mod.rs @@ -0,0 +1,44 @@ +pub mod consts; + +mod application_domain; +mod chain_spec; +mod config_and_preset; +mod enr_fork_id; +mod eth_spec; +mod graffiti; +mod non_zero_usize; +mod preset; +mod relative_epoch; +mod signing_data; +mod slot_data; +#[macro_use] +mod slot_epoch_macros; +mod slot_epoch; +#[cfg(feature = "sqlite")] +mod sqlite; + +pub use application_domain::{APPLICATION_DOMAIN_BUILDER, ApplicationDomain}; +pub use chain_spec::{BlobParameters, BlobSchedule, ChainSpec, Config, Domain}; +pub use config_and_preset::{ + ConfigAndPreset, ConfigAndPresetDeneb, ConfigAndPresetElectra, ConfigAndPresetFulu, + ConfigAndPresetGloas, get_extra_fields, +}; +pub use enr_fork_id::EnrForkId; +pub use eth_spec::{EthSpec, EthSpecId, GNOSIS, GnosisEthSpec, MainnetEthSpec, MinimalEthSpec}; +pub use graffiti::{GRAFFITI_BYTES_LEN, Graffiti, GraffitiString}; +pub use non_zero_usize::new_non_zero_usize; +pub use preset::{ + AltairPreset, BasePreset, BellatrixPreset, CapellaPreset, DenebPreset, ElectraPreset, + FuluPreset, GloasPreset, +}; +pub use relative_epoch::{Error as RelativeEpochError, RelativeEpoch}; +pub use signing_data::{SignedRoot, SigningData}; +pub use slot_data::SlotData; +pub use slot_epoch::{Epoch, Slot}; + +pub type Hash256 = alloy_primitives::B256; +pub type Uint256 = alloy_primitives::U256; +pub type Hash64 = alloy_primitives::B64; +pub type Address = alloy_primitives::Address; +pub type VersionedHash = Hash256; +pub type MerkleProof = Vec; diff --git a/consensus/types/src/non_zero_usize.rs b/consensus/types/src/core/non_zero_usize.rs similarity index 100% rename from consensus/types/src/non_zero_usize.rs rename to consensus/types/src/core/non_zero_usize.rs diff --git a/consensus/types/src/preset.rs b/consensus/types/src/core/preset.rs similarity index 99% rename from consensus/types/src/preset.rs rename to consensus/types/src/core/preset.rs index ab54c0345f..b436fafd3a 100644 --- a/consensus/types/src/preset.rs +++ b/consensus/types/src/core/preset.rs @@ -1,5 +1,7 @@ -use crate::{ChainSpec, Epoch, EthSpec, Unsigned}; use serde::{Deserialize, Serialize}; +use ssz_types::typenum::Unsigned; + +use crate::core::{ChainSpec, Epoch, EthSpec}; /// Value-level representation of an Ethereum consensus "preset". /// diff --git a/consensus/types/src/relative_epoch.rs b/consensus/types/src/core/relative_epoch.rs similarity index 99% rename from consensus/types/src/relative_epoch.rs rename to consensus/types/src/core/relative_epoch.rs index 2fa0ae41bd..d1ee7ecc7c 100644 --- a/consensus/types/src/relative_epoch.rs +++ b/consensus/types/src/core/relative_epoch.rs @@ -1,6 +1,7 @@ -use crate::*; use safe_arith::{ArithError, SafeArith}; +use crate::core::{Epoch, Slot}; + #[derive(Debug, PartialEq, Clone, Copy)] pub enum Error { EpochTooLow { base: Epoch, other: Epoch }, diff --git a/consensus/types/src/signing_data.rs b/consensus/types/src/core/signing_data.rs similarity index 85% rename from consensus/types/src/signing_data.rs rename to consensus/types/src/core/signing_data.rs index 69b7dabfe5..907f03fac7 100644 --- a/consensus/types/src/signing_data.rs +++ b/consensus/types/src/core/signing_data.rs @@ -1,13 +1,12 @@ -use crate::context_deserialize; -use crate::test_utils::TestRandom; -use crate::{ForkName, Hash256}; - +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash::TreeHash; use tree_hash_derive::TreeHash; +use crate::{core::Hash256, fork::ForkName, test_utils::TestRandom}; + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] #[context_deserialize(ForkName)] diff --git a/consensus/types/src/slot_data.rs b/consensus/types/src/core/slot_data.rs similarity index 92% rename from consensus/types/src/slot_data.rs rename to consensus/types/src/core/slot_data.rs index 19775913b9..f0bd01814f 100644 --- a/consensus/types/src/slot_data.rs +++ b/consensus/types/src/core/slot_data.rs @@ -1,4 +1,4 @@ -use crate::Slot; +use crate::core::Slot; /// A trait providing a `Slot` getter for messages that are related to a single slot. Useful in /// making parts of attestation and sync committee processing generic. diff --git a/consensus/types/src/slot_epoch.rs b/consensus/types/src/core/slot_epoch.rs similarity index 98% rename from consensus/types/src/slot_epoch.rs rename to consensus/types/src/core/slot_epoch.rs index 05af9c5232..97457701b1 100644 --- a/consensus/types/src/slot_epoch.rs +++ b/consensus/types/src/core/slot_epoch.rs @@ -10,15 +10,17 @@ //! implement `Into`, however this would allow operations between `Slots` and `Epochs` which //! may lead to programming errors which are not detected by the compiler. -use crate::test_utils::TestRandom; -use crate::{ChainSpec, SignedRoot}; +use std::{fmt, hash::Hash}; use rand::RngCore; use safe_arith::{ArithError, SafeArith}; use serde::{Deserialize, Serialize}; use ssz::{Decode, DecodeError, Encode}; -use std::fmt; -use std::hash::Hash; + +use crate::{ + core::{ChainSpec, SignedRoot}, + test_utils::TestRandom, +}; #[cfg(feature = "legacy-arith")] use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign}; diff --git a/consensus/types/src/slot_epoch_macros.rs b/consensus/types/src/core/slot_epoch_macros.rs similarity index 100% rename from consensus/types/src/slot_epoch_macros.rs rename to consensus/types/src/core/slot_epoch_macros.rs diff --git a/consensus/types/src/sqlite.rs b/consensus/types/src/core/sqlite.rs similarity index 96% rename from consensus/types/src/sqlite.rs rename to consensus/types/src/core/sqlite.rs index b6318dc4ce..de892b4e98 100644 --- a/consensus/types/src/sqlite.rs +++ b/consensus/types/src/core/sqlite.rs @@ -1,10 +1,11 @@ //! Implementations of SQLite compatibility traits. -use crate::{Epoch, Slot}; use rusqlite::{ Error, types::{FromSql, FromSqlError, ToSql, ToSqlOutput, ValueRef}, }; +use crate::core::{Epoch, Slot}; + macro_rules! impl_to_from_sql { ($type:ty) => { impl ToSql for $type { diff --git a/consensus/types/src/blob_sidecar.rs b/consensus/types/src/data/blob_sidecar.rs similarity index 94% rename from consensus/types/src/blob_sidecar.rs rename to consensus/types/src/data/blob_sidecar.rs index d2c7331a57..709e556933 100644 --- a/consensus/types/src/blob_sidecar.rs +++ b/consensus/types/src/data/blob_sidecar.rs @@ -1,12 +1,7 @@ -use crate::context_deserialize; -use crate::test_utils::TestRandom; -use crate::{ - AbstractExecPayload, BeaconBlockHeader, BeaconStateError, Blob, ChainSpec, Epoch, EthSpec, - FixedVector, ForkName, Hash256, KzgProofs, RuntimeFixedVector, RuntimeVariableList, - SignedBeaconBlock, SignedBeaconBlockHeader, Slot, VariableList, - beacon_block_body::BLOB_KZG_COMMITMENTS_INDEX, -}; +use std::{fmt::Debug, hash::Hash, sync::Arc}; + use bls::Signature; +use context_deserialize::context_deserialize; use educe::Educe; use kzg::{BYTES_PER_BLOB, BYTES_PER_FIELD_ELEMENT, Blob as KzgBlob, Kzg, KzgCommitment, KzgProof}; use merkle_proof::{MerkleTreeError, merkle_root_from_branch, verify_merkle_proof}; @@ -15,13 +10,24 @@ use safe_arith::ArithError; use serde::{Deserialize, Serialize}; use ssz::Encode; use ssz_derive::{Decode, Encode}; -use std::fmt::Debug; -use std::hash::Hash; -use std::sync::Arc; +use ssz_types::{FixedVector, RuntimeFixedVector, RuntimeVariableList, VariableList}; use test_random_derive::TestRandom; use tree_hash::TreeHash; use tree_hash_derive::TreeHash; +use crate::{ + block::{ + BLOB_KZG_COMMITMENTS_INDEX, BeaconBlockHeader, SignedBeaconBlock, SignedBeaconBlockHeader, + }, + core::{ChainSpec, Epoch, EthSpec, Hash256, Slot}, + data::Blob, + execution::AbstractExecPayload, + fork::ForkName, + kzg_ext::KzgProofs, + state::BeaconStateError, + test_utils::TestRandom, +}; + /// Container of the data that identifies an individual blob. #[derive( Serialize, Deserialize, Encode, Decode, TreeHash, Copy, Clone, Debug, PartialEq, Eq, Hash, diff --git a/consensus/types/src/data_column_custody_group.rs b/consensus/types/src/data/data_column_custody_group.rs similarity index 98% rename from consensus/types/src/data_column_custody_group.rs rename to consensus/types/src/data/data_column_custody_group.rs index 7ecabab0ab..d96d13cfff 100644 --- a/consensus/types/src/data_column_custody_group.rs +++ b/consensus/types/src/data/data_column_custody_group.rs @@ -1,8 +1,14 @@ -use crate::{ChainSpec, ColumnIndex, DataColumnSubnetId, EthSpec}; +use std::collections::HashSet; + use alloy_primitives::U256; use itertools::Itertools; use safe_arith::{ArithError, SafeArith}; -use std::collections::HashSet; + +use crate::{ + EthSpec, + core::ChainSpec, + data::{ColumnIndex, DataColumnSubnetId}, +}; pub type CustodyIndex = u64; diff --git a/consensus/types/src/data_column_sidecar.rs b/consensus/types/src/data/data_column_sidecar.rs similarity index 94% rename from consensus/types/src/data_column_sidecar.rs rename to consensus/types/src/data/data_column_sidecar.rs index 62ce4467df..71d821f83e 100644 --- a/consensus/types/src/data_column_sidecar.rs +++ b/consensus/types/src/data/data_column_sidecar.rs @@ -1,13 +1,8 @@ -use crate::beacon_block_body::{BLOB_KZG_COMMITMENTS_INDEX, KzgCommitments}; -use crate::context_deserialize; -use crate::test_utils::TestRandom; -use crate::{ - BeaconBlockHeader, BeaconStateError, Epoch, EthSpec, ForkName, Hash256, - SignedBeaconBlockHeader, Slot, -}; +use std::sync::Arc; + use bls::Signature; +use context_deserialize::context_deserialize; use educe::Educe; -use kzg::Error as KzgError; use kzg::{KzgCommitment, KzgProof}; use merkle_proof::verify_merkle_proof; use safe_arith::ArithError; @@ -16,11 +11,19 @@ use ssz::Encode; use ssz_derive::{Decode, Encode}; use ssz_types::Error as SszError; use ssz_types::{FixedVector, VariableList}; -use std::sync::Arc; use test_random_derive::TestRandom; use tree_hash::TreeHash; use tree_hash_derive::TreeHash; +use crate::{ + block::{BLOB_KZG_COMMITMENTS_INDEX, BeaconBlockHeader, SignedBeaconBlockHeader}, + core::{Epoch, EthSpec, Hash256, Slot}, + fork::ForkName, + kzg_ext::{KzgCommitments, KzgError}, + state::BeaconStateError, + test_utils::TestRandom, +}; + pub type ColumnIndex = u64; pub type Cell = FixedVector::BytesPerCell>; pub type DataColumn = VariableList, ::MaxBlobCommitmentsPerBlock>; diff --git a/consensus/types/src/data_column_subnet_id.rs b/consensus/types/src/data/data_column_subnet_id.rs similarity index 80% rename from consensus/types/src/data_column_subnet_id.rs rename to consensus/types/src/data/data_column_subnet_id.rs index c6b8846c78..c30ebbba20 100644 --- a/consensus/types/src/data_column_subnet_id.rs +++ b/consensus/types/src/data/data_column_subnet_id.rs @@ -1,10 +1,13 @@ //! Identifies each data column subnet by an integer identifier. -use crate::ChainSpec; -use crate::data_column_sidecar::ColumnIndex; -use safe_arith::{ArithError, SafeArith}; +use std::{ + fmt::{self, Display}, + ops::{Deref, DerefMut}, +}; + +use safe_arith::SafeArith; use serde::{Deserialize, Serialize}; -use std::fmt::{self, Display}; -use std::ops::{Deref, DerefMut}; + +use crate::{core::ChainSpec, data::ColumnIndex}; #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -69,15 +72,3 @@ impl From<&DataColumnSubnetId> for u64 { val.0 } } - -#[derive(Debug)] -pub enum Error { - ArithError(ArithError), - InvalidCustodySubnetCount(u64), -} - -impl From for Error { - fn from(e: ArithError) -> Self { - Error::ArithError(e) - } -} diff --git a/consensus/types/src/data/mod.rs b/consensus/types/src/data/mod.rs new file mode 100644 index 0000000000..10d062bada --- /dev/null +++ b/consensus/types/src/data/mod.rs @@ -0,0 +1,23 @@ +mod blob_sidecar; +mod data_column_custody_group; +mod data_column_sidecar; +mod data_column_subnet_id; + +pub use blob_sidecar::{ + BlobIdentifier, BlobSidecar, BlobSidecarError, BlobSidecarList, BlobsList, FixedBlobSidecarList, +}; +pub use data_column_custody_group::{ + CustodyIndex, DataColumnCustodyGroupError, compute_columns_for_custody_group, + compute_ordered_custody_column_indices, compute_subnets_for_node, + compute_subnets_from_custody_group, get_custody_groups, +}; +pub use data_column_sidecar::{ + Cell, ColumnIndex, DataColumn, DataColumnSidecar, DataColumnSidecarError, + DataColumnSidecarList, DataColumnsByRootIdentifier, +}; +pub use data_column_subnet_id::DataColumnSubnetId; + +use crate::core::EthSpec; +use ssz_types::FixedVector; + +pub type Blob = FixedVector::BytesPerBlob>; diff --git a/consensus/types/src/deposit.rs b/consensus/types/src/deposit/deposit.rs similarity index 78% rename from consensus/types/src/deposit.rs rename to consensus/types/src/deposit/deposit.rs index 724f3de2f0..67f8572def 100644 --- a/consensus/types/src/deposit.rs +++ b/consensus/types/src/deposit/deposit.rs @@ -1,12 +1,12 @@ -use crate::context_deserialize; -use crate::test_utils::TestRandom; -use crate::*; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; -use ssz_types::typenum::U33; +use ssz_types::{FixedVector, typenum::U33}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{core::Hash256, deposit::DepositData, fork::ForkName, test_utils::TestRandom}; + pub const DEPOSIT_TREE_DEPTH: usize = 32; /// A deposit to potentially become a beacon chain validator. diff --git a/consensus/types/src/deposit_data.rs b/consensus/types/src/deposit/deposit_data.rs similarity index 86% rename from consensus/types/src/deposit_data.rs rename to consensus/types/src/deposit/deposit_data.rs index 3d9ae12808..51697f5d1a 100644 --- a/consensus/types/src/deposit_data.rs +++ b/consensus/types/src/deposit/deposit_data.rs @@ -1,10 +1,17 @@ -use crate::test_utils::TestRandom; -use crate::*; +use bls::{PublicKeyBytes, SecretKey, SignatureBytes}; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{ChainSpec, Hash256, SignedRoot}, + deposit::DepositMessage, + fork::ForkName, + test_utils::TestRandom, +}; + /// The data supplied by the user to the deposit contract. /// /// Spec v0.12.1 diff --git a/consensus/types/src/deposit_message.rs b/consensus/types/src/deposit/deposit_message.rs similarity index 81% rename from consensus/types/src/deposit_message.rs rename to consensus/types/src/deposit/deposit_message.rs index 9fe3b87885..4495a5c023 100644 --- a/consensus/types/src/deposit_message.rs +++ b/consensus/types/src/deposit/deposit_message.rs @@ -1,11 +1,16 @@ -use crate::test_utils::TestRandom; -use crate::*; - +use bls::PublicKeyBytes; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{Hash256, SignedRoot}, + fork::ForkName, + test_utils::TestRandom, +}; + /// The data supplied by the user to the deposit contract. /// /// Spec v0.12.1 diff --git a/consensus/types/src/deposit_request.rs b/consensus/types/src/deposit/deposit_request.rs similarity index 86% rename from consensus/types/src/deposit_request.rs rename to consensus/types/src/deposit/deposit_request.rs index 16acfb3b44..8d3c6e88ba 100644 --- a/consensus/types/src/deposit_request.rs +++ b/consensus/types/src/deposit/deposit_request.rs @@ -1,13 +1,13 @@ -use crate::context_deserialize; -use crate::test_utils::TestRandom; -use crate::{ForkName, Hash256, PublicKeyBytes}; -use bls::SignatureBytes; +use bls::{PublicKeyBytes, SignatureBytes}; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz::Encode; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{core::Hash256, fork::ForkName, test_utils::TestRandom}; + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, diff --git a/consensus/types/src/deposit_tree_snapshot.rs b/consensus/types/src/deposit/deposit_tree_snapshot.rs similarity index 95% rename from consensus/types/src/deposit_tree_snapshot.rs rename to consensus/types/src/deposit/deposit_tree_snapshot.rs index 400fca217d..24f41397a0 100644 --- a/consensus/types/src/deposit_tree_snapshot.rs +++ b/consensus/types/src/deposit/deposit_tree_snapshot.rs @@ -1,10 +1,11 @@ -use crate::*; use ethereum_hashing::{ZERO_HASHES, hash32_concat}; +use fixed_bytes::FixedBytesExtended; use int_to_bytes::int_to_bytes32; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; -use test_utils::TestRandom; + +use crate::{core::Hash256, deposit::DEPOSIT_TREE_DEPTH, test_utils::TestRandom}; #[derive(Encode, Decode, Deserialize, Serialize, Clone, Debug, PartialEq, TestRandom)] pub struct FinalizedExecutionBlock { diff --git a/consensus/types/src/deposit/mod.rs b/consensus/types/src/deposit/mod.rs new file mode 100644 index 0000000000..ff80f65cdb --- /dev/null +++ b/consensus/types/src/deposit/mod.rs @@ -0,0 +1,13 @@ +mod deposit; +mod deposit_data; +mod deposit_message; +mod deposit_request; +mod deposit_tree_snapshot; +mod pending_deposit; + +pub use deposit::{DEPOSIT_TREE_DEPTH, Deposit}; +pub use deposit_data::DepositData; +pub use deposit_message::DepositMessage; +pub use deposit_request::DepositRequest; +pub use deposit_tree_snapshot::{DepositTreeSnapshot, FinalizedExecutionBlock}; +pub use pending_deposit::PendingDeposit; diff --git a/consensus/types/src/pending_deposit.rs b/consensus/types/src/deposit/pending_deposit.rs similarity index 78% rename from consensus/types/src/pending_deposit.rs rename to consensus/types/src/deposit/pending_deposit.rs index 4a921edd54..4c039af39c 100644 --- a/consensus/types/src/pending_deposit.rs +++ b/consensus/types/src/deposit/pending_deposit.rs @@ -1,10 +1,16 @@ -use crate::test_utils::TestRandom; -use crate::*; +use bls::{PublicKeyBytes, SignatureBytes}; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{Hash256, Slot}, + fork::ForkName, + test_utils::TestRandom, +}; + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, diff --git a/consensus/types/src/bls_to_execution_change.rs b/consensus/types/src/execution/bls_to_execution_change.rs similarity index 83% rename from consensus/types/src/bls_to_execution_change.rs rename to consensus/types/src/execution/bls_to_execution_change.rs index 72d737ac71..de14f1b4c5 100644 --- a/consensus/types/src/bls_to_execution_change.rs +++ b/consensus/types/src/execution/bls_to_execution_change.rs @@ -1,10 +1,17 @@ -use crate::test_utils::TestRandom; -use crate::*; +use bls::{PublicKeyBytes, SecretKey}; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{Address, ChainSpec, Domain, Hash256, SignedRoot}, + execution::SignedBlsToExecutionChange, + fork::ForkName, + test_utils::TestRandom, +}; + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, diff --git a/consensus/types/src/eth1_data.rs b/consensus/types/src/execution/eth1_data.rs similarity index 86% rename from consensus/types/src/eth1_data.rs rename to consensus/types/src/execution/eth1_data.rs index 800f3e25f9..89a4e634a6 100644 --- a/consensus/types/src/eth1_data.rs +++ b/consensus/types/src/execution/eth1_data.rs @@ -1,12 +1,11 @@ -use super::Hash256; -use crate::ForkName; -use crate::context_deserialize; -use crate::test_utils::TestRandom; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{core::Hash256, fork::ForkName, test_utils::TestRandom}; + /// Contains data obtained from the Eth1 chain. /// /// Spec v0.12.1 diff --git a/consensus/types/src/execution_block_hash.rs b/consensus/types/src/execution/execution_block_hash.rs similarity index 96% rename from consensus/types/src/execution_block_hash.rs rename to consensus/types/src/execution/execution_block_hash.rs index 31905d64df..91c019ce04 100644 --- a/consensus/types/src/execution_block_hash.rs +++ b/consensus/types/src/execution/execution_block_hash.rs @@ -1,10 +1,11 @@ -use crate::FixedBytesExtended; -use crate::Hash256; -use crate::test_utils::TestRandom; +use std::fmt; + +use fixed_bytes::FixedBytesExtended; use rand::RngCore; use serde::{Deserialize, Serialize}; use ssz::{Decode, DecodeError, Encode}; -use std::fmt; + +use crate::{core::Hash256, test_utils::TestRandom}; #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Default, Clone, Copy, Serialize, Deserialize, Eq, PartialEq, Hash)] diff --git a/consensus/types/src/execution_block_header.rs b/consensus/types/src/execution/execution_block_header.rs similarity index 98% rename from consensus/types/src/execution_block_header.rs rename to consensus/types/src/execution/execution_block_header.rs index 02152adbf7..e596ba1831 100644 --- a/consensus/types/src/execution_block_header.rs +++ b/consensus/types/src/execution/execution_block_header.rs @@ -17,10 +17,15 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -use crate::{Address, EthSpec, ExecutionPayloadRef, Hash64, Hash256, Uint256}; use alloy_rlp::RlpEncodable; +use fixed_bytes::Uint256; use metastruct::metastruct; +use crate::{ + core::{Address, EthSpec, Hash64, Hash256}, + execution::ExecutionPayloadRef, +}; + /// Execution block header as used for RLP encoding and Keccak hashing. /// /// Credit to Reth for the type definition. diff --git a/consensus/types/src/execution_payload.rs b/consensus/types/src/execution/execution_payload.rs similarity index 92% rename from consensus/types/src/execution_payload.rs rename to consensus/types/src/execution/execution_payload.rs index 3548f67db2..7973b7efdc 100644 --- a/consensus/types/src/execution_payload.rs +++ b/consensus/types/src/execution/execution_payload.rs @@ -1,19 +1,29 @@ -use crate::{test_utils::TestRandom, *}; +use context_deserialize::{ContextDeserialize, context_deserialize}; use educe::Educe; +use fixed_bytes::Uint256; use serde::{Deserialize, Deserializer, Serialize}; use ssz::{Decode, Encode}; use ssz_derive::{Decode, Encode}; +use ssz_types::{FixedVector, VariableList}; +use superstruct::superstruct; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{Address, EthSpec, Hash256}, + execution::ExecutionBlockHash, + fork::{ForkName, ForkVersionDecode}, + state::BeaconStateError, + test_utils::TestRandom, + withdrawal::Withdrawals, +}; + pub type Transaction = VariableList; pub type Transactions = VariableList< Transaction<::MaxBytesPerTransaction>, ::MaxTransactionsPerPayload, >; -pub type Withdrawals = VariableList::MaxWithdrawalsPerPayload>; - #[superstruct( variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( @@ -38,8 +48,14 @@ pub type Withdrawals = VariableList::MaxWithdrawal arbitrary(bound = "E: EthSpec"), ), ), - cast_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"), - partial_getter_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"), + cast_error( + ty = "BeaconStateError", + expr = "BeaconStateError::IncorrectStateVariant" + ), + partial_getter_error( + ty = "BeaconStateError", + expr = "BeaconStateError::IncorrectStateVariant" + ), map_into(FullPayload, BlindedPayload), map_ref_into(ExecutionPayloadHeader) )] diff --git a/consensus/types/src/execution_payload_header.rs b/consensus/types/src/execution/execution_payload_header.rs similarity index 96% rename from consensus/types/src/execution_payload_header.rs rename to consensus/types/src/execution/execution_payload_header.rs index 241ecb4ce6..bd91a6471b 100644 --- a/consensus/types/src/execution_payload_header.rs +++ b/consensus/types/src/execution/execution_payload_header.rs @@ -1,12 +1,27 @@ -use crate::{test_utils::TestRandom, *}; +use context_deserialize::{ContextDeserialize, context_deserialize}; use educe::Educe; +use fixed_bytes::FixedBytesExtended; use serde::{Deserialize, Deserializer, Serialize}; use ssz::{Decode, Encode}; use ssz_derive::{Decode, Encode}; +use ssz_types::{FixedVector, VariableList}; +use superstruct::superstruct; use test_random_derive::TestRandom; use tree_hash::TreeHash; use tree_hash_derive::TreeHash; +use crate::{ + core::{Address, EthSpec, Hash256, Uint256}, + execution::{ + ExecutionBlockHash, ExecutionPayloadBellatrix, ExecutionPayloadCapella, + ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu, + ExecutionPayloadGloas, ExecutionPayloadRef, Transactions, + }, + fork::ForkName, + state::BeaconStateError, + test_utils::TestRandom, +}; + #[superstruct( variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( @@ -35,8 +50,14 @@ use tree_hash_derive::TreeHash; derive(PartialEq, TreeHash, Debug), tree_hash(enum_behaviour = "transparent") ), - cast_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"), - partial_getter_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"), + cast_error( + ty = "BeaconStateError", + expr = "BeaconStateError::IncorrectStateVariant" + ), + partial_getter_error( + ty = "BeaconStateError", + expr = "BeaconStateError::IncorrectStateVariant" + ), map_ref_into(ExecutionPayloadHeader) )] #[cfg_attr( diff --git a/consensus/types/src/execution_requests.rs b/consensus/types/src/execution/execution_requests.rs similarity index 93% rename from consensus/types/src/execution_requests.rs rename to consensus/types/src/execution/execution_requests.rs index 67396af71d..92d717778e 100644 --- a/consensus/types/src/execution_requests.rs +++ b/consensus/types/src/execution/execution_requests.rs @@ -1,7 +1,5 @@ -use crate::context_deserialize; -use crate::test_utils::TestRandom; -use crate::{ConsolidationRequest, DepositRequest, EthSpec, ForkName, Hash256, WithdrawalRequest}; use alloy_primitives::Bytes; +use context_deserialize::context_deserialize; use educe::Educe; use ethereum_hashing::{DynamicContext, Sha256Context}; use serde::{Deserialize, Serialize}; @@ -11,6 +9,15 @@ use ssz_types::VariableList; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + consolidation::ConsolidationRequest, + core::{EthSpec, Hash256}, + deposit::DepositRequest, + fork::ForkName, + test_utils::TestRandom, + withdrawal::WithdrawalRequest, +}; + pub type DepositRequests = VariableList::MaxDepositRequestsPerPayload>; pub type WithdrawalRequests = diff --git a/consensus/types/src/execution/mod.rs b/consensus/types/src/execution/mod.rs new file mode 100644 index 0000000000..0708bc5d96 --- /dev/null +++ b/consensus/types/src/execution/mod.rs @@ -0,0 +1,36 @@ +mod eth1_data; +mod execution_block_hash; +mod execution_block_header; +#[macro_use] +mod execution_payload; +mod bls_to_execution_change; +mod execution_payload_header; +mod execution_requests; +mod payload; +mod signed_bls_to_execution_change; + +pub use bls_to_execution_change::BlsToExecutionChange; +pub use eth1_data::Eth1Data; +pub use execution_block_hash::ExecutionBlockHash; +pub use execution_block_header::{EncodableExecutionBlockHeader, ExecutionBlockHeader}; +pub use execution_payload::{ + ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb, + ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadGloas, ExecutionPayloadRef, + Transaction, Transactions, +}; +pub use execution_payload_header::{ + ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella, + ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu, + ExecutionPayloadHeaderGloas, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, +}; +pub use execution_requests::{ + ConsolidationRequests, DepositRequests, ExecutionRequests, RequestType, WithdrawalRequests, +}; +pub use payload::{ + AbstractExecPayload, BlindedPayload, BlindedPayloadBellatrix, BlindedPayloadCapella, + BlindedPayloadDeneb, BlindedPayloadElectra, BlindedPayloadFulu, BlindedPayloadGloas, + BlindedPayloadRef, BlockProductionVersion, BlockType, ExecPayload, FullPayload, + FullPayloadBellatrix, FullPayloadCapella, FullPayloadDeneb, FullPayloadElectra, + FullPayloadFulu, FullPayloadGloas, FullPayloadRef, OwnedExecPayload, +}; +pub use signed_bls_to_execution_change::SignedBlsToExecutionChange; diff --git a/consensus/types/src/payload.rs b/consensus/types/src/execution/payload.rs similarity index 91% rename from consensus/types/src/payload.rs rename to consensus/types/src/execution/payload.rs index 370c73ad0a..c1cc6c4eb6 100644 --- a/consensus/types/src/payload.rs +++ b/consensus/types/src/execution/payload.rs @@ -1,16 +1,29 @@ -use crate::{test_utils::TestRandom, *}; use educe::Educe; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use ssz::{Decode, Encode}; use ssz_derive::{Decode, Encode}; -use std::borrow::Cow; -use std::fmt::Debug; -use std::hash::Hash; +use ssz_types::VariableList; +use std::{borrow::Cow, fmt::Debug, hash::Hash}; +use superstruct::superstruct; use test_random_derive::TestRandom; use tree_hash::TreeHash; use tree_hash_derive::TreeHash; +use crate::{ + core::{Address, EthSpec, Hash256}, + execution::{ + ExecutionBlockHash, ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella, + ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu, + ExecutionPayloadGloas, ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix, + ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, + ExecutionPayloadHeaderFulu, ExecutionPayloadHeaderGloas, ExecutionPayloadRef, Transactions, + }, + fork::ForkName, + state::BeaconStateError, + test_utils::TestRandom, +}; + #[derive(Debug, PartialEq)] pub enum BlockType { Blinded, @@ -38,8 +51,8 @@ pub trait ExecPayload: Debug + Clone + PartialEq + Hash + TreeHash + fn gas_limit(&self) -> u64; fn transactions(&self) -> Option<&Transactions>; /// fork-specific fields - fn withdrawals_root(&self) -> Result; - fn blob_gas_used(&self) -> Result; + fn withdrawals_root(&self) -> Result; + fn blob_gas_used(&self) -> Result; /// Is this a default payload with 0x0 roots for transactions and withdrawals? fn is_default_with_zero_roots(&self) -> bool; @@ -179,8 +192,14 @@ pub trait AbstractExecPayload: ), map_into(ExecutionPayload), map_ref_into(ExecutionPayloadRef), - cast_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"), - partial_getter_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant") + cast_error( + ty = "BeaconStateError", + expr = "BeaconStateError::IncorrectStateVariant" + ), + partial_getter_error( + ty = "BeaconStateError", + expr = "BeaconStateError::IncorrectStateVariant" + ) )] #[cfg_attr( feature = "arbitrary", @@ -311,9 +330,9 @@ impl ExecPayload for FullPayload { }) } - fn withdrawals_root(&self) -> Result { + fn withdrawals_root(&self) -> Result { match self { - FullPayload::Bellatrix(_) => Err(Error::IncorrectStateVariant), + FullPayload::Bellatrix(_) => Err(BeaconStateError::IncorrectStateVariant), FullPayload::Capella(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()), FullPayload::Deneb(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()), FullPayload::Electra(inner) => Ok(inner.execution_payload.withdrawals.tree_hash_root()), @@ -322,10 +341,10 @@ impl ExecPayload for FullPayload { } } - fn blob_gas_used(&self) -> Result { + fn blob_gas_used(&self) -> Result { match self { FullPayload::Bellatrix(_) | FullPayload::Capella(_) => { - Err(Error::IncorrectStateVariant) + Err(BeaconStateError::IncorrectStateVariant) } FullPayload::Deneb(inner) => Ok(inner.execution_payload.blob_gas_used), FullPayload::Electra(inner) => Ok(inner.execution_payload.blob_gas_used), @@ -354,9 +373,9 @@ impl FullPayload { }) } - pub fn default_at_fork(fork_name: ForkName) -> Result { + pub fn default_at_fork(fork_name: ForkName) -> Result { match fork_name { - ForkName::Base | ForkName::Altair => Err(Error::IncorrectStateVariant), + ForkName::Base | ForkName::Altair => Err(BeaconStateError::IncorrectStateVariant), ForkName::Bellatrix => Ok(FullPayloadBellatrix::default().into()), ForkName::Capella => Ok(FullPayloadCapella::default().into()), ForkName::Deneb => Ok(FullPayloadDeneb::default().into()), @@ -450,9 +469,9 @@ impl ExecPayload for FullPayloadRef<'_, E> { }) } - fn withdrawals_root(&self) -> Result { + fn withdrawals_root(&self) -> Result { match self { - FullPayloadRef::Bellatrix(_) => Err(Error::IncorrectStateVariant), + FullPayloadRef::Bellatrix(_) => Err(BeaconStateError::IncorrectStateVariant), FullPayloadRef::Capella(inner) => { Ok(inner.execution_payload.withdrawals.tree_hash_root()) } @@ -469,10 +488,10 @@ impl ExecPayload for FullPayloadRef<'_, E> { } } - fn blob_gas_used(&self) -> Result { + fn blob_gas_used(&self) -> Result { match self { FullPayloadRef::Bellatrix(_) | FullPayloadRef::Capella(_) => { - Err(Error::IncorrectStateVariant) + Err(BeaconStateError::IncorrectStateVariant) } FullPayloadRef::Deneb(inner) => Ok(inner.execution_payload.blob_gas_used), FullPayloadRef::Electra(inner) => Ok(inner.execution_payload.blob_gas_used), @@ -548,8 +567,14 @@ impl TryFrom> for FullPayload { tree_hash(enum_behaviour = "transparent"), ), map_into(ExecutionPayloadHeader), - cast_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant"), - partial_getter_error(ty = "Error", expr = "BeaconStateError::IncorrectStateVariant") + cast_error( + ty = "BeaconStateError", + expr = "BeaconStateError::IncorrectStateVariant" + ), + partial_getter_error( + ty = "BeaconStateError", + expr = "BeaconStateError::IncorrectStateVariant" + ) )] #[cfg_attr( feature = "arbitrary", @@ -658,9 +683,9 @@ impl ExecPayload for BlindedPayload { None } - fn withdrawals_root(&self) -> Result { + fn withdrawals_root(&self) -> Result { match self { - BlindedPayload::Bellatrix(_) => Err(Error::IncorrectStateVariant), + BlindedPayload::Bellatrix(_) => Err(BeaconStateError::IncorrectStateVariant), BlindedPayload::Capella(inner) => Ok(inner.execution_payload_header.withdrawals_root), BlindedPayload::Deneb(inner) => Ok(inner.execution_payload_header.withdrawals_root), BlindedPayload::Electra(inner) => Ok(inner.execution_payload_header.withdrawals_root), @@ -669,10 +694,10 @@ impl ExecPayload for BlindedPayload { } } - fn blob_gas_used(&self) -> Result { + fn blob_gas_used(&self) -> Result { match self { BlindedPayload::Bellatrix(_) | BlindedPayload::Capella(_) => { - Err(Error::IncorrectStateVariant) + Err(BeaconStateError::IncorrectStateVariant) } BlindedPayload::Deneb(inner) => Ok(inner.execution_payload_header.blob_gas_used), BlindedPayload::Electra(inner) => Ok(inner.execution_payload_header.blob_gas_used), @@ -766,9 +791,9 @@ impl<'b, E: EthSpec> ExecPayload for BlindedPayloadRef<'b, E> { None } - fn withdrawals_root(&self) -> Result { + fn withdrawals_root(&self) -> Result { match self { - BlindedPayloadRef::Bellatrix(_) => Err(Error::IncorrectStateVariant), + BlindedPayloadRef::Bellatrix(_) => Err(BeaconStateError::IncorrectStateVariant), BlindedPayloadRef::Capella(inner) => { Ok(inner.execution_payload_header.withdrawals_root) } @@ -781,10 +806,10 @@ impl<'b, E: EthSpec> ExecPayload for BlindedPayloadRef<'b, E> { } } - fn blob_gas_used(&self) -> Result { + fn blob_gas_used(&self) -> Result { match self { BlindedPayloadRef::Bellatrix(_) | BlindedPayloadRef::Capella(_) => { - Err(Error::IncorrectStateVariant) + Err(BeaconStateError::IncorrectStateVariant) } BlindedPayloadRef::Deneb(inner) => Ok(inner.execution_payload_header.blob_gas_used), BlindedPayloadRef::Electra(inner) => Ok(inner.execution_payload_header.blob_gas_used), @@ -877,12 +902,12 @@ macro_rules! impl_exec_payload_common { f(self) } - fn withdrawals_root(&self) -> Result { + fn withdrawals_root(&self) -> Result { let g = $g; g(self) } - fn blob_gas_used(&self) -> Result { + fn blob_gas_used(&self) -> Result { let h = $h; h(self) } @@ -917,15 +942,16 @@ macro_rules! impl_exec_payload_for_fork { }, { |_| { None } }, { - let c: for<'a> fn(&'a $wrapper_type_header) -> Result = - |payload: &$wrapper_type_header| { - let wrapper_ref_type = BlindedPayloadRef::$fork_variant(&payload); - wrapper_ref_type.withdrawals_root() - }; + let c: for<'a> fn( + &'a $wrapper_type_header, + ) -> Result = |payload: &$wrapper_type_header| { + let wrapper_ref_type = BlindedPayloadRef::$fork_variant(&payload); + wrapper_ref_type.withdrawals_root() + }; c }, { - let c: for<'a> fn(&'a $wrapper_type_header) -> Result = + let c: for<'a> fn(&'a $wrapper_type_header) -> Result = |payload: &$wrapper_type_header| { let wrapper_ref_type = BlindedPayloadRef::$fork_variant(&payload); wrapper_ref_type.blob_gas_used() @@ -935,12 +961,12 @@ macro_rules! impl_exec_payload_for_fork { ); impl TryInto<$wrapper_type_header> for BlindedPayload { - type Error = Error; + type Error = BeaconStateError; fn try_into(self) -> Result<$wrapper_type_header, Self::Error> { match self { BlindedPayload::$fork_variant(payload) => Ok(payload), - _ => Err(Error::IncorrectStateVariant), + _ => Err(BeaconStateError::IncorrectStateVariant), } } } @@ -963,13 +989,13 @@ macro_rules! impl_exec_payload_for_fork { } impl TryFrom> for $wrapper_type_header { - type Error = Error; + type Error = BeaconStateError; fn try_from(header: ExecutionPayloadHeader) -> Result { match header { ExecutionPayloadHeader::$fork_variant(execution_payload_header) => { Ok(execution_payload_header.into()) } - _ => Err(Error::PayloadConversionLogicFlaw), + _ => Err(BeaconStateError::PayloadConversionLogicFlaw), } } } @@ -1004,7 +1030,7 @@ macro_rules! impl_exec_payload_for_fork { c }, { - let c: for<'a> fn(&'a $wrapper_type_full) -> Result = + let c: for<'a> fn(&'a $wrapper_type_full) -> Result = |payload: &$wrapper_type_full| { let wrapper_ref_type = FullPayloadRef::$fork_variant(&payload); wrapper_ref_type.withdrawals_root() @@ -1012,7 +1038,7 @@ macro_rules! impl_exec_payload_for_fork { c }, { - let c: for<'a> fn(&'a $wrapper_type_full) -> Result = + let c: for<'a> fn(&'a $wrapper_type_full) -> Result = |payload: &$wrapper_type_full| { let wrapper_ref_type = FullPayloadRef::$fork_variant(&payload); wrapper_ref_type.blob_gas_used() @@ -1039,26 +1065,26 @@ macro_rules! impl_exec_payload_for_fork { } impl TryFrom> for $wrapper_type_full { - type Error = Error; + type Error = BeaconStateError; fn try_from(_: ExecutionPayloadHeader) -> Result { - Err(Error::PayloadConversionLogicFlaw) + Err(BeaconStateError::PayloadConversionLogicFlaw) } } impl TryFrom<$wrapped_type_header> for $wrapper_type_full { - type Error = Error; + type Error = BeaconStateError; fn try_from(_: $wrapped_type_header) -> Result { - Err(Error::PayloadConversionLogicFlaw) + Err(BeaconStateError::PayloadConversionLogicFlaw) } } impl TryInto<$wrapper_type_full> for FullPayload { - type Error = Error; + type Error = BeaconStateError; fn try_into(self) -> Result<$wrapper_type_full, Self::Error> { match self { FullPayload::$fork_variant(payload) => Ok(payload), - _ => Err(Error::PayloadConversionLogicFlaw), + _ => Err(BeaconStateError::PayloadConversionLogicFlaw), } } } diff --git a/consensus/types/src/signed_bls_to_execution_change.rs b/consensus/types/src/execution/signed_bls_to_execution_change.rs similarity index 78% rename from consensus/types/src/signed_bls_to_execution_change.rs rename to consensus/types/src/execution/signed_bls_to_execution_change.rs index 910c4c7d7e..535960fb3f 100644 --- a/consensus/types/src/signed_bls_to_execution_change.rs +++ b/consensus/types/src/execution/signed_bls_to_execution_change.rs @@ -1,10 +1,12 @@ -use crate::test_utils::TestRandom; -use crate::*; +use bls::Signature; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{execution::BlsToExecutionChange, fork::ForkName, test_utils::TestRandom}; + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, diff --git a/consensus/types/src/exit/mod.rs b/consensus/types/src/exit/mod.rs new file mode 100644 index 0000000000..cb066d1d7a --- /dev/null +++ b/consensus/types/src/exit/mod.rs @@ -0,0 +1,5 @@ +mod signed_voluntary_exit; +mod voluntary_exit; + +pub use signed_voluntary_exit::SignedVoluntaryExit; +pub use voluntary_exit::VoluntaryExit; diff --git a/consensus/types/src/signed_voluntary_exit.rs b/consensus/types/src/exit/signed_voluntary_exit.rs similarity index 84% rename from consensus/types/src/signed_voluntary_exit.rs rename to consensus/types/src/exit/signed_voluntary_exit.rs index 0beffa1e04..b49401a721 100644 --- a/consensus/types/src/signed_voluntary_exit.rs +++ b/consensus/types/src/exit/signed_voluntary_exit.rs @@ -1,12 +1,12 @@ -use crate::context_deserialize; -use crate::{ForkName, VoluntaryExit, test_utils::TestRandom}; use bls::Signature; - +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{exit::VoluntaryExit, fork::ForkName, test_utils::TestRandom}; + /// An exit voluntarily submitted a validator who wishes to withdraw. /// /// Spec v0.12.1 diff --git a/consensus/types/src/voluntary_exit.rs b/consensus/types/src/exit/voluntary_exit.rs similarity index 90% rename from consensus/types/src/voluntary_exit.rs rename to consensus/types/src/exit/voluntary_exit.rs index 42d792a814..30c6a97c4d 100644 --- a/consensus/types/src/voluntary_exit.rs +++ b/consensus/types/src/exit/voluntary_exit.rs @@ -1,14 +1,17 @@ -use crate::context_deserialize; -use crate::{ - ChainSpec, Domain, Epoch, ForkName, Hash256, SecretKey, SignedRoot, SignedVoluntaryExit, - test_utils::TestRandom, -}; - +use bls::SecretKey; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{ChainSpec, Domain, Epoch, Hash256, SignedRoot}, + exit::SignedVoluntaryExit, + fork::ForkName, + test_utils::TestRandom, +}; + /// An exit voluntarily submitted a validator who wishes to withdraw. /// /// Spec v0.12.1 diff --git a/consensus/types/src/fork.rs b/consensus/types/src/fork/fork.rs similarity index 96% rename from consensus/types/src/fork.rs rename to consensus/types/src/fork/fork.rs index 5c5bd7ffd1..371b11e05c 100644 --- a/consensus/types/src/fork.rs +++ b/consensus/types/src/fork/fork.rs @@ -1,12 +1,11 @@ -use crate::test_utils::TestRandom; -use crate::{Epoch, ForkName}; use context_deserialize::context_deserialize; - use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{core::Epoch, fork::ForkName, test_utils::TestRandom}; + /// Specifies a fork of the `BeaconChain`, to prevent replay attacks. /// /// Spec v0.12.1 diff --git a/consensus/types/src/fork_context.rs b/consensus/types/src/fork/fork_context.rs similarity index 98% rename from consensus/types/src/fork_context.rs rename to consensus/types/src/fork/fork_context.rs index 66617326e1..aec7276124 100644 --- a/consensus/types/src/fork_context.rs +++ b/consensus/types/src/fork/fork_context.rs @@ -1,7 +1,11 @@ +use std::collections::BTreeMap; + use parking_lot::RwLock; -use crate::{ChainSpec, Epoch, EthSpec, ForkName, Hash256, Slot}; -use std::collections::BTreeMap; +use crate::{ + core::{ChainSpec, Epoch, EthSpec, Hash256, Slot}, + fork::ForkName, +}; /// Represents a hard fork in the consensus protocol. /// @@ -152,8 +156,7 @@ impl ForkContext { #[cfg(test)] mod tests { use super::*; - use crate::MainnetEthSpec; - use crate::chain_spec::{BlobParameters, BlobSchedule}; + use crate::core::{BlobParameters, BlobSchedule, MainnetEthSpec}; type E = MainnetEthSpec; diff --git a/consensus/types/src/fork_data.rs b/consensus/types/src/fork/fork_data.rs similarity index 88% rename from consensus/types/src/fork_data.rs rename to consensus/types/src/fork/fork_data.rs index 2d5e905efb..1b9c8bad9f 100644 --- a/consensus/types/src/fork_data.rs +++ b/consensus/types/src/fork/fork_data.rs @@ -1,12 +1,15 @@ -use crate::test_utils::TestRandom; -use crate::{ForkName, Hash256, SignedRoot}; use context_deserialize::context_deserialize; - use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{Hash256, SignedRoot}, + fork::ForkName, + test_utils::TestRandom, +}; + /// Specifies a fork of the `BeaconChain`, to prevent replay attacks. /// /// Spec v0.12.1 diff --git a/consensus/types/src/fork/fork_macros.rs b/consensus/types/src/fork/fork_macros.rs new file mode 100644 index 0000000000..0c7f382ffc --- /dev/null +++ b/consensus/types/src/fork/fork_macros.rs @@ -0,0 +1,60 @@ +/// Map a fork name into a fork-versioned superstruct type like `BeaconBlock`. +/// +/// The `$body` expression is where the magic happens. The macro allows us to achieve polymorphism +/// in the return type, which is not usually possible in Rust without trait objects. +/// +/// E.g. you could call `map_fork_name!(fork, BeaconBlock, serde_json::from_str(s))` to decode +/// different `BeaconBlock` variants depending on the value of `fork`. Note how the type of the body +/// will change between `BeaconBlockBase` and `BeaconBlockAltair` depending on which branch is +/// taken, the important thing is that they are re-unified by injecting them back into the +/// `BeaconBlock` parent enum. +/// +/// If you would also like to extract additional data alongside the superstruct type, use +/// the more flexible `map_fork_name_with` macro. +#[macro_export] +macro_rules! map_fork_name { + ($fork_name:expr, $t:tt, $body:expr) => { + $crate::map_fork_name_with!($fork_name, $t, { ($body, ()) }).0 + }; +} + +/// Map a fork name into a tuple of `(t, extra)` where `t` is a superstruct type. +#[macro_export] +macro_rules! map_fork_name_with { + ($fork_name:expr, $t:tt, $body:block) => { + match $fork_name { + $crate::fork::ForkName::Base => { + let (value, extra_data) = $body; + ($t::Base(value), extra_data) + } + $crate::fork::ForkName::Altair => { + let (value, extra_data) = $body; + ($t::Altair(value), extra_data) + } + $crate::fork::ForkName::Bellatrix => { + let (value, extra_data) = $body; + ($t::Bellatrix(value), extra_data) + } + $crate::fork::ForkName::Capella => { + let (value, extra_data) = $body; + ($t::Capella(value), extra_data) + } + $crate::fork::ForkName::Deneb => { + let (value, extra_data) = $body; + ($t::Deneb(value), extra_data) + } + $crate::fork::ForkName::Electra => { + let (value, extra_data) = $body; + ($t::Electra(value), extra_data) + } + $crate::fork::ForkName::Fulu => { + let (value, extra_data) = $body; + ($t::Fulu(value), extra_data) + } + $crate::fork::ForkName::Gloas => { + let (value, extra_data) = $body; + ($t::Gloas(value), extra_data) + } + } + }; +} diff --git a/consensus/types/src/fork_name.rs b/consensus/types/src/fork/fork_name.rs similarity index 84% rename from consensus/types/src/fork_name.rs rename to consensus/types/src/fork/fork_name.rs index 1d7bf3795b..e9ec5fbe41 100644 --- a/consensus/types/src/fork_name.rs +++ b/consensus/types/src/fork/fork_name.rs @@ -1,8 +1,12 @@ -use crate::{ChainSpec, Epoch}; +use std::{ + fmt::{self, Display, Formatter}, + str::FromStr, +}; + use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; -use std::fmt::{self, Display, Formatter}; -use std::str::FromStr; + +use crate::core::{ChainSpec, Epoch}; #[derive( Debug, Clone, Copy, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, @@ -243,67 +247,6 @@ impl ForkName { } } -/// Map a fork name into a fork-versioned superstruct type like `BeaconBlock`. -/// -/// The `$body` expression is where the magic happens. The macro allows us to achieve polymorphism -/// in the return type, which is not usually possible in Rust without trait objects. -/// -/// E.g. you could call `map_fork_name!(fork, BeaconBlock, serde_json::from_str(s))` to decode -/// different `BeaconBlock` variants depending on the value of `fork`. Note how the type of the body -/// will change between `BeaconBlockBase` and `BeaconBlockAltair` depending on which branch is -/// taken, the important thing is that they are re-unified by injecting them back into the -/// `BeaconBlock` parent enum. -/// -/// If you would also like to extract additional data alongside the superstruct type, use -/// the more flexible `map_fork_name_with` macro. -#[macro_export] -macro_rules! map_fork_name { - ($fork_name:expr, $t:tt, $body:expr) => { - map_fork_name_with!($fork_name, $t, { ($body, ()) }).0 - }; -} - -/// Map a fork name into a tuple of `(t, extra)` where `t` is a superstruct type. -#[macro_export] -macro_rules! map_fork_name_with { - ($fork_name:expr, $t:tt, $body:block) => { - match $fork_name { - ForkName::Base => { - let (value, extra_data) = $body; - ($t::Base(value), extra_data) - } - ForkName::Altair => { - let (value, extra_data) = $body; - ($t::Altair(value), extra_data) - } - ForkName::Bellatrix => { - let (value, extra_data) = $body; - ($t::Bellatrix(value), extra_data) - } - ForkName::Capella => { - let (value, extra_data) = $body; - ($t::Capella(value), extra_data) - } - ForkName::Deneb => { - let (value, extra_data) = $body; - ($t::Deneb(value), extra_data) - } - ForkName::Electra => { - let (value, extra_data) = $body; - ($t::Electra(value), extra_data) - } - ForkName::Fulu => { - let (value, extra_data) = $body; - ($t::Fulu(value), extra_data) - } - ForkName::Gloas => { - let (value, extra_data) = $body; - ($t::Gloas(value), extra_data) - } - } - }; -} - impl FromStr for ForkName { type Err = String; diff --git a/consensus/types/src/fork/fork_version_decode.rs b/consensus/types/src/fork/fork_version_decode.rs new file mode 100644 index 0000000000..4349efb21f --- /dev/null +++ b/consensus/types/src/fork/fork_version_decode.rs @@ -0,0 +1,6 @@ +use crate::fork::ForkName; + +pub trait ForkVersionDecode: Sized { + /// SSZ decode with explicit fork variant. + fn from_ssz_bytes_by_fork(bytes: &[u8], fork_name: ForkName) -> Result; +} diff --git a/consensus/types/src/fork/mod.rs b/consensus/types/src/fork/mod.rs new file mode 100644 index 0000000000..1ad1c7cb62 --- /dev/null +++ b/consensus/types/src/fork/mod.rs @@ -0,0 +1,15 @@ +mod fork; +mod fork_context; +mod fork_data; +mod fork_macros; +mod fork_name; +mod fork_version_decode; + +pub use crate::{map_fork_name, map_fork_name_with}; +pub use fork::Fork; +pub use fork_context::{ForkContext, HardFork}; +pub use fork_data::ForkData; +pub use fork_name::{ForkName, InconsistentFork}; +pub use fork_version_decode::ForkVersionDecode; + +pub type ForkVersion = [u8; 4]; diff --git a/consensus/types/src/kzg_ext/consts.rs b/consensus/types/src/kzg_ext/consts.rs new file mode 100644 index 0000000000..06c9f9c749 --- /dev/null +++ b/consensus/types/src/kzg_ext/consts.rs @@ -0,0 +1,3 @@ +pub use kzg::{ + BYTES_PER_BLOB, BYTES_PER_COMMITMENT, BYTES_PER_FIELD_ELEMENT, VERSIONED_HASH_VERSION_KZG, +}; diff --git a/consensus/types/src/kzg_ext/mod.rs b/consensus/types/src/kzg_ext/mod.rs new file mode 100644 index 0000000000..63533ec71f --- /dev/null +++ b/consensus/types/src/kzg_ext/mod.rs @@ -0,0 +1,27 @@ +pub mod consts; + +pub use kzg::{Blob as KzgBlob, Error as KzgError, Kzg, KzgCommitment, KzgProof}; + +use ssz_types::VariableList; + +use crate::core::EthSpec; + +// Note on List limit: +// - Deneb to Electra: `MaxBlobCommitmentsPerBlock` +// - Fulu: `MaxCellsPerBlock` +// We choose to use a single type (with the larger value from Fulu as `N`) instead of having to +// introduce a new type for Fulu. This is to avoid messy conversions and having to add extra types +// with no gains - as `N` does not impact serialisation at all, and only affects merkleization, +// which we don't current do on `KzgProofs` anyway. +pub type KzgProofs = VariableList::MaxCellsPerBlock>; + +pub type KzgCommitments = + VariableList::MaxBlobCommitmentsPerBlock>; + +/// Util method helpful for logging. +pub fn format_kzg_commitments(commitments: &[KzgCommitment]) -> String { + let commitment_strings: Vec = commitments.iter().map(|x| x.to_string()).collect(); + let commitments_joined = commitment_strings.join(", "); + let surrounded_commitments = format!("[{}]", commitments_joined); + surrounded_commitments +} diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index 35ef299ff7..9d02f71a3e 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -1,4 +1,4 @@ -//! Ethereum 2.0 types +//! Ethereum Consensus types // Clippy lint set up #![cfg_attr( not(test), @@ -12,287 +12,167 @@ #[macro_use] pub mod test_utils; -pub mod aggregate_and_proof; -pub mod application_domain; pub mod attestation; -pub mod attestation_data; -pub mod attestation_duty; -pub mod attester_slashing; -pub mod beacon_block; -pub mod beacon_block_body; -pub mod beacon_block_header; -pub mod beacon_committee; -pub mod beacon_response; -pub mod beacon_state; -pub mod bls_to_execution_change; -pub mod builder_bid; -pub mod chain_spec; -pub mod checkpoint; -pub mod consolidation_request; -pub mod consts; -pub mod contribution_and_proof; +pub mod block; +pub mod builder; +pub mod consolidation; +pub mod core; +pub mod data; pub mod deposit; -pub mod deposit_data; -pub mod deposit_message; -pub mod deposit_request; -pub mod deposit_tree_snapshot; -pub mod enr_fork_id; -pub mod eth1_data; -pub mod eth_spec; -pub mod execution_block_hash; -pub mod execution_payload; -pub mod execution_payload_header; +pub mod execution; +pub mod exit; pub mod fork; -pub mod fork_data; -pub mod fork_name; -pub mod graffiti; -pub mod historical_batch; -pub mod historical_summary; -pub mod indexed_attestation; -pub mod light_client_bootstrap; -pub mod light_client_finality_update; -pub mod light_client_optimistic_update; -pub mod light_client_update; -pub mod pending_attestation; -pub mod pending_consolidation; -pub mod pending_deposit; -pub mod pending_partial_withdrawal; -pub mod proposer_preparation_data; -pub mod proposer_slashing; -pub mod relative_epoch; -pub mod selection_proof; -pub mod shuffling_id; -pub mod signed_aggregate_and_proof; -pub mod signed_beacon_block; -pub mod signed_beacon_block_header; -pub mod signed_bls_to_execution_change; -pub mod signed_contribution_and_proof; -pub mod signed_voluntary_exit; -pub mod signing_data; -pub mod sync_committee_subscription; -pub mod sync_duty; -pub mod validator; -pub mod validator_subscription; -pub mod voluntary_exit; -pub mod withdrawal_credentials; -pub mod withdrawal_request; -#[macro_use] -pub mod slot_epoch_macros; -pub mod activation_queue; -pub mod config_and_preset; -pub mod execution_block_header; -pub mod execution_requests; -pub mod fork_context; -pub mod participation_flags; -pub mod payload; -pub mod preset; -pub mod slot_epoch; -pub mod subnet_id; -pub mod sync_aggregate; -pub mod sync_aggregator_selection_data; +pub mod kzg_ext; +pub mod light_client; +pub mod slashing; +pub mod state; pub mod sync_committee; -pub mod sync_committee_contribution; -pub mod sync_committee_message; -pub mod sync_selection_proof; -pub mod sync_subnet_id; -pub mod validator_registration_data; +pub mod validator; pub mod withdrawal; -pub mod epoch_cache; -pub mod slot_data; -#[cfg(feature = "sqlite")] -pub mod sqlite; +// Temporary root level exports to maintain backwards compatibility for Lighthouse. +pub use attestation::*; +pub use block::*; +pub use builder::*; +pub use consolidation::*; +pub use core::{consts, *}; +pub use data::*; +pub use deposit::*; +pub use execution::*; +pub use exit::*; +pub use fork::*; +pub use kzg_ext::*; +pub use light_client::*; +pub use slashing::*; +pub use state::*; +pub use sync_committee::*; +pub use validator::*; +pub use withdrawal::*; -pub mod blob_sidecar; -pub mod data_column_custody_group; -pub mod data_column_sidecar; -pub mod data_column_subnet_id; -pub mod light_client_header; -pub mod non_zero_usize; -pub mod runtime_fixed_vector; -pub mod runtime_var_list; +// Temporary facade modules to maintain backwards compatibility for Lighthouse. +pub mod eth_spec { + pub use crate::core::EthSpec; +} -pub use crate::activation_queue::ActivationQueue; -pub use crate::aggregate_and_proof::{ - AggregateAndProof, AggregateAndProofBase, AggregateAndProofElectra, AggregateAndProofRef, -}; -pub use crate::attestation::{ - Attestation, AttestationBase, AttestationElectra, AttestationRef, AttestationRefMut, - Error as AttestationError, SingleAttestation, -}; -pub use crate::attestation_data::AttestationData; -pub use crate::attestation_duty::AttestationDuty; -pub use crate::attester_slashing::{ - AttesterSlashing, AttesterSlashingBase, AttesterSlashingElectra, AttesterSlashingOnDisk, - AttesterSlashingRef, AttesterSlashingRefOnDisk, -}; -pub use crate::beacon_block::{ - BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockBellatrix, BeaconBlockCapella, - BeaconBlockDeneb, BeaconBlockElectra, BeaconBlockFulu, BeaconBlockGloas, BeaconBlockRef, - BeaconBlockRefMut, BlindedBeaconBlock, BlockImportSource, EmptyBlock, -}; -pub use crate::beacon_block_body::{ - BeaconBlockBody, BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyBellatrix, - BeaconBlockBodyCapella, BeaconBlockBodyDeneb, BeaconBlockBodyElectra, BeaconBlockBodyFulu, - BeaconBlockBodyGloas, BeaconBlockBodyRef, BeaconBlockBodyRefMut, -}; -pub use crate::beacon_block_header::BeaconBlockHeader; -pub use crate::beacon_committee::{BeaconCommittee, OwnedBeaconCommittee}; -pub use crate::beacon_response::{ - BeaconResponse, ForkVersionDecode, ForkVersionedResponse, UnversionedResponse, -}; -pub use crate::beacon_state::{Error as BeaconStateError, *}; -pub use crate::blob_sidecar::{BlobIdentifier, BlobSidecar, BlobSidecarList, BlobsList}; -pub use crate::bls_to_execution_change::BlsToExecutionChange; -pub use crate::chain_spec::{ChainSpec, Config, Domain}; -pub use crate::checkpoint::Checkpoint; -pub use crate::config_and_preset::{ - ConfigAndPreset, ConfigAndPresetDeneb, ConfigAndPresetElectra, ConfigAndPresetFulu, - ConfigAndPresetGloas, -}; -pub use crate::consolidation_request::ConsolidationRequest; -pub use crate::contribution_and_proof::ContributionAndProof; -pub use crate::data_column_sidecar::{ - ColumnIndex, DataColumnSidecar, DataColumnSidecarList, DataColumnsByRootIdentifier, -}; -pub use crate::data_column_subnet_id::DataColumnSubnetId; -pub use crate::deposit::{DEPOSIT_TREE_DEPTH, Deposit}; -pub use crate::deposit_data::DepositData; -pub use crate::deposit_message::DepositMessage; -pub use crate::deposit_request::DepositRequest; -pub use crate::deposit_tree_snapshot::{DepositTreeSnapshot, FinalizedExecutionBlock}; -pub use crate::enr_fork_id::EnrForkId; -pub use crate::epoch_cache::{EpochCache, EpochCacheError, EpochCacheKey}; -pub use crate::eth_spec::EthSpecId; -pub use crate::eth1_data::Eth1Data; -pub use crate::execution_block_hash::ExecutionBlockHash; -pub use crate::execution_block_header::{EncodableExecutionBlockHeader, ExecutionBlockHeader}; -pub use crate::execution_payload::{ - ExecutionPayload, ExecutionPayloadBellatrix, ExecutionPayloadCapella, ExecutionPayloadDeneb, - ExecutionPayloadElectra, ExecutionPayloadFulu, ExecutionPayloadGloas, ExecutionPayloadRef, - Transaction, Transactions, Withdrawals, -}; -pub use crate::execution_payload_header::{ - ExecutionPayloadHeader, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella, - ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu, - ExecutionPayloadHeaderGloas, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, -}; -pub use crate::execution_requests::{ExecutionRequests, RequestType}; -pub use crate::fork::Fork; -pub use crate::fork_context::ForkContext; -pub use crate::fork_data::ForkData; -pub use crate::fork_name::{ForkName, InconsistentFork}; -pub use crate::graffiti::{GRAFFITI_BYTES_LEN, Graffiti}; -pub use crate::historical_batch::HistoricalBatch; -pub use crate::indexed_attestation::{ - IndexedAttestation, IndexedAttestationBase, IndexedAttestationElectra, IndexedAttestationRef, -}; -pub use crate::light_client_bootstrap::{ - LightClientBootstrap, LightClientBootstrapAltair, LightClientBootstrapCapella, - LightClientBootstrapDeneb, LightClientBootstrapElectra, LightClientBootstrapFulu, - LightClientBootstrapGloas, -}; -pub use crate::light_client_finality_update::{ - LightClientFinalityUpdate, LightClientFinalityUpdateAltair, LightClientFinalityUpdateCapella, - LightClientFinalityUpdateDeneb, LightClientFinalityUpdateElectra, - LightClientFinalityUpdateFulu, LightClientFinalityUpdateGloas, -}; -pub use crate::light_client_header::{ - LightClientHeader, LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, - LightClientHeaderElectra, LightClientHeaderFulu, LightClientHeaderGloas, -}; -pub use crate::light_client_optimistic_update::{ - LightClientOptimisticUpdate, LightClientOptimisticUpdateAltair, - LightClientOptimisticUpdateCapella, LightClientOptimisticUpdateDeneb, - LightClientOptimisticUpdateElectra, LightClientOptimisticUpdateFulu, - LightClientOptimisticUpdateGloas, -}; -pub use crate::light_client_update::{ - Error as LightClientUpdateError, LightClientUpdate, LightClientUpdateAltair, - LightClientUpdateCapella, LightClientUpdateDeneb, LightClientUpdateElectra, - LightClientUpdateFulu, LightClientUpdateGloas, MerkleProof, -}; -pub use crate::participation_flags::ParticipationFlags; -pub use crate::payload::{ - AbstractExecPayload, BlindedPayload, BlindedPayloadBellatrix, BlindedPayloadCapella, - BlindedPayloadDeneb, BlindedPayloadElectra, BlindedPayloadFulu, BlindedPayloadGloas, - BlindedPayloadRef, BlockType, ExecPayload, FullPayload, FullPayloadBellatrix, - FullPayloadCapella, FullPayloadDeneb, FullPayloadElectra, FullPayloadFulu, FullPayloadGloas, - FullPayloadRef, OwnedExecPayload, -}; -pub use crate::pending_attestation::PendingAttestation; -pub use crate::pending_consolidation::PendingConsolidation; -pub use crate::pending_deposit::PendingDeposit; -pub use crate::pending_partial_withdrawal::PendingPartialWithdrawal; -pub use crate::preset::{ - AltairPreset, BasePreset, BellatrixPreset, CapellaPreset, DenebPreset, ElectraPreset, - FuluPreset, GloasPreset, -}; -pub use crate::proposer_preparation_data::ProposerPreparationData; -pub use crate::proposer_slashing::ProposerSlashing; -pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch}; -pub use crate::runtime_fixed_vector::RuntimeFixedVector; -pub use crate::runtime_var_list::RuntimeVariableList; -pub use crate::selection_proof::SelectionProof; -pub use crate::shuffling_id::AttestationShufflingId; -pub use crate::signed_aggregate_and_proof::{ - SignedAggregateAndProof, SignedAggregateAndProofBase, SignedAggregateAndProofElectra, -}; -pub use crate::signed_beacon_block::{ - SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockBellatrix, - SignedBeaconBlockCapella, SignedBeaconBlockDeneb, SignedBeaconBlockElectra, - SignedBeaconBlockFulu, SignedBeaconBlockGloas, SignedBeaconBlockHash, SignedBlindedBeaconBlock, - ssz_tagged_signed_beacon_block, ssz_tagged_signed_beacon_block_arc, -}; -pub use crate::signed_beacon_block_header::SignedBeaconBlockHeader; -pub use crate::signed_bls_to_execution_change::SignedBlsToExecutionChange; -pub use crate::signed_contribution_and_proof::SignedContributionAndProof; -pub use crate::signed_voluntary_exit::SignedVoluntaryExit; -pub use crate::signing_data::{SignedRoot, SigningData}; -pub use crate::slot_epoch::{Epoch, Slot}; -pub use crate::subnet_id::SubnetId; -pub use crate::sync_aggregate::SyncAggregate; -pub use crate::sync_aggregator_selection_data::SyncAggregatorSelectionData; -pub use crate::sync_committee::SyncCommittee; -pub use crate::sync_committee_contribution::{SyncCommitteeContribution, SyncContributionData}; -pub use crate::sync_committee_message::SyncCommitteeMessage; -pub use crate::sync_committee_subscription::SyncCommitteeSubscription; -pub use crate::sync_duty::SyncDuty; -pub use crate::sync_selection_proof::SyncSelectionProof; -pub use crate::sync_subnet_id::SyncSubnetId; -pub use crate::validator::Validator; -pub use crate::validator_registration_data::*; -pub use crate::validator_subscription::ValidatorSubscription; -pub use crate::voluntary_exit::VoluntaryExit; -pub use crate::withdrawal::Withdrawal; -pub use crate::withdrawal_credentials::WithdrawalCredentials; -pub use crate::withdrawal_request::WithdrawalRequest; -pub use fixed_bytes::FixedBytesExtended; +pub mod chain_spec { + pub use crate::core::ChainSpec; +} -pub type CommitteeIndex = u64; -pub type Hash256 = fixed_bytes::Hash256; -pub type Uint256 = fixed_bytes::Uint256; -pub type Address = fixed_bytes::Address; -pub type ForkVersion = [u8; 4]; -pub type BLSFieldElement = Uint256; -pub type Blob = FixedVector::BytesPerBlob>; -// Note on List limit: -// - Deneb to Electra: `MaxBlobCommitmentsPerBlock` -// - Fulu: `MaxCellsPerBlock` -// We choose to use a single type (with the larger value from Fulu as `N`) instead of having to -// introduce a new type for Fulu. This is to avoid messy conversions and having to add extra types -// with no gains - as `N` does not impact serialisation at all, and only affects merkleization, -// which we don't current do on `KzgProofs` anyway. -pub type KzgProofs = VariableList::MaxCellsPerBlock>; -pub type VersionedHash = Hash256; -pub type Hash64 = alloy_primitives::B64; +pub mod beacon_block { + pub use crate::block::{BlindedBeaconBlock, BlockImportSource}; +} + +pub mod beacon_block_body { + pub use crate::kzg_ext::{KzgCommitments, format_kzg_commitments}; +} + +pub mod beacon_state { + pub use crate::state::{ + BeaconState, BeaconStateBase, CommitteeCache, compute_committee_index_in_epoch, + compute_committee_range_in_epoch, epoch_committee_count, + }; +} + +pub mod graffiti { + pub use crate::core::GraffitiString; +} + +pub mod indexed_attestation { + pub use crate::attestation::{IndexedAttestationBase, IndexedAttestationElectra}; +} + +pub mod historical_summary { + pub use crate::state::HistoricalSummary; +} + +pub mod participation_flags { + pub use crate::attestation::ParticipationFlags; +} + +pub mod epoch_cache { + pub use crate::state::{EpochCache, EpochCacheError, EpochCacheKey}; +} + +pub mod non_zero_usize { + pub use crate::core::new_non_zero_usize; +} + +pub mod data_column_sidecar { + pub use crate::data::{ + Cell, ColumnIndex, DataColumn, DataColumnSidecar, DataColumnSidecarError, + DataColumnSidecarList, + }; +} + +pub mod builder_bid { + pub use crate::builder::*; +} + +pub mod blob_sidecar { + pub use crate::data::{ + BlobIdentifier, BlobSidecar, BlobSidecarError, BlobsList, FixedBlobSidecarList, + }; +} + +pub mod payload { + pub use crate::execution::BlockProductionVersion; +} + +pub mod execution_requests { + pub use crate::execution::{ + ConsolidationRequests, DepositRequests, ExecutionRequests, RequestType, WithdrawalRequests, + }; +} + +pub mod data_column_custody_group { + pub use crate::data::{ + CustodyIndex, compute_columns_for_custody_group, compute_ordered_custody_column_indices, + compute_subnets_for_node, compute_subnets_from_custody_group, get_custody_groups, + }; +} + +pub mod sync_aggregate { + pub use crate::sync_committee::SyncAggregateError as Error; +} + +pub mod light_client_update { + pub use crate::light_client::consts::{ + CURRENT_SYNC_COMMITTEE_INDEX, CURRENT_SYNC_COMMITTEE_INDEX_ELECTRA, FINALIZED_ROOT_INDEX, + FINALIZED_ROOT_INDEX_ELECTRA, MAX_REQUEST_LIGHT_CLIENT_UPDATES, NEXT_SYNC_COMMITTEE_INDEX, + NEXT_SYNC_COMMITTEE_INDEX_ELECTRA, + }; +} + +pub mod sync_committee_contribution { + pub use crate::sync_committee::{ + SyncCommitteeContributionError as Error, SyncContributionData, + }; +} + +pub mod slot_data { + pub use crate::core::SlotData; +} + +pub mod signed_aggregate_and_proof { + pub use crate::attestation::SignedAggregateAndProofRefMut; +} + +pub mod application_domain { + pub use crate::core::ApplicationDomain; +} + +// Temporary re-exports to maintain backwards compatibility for Lighthouse. +pub use crate::kzg_ext::consts::VERSIONED_HASH_VERSION_KZG; +pub use crate::light_client::LightClientError as LightClientUpdateError; +pub use crate::state::BeaconStateError as Error; pub use bls::{ - AggregatePublicKey, AggregateSignature, Keypair, PublicKey, PublicKeyBytes, SecretKey, - Signature, SignatureBytes, + AggregatePublicKey, AggregateSignature, Error as BlsError, Keypair, PUBLIC_KEY_BYTES_LEN, + PublicKey, PublicKeyBytes, SIGNATURE_BYTES_LEN, SecretKey, Signature, SignatureBytes, + get_withdrawal_credentials, }; pub use context_deserialize::{ContextDeserialize, context_deserialize}; -pub use kzg::{KzgCommitment, KzgProof, VERSIONED_HASH_VERSION_KZG}; +pub use fixed_bytes::FixedBytesExtended; pub use milhouse::{self, List, ProgressiveList, Vector}; pub use ssz_types::{BitList, BitVector, FixedVector, VariableList, typenum, typenum::Unsigned}; pub use superstruct::superstruct; diff --git a/consensus/types/src/light_client/consts.rs b/consensus/types/src/light_client/consts.rs new file mode 100644 index 0000000000..0092e75e87 --- /dev/null +++ b/consensus/types/src/light_client/consts.rs @@ -0,0 +1,21 @@ +pub const FINALIZED_ROOT_PROOF_LEN: usize = 6; +pub const CURRENT_SYNC_COMMITTEE_PROOF_LEN: usize = 5; +pub const NEXT_SYNC_COMMITTEE_PROOF_LEN: usize = 5; +pub const EXECUTION_PAYLOAD_PROOF_LEN: usize = 4; + +pub const FINALIZED_ROOT_PROOF_LEN_ELECTRA: usize = 7; +pub const NEXT_SYNC_COMMITTEE_PROOF_LEN_ELECTRA: usize = 6; +pub const CURRENT_SYNC_COMMITTEE_PROOF_LEN_ELECTRA: usize = 6; + +pub const FINALIZED_ROOT_INDEX: usize = 105; +pub const CURRENT_SYNC_COMMITTEE_INDEX: usize = 54; +pub const NEXT_SYNC_COMMITTEE_INDEX: usize = 55; +pub const EXECUTION_PAYLOAD_INDEX: usize = 25; + +pub const FINALIZED_ROOT_INDEX_ELECTRA: usize = 169; +pub const CURRENT_SYNC_COMMITTEE_INDEX_ELECTRA: usize = 86; +pub const NEXT_SYNC_COMMITTEE_INDEX_ELECTRA: usize = 87; + +// Max light client updates by range request limits +// spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/p2p-interface.md#configuration +pub const MAX_REQUEST_LIGHT_CLIENT_UPDATES: u64 = 128; diff --git a/consensus/types/src/light_client/error.rs b/consensus/types/src/light_client/error.rs new file mode 100644 index 0000000000..c492cfcbde --- /dev/null +++ b/consensus/types/src/light_client/error.rs @@ -0,0 +1,41 @@ +use safe_arith::ArithError; + +use crate::state::BeaconStateError; + +#[derive(Debug, PartialEq, Clone)] +pub enum LightClientError { + SszTypesError(ssz_types::Error), + MilhouseError(milhouse::Error), + BeaconStateError(BeaconStateError), + ArithError(ArithError), + AltairForkNotActive, + NotEnoughSyncCommitteeParticipants, + MismatchingPeriods, + InvalidFinalizedBlock, + BeaconBlockBodyError, + InconsistentFork, +} + +impl From for LightClientError { + fn from(e: ssz_types::Error) -> LightClientError { + LightClientError::SszTypesError(e) + } +} + +impl From for LightClientError { + fn from(e: BeaconStateError) -> LightClientError { + LightClientError::BeaconStateError(e) + } +} + +impl From for LightClientError { + fn from(e: ArithError) -> LightClientError { + LightClientError::ArithError(e) + } +} + +impl From for LightClientError { + fn from(e: milhouse::Error) -> LightClientError { + LightClientError::MilhouseError(e) + } +} diff --git a/consensus/types/src/light_client_bootstrap.rs b/consensus/types/src/light_client/light_client_bootstrap.rs similarity index 88% rename from consensus/types/src/light_client_bootstrap.rs rename to consensus/types/src/light_client/light_client_bootstrap.rs index 80d5bbacf9..847b2a2a96 100644 --- a/consensus/types/src/light_client_bootstrap.rs +++ b/consensus/types/src/light_client/light_client_bootstrap.rs @@ -1,19 +1,30 @@ -use crate::context_deserialize; -use crate::{ - BeaconState, ChainSpec, ContextDeserialize, EthSpec, FixedVector, ForkName, Hash256, - LightClientHeader, LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, - LightClientHeaderElectra, LightClientHeaderFulu, LightClientHeaderGloas, - SignedBlindedBeaconBlock, Slot, SyncCommittee, light_client_update::*, test_utils::TestRandom, -}; +use std::sync::Arc; + +use context_deserialize::{ContextDeserialize, context_deserialize}; use educe::Educe; use serde::{Deserialize, Deserializer, Serialize}; use ssz::{Decode, Encode}; use ssz_derive::{Decode, Encode}; -use std::sync::Arc; +use ssz_types::FixedVector; use superstruct::superstruct; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + block::SignedBlindedBeaconBlock, + core::{ChainSpec, EthSpec, Hash256, Slot}, + fork::ForkName, + light_client::{ + CurrentSyncCommitteeProofLen, CurrentSyncCommitteeProofLenElectra, LightClientError, + LightClientHeader, LightClientHeaderAltair, LightClientHeaderCapella, + LightClientHeaderDeneb, LightClientHeaderElectra, LightClientHeaderFulu, + LightClientHeaderGloas, + }, + state::BeaconState, + sync_committee::SyncCommittee, + test_utils::TestRandom, +}; + /// A LightClientBootstrap is the initializer we send over to light_client nodes /// that are trying to generate their basic storage when booting up. #[superstruct( @@ -142,53 +153,53 @@ impl LightClientBootstrap { current_sync_committee: Arc>, current_sync_committee_branch: Vec, chain_spec: &ChainSpec, - ) -> Result { + ) -> Result { let light_client_bootstrap = match block .fork_name(chain_spec) - .map_err(|_| Error::InconsistentFork)? + .map_err(|_| LightClientError::InconsistentFork)? { - ForkName::Base => return Err(Error::AltairForkNotActive), + ForkName::Base => return Err(LightClientError::AltairForkNotActive), ForkName::Altair | ForkName::Bellatrix => Self::Altair(LightClientBootstrapAltair { header: LightClientHeaderAltair::block_to_light_client_header(block)?, current_sync_committee, current_sync_committee_branch: current_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, }), ForkName::Capella => Self::Capella(LightClientBootstrapCapella { header: LightClientHeaderCapella::block_to_light_client_header(block)?, current_sync_committee, current_sync_committee_branch: current_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, }), ForkName::Deneb => Self::Deneb(LightClientBootstrapDeneb { header: LightClientHeaderDeneb::block_to_light_client_header(block)?, current_sync_committee, current_sync_committee_branch: current_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, }), ForkName::Electra => Self::Electra(LightClientBootstrapElectra { header: LightClientHeaderElectra::block_to_light_client_header(block)?, current_sync_committee, current_sync_committee_branch: current_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, }), ForkName::Fulu => Self::Fulu(LightClientBootstrapFulu { header: LightClientHeaderFulu::block_to_light_client_header(block)?, current_sync_committee, current_sync_committee_branch: current_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, }), ForkName::Gloas => Self::Gloas(LightClientBootstrapGloas { header: LightClientHeaderGloas::block_to_light_client_header(block)?, current_sync_committee, current_sync_committee_branch: current_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, }), }; @@ -199,56 +210,56 @@ impl LightClientBootstrap { beacon_state: &mut BeaconState, block: &SignedBlindedBeaconBlock, chain_spec: &ChainSpec, - ) -> Result { + ) -> Result { let current_sync_committee_branch = beacon_state.compute_current_sync_committee_proof()?; let current_sync_committee = beacon_state.current_sync_committee()?.clone(); let light_client_bootstrap = match block .fork_name(chain_spec) - .map_err(|_| Error::InconsistentFork)? + .map_err(|_| LightClientError::InconsistentFork)? { - ForkName::Base => return Err(Error::AltairForkNotActive), + ForkName::Base => return Err(LightClientError::AltairForkNotActive), ForkName::Altair | ForkName::Bellatrix => Self::Altair(LightClientBootstrapAltair { header: LightClientHeaderAltair::block_to_light_client_header(block)?, current_sync_committee, current_sync_committee_branch: current_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, }), ForkName::Capella => Self::Capella(LightClientBootstrapCapella { header: LightClientHeaderCapella::block_to_light_client_header(block)?, current_sync_committee, current_sync_committee_branch: current_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, }), ForkName::Deneb => Self::Deneb(LightClientBootstrapDeneb { header: LightClientHeaderDeneb::block_to_light_client_header(block)?, current_sync_committee, current_sync_committee_branch: current_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, }), ForkName::Electra => Self::Electra(LightClientBootstrapElectra { header: LightClientHeaderElectra::block_to_light_client_header(block)?, current_sync_committee, current_sync_committee_branch: current_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, }), ForkName::Fulu => Self::Fulu(LightClientBootstrapFulu { header: LightClientHeaderFulu::block_to_light_client_header(block)?, current_sync_committee, current_sync_committee_branch: current_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, }), ForkName::Gloas => Self::Gloas(LightClientBootstrapGloas { header: LightClientHeaderGloas::block_to_light_client_header(block)?, current_sync_committee, current_sync_committee_branch: current_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, }), }; diff --git a/consensus/types/src/light_client_finality_update.rs b/consensus/types/src/light_client/light_client_finality_update.rs similarity index 89% rename from consensus/types/src/light_client_finality_update.rs rename to consensus/types/src/light_client/light_client_finality_update.rs index e58d7f4d72..04374edcd9 100644 --- a/consensus/types/src/light_client_finality_update.rs +++ b/consensus/types/src/light_client/light_client_finality_update.rs @@ -1,21 +1,27 @@ -use super::{EthSpec, FixedVector, Hash256, LightClientHeader, Slot, SyncAggregate}; -use crate::ChainSpec; -use crate::context_deserialize; -use crate::{ - ContextDeserialize, ForkName, LightClientHeaderAltair, LightClientHeaderCapella, - LightClientHeaderDeneb, LightClientHeaderElectra, LightClientHeaderFulu, - LightClientHeaderGloas, SignedBlindedBeaconBlock, light_client_update::*, - test_utils::TestRandom, -}; +use context_deserialize::{ContextDeserialize, context_deserialize}; use educe::Educe; use serde::{Deserialize, Deserializer, Serialize}; use ssz::{Decode, Encode}; use ssz_derive::Decode; use ssz_derive::Encode; +use ssz_types::FixedVector; use superstruct::superstruct; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + block::SignedBlindedBeaconBlock, + core::{ChainSpec, EthSpec, Hash256, Slot}, + fork::ForkName, + light_client::{ + FinalizedRootProofLen, FinalizedRootProofLenElectra, LightClientError, LightClientHeader, + LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, + LightClientHeaderElectra, LightClientHeaderFulu, LightClientHeaderGloas, + }, + sync_committee::SyncAggregate, + test_utils::TestRandom, +}; + #[superstruct( variants(Altair, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( @@ -103,10 +109,10 @@ impl LightClientFinalityUpdate { sync_aggregate: SyncAggregate, signature_slot: Slot, chain_spec: &ChainSpec, - ) -> Result { + ) -> Result { let finality_update = match attested_block .fork_name(chain_spec) - .map_err(|_| Error::InconsistentFork)? + .map_err(|_| LightClientError::InconsistentFork)? { ForkName::Altair | ForkName::Bellatrix => { Self::Altair(LightClientFinalityUpdateAltair { @@ -116,7 +122,9 @@ impl LightClientFinalityUpdate { finalized_header: LightClientHeaderAltair::block_to_light_client_header( finalized_block, )?, - finality_branch: finality_branch.try_into().map_err(Error::SszTypesError)?, + finality_branch: finality_branch + .try_into() + .map_err(LightClientError::SszTypesError)?, sync_aggregate, signature_slot, }) @@ -128,7 +136,9 @@ impl LightClientFinalityUpdate { finalized_header: LightClientHeaderCapella::block_to_light_client_header( finalized_block, )?, - finality_branch: finality_branch.try_into().map_err(Error::SszTypesError)?, + finality_branch: finality_branch + .try_into() + .map_err(LightClientError::SszTypesError)?, sync_aggregate, signature_slot, }), @@ -139,7 +149,9 @@ impl LightClientFinalityUpdate { finalized_header: LightClientHeaderDeneb::block_to_light_client_header( finalized_block, )?, - finality_branch: finality_branch.try_into().map_err(Error::SszTypesError)?, + finality_branch: finality_branch + .try_into() + .map_err(LightClientError::SszTypesError)?, sync_aggregate, signature_slot, }), @@ -150,7 +162,9 @@ impl LightClientFinalityUpdate { finalized_header: LightClientHeaderElectra::block_to_light_client_header( finalized_block, )?, - finality_branch: finality_branch.try_into().map_err(Error::SszTypesError)?, + finality_branch: finality_branch + .try_into() + .map_err(LightClientError::SszTypesError)?, sync_aggregate, signature_slot, }), @@ -161,7 +175,9 @@ impl LightClientFinalityUpdate { finalized_header: LightClientHeaderFulu::block_to_light_client_header( finalized_block, )?, - finality_branch: finality_branch.try_into().map_err(Error::SszTypesError)?, + finality_branch: finality_branch + .try_into() + .map_err(LightClientError::SszTypesError)?, sync_aggregate, signature_slot, }), @@ -172,12 +188,14 @@ impl LightClientFinalityUpdate { finalized_header: LightClientHeaderGloas::block_to_light_client_header( finalized_block, )?, - finality_branch: finality_branch.try_into().map_err(Error::SszTypesError)?, + finality_branch: finality_branch + .try_into() + .map_err(LightClientError::SszTypesError)?, sync_aggregate, signature_slot, }), - ForkName::Base => return Err(Error::AltairForkNotActive), + ForkName::Base => return Err(LightClientError::AltairForkNotActive), }; Ok(finality_update) diff --git a/consensus/types/src/light_client_header.rs b/consensus/types/src/light_client/light_client_header.rs similarity index 91% rename from consensus/types/src/light_client_header.rs rename to consensus/types/src/light_client/light_client_header.rs index 5820efcc91..a7ecd3b7fb 100644 --- a/consensus/types/src/light_client_header.rs +++ b/consensus/types/src/light_client/light_client_header.rs @@ -1,22 +1,27 @@ -use crate::ChainSpec; -use crate::context_deserialize; -use crate::{BeaconBlockBody, light_client_update::*}; -use crate::{BeaconBlockHeader, ExecutionPayloadHeader}; -use crate::{ContextDeserialize, ForkName}; -use crate::{ - EthSpec, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, - ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu, ExecutionPayloadHeaderGloas, - FixedVector, Hash256, SignedBlindedBeaconBlock, test_utils::TestRandom, -}; +use std::marker::PhantomData; + +use context_deserialize::{ContextDeserialize, context_deserialize}; use educe::Educe; use serde::{Deserialize, Deserializer, Serialize}; use ssz::Decode; use ssz_derive::{Decode, Encode}; -use std::marker::PhantomData; +use ssz_types::FixedVector; use superstruct::superstruct; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + block::{BeaconBlockBody, BeaconBlockHeader, SignedBlindedBeaconBlock}, + core::{ChainSpec, EthSpec, Hash256}, + execution::{ + ExecutionPayloadHeader, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, + ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu, ExecutionPayloadHeaderGloas, + }, + fork::ForkName, + light_client::{ExecutionPayloadProofLen, LightClientError, consts::EXECUTION_PAYLOAD_INDEX}, + test_utils::TestRandom, +}; + #[superstruct( variants(Altair, Capella, Deneb, Electra, Fulu, Gloas), variant_attributes( @@ -85,12 +90,12 @@ impl LightClientHeader { pub fn block_to_light_client_header( block: &SignedBlindedBeaconBlock, chain_spec: &ChainSpec, - ) -> Result { + ) -> Result { let header = match block .fork_name(chain_spec) - .map_err(|_| Error::InconsistentFork)? + .map_err(|_| LightClientError::InconsistentFork)? { - ForkName::Base => return Err(Error::AltairForkNotActive), + ForkName::Base => return Err(LightClientError::AltairForkNotActive), ForkName::Altair | ForkName::Bellatrix => LightClientHeader::Altair( LightClientHeaderAltair::block_to_light_client_header(block)?, ), @@ -163,7 +168,7 @@ impl LightClientHeader { impl LightClientHeaderAltair { pub fn block_to_light_client_header( block: &SignedBlindedBeaconBlock, - ) -> Result { + ) -> Result { Ok(LightClientHeaderAltair { beacon: block.message().block_header(), _phantom_data: PhantomData, @@ -183,7 +188,7 @@ impl Default for LightClientHeaderAltair { impl LightClientHeaderCapella { pub fn block_to_light_client_header( block: &SignedBlindedBeaconBlock, - ) -> Result { + ) -> Result { let payload = block .message() .execution_payload()? @@ -194,7 +199,7 @@ impl LightClientHeaderCapella { block .message() .body_capella() - .map_err(|_| Error::BeaconBlockBodyError)? + .map_err(|_| LightClientError::BeaconBlockBodyError)? .to_owned(), ); @@ -225,7 +230,7 @@ impl Default for LightClientHeaderCapella { impl LightClientHeaderDeneb { pub fn block_to_light_client_header( block: &SignedBlindedBeaconBlock, - ) -> Result { + ) -> Result { let header = block .message() .execution_payload()? @@ -236,7 +241,7 @@ impl LightClientHeaderDeneb { block .message() .body_deneb() - .map_err(|_| Error::BeaconBlockBodyError)? + .map_err(|_| LightClientError::BeaconBlockBodyError)? .to_owned(), ); @@ -267,7 +272,7 @@ impl Default for LightClientHeaderDeneb { impl LightClientHeaderElectra { pub fn block_to_light_client_header( block: &SignedBlindedBeaconBlock, - ) -> Result { + ) -> Result { let payload = block .message() .execution_payload()? @@ -278,7 +283,7 @@ impl LightClientHeaderElectra { block .message() .body_electra() - .map_err(|_| Error::BeaconBlockBodyError)? + .map_err(|_| LightClientError::BeaconBlockBodyError)? .to_owned(), ); @@ -309,7 +314,7 @@ impl Default for LightClientHeaderElectra { impl LightClientHeaderFulu { pub fn block_to_light_client_header( block: &SignedBlindedBeaconBlock, - ) -> Result { + ) -> Result { let payload = block .message() .execution_payload()? @@ -320,7 +325,7 @@ impl LightClientHeaderFulu { block .message() .body_fulu() - .map_err(|_| Error::BeaconBlockBodyError)? + .map_err(|_| LightClientError::BeaconBlockBodyError)? .to_owned(), ); @@ -351,7 +356,7 @@ impl Default for LightClientHeaderFulu { impl LightClientHeaderGloas { pub fn block_to_light_client_header( block: &SignedBlindedBeaconBlock, - ) -> Result { + ) -> Result { let payload = block .message() .execution_payload()? @@ -362,7 +367,7 @@ impl LightClientHeaderGloas { block .message() .body_gloas() - .map_err(|_| Error::BeaconBlockBodyError)? + .map_err(|_| LightClientError::BeaconBlockBodyError)? .to_owned(), ); diff --git a/consensus/types/src/light_client_optimistic_update.rs b/consensus/types/src/light_client/light_client_optimistic_update.rs similarity index 94% rename from consensus/types/src/light_client_optimistic_update.rs rename to consensus/types/src/light_client/light_client_optimistic_update.rs index ca9957331f..9266ce647a 100644 --- a/consensus/types/src/light_client_optimistic_update.rs +++ b/consensus/types/src/light_client/light_client_optimistic_update.rs @@ -1,21 +1,26 @@ -use super::{ContextDeserialize, EthSpec, ForkName, LightClientHeader, Slot, SyncAggregate}; -use crate::context_deserialize; -use crate::test_utils::TestRandom; -use crate::{ - ChainSpec, LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, - LightClientHeaderElectra, LightClientHeaderFulu, LightClientHeaderGloas, - SignedBlindedBeaconBlock, light_client_update::*, -}; +use context_deserialize::{ContextDeserialize, context_deserialize}; use educe::Educe; use serde::{Deserialize, Deserializer, Serialize}; use ssz::{Decode, Encode}; -use ssz_derive::Decode; -use ssz_derive::Encode; +use ssz_derive::{Decode, Encode}; use superstruct::superstruct; use test_random_derive::TestRandom; use tree_hash::Hash256; use tree_hash_derive::TreeHash; +use crate::{ + block::SignedBlindedBeaconBlock, + core::{ChainSpec, EthSpec, Slot}, + fork::ForkName, + light_client::{ + LightClientError, LightClientHeader, LightClientHeaderAltair, LightClientHeaderCapella, + LightClientHeaderDeneb, LightClientHeaderElectra, LightClientHeaderFulu, + LightClientHeaderGloas, + }, + sync_committee::SyncAggregate, + test_utils::TestRandom, +}; + /// A LightClientOptimisticUpdate is the update we send on each slot, /// it is based off the current unfinalized epoch is verified only against BLS signature. #[superstruct( @@ -79,10 +84,10 @@ impl LightClientOptimisticUpdate { sync_aggregate: SyncAggregate, signature_slot: Slot, chain_spec: &ChainSpec, - ) -> Result { + ) -> Result { let optimistic_update = match attested_block .fork_name(chain_spec) - .map_err(|_| Error::InconsistentFork)? + .map_err(|_| LightClientError::InconsistentFork)? { ForkName::Altair | ForkName::Bellatrix => { Self::Altair(LightClientOptimisticUpdateAltair { @@ -128,7 +133,7 @@ impl LightClientOptimisticUpdate { sync_aggregate, signature_slot, }), - ForkName::Base => return Err(Error::AltairForkNotActive), + ForkName::Base => return Err(LightClientError::AltairForkNotActive), }; Ok(optimistic_update) diff --git a/consensus/types/src/light_client_update.rs b/consensus/types/src/light_client/light_client_update.rs similarity index 87% rename from consensus/types/src/light_client_update.rs rename to consensus/types/src/light_client/light_client_update.rs index ede9436c50..7fc2c36239 100644 --- a/consensus/types/src/light_client_update.rs +++ b/consensus/types/src/light_client/light_client_update.rs @@ -1,12 +1,6 @@ -use super::{EthSpec, FixedVector, Hash256, Slot, SyncAggregate, SyncCommittee}; -use crate::LightClientHeader; -use crate::context_deserialize; -use crate::light_client_header::LightClientHeaderElectra; -use crate::{ - ChainSpec, ContextDeserialize, Epoch, ForkName, LightClientHeaderAltair, - LightClientHeaderCapella, LightClientHeaderDeneb, LightClientHeaderFulu, - LightClientHeaderGloas, SignedBlindedBeaconBlock, beacon_state, test_utils::TestRandom, -}; +use std::sync::Arc; + +use context_deserialize::{ContextDeserialize, context_deserialize}; use educe::Educe; use safe_arith::ArithError; use safe_arith::SafeArith; @@ -14,20 +8,24 @@ use serde::{Deserialize, Deserializer, Serialize}; use ssz::{Decode, Encode}; use ssz_derive::Decode; use ssz_derive::Encode; +use ssz_types::FixedVector; use ssz_types::typenum::{U4, U5, U6, U7}; -use std::sync::Arc; use superstruct::superstruct; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; -pub const FINALIZED_ROOT_INDEX: usize = 105; -pub const CURRENT_SYNC_COMMITTEE_INDEX: usize = 54; -pub const NEXT_SYNC_COMMITTEE_INDEX: usize = 55; -pub const EXECUTION_PAYLOAD_INDEX: usize = 25; - -pub const FINALIZED_ROOT_INDEX_ELECTRA: usize = 169; -pub const CURRENT_SYNC_COMMITTEE_INDEX_ELECTRA: usize = 86; -pub const NEXT_SYNC_COMMITTEE_INDEX_ELECTRA: usize = 87; +use crate::{ + block::SignedBlindedBeaconBlock, + core::{ChainSpec, Epoch, EthSpec, Hash256, Slot}, + fork::ForkName, + light_client::{ + LightClientError, LightClientHeader, LightClientHeaderAltair, LightClientHeaderCapella, + LightClientHeaderDeneb, LightClientHeaderElectra, LightClientHeaderFulu, + LightClientHeaderGloas, + }, + sync_committee::{SyncAggregate, SyncCommittee}, + test_utils::TestRandom, +}; pub type FinalizedRootProofLen = U6; pub type CurrentSyncCommitteeProofLen = U5; @@ -38,64 +36,12 @@ pub type FinalizedRootProofLenElectra = U7; pub type CurrentSyncCommitteeProofLenElectra = U6; pub type NextSyncCommitteeProofLenElectra = U6; -pub const FINALIZED_ROOT_PROOF_LEN: usize = 6; -pub const CURRENT_SYNC_COMMITTEE_PROOF_LEN: usize = 5; -pub const NEXT_SYNC_COMMITTEE_PROOF_LEN: usize = 5; -pub const EXECUTION_PAYLOAD_PROOF_LEN: usize = 4; - -pub const FINALIZED_ROOT_PROOF_LEN_ELECTRA: usize = 7; -pub const NEXT_SYNC_COMMITTEE_PROOF_LEN_ELECTRA: usize = 6; -pub const CURRENT_SYNC_COMMITTEE_PROOF_LEN_ELECTRA: usize = 6; - -pub type MerkleProof = Vec; -// Max light client updates by range request limits -// spec: https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/p2p-interface.md#configuration -pub const MAX_REQUEST_LIGHT_CLIENT_UPDATES: u64 = 128; - type FinalityBranch = FixedVector; type FinalityBranchElectra = FixedVector; type NextSyncCommitteeBranch = FixedVector; type NextSyncCommitteeBranchElectra = FixedVector; -#[derive(Debug, PartialEq, Clone)] -pub enum Error { - SszTypesError(ssz_types::Error), - MilhouseError(milhouse::Error), - BeaconStateError(beacon_state::Error), - ArithError(ArithError), - AltairForkNotActive, - NotEnoughSyncCommitteeParticipants, - MismatchingPeriods, - InvalidFinalizedBlock, - BeaconBlockBodyError, - InconsistentFork, -} - -impl From for Error { - fn from(e: ssz_types::Error) -> Error { - Error::SszTypesError(e) - } -} - -impl From for Error { - fn from(e: beacon_state::Error) -> Error { - Error::BeaconStateError(e) - } -} - -impl From for Error { - fn from(e: ArithError) -> Error { - Error::ArithError(e) - } -} - -impl From for Error { - fn from(e: milhouse::Error) -> Error { - Error::MilhouseError(e) - } -} - /// A LightClientUpdate is the update we request solely to either complete the bootstrapping process, /// or to sync up to the last committee period, we need to have one ready for each ALTAIR period /// we go over, note: there is no need to keep all of the updates from [ALTAIR_PERIOD, CURRENT_PERIOD]. @@ -238,12 +184,12 @@ impl LightClientUpdate { attested_block: &SignedBlindedBeaconBlock, finalized_block: Option<&SignedBlindedBeaconBlock>, chain_spec: &ChainSpec, - ) -> Result { + ) -> Result { let light_client_update = match attested_block .fork_name(chain_spec) - .map_err(|_| Error::InconsistentFork)? + .map_err(|_| LightClientError::InconsistentFork)? { - ForkName::Base => return Err(Error::AltairForkNotActive), + ForkName::Base => return Err(LightClientError::AltairForkNotActive), fork_name @ ForkName::Altair | fork_name @ ForkName::Bellatrix => { let attested_header = LightClientHeaderAltair::block_to_light_client_header(attested_block)?; @@ -263,9 +209,11 @@ impl LightClientUpdate { next_sync_committee, next_sync_committee_branch: next_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, finalized_header, - finality_branch: finality_branch.try_into().map_err(Error::SszTypesError)?, + finality_branch: finality_branch + .try_into() + .map_err(LightClientError::SszTypesError)?, sync_aggregate: sync_aggregate.clone(), signature_slot: block_slot, }) @@ -289,9 +237,11 @@ impl LightClientUpdate { next_sync_committee, next_sync_committee_branch: next_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, finalized_header, - finality_branch: finality_branch.try_into().map_err(Error::SszTypesError)?, + finality_branch: finality_branch + .try_into() + .map_err(LightClientError::SszTypesError)?, sync_aggregate: sync_aggregate.clone(), signature_slot: block_slot, }) @@ -315,9 +265,11 @@ impl LightClientUpdate { next_sync_committee, next_sync_committee_branch: next_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, finalized_header, - finality_branch: finality_branch.try_into().map_err(Error::SszTypesError)?, + finality_branch: finality_branch + .try_into() + .map_err(LightClientError::SszTypesError)?, sync_aggregate: sync_aggregate.clone(), signature_slot: block_slot, }) @@ -341,9 +293,11 @@ impl LightClientUpdate { next_sync_committee, next_sync_committee_branch: next_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, finalized_header, - finality_branch: finality_branch.try_into().map_err(Error::SszTypesError)?, + finality_branch: finality_branch + .try_into() + .map_err(LightClientError::SszTypesError)?, sync_aggregate: sync_aggregate.clone(), signature_slot: block_slot, }) @@ -367,9 +321,11 @@ impl LightClientUpdate { next_sync_committee, next_sync_committee_branch: next_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, finalized_header, - finality_branch: finality_branch.try_into().map_err(Error::SszTypesError)?, + finality_branch: finality_branch + .try_into() + .map_err(LightClientError::SszTypesError)?, sync_aggregate: sync_aggregate.clone(), signature_slot: block_slot, }) @@ -393,9 +349,11 @@ impl LightClientUpdate { next_sync_committee, next_sync_committee_branch: next_sync_committee_branch .try_into() - .map_err(Error::SszTypesError)?, + .map_err(LightClientError::SszTypesError)?, finalized_header, - finality_branch: finality_branch.try_into().map_err(Error::SszTypesError)?, + finality_branch: finality_branch + .try_into() + .map_err(LightClientError::SszTypesError)?, sync_aggregate: sync_aggregate.clone(), signature_slot: block_slot, }) @@ -452,23 +410,32 @@ impl LightClientUpdate { fn attested_header_sync_committee_period( &self, chain_spec: &ChainSpec, - ) -> Result { + ) -> Result { compute_sync_committee_period_at_slot::(self.attested_header_slot(), chain_spec) - .map_err(Error::ArithError) + .map_err(LightClientError::ArithError) } - fn signature_slot_sync_committee_period(&self, chain_spec: &ChainSpec) -> Result { + fn signature_slot_sync_committee_period( + &self, + chain_spec: &ChainSpec, + ) -> Result { compute_sync_committee_period_at_slot::(*self.signature_slot(), chain_spec) - .map_err(Error::ArithError) + .map_err(LightClientError::ArithError) } - pub fn is_sync_committee_update(&self, chain_spec: &ChainSpec) -> Result { + pub fn is_sync_committee_update( + &self, + chain_spec: &ChainSpec, + ) -> Result { Ok(!self.is_next_sync_committee_branch_empty() && (self.attested_header_sync_committee_period(chain_spec)? == self.signature_slot_sync_committee_period(chain_spec)?)) } - pub fn has_sync_committee_finality(&self, chain_spec: &ChainSpec) -> Result { + pub fn has_sync_committee_finality( + &self, + chain_spec: &ChainSpec, + ) -> Result { Ok( compute_sync_committee_period_at_slot::(self.finalized_header_slot(), chain_spec)? == self.attested_header_sync_committee_period(chain_spec)?, @@ -482,7 +449,7 @@ impl LightClientUpdate { &self, new: &Self, chain_spec: &ChainSpec, - ) -> Result { + ) -> Result { // Compare super majority (> 2/3) sync committee participation let max_active_participants = new.sync_aggregate().sync_committee_bits.len(); @@ -606,6 +573,7 @@ fn compute_sync_committee_period_at_slot( #[cfg(test)] mod tests { use super::*; + use crate::light_client::consts::*; use ssz_types::typenum::Unsigned; // `ssz_tests!` can only be defined once per namespace diff --git a/consensus/types/src/light_client/mod.rs b/consensus/types/src/light_client/mod.rs new file mode 100644 index 0000000000..4e287c2294 --- /dev/null +++ b/consensus/types/src/light_client/mod.rs @@ -0,0 +1,37 @@ +mod error; +mod light_client_bootstrap; +mod light_client_finality_update; +mod light_client_header; +mod light_client_optimistic_update; +mod light_client_update; + +pub mod consts; + +pub use error::LightClientError; +pub use light_client_bootstrap::{ + LightClientBootstrap, LightClientBootstrapAltair, LightClientBootstrapCapella, + LightClientBootstrapDeneb, LightClientBootstrapElectra, LightClientBootstrapFulu, + LightClientBootstrapGloas, +}; +pub use light_client_finality_update::{ + LightClientFinalityUpdate, LightClientFinalityUpdateAltair, LightClientFinalityUpdateCapella, + LightClientFinalityUpdateDeneb, LightClientFinalityUpdateElectra, + LightClientFinalityUpdateFulu, LightClientFinalityUpdateGloas, +}; +pub use light_client_header::{ + LightClientHeader, LightClientHeaderAltair, LightClientHeaderCapella, LightClientHeaderDeneb, + LightClientHeaderElectra, LightClientHeaderFulu, LightClientHeaderGloas, +}; +pub use light_client_optimistic_update::{ + LightClientOptimisticUpdate, LightClientOptimisticUpdateAltair, + LightClientOptimisticUpdateCapella, LightClientOptimisticUpdateDeneb, + LightClientOptimisticUpdateElectra, LightClientOptimisticUpdateFulu, + LightClientOptimisticUpdateGloas, +}; +pub use light_client_update::{ + CurrentSyncCommitteeProofLen, CurrentSyncCommitteeProofLenElectra, ExecutionPayloadProofLen, + FinalizedRootProofLen, FinalizedRootProofLenElectra, LightClientUpdate, + LightClientUpdateAltair, LightClientUpdateCapella, LightClientUpdateDeneb, + LightClientUpdateElectra, LightClientUpdateFulu, LightClientUpdateGloas, + NextSyncCommitteeProofLen, NextSyncCommitteeProofLenElectra, +}; diff --git a/consensus/types/src/runtime_fixed_vector.rs b/consensus/types/src/runtime_fixed_vector.rs deleted file mode 100644 index f562322a3d..0000000000 --- a/consensus/types/src/runtime_fixed_vector.rs +++ /dev/null @@ -1,90 +0,0 @@ -//! Emulates a fixed size array but with the length set at runtime. -//! -//! The length of the list cannot be changed once it is set. - -use std::fmt; -use std::fmt::Debug; - -#[derive(Clone)] -pub struct RuntimeFixedVector { - vec: Vec, - len: usize, -} - -impl Debug for RuntimeFixedVector { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?} (len={})", self.vec, self.len) - } -} - -impl RuntimeFixedVector { - pub fn new(vec: Vec) -> Self { - let len = vec.len(); - Self { vec, len } - } - - pub fn to_vec(&self) -> Vec { - self.vec.clone() - } - - pub fn as_slice(&self) -> &[T] { - self.vec.as_slice() - } - - #[allow(clippy::len_without_is_empty)] - pub fn len(&self) -> usize { - self.len - } - - pub fn into_vec(self) -> Vec { - self.vec - } - - pub fn default(max_len: usize) -> Self { - Self { - vec: vec![T::default(); max_len], - len: max_len, - } - } - - pub fn take(&mut self) -> Self { - let new = std::mem::take(&mut self.vec); - *self = Self::new(vec![T::default(); self.len]); - Self { - vec: new, - len: self.len, - } - } -} - -impl std::ops::Deref for RuntimeFixedVector { - type Target = [T]; - - fn deref(&self) -> &[T] { - &self.vec[..] - } -} - -impl std::ops::DerefMut for RuntimeFixedVector { - fn deref_mut(&mut self) -> &mut [T] { - &mut self.vec[..] - } -} - -impl IntoIterator for RuntimeFixedVector { - type Item = T; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.vec.into_iter() - } -} - -impl<'a, T> IntoIterator for &'a RuntimeFixedVector { - type Item = &'a T; - type IntoIter = std::slice::Iter<'a, T>; - - fn into_iter(self) -> Self::IntoIter { - self.vec.iter() - } -} diff --git a/consensus/types/src/runtime_var_list.rs b/consensus/types/src/runtime_var_list.rs deleted file mode 100644 index e7b846029e..0000000000 --- a/consensus/types/src/runtime_var_list.rs +++ /dev/null @@ -1,387 +0,0 @@ -use crate::ContextDeserialize; -use educe::Educe; -use serde::de::Error as DeError; -use serde::{Deserialize, Deserializer, Serialize}; -use ssz::Decode; -use ssz_types::Error; -use std::fmt; -use std::fmt::Debug; -use std::ops::{Deref, Index, IndexMut}; -use std::slice::SliceIndex; -use tree_hash::{Hash256, MerkleHasher, PackedEncoding, TreeHash, TreeHashType}; - -/// Emulates a SSZ `List`. -/// -/// An ordered, heap-allocated, variable-length, homogeneous collection of `T`, with no more than -/// `max_len` values. -/// -/// To ensure there are no inconsistent states, we do not allow any mutating operation if `max_len` is not set. -/// -/// ## Example -/// -/// ``` -/// use types::{RuntimeVariableList}; -/// -/// let base: Vec = vec![1, 2, 3, 4]; -/// -/// // Create a `RuntimeVariableList` from a `Vec` that has the expected length. -/// let exact: RuntimeVariableList<_> = RuntimeVariableList::new(base.clone(), 4).unwrap(); -/// assert_eq!(&exact[..], &[1, 2, 3, 4]); -/// -/// // Create a `RuntimeVariableList` from a `Vec` that is too long you'll get an error. -/// let err = RuntimeVariableList::new(base.clone(), 3).unwrap_err(); -/// assert_eq!(err, ssz_types::Error::OutOfBounds { i: 4, len: 3 }); -/// -/// // Create a `RuntimeVariableList` from a `Vec` that is shorter than the maximum. -/// let mut long: RuntimeVariableList<_> = RuntimeVariableList::new(base, 5).unwrap(); -/// assert_eq!(&long[..], &[1, 2, 3, 4]); -/// -/// // Push a value to if it does not exceed the maximum -/// long.push(5).unwrap(); -/// assert_eq!(&long[..], &[1, 2, 3, 4, 5]); -/// -/// // Push a value to if it _does_ exceed the maximum. -/// assert!(long.push(6).is_err()); -/// -/// ``` -#[derive(Clone, Serialize, Deserialize, Educe)] -#[educe(PartialEq, Eq, Hash(bound(T: std::hash::Hash)))] -#[serde(transparent)] -pub struct RuntimeVariableList { - vec: Vec, - #[serde(skip)] - max_len: usize, -} - -impl Debug for RuntimeVariableList { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?} (max_len={})", self.vec, self.max_len) - } -} - -impl RuntimeVariableList { - /// Returns `Ok` if the given `vec` equals the fixed length of `Self`. Otherwise returns - /// `Err(OutOfBounds { .. })`. - pub fn new(vec: Vec, max_len: usize) -> Result { - if vec.len() <= max_len { - Ok(Self { vec, max_len }) - } else { - Err(Error::OutOfBounds { - i: vec.len(), - len: max_len, - }) - } - } - - /// Create an empty list with the given `max_len`. - pub fn empty(max_len: usize) -> Self { - Self { - vec: vec![], - max_len, - } - } - - pub fn as_slice(&self) -> &[T] { - self.vec.as_slice() - } - - pub fn as_mut_slice(&mut self) -> &mut [T] { - self.vec.as_mut_slice() - } - - /// Returns the number of values presently in `self`. - pub fn len(&self) -> usize { - self.vec.len() - } - - /// True if `self` does not contain any values. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns the type-level maximum length. - /// - /// Returns `None` if self is uninitialized with a max_len. - pub fn max_len(&self) -> usize { - self.max_len - } - - /// Appends `value` to the back of `self`. - /// - /// Returns `Err(())` when appending `value` would exceed the maximum length. - pub fn push(&mut self, value: T) -> Result<(), Error> { - if self.vec.len() < self.max_len { - self.vec.push(value); - Ok(()) - } else { - Err(Error::OutOfBounds { - i: self.vec.len().saturating_add(1), - len: self.max_len, - }) - } - } -} - -impl RuntimeVariableList { - pub fn from_ssz_bytes(bytes: &[u8], max_len: usize) -> Result { - let vec = if bytes.is_empty() { - vec![] - } else if ::is_ssz_fixed_len() { - let num_items = bytes - .len() - .checked_div(::ssz_fixed_len()) - .ok_or(ssz::DecodeError::ZeroLengthItem)?; - - if num_items > max_len { - return Err(ssz::DecodeError::BytesInvalid(format!( - "RuntimeVariableList of {} items exceeds maximum of {}", - num_items, max_len - ))); - } - - bytes.chunks(::ssz_fixed_len()).try_fold( - Vec::with_capacity(num_items), - |mut vec, chunk| { - vec.push(::from_ssz_bytes(chunk)?); - Ok(vec) - }, - )? - } else { - ssz::decode_list_of_variable_length_items(bytes, Some(max_len))? - }; - Ok(Self { vec, max_len }) - } -} - -impl From> for Vec { - fn from(list: RuntimeVariableList) -> Vec { - list.vec - } -} - -impl> Index for RuntimeVariableList { - type Output = I::Output; - - #[inline] - fn index(&self, index: I) -> &Self::Output { - Index::index(&self.vec, index) - } -} - -impl> IndexMut for RuntimeVariableList { - #[inline] - fn index_mut(&mut self, index: I) -> &mut Self::Output { - IndexMut::index_mut(&mut self.vec, index) - } -} - -impl Deref for RuntimeVariableList { - type Target = [T]; - - fn deref(&self) -> &[T] { - &self.vec[..] - } -} - -impl<'a, T> IntoIterator for &'a RuntimeVariableList { - type Item = &'a T; - type IntoIter = std::slice::Iter<'a, T>; - - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -impl IntoIterator for RuntimeVariableList { - type Item = T; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.vec.into_iter() - } -} - -impl ssz::Encode for RuntimeVariableList -where - T: ssz::Encode, -{ - fn is_ssz_fixed_len() -> bool { - >::is_ssz_fixed_len() - } - - fn ssz_append(&self, buf: &mut Vec) { - self.vec.ssz_append(buf) - } - - fn ssz_fixed_len() -> usize { - >::ssz_fixed_len() - } - - fn ssz_bytes_len(&self) -> usize { - self.vec.ssz_bytes_len() - } -} - -impl<'de, C, T> ContextDeserialize<'de, (C, usize)> for RuntimeVariableList -where - T: ContextDeserialize<'de, C>, - C: Clone, -{ - fn context_deserialize(deserializer: D, context: (C, usize)) -> Result - where - D: Deserializer<'de>, - { - // first parse out a Vec using the Vec impl you already have - let vec: Vec = Vec::context_deserialize(deserializer, context.0)?; - let vec_len = vec.len(); - RuntimeVariableList::new(vec, context.1).map_err(|e| { - DeError::custom(format!( - "RuntimeVariableList length {} exceeds max_len {}: {e:?}", - vec_len, context.1, - )) - }) - } -} - -impl TreeHash for RuntimeVariableList { - fn tree_hash_type() -> tree_hash::TreeHashType { - tree_hash::TreeHashType::List - } - - fn tree_hash_packed_encoding(&self) -> PackedEncoding { - unreachable!("List should never be packed.") - } - - fn tree_hash_packing_factor() -> usize { - unreachable!("List should never be packed.") - } - - fn tree_hash_root(&self) -> Hash256 { - let root = runtime_vec_tree_hash_root::(&self.vec, self.max_len); - - tree_hash::mix_in_length(&root, self.len()) - } -} - -// We can delete this once the upstream `vec_tree_hash_root` is modified to use a runtime max len. -pub fn runtime_vec_tree_hash_root(vec: &[T], max_len: usize) -> Hash256 -where - T: TreeHash, -{ - match T::tree_hash_type() { - TreeHashType::Basic => { - let mut hasher = - MerkleHasher::with_leaves(max_len.div_ceil(T::tree_hash_packing_factor())); - - for item in vec { - hasher - .write(&item.tree_hash_packed_encoding()) - .expect("ssz_types variable vec should not contain more elements than max"); - } - - hasher - .finish() - .expect("ssz_types variable vec should not have a remaining buffer") - } - TreeHashType::Container | TreeHashType::List | TreeHashType::Vector => { - let mut hasher = MerkleHasher::with_leaves(max_len); - - for item in vec { - hasher - .write(item.tree_hash_root().as_slice()) - .expect("ssz_types vec should not contain more elements than max"); - } - - hasher - .finish() - .expect("ssz_types vec should not have a remaining buffer") - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use ssz::*; - use std::fmt::Debug; - - #[test] - fn new() { - let vec = vec![42; 5]; - let fixed: Result, _> = RuntimeVariableList::new(vec, 4); - assert!(fixed.is_err()); - - let vec = vec![42; 3]; - let fixed: Result, _> = RuntimeVariableList::new(vec, 4); - assert!(fixed.is_ok()); - - let vec = vec![42; 4]; - let fixed: Result, _> = RuntimeVariableList::new(vec, 4); - assert!(fixed.is_ok()); - } - - #[test] - fn indexing() { - let vec = vec![1, 2]; - - let mut fixed: RuntimeVariableList = - RuntimeVariableList::new(vec.clone(), 8192).unwrap(); - - assert_eq!(fixed[0], 1); - assert_eq!(&fixed[0..1], &vec[0..1]); - assert_eq!(fixed[..].len(), 2); - - fixed[1] = 3; - assert_eq!(fixed[1], 3); - } - - #[test] - fn length() { - // Too long. - let vec = vec![42; 5]; - let err = RuntimeVariableList::::new(vec.clone(), 4).unwrap_err(); - assert_eq!(err, Error::OutOfBounds { i: 5, len: 4 }); - - let vec = vec![42; 3]; - let fixed: RuntimeVariableList = RuntimeVariableList::new(vec.clone(), 4).unwrap(); - assert_eq!(&fixed[0..3], &vec[..]); - assert_eq!(&fixed[..], &vec![42, 42, 42][..]); - - let vec = vec![]; - let fixed: RuntimeVariableList = RuntimeVariableList::new(vec, 4).unwrap(); - assert_eq!(&fixed[..], &[] as &[u64]); - } - - #[test] - fn deref() { - let vec = vec![0, 2, 4, 6]; - let fixed: RuntimeVariableList = RuntimeVariableList::new(vec, 4).unwrap(); - - assert_eq!(fixed.first(), Some(&0)); - assert_eq!(fixed.get(3), Some(&6)); - assert_eq!(fixed.get(4), None); - } - - #[test] - fn encode() { - let vec: RuntimeVariableList = RuntimeVariableList::new(vec![0; 2], 2).unwrap(); - assert_eq!(vec.as_ssz_bytes(), vec![0, 0, 0, 0]); - assert_eq!( as Encode>::ssz_fixed_len(), 4); - } - - fn round_trip(item: RuntimeVariableList) { - let max_len = item.max_len(); - let encoded = &item.as_ssz_bytes(); - assert_eq!(item.ssz_bytes_len(), encoded.len()); - assert_eq!( - RuntimeVariableList::from_ssz_bytes(encoded, max_len), - Ok(item) - ); - } - - #[test] - fn u16_len_8() { - round_trip::(RuntimeVariableList::new(vec![42; 8], 8).unwrap()); - round_trip::(RuntimeVariableList::new(vec![0; 8], 8).unwrap()); - } -} diff --git a/consensus/types/src/attester_slashing.rs b/consensus/types/src/slashing/attester_slashing.rs similarity index 96% rename from consensus/types/src/attester_slashing.rs rename to consensus/types/src/slashing/attester_slashing.rs index 2bfb65653c..5c214b35f7 100644 --- a/consensus/types/src/attester_slashing.rs +++ b/consensus/types/src/slashing/attester_slashing.rs @@ -1,9 +1,4 @@ -use crate::context_deserialize; -use crate::indexed_attestation::{ - IndexedAttestationBase, IndexedAttestationElectra, IndexedAttestationRef, -}; -use crate::{ContextDeserialize, ForkName}; -use crate::{EthSpec, test_utils::TestRandom}; +use context_deserialize::{ContextDeserialize, context_deserialize}; use educe::Educe; use rand::{Rng, RngCore}; use serde::{Deserialize, Deserializer, Serialize}; @@ -12,6 +7,13 @@ use superstruct::superstruct; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + attestation::{IndexedAttestationBase, IndexedAttestationElectra, IndexedAttestationRef}, + core::EthSpec, + fork::ForkName, + test_utils::TestRandom, +}; + #[superstruct( variants(Base, Electra), variant_attributes( diff --git a/consensus/types/src/slashing/mod.rs b/consensus/types/src/slashing/mod.rs new file mode 100644 index 0000000000..551b8e3137 --- /dev/null +++ b/consensus/types/src/slashing/mod.rs @@ -0,0 +1,8 @@ +mod attester_slashing; +mod proposer_slashing; + +pub use attester_slashing::{ + AttesterSlashing, AttesterSlashingBase, AttesterSlashingElectra, AttesterSlashingOnDisk, + AttesterSlashingRef, AttesterSlashingRefOnDisk, +}; +pub use proposer_slashing::ProposerSlashing; diff --git a/consensus/types/src/proposer_slashing.rs b/consensus/types/src/slashing/proposer_slashing.rs similarity index 86% rename from consensus/types/src/proposer_slashing.rs rename to consensus/types/src/slashing/proposer_slashing.rs index f4d914c1e5..697bd1a9aa 100644 --- a/consensus/types/src/proposer_slashing.rs +++ b/consensus/types/src/slashing/proposer_slashing.rs @@ -1,12 +1,11 @@ -use crate::context_deserialize; -use crate::test_utils::TestRandom; -use crate::{ForkName, SignedBeaconBlockHeader}; - +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{block::SignedBeaconBlockHeader, fork::ForkName, test_utils::TestRandom}; + /// Two conflicting proposals from the same proposer (validator). /// /// Spec v0.12.1 diff --git a/consensus/types/src/activation_queue.rs b/consensus/types/src/state/activation_queue.rs similarity index 95% rename from consensus/types/src/activation_queue.rs rename to consensus/types/src/state/activation_queue.rs index dd3ce5f88c..0d920a20cf 100644 --- a/consensus/types/src/activation_queue.rs +++ b/consensus/types/src/state/activation_queue.rs @@ -1,6 +1,10 @@ -use crate::{ChainSpec, Epoch, Validator}; use std::collections::BTreeSet; +use crate::{ + core::{ChainSpec, Epoch}, + validator::Validator, +}; + /// Activation queue computed during epoch processing for use in the *next* epoch. #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Debug, PartialEq, Eq, Default, Clone)] diff --git a/consensus/types/src/beacon_state/balance.rs b/consensus/types/src/state/balance.rs similarity index 100% rename from consensus/types/src/beacon_state/balance.rs rename to consensus/types/src/state/balance.rs diff --git a/consensus/types/src/beacon_state.rs b/consensus/types/src/state/beacon_state.rs similarity index 88% rename from consensus/types/src/beacon_state.rs rename to consensus/types/src/state/beacon_state.rs index d13e223557..948899c98d 100644 --- a/consensus/types/src/beacon_state.rs +++ b/consensus/types/src/state/beacon_state.rs @@ -1,49 +1,55 @@ -use self::committee_cache::get_active_validator_indices; -use crate::ContextDeserialize; -use crate::FixedBytesExtended; -use crate::historical_summary::HistoricalSummary; -use crate::test_utils::TestRandom; -use crate::*; +use std::{fmt, hash::Hash, mem, sync::Arc}; + +use bls::{AggregatePublicKey, PublicKeyBytes, Signature}; use compare_fields::CompareFields; +use context_deserialize::ContextDeserialize; use educe::Educe; use ethereum_hashing::hash; +use fixed_bytes::FixedBytesExtended; use int_to_bytes::{int_to_bytes4, int_to_bytes8}; use metastruct::{NumFields, metastruct}; -pub use pubkey_cache::PubkeyCache; +use milhouse::{List, Vector}; use safe_arith::{ArithError, SafeArith}; use serde::{Deserialize, Deserializer, Serialize}; use ssz::{Decode, DecodeError, Encode, ssz_encode}; use ssz_derive::{Decode, Encode}; -use std::hash::Hash; -use std::{fmt, mem, sync::Arc}; +use ssz_types::{BitVector, FixedVector, typenum::Unsigned}; use superstruct::superstruct; use swap_or_not_shuffle::compute_shuffled_index; use test_random_derive::TestRandom; +use tracing::instrument; use tree_hash::TreeHash; use tree_hash_derive::TreeHash; -pub use self::committee_cache::{ - CommitteeCache, compute_committee_index_in_epoch, compute_committee_range_in_epoch, - epoch_committee_count, +use crate::{ + attestation::{ + AttestationDuty, BeaconCommittee, Checkpoint, CommitteeIndex, ParticipationFlags, + PendingAttestation, + }, + block::{BeaconBlock, BeaconBlockHeader, SignedBeaconBlockHash}, + consolidation::PendingConsolidation, + core::{ChainSpec, Domain, Epoch, EthSpec, Hash256, RelativeEpoch, RelativeEpochError, Slot}, + deposit::PendingDeposit, + execution::{ + Eth1Data, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella, + ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderFulu, + ExecutionPayloadHeaderGloas, ExecutionPayloadHeaderRef, ExecutionPayloadHeaderRefMut, + }, + fork::{Fork, ForkName, ForkVersionDecode, InconsistentFork, map_fork_name}, + light_client::consts::{ + CURRENT_SYNC_COMMITTEE_INDEX, CURRENT_SYNC_COMMITTEE_INDEX_ELECTRA, FINALIZED_ROOT_INDEX, + FINALIZED_ROOT_INDEX_ELECTRA, NEXT_SYNC_COMMITTEE_INDEX, NEXT_SYNC_COMMITTEE_INDEX_ELECTRA, + }, + state::{ + BlockRootsIter, CommitteeCache, EpochCache, EpochCacheError, ExitCache, HistoricalBatch, + HistoricalSummary, ProgressiveBalancesCache, PubkeyCache, SlashingsCache, + get_active_validator_indices, + }, + sync_committee::{SyncCommittee, SyncDuty}, + test_utils::TestRandom, + validator::Validator, + withdrawal::PendingPartialWithdrawal, }; -pub use crate::beacon_state::balance::Balance; -pub use crate::beacon_state::exit_cache::ExitCache; -pub use crate::beacon_state::progressive_balances_cache::*; -pub use crate::beacon_state::slashings_cache::SlashingsCache; -pub use eth_spec::*; -pub use iter::BlockRootsIter; -pub use milhouse::{List, Vector, interface::Interface}; -use tracing::instrument; - -#[macro_use] -mod committee_cache; -mod balance; -mod exit_cache; -mod iter; -mod progressive_balances_cache; -mod pubkey_cache; -mod slashings_cache; -mod tests; pub const CACHED_EPOCHS: usize = 3; const MAX_RANDOM_BYTE: u64 = (1 << 8) - 1; @@ -53,7 +59,7 @@ pub type Validators = List::ValidatorRegistryLimit> pub type Balances = List::ValidatorRegistryLimit>; #[derive(Debug, PartialEq, Clone)] -pub enum Error { +pub enum BeaconStateError { /// A state for a different hard-fork was required -- a severe logic error. IncorrectStateVariant, EpochOutOfBounds, @@ -197,7 +203,7 @@ enum AllowNextEpoch { } impl AllowNextEpoch { - fn upper_bound_of(self, current_epoch: Epoch) -> Result { + fn upper_bound_of(self, current_epoch: Epoch) -> Result { match self { AllowNextEpoch::True => Ok(current_epoch.safe_add(1)?), AllowNextEpoch::False => Ok(current_epoch), @@ -378,8 +384,14 @@ impl From for Hash256 { num_fields(all()), )) ), - cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"), - partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant"), + cast_error( + ty = "BeaconStateError", + expr = "BeaconStateError::IncorrectStateVariant" + ), + partial_getter_error( + ty = "BeaconStateError", + expr = "BeaconStateError::IncorrectStateVariant" + ), map_ref_mut_into(BeaconStateRef) )] #[cfg_attr( @@ -740,11 +752,11 @@ impl BeaconState { } /// Returns the `tree_hash_root` of the state. - pub fn canonical_root(&mut self) -> Result { + pub fn canonical_root(&mut self) -> Result { self.update_tree_hash_cache() } - pub fn historical_batch(&mut self) -> Result, Error> { + pub fn historical_batch(&mut self) -> Result, BeaconStateError> { // Updating before cloning makes the clone cheap and saves repeated hashing. self.block_roots_mut().apply_updates()?; self.state_roots_mut().apply_updates()?; @@ -758,7 +770,10 @@ impl BeaconState { /// This method ensures the state's pubkey cache is fully up-to-date before checking if the validator /// exists in the registry. If a validator pubkey exists in the validator registry, returns `Some(i)`, /// otherwise returns `None`. - pub fn get_validator_index(&mut self, pubkey: &PublicKeyBytes) -> Result, Error> { + pub fn get_validator_index( + &mut self, + pubkey: &PublicKeyBytes, + ) -> Result, BeaconStateError> { self.update_pubkey_cache()?; Ok(self.pubkey_cache().get(pubkey)) } @@ -783,7 +798,7 @@ impl BeaconState { /// The epoch following `self.current_epoch()`. /// /// Spec v0.12.1 - pub fn next_epoch(&self) -> Result { + pub fn next_epoch(&self) -> Result { Ok(self.current_epoch().safe_add(1)?) } @@ -792,7 +807,7 @@ impl BeaconState { /// Makes use of the committee cache and will fail if no cache exists for the slot's epoch. /// /// Spec v0.12.1 - pub fn get_committee_count_at_slot(&self, slot: Slot) -> Result { + pub fn get_committee_count_at_slot(&self, slot: Slot) -> Result { let cache = self.committee_cache_at_slot(slot)?; Ok(cache.committees_per_slot()) } @@ -800,7 +815,10 @@ impl BeaconState { /// Compute the number of committees in an entire epoch. /// /// Spec v0.12.1 - pub fn get_epoch_committee_count(&self, relative_epoch: RelativeEpoch) -> Result { + pub fn get_epoch_committee_count( + &self, + relative_epoch: RelativeEpoch, + ) -> Result { let cache = self.committee_cache(relative_epoch)?; Ok(cache.epoch_committee_count() as u64) } @@ -813,7 +831,7 @@ impl BeaconState { pub fn get_cached_active_validator_indices( &self, relative_epoch: RelativeEpoch, - ) -> Result<&[usize], Error> { + ) -> Result<&[usize], BeaconStateError> { let cache = self.committee_cache(relative_epoch)?; Ok(cache.active_validator_indices()) @@ -826,7 +844,7 @@ impl BeaconState { &self, epoch: Epoch, spec: &ChainSpec, - ) -> Result, Error> { + ) -> Result, BeaconStateError> { if epoch >= self.compute_activation_exit_epoch(self.current_epoch(), spec)? { Err(BeaconStateError::EpochOutOfBounds) } else { @@ -839,7 +857,10 @@ impl BeaconState { /// Note: the indices are shuffled (i.e., not in ascending order). /// /// Returns an error if that epoch is not cached, or the cache is not initialized. - pub fn get_shuffling(&self, relative_epoch: RelativeEpoch) -> Result<&[usize], Error> { + pub fn get_shuffling( + &self, + relative_epoch: RelativeEpoch, + ) -> Result<&[usize], BeaconStateError> { let cache = self.committee_cache(relative_epoch)?; Ok(cache.shuffling()) @@ -854,14 +875,14 @@ impl BeaconState { &self, slot: Slot, index: CommitteeIndex, - ) -> Result, Error> { + ) -> Result, BeaconStateError> { let epoch = slot.epoch(E::slots_per_epoch()); let relative_epoch = RelativeEpoch::from_epoch(self.current_epoch(), epoch)?; let cache = self.committee_cache(relative_epoch)?; cache .get_beacon_committee(slot, index) - .ok_or(Error::NoCommittee { slot, index }) + .ok_or(BeaconStateError::NoCommittee { slot, index }) } /// Get all of the Beacon committees at a given slot. @@ -872,7 +893,7 @@ impl BeaconState { pub fn get_beacon_committees_at_slot( &self, slot: Slot, - ) -> Result>, Error> { + ) -> Result>, BeaconStateError> { let cache = self.committee_cache_at_slot(slot)?; cache.get_beacon_committees_at_slot(slot) } @@ -885,7 +906,7 @@ impl BeaconState { pub fn get_beacon_committees_at_epoch( &self, relative_epoch: RelativeEpoch, - ) -> Result>, Error> { + ) -> Result>, BeaconStateError> { let cache = self.committee_cache(relative_epoch)?; cache.get_all_beacon_committees() } @@ -901,7 +922,7 @@ impl BeaconState { epoch: Epoch, block_root: Hash256, spec: &ChainSpec, - ) -> Result { + ) -> Result { let decision_slot = spec.proposer_shuffling_decision_slot::(epoch); if self.slot() <= decision_slot { Ok(block_root) @@ -917,7 +938,7 @@ impl BeaconState { &self, epoch: Epoch, head_block_root: Hash256, - ) -> Result { + ) -> Result { let decision_slot = epoch.saturating_sub(1u64).end_slot(E::slots_per_epoch()); if self.slot() <= decision_slot { Ok(head_block_root) @@ -937,11 +958,14 @@ impl BeaconState { &self, block_root: Hash256, spec: &ChainSpec, - ) -> Result { + ) -> Result { self.proposer_shuffling_decision_root_at_epoch(self.current_epoch(), block_root, spec) } - pub fn epoch_cache_decision_root(&self, block_root: Hash256) -> Result { + pub fn epoch_cache_decision_root( + &self, + block_root: Hash256, + ) -> Result { // Epoch cache decision root for the current epoch (N) is the block root at the end of epoch // N - 1. This is the same as the root that determines the next epoch attester shuffling. self.attester_shuffling_decision_root(block_root, RelativeEpoch::Next) @@ -958,7 +982,7 @@ impl BeaconState { &self, block_root: Hash256, relative_epoch: RelativeEpoch, - ) -> Result { + ) -> Result { let decision_slot = self.attester_shuffling_decision_slot(relative_epoch); if self.slot() == decision_slot { Ok(block_root) @@ -985,9 +1009,9 @@ impl BeaconState { indices: &[usize], seed: &[u8], spec: &ChainSpec, - ) -> Result { + ) -> Result { if indices.is_empty() { - return Err(Error::InsufficientValidators); + return Err(BeaconStateError::InsufficientValidators); } let max_effective_balance = spec.max_effective_balance_for_fork(self.fork_name_unchecked()); @@ -1005,10 +1029,10 @@ impl BeaconState { seed, spec.shuffle_round_count, ) - .ok_or(Error::UnableToShuffle)?; + .ok_or(BeaconStateError::UnableToShuffle)?; let candidate_index = *indices .get(shuffled_index) - .ok_or(Error::ShuffleIndexOutOfBounds(shuffled_index))?; + .ok_or(BeaconStateError::ShuffleIndexOutOfBounds(shuffled_index))?; let random_value = self.shuffling_random_value(i, seed)?; let effective_balance = self.get_effective_balance(candidate_index)?; if effective_balance.safe_mul(max_random_value)? @@ -1027,11 +1051,11 @@ impl BeaconState { seed: &[u8], indices: &[usize], spec: &ChainSpec, - ) -> Result, Error> { + ) -> Result, BeaconStateError> { // Regardless of fork, we never support computing proposer indices for past epochs. let current_epoch = self.current_epoch(); if epoch < current_epoch { - return Err(Error::ComputeProposerIndicesPastEpoch { + return Err(BeaconStateError::ComputeProposerIndicesPastEpoch { current_epoch, request_epoch: epoch, }); @@ -1050,17 +1074,19 @@ impl BeaconState { if self.fork_name_unchecked().fulu_enabled() && epoch < current_epoch.safe_add(spec.min_seed_lookahead)? { - return Err(Error::ComputeProposerIndicesInsufficientLookahead { - current_epoch, - request_epoch: epoch, - }); + return Err( + BeaconStateError::ComputeProposerIndicesInsufficientLookahead { + current_epoch, + request_epoch: epoch, + }, + ); } } else { // Pre-Fulu the situation is reversed, we *should not* compute proposer indices using // too much lookahead. To do so would make us vulnerable to changes in the proposer // indices caused by effective balance changes. if epoch >= current_epoch.safe_add(spec.min_seed_lookahead)? { - return Err(Error::ComputeProposerIndicesExcessiveLookahead { + return Err(BeaconStateError::ComputeProposerIndicesExcessiveLookahead { current_epoch, request_epoch: epoch, }); @@ -1083,7 +1109,7 @@ impl BeaconState { /// In Electra and later, the random value is a 16-bit integer stored in a `u64`. /// /// Prior to Electra, the random value is an 8-bit integer stored in a `u64`. - fn shuffling_random_value(&self, i: usize, seed: &[u8]) -> Result { + fn shuffling_random_value(&self, i: usize, seed: &[u8]) -> Result { if self.fork_name_unchecked().electra_enabled() { Self::shuffling_random_u16_electra(i, seed).map(u64::from) } else { @@ -1094,37 +1120,39 @@ impl BeaconState { /// Get a random byte from the given `seed`. /// /// Used by the proposer & sync committee selection functions. - fn shuffling_random_byte(i: usize, seed: &[u8]) -> Result { + fn shuffling_random_byte(i: usize, seed: &[u8]) -> Result { let mut preimage = seed.to_vec(); preimage.append(&mut int_to_bytes8(i.safe_div(32)? as u64)); let index = i.safe_rem(32)?; hash(&preimage) .get(index) .copied() - .ok_or(Error::ShuffleIndexOutOfBounds(index)) + .ok_or(BeaconStateError::ShuffleIndexOutOfBounds(index)) } /// Get two random bytes from the given `seed`. /// /// This is used in place of `shuffling_random_byte` from Electra onwards. - fn shuffling_random_u16_electra(i: usize, seed: &[u8]) -> Result { + fn shuffling_random_u16_electra(i: usize, seed: &[u8]) -> Result { let mut preimage = seed.to_vec(); preimage.append(&mut int_to_bytes8(i.safe_div(16)? as u64)); let offset = i.safe_rem(16)?.safe_mul(2)?; hash(&preimage) .get(offset..offset.safe_add(2)?) - .ok_or(Error::ShuffleIndexOutOfBounds(offset))? + .ok_or(BeaconStateError::ShuffleIndexOutOfBounds(offset))? .try_into() .map(u16::from_le_bytes) - .map_err(|_| Error::ShuffleIndexOutOfBounds(offset)) + .map_err(|_| BeaconStateError::ShuffleIndexOutOfBounds(offset)) } /// Convenience accessor for the `execution_payload_header` as an `ExecutionPayloadHeaderRef`. pub fn latest_execution_payload_header( &self, - ) -> Result, Error> { + ) -> Result, BeaconStateError> { match self { - BeaconState::Base(_) | BeaconState::Altair(_) => Err(Error::IncorrectStateVariant), + BeaconState::Base(_) | BeaconState::Altair(_) => { + Err(BeaconStateError::IncorrectStateVariant) + } BeaconState::Bellatrix(state) => Ok(ExecutionPayloadHeaderRef::Bellatrix( &state.latest_execution_payload_header, )), @@ -1148,9 +1176,11 @@ impl BeaconState { pub fn latest_execution_payload_header_mut( &mut self, - ) -> Result, Error> { + ) -> Result, BeaconStateError> { match self { - BeaconState::Base(_) | BeaconState::Altair(_) => Err(Error::IncorrectStateVariant), + BeaconState::Base(_) | BeaconState::Altair(_) => { + Err(BeaconStateError::IncorrectStateVariant) + } BeaconState::Bellatrix(state) => Ok(ExecutionPayloadHeaderRefMut::Bellatrix( &mut state.latest_execution_payload_header, )), @@ -1181,7 +1211,7 @@ impl BeaconState { index: CommitteeIndex, slot_signature: &Signature, spec: &ChainSpec, - ) -> Result { + ) -> Result { let committee = self.get_beacon_committee(slot, index)?; let modulo = std::cmp::max( 1, @@ -1192,7 +1222,7 @@ impl BeaconState { signature_hash .get(0..8) .and_then(|bytes| bytes.try_into().ok()) - .ok_or(Error::IsAggregatorOutOfBounds)?, + .ok_or(BeaconStateError::IsAggregatorOutOfBounds)?, ); Ok(signature_hash_int.safe_rem(modulo)? == 0) @@ -1201,13 +1231,17 @@ impl BeaconState { /// Returns the beacon proposer index for the `slot` in `self.current_epoch()`. /// /// Spec v1.6.0-alpha.1 - pub fn get_beacon_proposer_index(&self, slot: Slot, spec: &ChainSpec) -> Result { + pub fn get_beacon_proposer_index( + &self, + slot: Slot, + spec: &ChainSpec, + ) -> Result { // Proposer indices are only known for the current epoch, due to the dependence on the // effective balances of validators, which change at every epoch transition. let epoch = slot.epoch(E::slots_per_epoch()); // TODO(EIP-7917): Explore allowing this function to be called with a slot one epoch in the future. if epoch != self.current_epoch() { - return Err(Error::SlotOutOfBounds); + return Err(BeaconStateError::SlotOutOfBounds); } if let Ok(proposer_lookahead) = self.proposer_lookahead() { @@ -1215,7 +1249,7 @@ impl BeaconState { let index = slot.as_usize().safe_rem(E::slots_per_epoch() as usize)?; proposer_lookahead .get(index) - .ok_or(Error::ProposerLookaheadOutOfBounds { i: index }) + .ok_or(BeaconStateError::ProposerLookaheadOutOfBounds { i: index }) .map(|index| *index as usize) } else { // Pre-Fulu @@ -1233,7 +1267,7 @@ impl BeaconState { &self, epoch: Epoch, spec: &ChainSpec, - ) -> Result, Error> { + ) -> Result, BeaconStateError> { // This isn't in the spec, but we remove the footgun that is requesting the current epoch // for a Fulu state. if let Ok(proposer_lookahead) = self.proposer_lookahead() @@ -1263,7 +1297,11 @@ impl BeaconState { /// Compute the seed to use for the beacon proposer selection at the given `slot`. /// /// Spec v0.12.1 - pub fn get_beacon_proposer_seed(&self, slot: Slot, spec: &ChainSpec) -> Result, Error> { + pub fn get_beacon_proposer_seed( + &self, + slot: Slot, + spec: &ChainSpec, + ) -> Result, BeaconStateError> { let epoch = slot.epoch(E::slots_per_epoch()); let mut preimage = self .get_seed(epoch, Domain::BeaconProposer, spec)? @@ -1278,7 +1316,7 @@ impl BeaconState { &self, epoch: Epoch, spec: &ChainSpec, - ) -> Result<&Arc>, Error> { + ) -> Result<&Arc>, BeaconStateError> { let sync_committee_period = epoch.sync_committee_period(spec)?; let current_sync_committee_period = self.current_epoch().sync_committee_period(spec)?; let next_sync_committee_period = current_sync_committee_period.safe_add(1)?; @@ -1288,7 +1326,7 @@ impl BeaconState { } else if sync_committee_period == next_sync_committee_period { self.next_sync_committee() } else { - Err(Error::SyncCommitteeNotKnown { + Err(BeaconStateError::SyncCommitteeNotKnown { current_epoch: self.current_epoch(), epoch, }) @@ -1299,7 +1337,7 @@ impl BeaconState { pub fn get_sync_committee_indices( &mut self, sync_committee: &SyncCommittee, - ) -> Result, Error> { + ) -> Result, BeaconStateError> { self.update_pubkey_cache()?; sync_committee .pubkeys @@ -1307,13 +1345,16 @@ impl BeaconState { .map(|pubkey| { self.pubkey_cache() .get(pubkey) - .ok_or(Error::PubkeyCacheInconsistent) + .ok_or(BeaconStateError::PubkeyCacheInconsistent) }) .collect() } /// Compute the sync committee indices for the next sync committee. - fn get_next_sync_committee_indices(&self, spec: &ChainSpec) -> Result, Error> { + fn get_next_sync_committee_indices( + &self, + spec: &ChainSpec, + ) -> Result, BeaconStateError> { let epoch = self.current_epoch().safe_add(1)?; let active_validator_indices = self.get_active_validator_indices(epoch, spec)?; @@ -1336,10 +1377,10 @@ impl BeaconState { seed.as_slice(), spec.shuffle_round_count, ) - .ok_or(Error::UnableToShuffle)?; + .ok_or(BeaconStateError::UnableToShuffle)?; let candidate_index = *active_validator_indices .get(shuffled_index) - .ok_or(Error::ShuffleIndexOutOfBounds(shuffled_index))?; + .ok_or(BeaconStateError::ShuffleIndexOutOfBounds(shuffled_index))?; let random_value = self.shuffling_random_value(i, seed.as_slice())?; let effective_balance = self.get_validator(candidate_index)?.effective_balance; if effective_balance.safe_mul(max_random_value)? @@ -1353,7 +1394,10 @@ impl BeaconState { } /// Compute the next sync committee. - pub fn get_next_sync_committee(&self, spec: &ChainSpec) -> Result, Error> { + pub fn get_next_sync_committee( + &self, + spec: &ChainSpec, + ) -> Result, BeaconStateError> { let sync_committee_indices = self.get_next_sync_committee_indices(spec)?; let pubkeys = sync_committee_indices @@ -1362,7 +1406,7 @@ impl BeaconState { self.validators() .get(index) .map(|v| v.pubkey) - .ok_or(Error::UnknownValidator(index)) + .ok_or(BeaconStateError::UnknownValidator(index)) }) .collect::, _>>()?; let decompressed_pubkeys = pubkeys @@ -1386,7 +1430,7 @@ impl BeaconState { epoch: Epoch, validator_indices: &[u64], spec: &ChainSpec, - ) -> Result, Error>>, Error> { + ) -> Result, BeaconStateError>>, BeaconStateError> { let sync_committee = self.get_built_sync_committee(epoch, spec)?; Ok(validator_indices @@ -1421,7 +1465,7 @@ impl BeaconState { /// Safely obtains the index for latest block roots, given some `slot`. /// /// Spec v0.12.1 - fn get_latest_block_roots_index(&self, slot: Slot) -> Result { + fn get_latest_block_roots_index(&self, slot: Slot) -> Result { if slot < self.slot() && self.slot() <= slot.safe_add(self.block_roots().len() as u64)? { Ok(slot.as_usize().safe_rem(self.block_roots().len())?) } else { @@ -1441,7 +1485,7 @@ impl BeaconState { let i = self.get_latest_block_roots_index(slot)?; self.block_roots() .get(i) - .ok_or(Error::BlockRootsOutOfBounds(i)) + .ok_or(BeaconStateError::BlockRootsOutOfBounds(i)) } /// Return the block root at a recent `epoch`. @@ -1461,12 +1505,12 @@ impl BeaconState { *self .block_roots_mut() .get_mut(i) - .ok_or(Error::BlockRootsOutOfBounds(i))? = block_root; + .ok_or(BeaconStateError::BlockRootsOutOfBounds(i))? = block_root; Ok(()) } /// Fill `randao_mixes` with - pub fn fill_randao_mixes_with(&mut self, index_root: Hash256) -> Result<(), Error> { + pub fn fill_randao_mixes_with(&mut self, index_root: Hash256) -> Result<(), BeaconStateError> { *self.randao_mixes_mut() = Vector::from_elem(index_root)?; Ok(()) } @@ -1478,7 +1522,7 @@ impl BeaconState { &self, epoch: Epoch, allow_next_epoch: AllowNextEpoch, - ) -> Result { + ) -> Result { let current_epoch = self.current_epoch(); let len = E::EpochsPerHistoricalVector::to_u64(); @@ -1487,7 +1531,7 @@ impl BeaconState { { Ok(epoch.as_usize().safe_rem(len as usize)?) } else { - Err(Error::EpochOutOfBounds) + Err(BeaconStateError::EpochOutOfBounds) } } @@ -1503,7 +1547,11 @@ impl BeaconState { /// # Errors: /// /// See `Self::get_randao_mix`. - pub fn update_randao_mix(&mut self, epoch: Epoch, signature: &Signature) -> Result<(), Error> { + pub fn update_randao_mix( + &mut self, + epoch: Epoch, + signature: &Signature, + ) -> Result<(), BeaconStateError> { let i = epoch .as_usize() .safe_rem(E::EpochsPerHistoricalVector::to_usize())?; @@ -1513,36 +1561,36 @@ impl BeaconState { *self .randao_mixes_mut() .get_mut(i) - .ok_or(Error::RandaoMixesOutOfBounds(i))? = + .ok_or(BeaconStateError::RandaoMixesOutOfBounds(i))? = *self.get_randao_mix(epoch)? ^ signature_hash; Ok(()) } /// Return the randao mix at a recent ``epoch``. - pub fn get_randao_mix(&self, epoch: Epoch) -> Result<&Hash256, Error> { + pub fn get_randao_mix(&self, epoch: Epoch) -> Result<&Hash256, BeaconStateError> { let i = self.get_randao_mix_index(epoch, AllowNextEpoch::False)?; self.randao_mixes() .get(i) - .ok_or(Error::RandaoMixesOutOfBounds(i)) + .ok_or(BeaconStateError::RandaoMixesOutOfBounds(i)) } /// Set the randao mix at a recent ``epoch``. /// /// Spec v0.12.1 - pub fn set_randao_mix(&mut self, epoch: Epoch, mix: Hash256) -> Result<(), Error> { + pub fn set_randao_mix(&mut self, epoch: Epoch, mix: Hash256) -> Result<(), BeaconStateError> { let i = self.get_randao_mix_index(epoch, AllowNextEpoch::True)?; *self .randao_mixes_mut() .get_mut(i) - .ok_or(Error::RandaoMixesOutOfBounds(i))? = mix; + .ok_or(BeaconStateError::RandaoMixesOutOfBounds(i))? = mix; Ok(()) } /// Safely obtains the index for latest state roots, given some `slot`. /// /// Spec v0.12.1 - fn get_latest_state_roots_index(&self, slot: Slot) -> Result { + fn get_latest_state_roots_index(&self, slot: Slot) -> Result { if slot < self.slot() && self.slot() <= slot.safe_add(self.state_roots().len() as u64)? { Ok(slot.as_usize().safe_rem(self.state_roots().len())?) } else { @@ -1551,38 +1599,42 @@ impl BeaconState { } /// Gets the state root for some slot. - pub fn get_state_root(&self, slot: Slot) -> Result<&Hash256, Error> { + pub fn get_state_root(&self, slot: Slot) -> Result<&Hash256, BeaconStateError> { let i = self.get_latest_state_roots_index(slot)?; self.state_roots() .get(i) - .ok_or(Error::StateRootsOutOfBounds(i)) + .ok_or(BeaconStateError::StateRootsOutOfBounds(i)) } /// Gets the state root for the start slot of some epoch. - pub fn get_state_root_at_epoch_start(&self, epoch: Epoch) -> Result { + pub fn get_state_root_at_epoch_start(&self, epoch: Epoch) -> Result { self.get_state_root(epoch.start_slot(E::slots_per_epoch())) .copied() } /// Gets the oldest (earliest slot) state root. - pub fn get_oldest_state_root(&self) -> Result<&Hash256, Error> { + pub fn get_oldest_state_root(&self) -> Result<&Hash256, BeaconStateError> { let oldest_slot = self.slot().saturating_sub(self.state_roots().len()); self.get_state_root(oldest_slot) } /// Gets the oldest (earliest slot) block root. - pub fn get_oldest_block_root(&self) -> Result<&Hash256, Error> { + pub fn get_oldest_block_root(&self) -> Result<&Hash256, BeaconStateError> { let oldest_slot = self.slot().saturating_sub(self.block_roots().len()); self.get_block_root(oldest_slot) } /// Sets the latest state root for slot. - pub fn set_state_root(&mut self, slot: Slot, state_root: Hash256) -> Result<(), Error> { + pub fn set_state_root( + &mut self, + slot: Slot, + state_root: Hash256, + ) -> Result<(), BeaconStateError> { let i = self.get_latest_state_roots_index(slot)?; *self .state_roots_mut() .get_mut(i) - .ok_or(Error::StateRootsOutOfBounds(i))? = state_root; + .ok_or(BeaconStateError::StateRootsOutOfBounds(i))? = state_root; Ok(()) } @@ -1591,7 +1643,7 @@ impl BeaconState { &self, epoch: Epoch, allow_next_epoch: AllowNextEpoch, - ) -> Result { + ) -> Result { // We allow the slashings vector to be accessed at any cached epoch at or before // the current epoch, or the next epoch if `AllowNextEpoch::True` is passed. let current_epoch = self.current_epoch(); @@ -1602,7 +1654,7 @@ impl BeaconState { .as_usize() .safe_rem(E::EpochsPerSlashingsVector::to_usize())?) } else { - Err(Error::EpochOutOfBounds) + Err(BeaconStateError::EpochOutOfBounds) } } @@ -1612,21 +1664,21 @@ impl BeaconState { } /// Get the total slashed balances for some epoch. - pub fn get_slashings(&self, epoch: Epoch) -> Result { + pub fn get_slashings(&self, epoch: Epoch) -> Result { let i = self.get_slashings_index(epoch, AllowNextEpoch::False)?; self.slashings() .get(i) .copied() - .ok_or(Error::SlashingsOutOfBounds(i)) + .ok_or(BeaconStateError::SlashingsOutOfBounds(i)) } /// Set the total slashed balances for some epoch. - pub fn set_slashings(&mut self, epoch: Epoch, value: u64) -> Result<(), Error> { + pub fn set_slashings(&mut self, epoch: Epoch, value: u64) -> Result<(), BeaconStateError> { let i = self.get_slashings_index(epoch, AllowNextEpoch::True)?; *self .slashings_mut() .get_mut(i) - .ok_or(Error::SlashingsOutOfBounds(i))? = value; + .ok_or(BeaconStateError::SlashingsOutOfBounds(i))? = value; Ok(()) } @@ -1666,10 +1718,10 @@ impl BeaconState { &mut ExitCache, &mut EpochCache, ), - Error, + BeaconStateError, > { match self { - BeaconState::Base(_) => Err(Error::IncorrectStateVariant), + BeaconState::Base(_) => Err(BeaconStateError::IncorrectStateVariant), BeaconState::Altair(state) => Ok(( &mut state.validators, &mut state.balances, @@ -1744,18 +1796,21 @@ impl BeaconState { } /// Get the balance of a single validator. - pub fn get_balance(&self, validator_index: usize) -> Result { + pub fn get_balance(&self, validator_index: usize) -> Result { self.balances() .get(validator_index) - .ok_or(Error::BalancesOutOfBounds(validator_index)) + .ok_or(BeaconStateError::BalancesOutOfBounds(validator_index)) .copied() } /// Get a mutable reference to the balance of a single validator. - pub fn get_balance_mut(&mut self, validator_index: usize) -> Result<&mut u64, Error> { + pub fn get_balance_mut( + &mut self, + validator_index: usize, + ) -> Result<&mut u64, BeaconStateError> { self.balances_mut() .get_mut(validator_index) - .ok_or(Error::BalancesOutOfBounds(validator_index)) + .ok_or(BeaconStateError::BalancesOutOfBounds(validator_index)) } /// Generate a seed for the given `epoch`. @@ -1764,7 +1819,7 @@ impl BeaconState { epoch: Epoch, domain_type: Domain, spec: &ChainSpec, - ) -> Result { + ) -> Result { // Bypass the safe getter for RANDAO so we can gracefully handle the scenario where `epoch // == 0`. let mix = { @@ -1775,7 +1830,7 @@ impl BeaconState { let i_mod = i.as_usize().safe_rem(self.randao_mixes().len())?; self.randao_mixes() .get(i_mod) - .ok_or(Error::RandaoMixesOutOfBounds(i_mod))? + .ok_or(BeaconStateError::RandaoMixesOutOfBounds(i_mod))? }; let domain_bytes = int_to_bytes4(spec.get_domain_constant(domain_type)); let epoch_bytes = int_to_bytes8(epoch.as_u64()); @@ -1794,17 +1849,20 @@ impl BeaconState { } /// Safe indexer for the `validators` list. - pub fn get_validator(&self, validator_index: usize) -> Result<&Validator, Error> { + pub fn get_validator(&self, validator_index: usize) -> Result<&Validator, BeaconStateError> { self.validators() .get(validator_index) - .ok_or(Error::UnknownValidator(validator_index)) + .ok_or(BeaconStateError::UnknownValidator(validator_index)) } /// Safe mutator for the `validators` list. - pub fn get_validator_mut(&mut self, validator_index: usize) -> Result<&mut Validator, Error> { + pub fn get_validator_mut( + &mut self, + validator_index: usize, + ) -> Result<&mut Validator, BeaconStateError> { self.validators_mut() .get_mut(validator_index) - .ok_or(Error::UnknownValidator(validator_index)) + .ok_or(BeaconStateError::UnknownValidator(validator_index)) } /// Add a validator to the registry and return the validator index that was allocated for it. @@ -1814,7 +1872,7 @@ impl BeaconState { withdrawal_credentials: Hash256, amount: u64, spec: &ChainSpec, - ) -> Result { + ) -> Result { let index = self.validators().len(); let fork_name = self.fork_name_unchecked(); self.validators_mut().push(Validator::from_deposit( @@ -1846,7 +1904,7 @@ impl BeaconState { if pubkey_cache.len() == index { let success = pubkey_cache.insert(pubkey, index); if !success { - return Err(Error::PubkeyCacheInconsistent); + return Err(BeaconStateError::PubkeyCacheInconsistent); } } @@ -1857,14 +1915,14 @@ impl BeaconState { pub fn get_validator_cow( &mut self, validator_index: usize, - ) -> Result, Error> { + ) -> Result, BeaconStateError> { self.validators_mut() .get_cow(validator_index) - .ok_or(Error::UnknownValidator(validator_index)) + .ok_or(BeaconStateError::UnknownValidator(validator_index)) } /// Return the effective balance for a validator with the given `validator_index`. - pub fn get_effective_balance(&self, validator_index: usize) -> Result { + pub fn get_effective_balance(&self, validator_index: usize) -> Result { self.get_validator(validator_index) .map(|v| v.effective_balance) } @@ -1872,20 +1930,27 @@ impl BeaconState { /// Get the inactivity score for a single validator. /// /// Will error if the state lacks an `inactivity_scores` field. - pub fn get_inactivity_score(&self, validator_index: usize) -> Result { + pub fn get_inactivity_score(&self, validator_index: usize) -> Result { self.inactivity_scores()? .get(validator_index) .copied() - .ok_or(Error::InactivityScoresOutOfBounds(validator_index)) + .ok_or(BeaconStateError::InactivityScoresOutOfBounds( + validator_index, + )) } /// Get a mutable reference to the inactivity score for a single validator. /// /// Will error if the state lacks an `inactivity_scores` field. - pub fn get_inactivity_score_mut(&mut self, validator_index: usize) -> Result<&mut u64, Error> { + pub fn get_inactivity_score_mut( + &mut self, + validator_index: usize, + ) -> Result<&mut u64, BeaconStateError> { self.inactivity_scores_mut()? .get_mut(validator_index) - .ok_or(Error::InactivityScoresOutOfBounds(validator_index)) + .ok_or(BeaconStateError::InactivityScoresOutOfBounds( + validator_index, + )) } /// Return the epoch at which an activation or exit triggered in ``epoch`` takes effect. @@ -1895,14 +1960,14 @@ impl BeaconState { &self, epoch: Epoch, spec: &ChainSpec, - ) -> Result { + ) -> Result { Ok(spec.compute_activation_exit_epoch(epoch)?) } /// Return the churn limit for the current epoch (number of validators who can leave per epoch). /// /// Uses the current epoch committee cache, and will error if it isn't initialized. - pub fn get_validator_churn_limit(&self, spec: &ChainSpec) -> Result { + pub fn get_validator_churn_limit(&self, spec: &ChainSpec) -> Result { Ok(std::cmp::max( spec.min_per_epoch_churn_limit, (self @@ -1915,7 +1980,7 @@ impl BeaconState { /// Return the activation churn limit for the current epoch (number of validators who can enter per epoch). /// /// Uses the current epoch committee cache, and will error if it isn't initialized. - pub fn get_activation_churn_limit(&self, spec: &ChainSpec) -> Result { + pub fn get_activation_churn_limit(&self, spec: &ChainSpec) -> Result { Ok(match self { BeaconState::Base(_) | BeaconState::Altair(_) @@ -1941,7 +2006,7 @@ impl BeaconState { &self, validator_index: usize, relative_epoch: RelativeEpoch, - ) -> Result, Error> { + ) -> Result, BeaconStateError> { let cache = self.committee_cache(relative_epoch)?; Ok(cache.get_attestation_duties(validator_index)) @@ -1951,7 +2016,10 @@ impl BeaconState { /// /// This method should rarely be invoked because single-pass epoch processing keeps the total /// active balance cache up to date. - pub fn compute_total_active_balance_slow(&self, spec: &ChainSpec) -> Result { + pub fn compute_total_active_balance_slow( + &self, + spec: &ChainSpec, + ) -> Result { let current_epoch = self.current_epoch(); let mut total_active_balance = 0; @@ -1973,20 +2041,20 @@ impl BeaconState { /// the current committee cache is. /// /// Returns minimum `EFFECTIVE_BALANCE_INCREMENT`, to avoid div by 0. - pub fn get_total_active_balance(&self) -> Result { + pub fn get_total_active_balance(&self) -> Result { self.get_total_active_balance_at_epoch(self.current_epoch()) } /// Get the cached total active balance while checking that it is for the correct `epoch`. - pub fn get_total_active_balance_at_epoch(&self, epoch: Epoch) -> Result { + pub fn get_total_active_balance_at_epoch(&self, epoch: Epoch) -> Result { let (initialized_epoch, balance) = self .total_active_balance() - .ok_or(Error::TotalActiveBalanceCacheUninitialized)?; + .ok_or(BeaconStateError::TotalActiveBalanceCacheUninitialized)?; if initialized_epoch == epoch { Ok(balance) } else { - Err(Error::TotalActiveBalanceCacheInconsistent { + Err(BeaconStateError::TotalActiveBalanceCacheInconsistent { initialized_epoch, current_epoch: epoch, }) @@ -2006,7 +2074,10 @@ impl BeaconState { /// Build the total active balance cache for the current epoch if it is not already built. #[instrument(skip_all, level = "debug")] - pub fn build_total_active_balance_cache(&mut self, spec: &ChainSpec) -> Result<(), Error> { + pub fn build_total_active_balance_cache( + &mut self, + spec: &ChainSpec, + ) -> Result<(), BeaconStateError> { if self .get_total_active_balance_at_epoch(self.current_epoch()) .is_err() @@ -2020,7 +2091,7 @@ impl BeaconState { pub fn force_build_total_active_balance_cache( &mut self, spec: &ChainSpec, - ) -> Result<(), Error> { + ) -> Result<(), BeaconStateError> { let total_active_balance = self.compute_total_active_balance_slow(spec)?; *self.total_active_balance_mut() = Some((self.current_epoch(), total_active_balance)); Ok(()) @@ -2037,7 +2108,7 @@ impl BeaconState { epoch: Epoch, previous_epoch: Epoch, current_epoch: Epoch, - ) -> Result<&mut List, Error> { + ) -> Result<&mut List, BeaconStateError> { if epoch == current_epoch { match self { BeaconState::Base(_) => Err(BeaconStateError::IncorrectStateVariant), @@ -2067,7 +2138,7 @@ impl BeaconState { /// Build all caches (except the tree hash cache), if they need to be built. #[instrument(skip_all, level = "debug")] - pub fn build_caches(&mut self, spec: &ChainSpec) -> Result<(), Error> { + pub fn build_caches(&mut self, spec: &ChainSpec) -> Result<(), BeaconStateError> { self.build_all_committee_caches(spec)?; self.update_pubkey_cache()?; self.build_exit_cache(spec)?; @@ -2078,7 +2149,7 @@ impl BeaconState { /// Build all committee caches, if they need to be built. #[instrument(skip_all, level = "debug")] - pub fn build_all_committee_caches(&mut self, spec: &ChainSpec) -> Result<(), Error> { + pub fn build_all_committee_caches(&mut self, spec: &ChainSpec) -> Result<(), BeaconStateError> { self.build_committee_cache(RelativeEpoch::Previous, spec)?; self.build_committee_cache(RelativeEpoch::Current, spec)?; self.build_committee_cache(RelativeEpoch::Next, spec)?; @@ -2087,7 +2158,7 @@ impl BeaconState { /// Build the exit cache, if it needs to be built. #[instrument(skip_all, level = "debug")] - pub fn build_exit_cache(&mut self, spec: &ChainSpec) -> Result<(), Error> { + pub fn build_exit_cache(&mut self, spec: &ChainSpec) -> Result<(), BeaconStateError> { if self.exit_cache().check_initialized().is_err() { *self.exit_cache_mut() = ExitCache::new(self.validators(), spec)?; } @@ -2096,7 +2167,7 @@ impl BeaconState { /// Build the slashings cache if it needs to be built. #[instrument(skip_all, level = "debug")] - pub fn build_slashings_cache(&mut self) -> Result<(), Error> { + pub fn build_slashings_cache(&mut self) -> Result<(), BeaconStateError> { let latest_block_slot = self.latest_block_header().slot; if !self.slashings_cache().is_initialized(latest_block_slot) { *self.slashings_cache_mut() = SlashingsCache::new(latest_block_slot, self.validators()); @@ -2110,7 +2181,7 @@ impl BeaconState { } /// Drop all caches on the state. - pub fn drop_all_caches(&mut self) -> Result<(), Error> { + pub fn drop_all_caches(&mut self) -> Result<(), BeaconStateError> { self.drop_total_active_balance_cache(); self.drop_committee_cache(RelativeEpoch::Previous)?; self.drop_committee_cache(RelativeEpoch::Current)?; @@ -2138,7 +2209,7 @@ impl BeaconState { &mut self, relative_epoch: RelativeEpoch, spec: &ChainSpec, - ) -> Result<(), Error> { + ) -> Result<(), BeaconStateError> { let i = Self::committee_cache_index(relative_epoch); let is_initialized = self .committee_cache_at_index(i)? @@ -2159,7 +2230,7 @@ impl BeaconState { &mut self, relative_epoch: RelativeEpoch, spec: &ChainSpec, - ) -> Result<(), Error> { + ) -> Result<(), BeaconStateError> { let epoch = relative_epoch.into_epoch(self.current_epoch()); let i = Self::committee_cache_index(relative_epoch); @@ -2175,7 +2246,7 @@ impl BeaconState { &self, epoch: Epoch, spec: &ChainSpec, - ) -> Result, Error> { + ) -> Result, BeaconStateError> { CommitteeCache::initialized(self, epoch, spec) } @@ -2185,7 +2256,7 @@ impl BeaconState { /// /// Note: this function will not build any new committee caches, nor will it update the total /// active balance cache. The total active balance cache must be updated separately. - pub fn advance_caches(&mut self) -> Result<(), Error> { + pub fn advance_caches(&mut self) -> Result<(), BeaconStateError> { self.committee_caches_mut().rotate_left(1); let next = Self::committee_cache_index(RelativeEpoch::Next); @@ -2204,27 +2275,33 @@ impl BeaconState { /// Get the committee cache for some `slot`. /// /// Return an error if the cache for the slot's epoch is not initialized. - fn committee_cache_at_slot(&self, slot: Slot) -> Result<&Arc, Error> { + fn committee_cache_at_slot( + &self, + slot: Slot, + ) -> Result<&Arc, BeaconStateError> { let epoch = slot.epoch(E::slots_per_epoch()); let relative_epoch = RelativeEpoch::from_epoch(self.current_epoch(), epoch)?; self.committee_cache(relative_epoch) } /// Get the committee cache at a given index. - fn committee_cache_at_index(&self, index: usize) -> Result<&Arc, Error> { + fn committee_cache_at_index( + &self, + index: usize, + ) -> Result<&Arc, BeaconStateError> { self.committee_caches() .get(index) - .ok_or(Error::CommitteeCachesOutOfBounds(index)) + .ok_or(BeaconStateError::CommitteeCachesOutOfBounds(index)) } /// Get a mutable reference to the committee cache at a given index. fn committee_cache_at_index_mut( &mut self, index: usize, - ) -> Result<&mut Arc, Error> { + ) -> Result<&mut Arc, BeaconStateError> { self.committee_caches_mut() .get_mut(index) - .ok_or(Error::CommitteeCachesOutOfBounds(index)) + .ok_or(BeaconStateError::CommitteeCachesOutOfBounds(index)) } /// Returns the cache for some `RelativeEpoch`. Returns an error if the cache has not been @@ -2232,19 +2309,24 @@ impl BeaconState { pub fn committee_cache( &self, relative_epoch: RelativeEpoch, - ) -> Result<&Arc, Error> { + ) -> Result<&Arc, BeaconStateError> { let i = Self::committee_cache_index(relative_epoch); let cache = self.committee_cache_at_index(i)?; if cache.is_initialized_at(relative_epoch.into_epoch(self.current_epoch())) { Ok(cache) } else { - Err(Error::CommitteeCacheUninitialized(Some(relative_epoch))) + Err(BeaconStateError::CommitteeCacheUninitialized(Some( + relative_epoch, + ))) } } /// Drops the cache, leaving it in an uninitialized state. - pub fn drop_committee_cache(&mut self, relative_epoch: RelativeEpoch) -> Result<(), Error> { + pub fn drop_committee_cache( + &mut self, + relative_epoch: RelativeEpoch, + ) -> Result<(), BeaconStateError> { *self.committee_cache_at_index_mut(Self::committee_cache_index(relative_epoch))? = Arc::new(CommitteeCache::default()); Ok(()) @@ -2255,7 +2337,7 @@ impl BeaconState { /// Adds all `pubkeys` from the `validators` which are not already in the cache. Will /// never re-add a pubkey. #[instrument(skip_all, level = "debug")] - pub fn update_pubkey_cache(&mut self) -> Result<(), Error> { + pub fn update_pubkey_cache(&mut self) -> Result<(), BeaconStateError> { let mut pubkey_cache = mem::take(self.pubkey_cache_mut()); let start_index = pubkey_cache.len(); @@ -2263,7 +2345,7 @@ impl BeaconState { let index = start_index.safe_add(i)?; let success = pubkey_cache.insert(validator.pubkey, index); if !success { - return Err(Error::PubkeyCacheInconsistent); + return Err(BeaconStateError::PubkeyCacheInconsistent); } } *self.pubkey_cache_mut() = pubkey_cache; @@ -2341,7 +2423,7 @@ impl BeaconState { /// /// Initialize the tree hash cache if it isn't already initialized. #[instrument(skip_all, level = "debug")] - pub fn update_tree_hash_cache<'a>(&'a mut self) -> Result { + pub fn update_tree_hash_cache<'a>(&'a mut self) -> Result { self.apply_pending_mutations()?; map_beacon_state_ref!(&'a _, self.to_ref(), |inner, cons| { let root = inner.tree_hash_root(); @@ -2353,7 +2435,7 @@ impl BeaconState { /// Compute the tree hash root of the validators using the tree hash cache. /// /// Initialize the tree hash cache if it isn't already initialized. - pub fn update_validators_tree_hash_cache(&mut self) -> Result { + pub fn update_validators_tree_hash_cache(&mut self) -> Result { self.validators_mut().apply_updates()?; Ok(self.validators().tree_hash_root()) } @@ -2364,7 +2446,7 @@ impl BeaconState { &self, previous_epoch: Epoch, val: &Validator, - ) -> Result { + ) -> Result { Ok(val.is_active_at(previous_epoch) || (val.slashed && previous_epoch.safe_add(Epoch::new(1))? < val.withdrawable_epoch)) } @@ -2388,7 +2470,7 @@ impl BeaconState { pub fn get_sync_committee_for_next_slot( &self, spec: &ChainSpec, - ) -> Result>, Error> { + ) -> Result>, BeaconStateError> { let next_slot_epoch = self .slot() .saturating_add(Slot::new(1)) @@ -2414,7 +2496,7 @@ impl BeaconState { // ******* Electra accessors ******* /// Return the churn limit for the current epoch. - pub fn get_balance_churn_limit(&self, spec: &ChainSpec) -> Result { + pub fn get_balance_churn_limit(&self, spec: &ChainSpec) -> Result { let total_active_balance = self.get_total_active_balance()?; let churn = std::cmp::max( spec.min_per_epoch_churn_limit_electra, @@ -2425,20 +2507,26 @@ impl BeaconState { } /// Return the churn limit for the current epoch dedicated to activations and exits. - pub fn get_activation_exit_churn_limit(&self, spec: &ChainSpec) -> Result { + pub fn get_activation_exit_churn_limit( + &self, + spec: &ChainSpec, + ) -> Result { Ok(std::cmp::min( spec.max_per_epoch_activation_exit_churn_limit, self.get_balance_churn_limit(spec)?, )) } - pub fn get_consolidation_churn_limit(&self, spec: &ChainSpec) -> Result { + pub fn get_consolidation_churn_limit(&self, spec: &ChainSpec) -> Result { self.get_balance_churn_limit(spec)? .safe_sub(self.get_activation_exit_churn_limit(spec)?) .map_err(Into::into) } - pub fn get_pending_balance_to_withdraw(&self, validator_index: usize) -> Result { + pub fn get_pending_balance_to_withdraw( + &self, + validator_index: usize, + ) -> Result { let mut pending_balance = 0; for withdrawal in self .pending_partial_withdrawals()? @@ -2456,11 +2544,11 @@ impl BeaconState { &mut self, validator_index: usize, spec: &ChainSpec, - ) -> Result<(), Error> { + ) -> Result<(), BeaconStateError> { let balance = self .balances_mut() .get_mut(validator_index) - .ok_or(Error::UnknownValidator(validator_index))?; + .ok_or(BeaconStateError::UnknownValidator(validator_index))?; if *balance > spec.min_activation_balance { let excess_balance = balance.safe_sub(spec.min_activation_balance)?; *balance = spec.min_activation_balance; @@ -2481,11 +2569,11 @@ impl BeaconState { &mut self, validator_index: usize, spec: &ChainSpec, - ) -> Result<(), Error> { + ) -> Result<(), BeaconStateError> { let validator = self .validators_mut() .get_mut(validator_index) - .ok_or(Error::UnknownValidator(validator_index))?; + .ok_or(BeaconStateError::UnknownValidator(validator_index))?; AsMut::<[u8; 32]>::as_mut(&mut validator.withdrawal_credentials)[0] = spec.compounding_withdrawal_prefix_byte; @@ -2497,7 +2585,7 @@ impl BeaconState { &mut self, exit_balance: u64, spec: &ChainSpec, - ) -> Result { + ) -> Result { let mut earliest_exit_epoch = std::cmp::max( self.earliest_exit_epoch()?, self.compute_activation_exit_epoch(self.current_epoch(), spec)?, @@ -2527,7 +2615,7 @@ impl BeaconState { | BeaconState::Altair(_) | BeaconState::Bellatrix(_) | BeaconState::Capella(_) - | BeaconState::Deneb(_) => Err(Error::IncorrectStateVariant), + | BeaconState::Deneb(_) => Err(BeaconStateError::IncorrectStateVariant), BeaconState::Electra(_) | BeaconState::Fulu(_) | BeaconState::Gloas(_) => { // Consume the balance and update state variables *self.exit_balance_to_consume_mut()? = @@ -2542,7 +2630,7 @@ impl BeaconState { &mut self, consolidation_balance: u64, spec: &ChainSpec, - ) -> Result { + ) -> Result { let mut earliest_consolidation_epoch = std::cmp::max( self.earliest_consolidation_epoch()?, self.compute_activation_exit_epoch(self.current_epoch(), spec)?, @@ -2574,7 +2662,7 @@ impl BeaconState { | BeaconState::Altair(_) | BeaconState::Bellatrix(_) | BeaconState::Capella(_) - | BeaconState::Deneb(_) => Err(Error::IncorrectStateVariant), + | BeaconState::Deneb(_) => Err(BeaconStateError::IncorrectStateVariant), BeaconState::Electra(_) | BeaconState::Fulu(_) | BeaconState::Gloas(_) => { // Consume the balance and update state variables. *self.consolidation_balance_to_consume_mut()? = @@ -2586,7 +2674,7 @@ impl BeaconState { } #[allow(clippy::arithmetic_side_effects)] - pub fn rebase_on(&mut self, base: &Self, spec: &ChainSpec) -> Result<(), Error> { + pub fn rebase_on(&mut self, base: &Self, spec: &ChainSpec) -> Result<(), BeaconStateError> { // Required for macros (which use type-hints internally). match (&mut *self, base) { @@ -2677,7 +2765,11 @@ impl BeaconState { Ok(()) } - pub fn rebase_caches_on(&mut self, base: &Self, spec: &ChainSpec) -> Result<(), Error> { + pub fn rebase_caches_on( + &mut self, + base: &Self, + spec: &ChainSpec, + ) -> Result<(), BeaconStateError> { // Use pubkey cache from `base` if it contains superior information (likely if our cache is // uninitialized). Be careful not to use a cache which has *more* validators than expected, // as other code expects `self.pubkey_cache().len() <= self.validators.len()`. @@ -2766,7 +2858,7 @@ impl BeaconState { } #[allow(clippy::arithmetic_side_effects)] - pub fn apply_pending_mutations(&mut self) -> Result<(), Error> { + pub fn apply_pending_mutations(&mut self) -> Result<(), BeaconStateError> { match self { Self::Base(inner) => { map_beacon_state_base_tree_list_fields!(inner, |_, x| { x.apply_updates() }) @@ -2796,43 +2888,43 @@ impl BeaconState { Ok(()) } - pub fn compute_current_sync_committee_proof(&self) -> Result, Error> { + pub fn compute_current_sync_committee_proof(&self) -> Result, BeaconStateError> { // Sync committees are top-level fields, subtract off the generalized indices // for the internal nodes. Result should be 22 or 23, the field offset of the committee // in the `BeaconState`: // https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#beaconstate let field_gindex = if self.fork_name_unchecked().electra_enabled() { - light_client_update::CURRENT_SYNC_COMMITTEE_INDEX_ELECTRA + CURRENT_SYNC_COMMITTEE_INDEX_ELECTRA } else { - light_client_update::CURRENT_SYNC_COMMITTEE_INDEX + CURRENT_SYNC_COMMITTEE_INDEX }; let field_index = field_gindex.safe_sub(self.num_fields_pow2())?; let leaves = self.get_beacon_state_leaves(); self.generate_proof(field_index, &leaves) } - pub fn compute_next_sync_committee_proof(&self) -> Result, Error> { + pub fn compute_next_sync_committee_proof(&self) -> Result, BeaconStateError> { // Sync committees are top-level fields, subtract off the generalized indices // for the internal nodes. Result should be 22 or 23, the field offset of the committee // in the `BeaconState`: // https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#beaconstate let field_gindex = if self.fork_name_unchecked().electra_enabled() { - light_client_update::NEXT_SYNC_COMMITTEE_INDEX_ELECTRA + NEXT_SYNC_COMMITTEE_INDEX_ELECTRA } else { - light_client_update::NEXT_SYNC_COMMITTEE_INDEX + NEXT_SYNC_COMMITTEE_INDEX }; let field_index = field_gindex.safe_sub(self.num_fields_pow2())?; let leaves = self.get_beacon_state_leaves(); self.generate_proof(field_index, &leaves) } - pub fn compute_finalized_root_proof(&self) -> Result, Error> { + pub fn compute_finalized_root_proof(&self) -> Result, BeaconStateError> { // Finalized root is the right child of `finalized_checkpoint`, divide by two to get // the generalized index of `state.finalized_checkpoint`. let checkpoint_root_gindex = if self.fork_name_unchecked().electra_enabled() { - light_client_update::FINALIZED_ROOT_INDEX_ELECTRA + FINALIZED_ROOT_INDEX_ELECTRA } else { - light_client_update::FINALIZED_ROOT_INDEX + FINALIZED_ROOT_INDEX }; let checkpoint_gindex = checkpoint_root_gindex / 2; @@ -2855,9 +2947,9 @@ impl BeaconState { &self, field_index: usize, leaves: &[Hash256], - ) -> Result, Error> { + ) -> Result, BeaconStateError> { if field_index >= leaves.len() { - return Err(Error::IndexNotSupported(field_index)); + return Err(BeaconStateError::IndexNotSupported(field_index)); } let depth = self.num_fields_pow2().ilog2() as usize; @@ -2916,45 +3008,45 @@ impl BeaconState { } } -impl From for Error { - fn from(e: RelativeEpochError) -> Error { - Error::RelativeEpochError(e) +impl From for BeaconStateError { + fn from(e: RelativeEpochError) -> BeaconStateError { + BeaconStateError::RelativeEpochError(e) } } -impl From for Error { - fn from(e: ssz_types::Error) -> Error { - Error::SszTypesError(e) +impl From for BeaconStateError { + fn from(e: ssz_types::Error) -> BeaconStateError { + BeaconStateError::SszTypesError(e) } } -impl From for Error { - fn from(e: bls::Error) -> Error { - Error::BlsError(e) +impl From for BeaconStateError { + fn from(e: bls::Error) -> BeaconStateError { + BeaconStateError::BlsError(e) } } -impl From for Error { - fn from(e: tree_hash::Error) -> Error { - Error::TreeHashError(e) +impl From for BeaconStateError { + fn from(e: tree_hash::Error) -> BeaconStateError { + BeaconStateError::TreeHashError(e) } } -impl From for Error { - fn from(e: merkle_proof::MerkleTreeError) -> Error { - Error::MerkleTreeError(e) +impl From for BeaconStateError { + fn from(e: merkle_proof::MerkleTreeError) -> BeaconStateError { + BeaconStateError::MerkleTreeError(e) } } -impl From for Error { - fn from(e: ArithError) -> Error { - Error::ArithError(e) +impl From for BeaconStateError { + fn from(e: ArithError) -> BeaconStateError { + BeaconStateError::ArithError(e) } } -impl From for Error { - fn from(e: milhouse::Error) -> Self { - Self::MilhouseError(e) +impl From for BeaconStateError { + fn from(e: milhouse::Error) -> BeaconStateError { + BeaconStateError::MilhouseError(e) } } diff --git a/consensus/types/src/beacon_state/committee_cache.rs b/consensus/types/src/state/committee_cache.rs similarity index 93% rename from consensus/types/src/beacon_state/committee_cache.rs rename to consensus/types/src/state/committee_cache.rs index 408c269da5..15f6a4cd37 100644 --- a/consensus/types/src/beacon_state/committee_cache.rs +++ b/consensus/types/src/state/committee_cache.rs @@ -1,17 +1,20 @@ #![allow(clippy::arithmetic_side_effects)] -use crate::*; -use core::num::NonZeroUsize; +use std::{num::NonZeroUsize, ops::Range, sync::Arc}; + use educe::Educe; use safe_arith::SafeArith; use serde::{Deserialize, Serialize}; use ssz::{Decode, DecodeError, Encode, four_byte_option_impl}; use ssz_derive::{Decode, Encode}; -use std::ops::Range; -use std::sync::Arc; use swap_or_not_shuffle::shuffle_list; -mod tests; +use crate::{ + attestation::{AttestationDuty, BeaconCommittee, CommitteeIndex}, + core::{ChainSpec, Domain, Epoch, EthSpec, Slot}, + state::{BeaconState, BeaconStateError}, + validator::Validator, +}; // Define "legacy" implementations of `Option`, `Option` which use four bytes // for encoding the union selector. @@ -66,7 +69,7 @@ impl CommitteeCache { state: &BeaconState, epoch: Epoch, spec: &ChainSpec, - ) -> Result, Error> { + ) -> Result, BeaconStateError> { // Check that the cache is being built for an in-range epoch. // // We allow caches to be constructed for historic epochs, per: @@ -77,23 +80,23 @@ impl CommitteeCache { .saturating_sub(1u64); if reqd_randao_epoch < state.min_randao_epoch() || epoch > state.current_epoch() + 1 { - return Err(Error::EpochOutOfBounds); + return Err(BeaconStateError::EpochOutOfBounds); } // May cause divide-by-zero errors. if E::slots_per_epoch() == 0 { - return Err(Error::ZeroSlotsPerEpoch); + return Err(BeaconStateError::ZeroSlotsPerEpoch); } // The use of `NonZeroUsize` reduces the maximum number of possible validators by one. if state.validators().len() == usize::MAX { - return Err(Error::TooManyValidators); + return Err(BeaconStateError::TooManyValidators); } let active_validator_indices = get_active_validator_indices(state.validators(), epoch); if active_validator_indices.is_empty() { - return Err(Error::InsufficientValidators); + return Err(BeaconStateError::InsufficientValidators); } let committees_per_slot = @@ -107,13 +110,14 @@ impl CommitteeCache { &seed[..], false, ) - .ok_or(Error::UnableToShuffle)?; + .ok_or(BeaconStateError::UnableToShuffle)?; let mut shuffling_positions = vec![<_>::default(); state.validators().len()]; for (i, &v) in shuffling.iter().enumerate() { *shuffling_positions .get_mut(v) - .ok_or(Error::ShuffleIndexOutOfBounds(v))? = NonZeroUsize::new(i + 1).into(); + .ok_or(BeaconStateError::ShuffleIndexOutOfBounds(v))? = + NonZeroUsize::new(i + 1).into(); } Ok(Arc::new(CommitteeCache { @@ -188,24 +192,24 @@ impl CommitteeCache { pub fn get_beacon_committees_at_slot( &self, slot: Slot, - ) -> Result>, Error> { + ) -> Result>, BeaconStateError> { if self.initialized_epoch.is_none() { - return Err(Error::CommitteeCacheUninitialized(None)); + return Err(BeaconStateError::CommitteeCacheUninitialized(None)); } (0..self.committees_per_slot()) .map(|index| { self.get_beacon_committee(slot, index) - .ok_or(Error::NoCommittee { slot, index }) + .ok_or(BeaconStateError::NoCommittee { slot, index }) }) .collect() } /// Returns all committees for `self.initialized_epoch`. - pub fn get_all_beacon_committees(&self) -> Result>, Error> { + pub fn get_all_beacon_committees(&self) -> Result>, BeaconStateError> { let initialized_epoch = self .initialized_epoch - .ok_or(Error::CommitteeCacheUninitialized(None))?; + .ok_or(BeaconStateError::CommitteeCacheUninitialized(None))?; initialized_epoch.slot_iter(self.slots_per_epoch).try_fold( Vec::with_capacity(self.epoch_committee_count()), diff --git a/consensus/types/src/epoch_cache.rs b/consensus/types/src/state/epoch_cache.rs similarity index 97% rename from consensus/types/src/epoch_cache.rs rename to consensus/types/src/state/epoch_cache.rs index 9956cb400a..cdea0d143d 100644 --- a/consensus/types/src/epoch_cache.rs +++ b/consensus/types/src/state/epoch_cache.rs @@ -1,7 +1,12 @@ -use crate::{ActivationQueue, BeaconStateError, ChainSpec, Epoch, Hash256, Slot}; -use safe_arith::{ArithError, SafeArith}; use std::sync::Arc; +use safe_arith::{ArithError, SafeArith}; + +use crate::{ + core::{ChainSpec, Epoch, Hash256, Slot}, + state::{ActivationQueue, BeaconStateError}, +}; + /// Cache of values which are uniquely determined at the start of an epoch. /// /// The values are fixed with respect to the last block of the _prior_ epoch, which we refer diff --git a/consensus/types/src/beacon_state/exit_cache.rs b/consensus/types/src/state/exit_cache.rs similarity index 97% rename from consensus/types/src/beacon_state/exit_cache.rs rename to consensus/types/src/state/exit_cache.rs index 2828a6138c..43809d1af0 100644 --- a/consensus/types/src/beacon_state/exit_cache.rs +++ b/consensus/types/src/state/exit_cache.rs @@ -1,7 +1,13 @@ -use super::{BeaconStateError, ChainSpec, Epoch, Validator}; -use safe_arith::SafeArith; use std::cmp::Ordering; +use safe_arith::SafeArith; + +use crate::{ + core::{ChainSpec, Epoch}, + state::BeaconStateError, + validator::Validator, +}; + /// Map from exit epoch to the number of validators with that exit epoch. #[derive(Debug, Default, Clone, PartialEq)] pub struct ExitCache { diff --git a/consensus/types/src/historical_batch.rs b/consensus/types/src/state/historical_batch.rs similarity index 81% rename from consensus/types/src/historical_batch.rs rename to consensus/types/src/state/historical_batch.rs index 55377f2489..0167d64f62 100644 --- a/consensus/types/src/historical_batch.rs +++ b/consensus/types/src/state/historical_batch.rs @@ -1,11 +1,16 @@ -use crate::test_utils::TestRandom; -use crate::*; - +use context_deserialize::context_deserialize; +use milhouse::Vector; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{EthSpec, Hash256}, + fork::ForkName, + test_utils::TestRandom, +}; + /// Historical block and state roots. /// /// Spec v0.12.1 @@ -26,6 +31,7 @@ pub struct HistoricalBatch { #[cfg(test)] mod tests { use super::*; + use crate::core::MainnetEthSpec; pub type FoundationHistoricalBatch = HistoricalBatch; diff --git a/consensus/types/src/historical_summary.rs b/consensus/types/src/state/historical_summary.rs similarity index 87% rename from consensus/types/src/historical_summary.rs rename to consensus/types/src/state/historical_summary.rs index dc147ad042..f520e46483 100644 --- a/consensus/types/src/historical_summary.rs +++ b/consensus/types/src/state/historical_summary.rs @@ -1,13 +1,18 @@ -use crate::context_deserialize; -use crate::test_utils::TestRandom; -use crate::{BeaconState, EthSpec, ForkName, Hash256}; use compare_fields::CompareFields; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash::TreeHash; use tree_hash_derive::TreeHash; +use crate::{ + core::{EthSpec, Hash256}, + fork::ForkName, + state::BeaconState, + test_utils::TestRandom, +}; + /// `HistoricalSummary` matches the components of the phase0 `HistoricalBatch` /// making the two hash_tree_root-compatible. This struct is introduced into the beacon state /// in the Capella hard fork. diff --git a/consensus/types/src/beacon_state/iter.rs b/consensus/types/src/state/iter.rs similarity index 96% rename from consensus/types/src/beacon_state/iter.rs rename to consensus/types/src/state/iter.rs index d99c769e40..d761a6bd85 100644 --- a/consensus/types/src/beacon_state/iter.rs +++ b/consensus/types/src/state/iter.rs @@ -1,4 +1,7 @@ -use crate::*; +use crate::{ + core::{EthSpec, Hash256, Slot}, + state::{BeaconState, BeaconStateError}, +}; /// Returns an iterator across the past block roots of `state` in descending slot-order. /// @@ -28,7 +31,7 @@ impl<'a, E: EthSpec> BlockRootsIter<'a, E> { } impl Iterator for BlockRootsIter<'_, E> { - type Item = Result<(Slot, Hash256), Error>; + type Item = Result<(Slot, Hash256), BeaconStateError>; fn next(&mut self) -> Option { if self.prev > self.genesis_slot diff --git a/consensus/types/src/state/mod.rs b/consensus/types/src/state/mod.rs new file mode 100644 index 0000000000..309796d359 --- /dev/null +++ b/consensus/types/src/state/mod.rs @@ -0,0 +1,35 @@ +mod activation_queue; +mod balance; +mod beacon_state; +#[macro_use] +mod committee_cache; +mod epoch_cache; +mod exit_cache; +mod historical_batch; +mod historical_summary; +mod iter; +mod progressive_balances_cache; +mod pubkey_cache; +mod slashings_cache; + +pub use activation_queue::ActivationQueue; +pub use balance::Balance; +pub use beacon_state::{ + BeaconState, BeaconStateAltair, BeaconStateBase, BeaconStateBellatrix, BeaconStateCapella, + BeaconStateDeneb, BeaconStateElectra, BeaconStateError, BeaconStateFulu, BeaconStateGloas, + BeaconStateHash, BeaconStateRef, CACHED_EPOCHS, +}; +pub use committee_cache::{ + CommitteeCache, compute_committee_index_in_epoch, compute_committee_range_in_epoch, + epoch_committee_count, get_active_validator_indices, +}; +pub use epoch_cache::{EpochCache, EpochCacheError, EpochCacheKey}; +pub use exit_cache::ExitCache; +pub use historical_batch::HistoricalBatch; +pub use historical_summary::HistoricalSummary; +pub use iter::BlockRootsIter; +pub use progressive_balances_cache::{ + EpochTotalBalances, ProgressiveBalancesCache, is_progressive_balances_enabled, +}; +pub use pubkey_cache::PubkeyCache; +pub use slashings_cache::SlashingsCache; diff --git a/consensus/types/src/beacon_state/progressive_balances_cache.rs b/consensus/types/src/state/progressive_balances_cache.rs similarity index 98% rename from consensus/types/src/beacon_state/progressive_balances_cache.rs rename to consensus/types/src/state/progressive_balances_cache.rs index 67d1155dbf..1e4c311f9a 100644 --- a/consensus/types/src/beacon_state/progressive_balances_cache.rs +++ b/consensus/types/src/state/progressive_balances_cache.rs @@ -1,15 +1,17 @@ -use crate::beacon_state::balance::Balance; -use crate::{ - BeaconState, BeaconStateError, ChainSpec, Epoch, EthSpec, ParticipationFlags, - consts::altair::{ - NUM_FLAG_INDICES, TIMELY_HEAD_FLAG_INDEX, TIMELY_SOURCE_FLAG_INDEX, - TIMELY_TARGET_FLAG_INDEX, - }, -}; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use safe_arith::SafeArith; +use crate::{ + attestation::ParticipationFlags, + core::consts::altair::{ + NUM_FLAG_INDICES, TIMELY_HEAD_FLAG_INDEX, TIMELY_SOURCE_FLAG_INDEX, + TIMELY_TARGET_FLAG_INDEX, + }, + core::{ChainSpec, Epoch, EthSpec}, + state::{Balance, BeaconState, BeaconStateError}, +}; + /// This cache keeps track of the accumulated target attestation balance for the current & previous /// epochs. The cached values can be utilised by fork choice to calculate unrealized justification /// and finalization instead of converting epoch participation arrays to balances for each block we diff --git a/consensus/types/src/beacon_state/pubkey_cache.rs b/consensus/types/src/state/pubkey_cache.rs similarity index 98% rename from consensus/types/src/beacon_state/pubkey_cache.rs rename to consensus/types/src/state/pubkey_cache.rs index 85ed00340d..e62fafb53a 100644 --- a/consensus/types/src/beacon_state/pubkey_cache.rs +++ b/consensus/types/src/state/pubkey_cache.rs @@ -1,4 +1,4 @@ -use crate::*; +use bls::PublicKeyBytes; use rpds::HashTrieMapSync as HashTrieMap; type ValidatorIndex = usize; diff --git a/consensus/types/src/beacon_state/slashings_cache.rs b/consensus/types/src/state/slashings_cache.rs similarity index 96% rename from consensus/types/src/beacon_state/slashings_cache.rs rename to consensus/types/src/state/slashings_cache.rs index 6530f795e9..b6ed583df8 100644 --- a/consensus/types/src/beacon_state/slashings_cache.rs +++ b/consensus/types/src/state/slashings_cache.rs @@ -1,8 +1,9 @@ -use crate::{BeaconStateError, Slot, Validator}; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; use rpds::HashTrieSetSync as HashTrieSet; +use crate::{core::Slot, state::BeaconStateError, validator::Validator}; + /// Persistent (cheap to clone) cache of all slashed validator indices. #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] #[derive(Debug, Default, Clone, PartialEq)] diff --git a/consensus/types/src/contribution_and_proof.rs b/consensus/types/src/sync_committee/contribution_and_proof.rs similarity index 88% rename from consensus/types/src/contribution_and_proof.rs rename to consensus/types/src/sync_committee/contribution_and_proof.rs index 4d70cd1f8a..2a344b89de 100644 --- a/consensus/types/src/contribution_and_proof.rs +++ b/consensus/types/src/sync_committee/contribution_and_proof.rs @@ -1,14 +1,17 @@ -use super::{ - ChainSpec, EthSpec, Fork, ForkName, Hash256, SecretKey, Signature, SignedRoot, - SyncCommitteeContribution, SyncSelectionProof, -}; -use crate::context_deserialize; -use crate::test_utils::TestRandom; +use bls::{SecretKey, Signature}; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{ChainSpec, EthSpec, Hash256, SignedRoot}, + fork::{Fork, ForkName}, + sync_committee::{SyncCommitteeContribution, SyncSelectionProof}, + test_utils::TestRandom, +}; + /// A Validators aggregate sync committee contribution and selection proof. #[cfg_attr( feature = "arbitrary", diff --git a/consensus/types/src/sync_committee/mod.rs b/consensus/types/src/sync_committee/mod.rs new file mode 100644 index 0000000000..5a75975fe0 --- /dev/null +++ b/consensus/types/src/sync_committee/mod.rs @@ -0,0 +1,25 @@ +mod contribution_and_proof; +mod signed_contribution_and_proof; +mod sync_aggregate; +mod sync_aggregator_selection_data; +mod sync_committee; +mod sync_committee_contribution; +mod sync_committee_message; +mod sync_committee_subscription; +mod sync_duty; +mod sync_selection_proof; +mod sync_subnet_id; + +pub use contribution_and_proof::ContributionAndProof; +pub use signed_contribution_and_proof::SignedContributionAndProof; +pub use sync_aggregate::{Error as SyncAggregateError, SyncAggregate}; +pub use sync_aggregator_selection_data::SyncAggregatorSelectionData; +pub use sync_committee::{Error as SyncCommitteeError, SyncCommittee}; +pub use sync_committee_contribution::{ + Error as SyncCommitteeContributionError, SyncCommitteeContribution, SyncContributionData, +}; +pub use sync_committee_message::SyncCommitteeMessage; +pub use sync_committee_subscription::SyncCommitteeSubscription; +pub use sync_duty::SyncDuty; +pub use sync_selection_proof::SyncSelectionProof; +pub use sync_subnet_id::{SyncSubnetId, sync_subnet_id_to_string}; diff --git a/consensus/types/src/signed_contribution_and_proof.rs b/consensus/types/src/sync_committee/signed_contribution_and_proof.rs similarity index 87% rename from consensus/types/src/signed_contribution_and_proof.rs rename to consensus/types/src/sync_committee/signed_contribution_and_proof.rs index 51c453d32f..0027003b9f 100644 --- a/consensus/types/src/signed_contribution_and_proof.rs +++ b/consensus/types/src/sync_committee/signed_contribution_and_proof.rs @@ -1,14 +1,17 @@ -use super::{ - ChainSpec, ContributionAndProof, Domain, EthSpec, Fork, ForkName, Hash256, SecretKey, - Signature, SignedRoot, SyncCommitteeContribution, SyncSelectionProof, -}; -use crate::context_deserialize; -use crate::test_utils::TestRandom; +use bls::{SecretKey, Signature}; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{ChainSpec, Domain, EthSpec, Hash256, SignedRoot}, + fork::{Fork, ForkName}, + sync_committee::{ContributionAndProof, SyncCommitteeContribution, SyncSelectionProof}, + test_utils::TestRandom, +}; + /// A Validators signed contribution proof to publish on the `sync_committee_contribution_and_proof` /// gossipsub topic. #[cfg_attr( diff --git a/consensus/types/src/sync_aggregate.rs b/consensus/types/src/sync_committee/sync_aggregate.rs similarity index 91% rename from consensus/types/src/sync_aggregate.rs rename to consensus/types/src/sync_committee/sync_aggregate.rs index ba6d840a52..e5848aa22c 100644 --- a/consensus/types/src/sync_aggregate.rs +++ b/consensus/types/src/sync_committee/sync_aggregate.rs @@ -1,14 +1,20 @@ -use crate::consts::altair::SYNC_COMMITTEE_SUBNET_COUNT; -use crate::context_deserialize; -use crate::test_utils::TestRandom; -use crate::{AggregateSignature, BitVector, EthSpec, ForkName, SyncCommitteeContribution}; +use bls::AggregateSignature; +use context_deserialize::context_deserialize; use educe::Educe; use safe_arith::{ArithError, SafeArith}; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; +use ssz_types::BitVector; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{EthSpec, consts::altair::SYNC_COMMITTEE_SUBNET_COUNT}, + fork::ForkName, + sync_committee::SyncCommitteeContribution, + test_utils::TestRandom, +}; + #[derive(Debug, PartialEq)] pub enum Error { SszTypesError(ssz_types::Error), diff --git a/consensus/types/src/sync_aggregator_selection_data.rs b/consensus/types/src/sync_committee/sync_aggregator_selection_data.rs similarity index 82% rename from consensus/types/src/sync_aggregator_selection_data.rs rename to consensus/types/src/sync_committee/sync_aggregator_selection_data.rs index a280369fea..e905ca036b 100644 --- a/consensus/types/src/sync_aggregator_selection_data.rs +++ b/consensus/types/src/sync_committee/sync_aggregator_selection_data.rs @@ -1,11 +1,15 @@ -use crate::context_deserialize; -use crate::test_utils::TestRandom; -use crate::{ForkName, SignedRoot, Slot}; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{SignedRoot, Slot}, + fork::ForkName, + test_utils::TestRandom, +}; + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Debug, PartialEq, Clone, Serialize, Deserialize, Hash, Encode, Decode, TreeHash, TestRandom, diff --git a/consensus/types/src/sync_committee.rs b/consensus/types/src/sync_committee/sync_committee.rs similarity index 95% rename from consensus/types/src/sync_committee.rs rename to consensus/types/src/sync_committee/sync_committee.rs index a9fde42554..5448411800 100644 --- a/consensus/types/src/sync_committee.rs +++ b/consensus/types/src/sync_committee/sync_committee.rs @@ -1,14 +1,16 @@ -use crate::context_deserialize; -use crate::test_utils::TestRandom; -use crate::{EthSpec, FixedVector, ForkName, SyncSubnetId}; +use std::collections::HashMap; + use bls::PublicKeyBytes; +use context_deserialize::context_deserialize; use safe_arith::{ArithError, SafeArith}; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; -use std::collections::HashMap; +use ssz_types::FixedVector; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{core::EthSpec, fork::ForkName, sync_committee::SyncSubnetId, test_utils::TestRandom}; + #[derive(Debug, PartialEq)] pub enum Error { ArithError(ArithError), diff --git a/consensus/types/src/sync_committee_contribution.rs b/consensus/types/src/sync_committee/sync_committee_contribution.rs similarity index 93% rename from consensus/types/src/sync_committee_contribution.rs rename to consensus/types/src/sync_committee/sync_committee_contribution.rs index db22a3bdbc..09376fbe5c 100644 --- a/consensus/types/src/sync_committee_contribution.rs +++ b/consensus/types/src/sync_committee/sync_committee_contribution.rs @@ -1,12 +1,18 @@ -use super::{AggregateSignature, EthSpec, ForkName, SignedRoot}; -use crate::context_deserialize; -use crate::slot_data::SlotData; -use crate::{BitVector, Hash256, Slot, SyncCommitteeMessage, test_utils::TestRandom}; +use bls::AggregateSignature; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; +use ssz_types::BitVector; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{EthSpec, Hash256, SignedRoot, Slot, SlotData}, + fork::ForkName, + sync_committee::SyncCommitteeMessage, + test_utils::TestRandom, +}; + #[derive(Debug, PartialEq)] pub enum Error { SszTypesError(ssz_types::Error), diff --git a/consensus/types/src/sync_committee_message.rs b/consensus/types/src/sync_committee/sync_committee_message.rs similarity index 88% rename from consensus/types/src/sync_committee_message.rs rename to consensus/types/src/sync_committee/sync_committee_message.rs index d5bb7250bb..ed42555c43 100644 --- a/consensus/types/src/sync_committee_message.rs +++ b/consensus/types/src/sync_committee/sync_committee_message.rs @@ -1,14 +1,16 @@ -use crate::context_deserialize; -use crate::slot_data::SlotData; -use crate::test_utils::TestRandom; -use crate::{ - ChainSpec, Domain, EthSpec, Fork, ForkName, Hash256, SecretKey, Signature, SignedRoot, Slot, -}; +use bls::{SecretKey, Signature}; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{ChainSpec, Domain, EthSpec, Hash256, SignedRoot, Slot, SlotData}, + fork::{Fork, ForkName}, + test_utils::TestRandom, +}; + /// The data upon which a `SyncCommitteeContribution` is based. #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom)] diff --git a/consensus/types/src/sync_committee_subscription.rs b/consensus/types/src/sync_committee/sync_committee_subscription.rs similarity index 96% rename from consensus/types/src/sync_committee_subscription.rs rename to consensus/types/src/sync_committee/sync_committee_subscription.rs index 8e040279d7..6365b015dd 100644 --- a/consensus/types/src/sync_committee_subscription.rs +++ b/consensus/types/src/sync_committee/sync_committee_subscription.rs @@ -1,7 +1,8 @@ -use crate::Epoch; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; +use crate::core::Epoch; + /// A sync committee subscription created when a validator subscribes to sync committee subnets to perform /// sync committee duties. #[derive(PartialEq, Debug, Serialize, Deserialize, Clone, Encode, Decode)] diff --git a/consensus/types/src/sync_duty.rs b/consensus/types/src/sync_committee/sync_duty.rs similarity index 96% rename from consensus/types/src/sync_duty.rs rename to consensus/types/src/sync_committee/sync_duty.rs index 59fbc960db..773cc008f9 100644 --- a/consensus/types/src/sync_duty.rs +++ b/consensus/types/src/sync_committee/sync_duty.rs @@ -1,8 +1,13 @@ -use crate::{EthSpec, SyncCommittee, SyncSubnetId}; +use std::collections::HashSet; + use bls::PublicKeyBytes; use safe_arith::ArithError; use serde::{Deserialize, Serialize}; -use std::collections::HashSet; + +use crate::{ + core::EthSpec, + sync_committee::{SyncCommittee, SyncSubnetId}, +}; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct SyncDuty { diff --git a/consensus/types/src/sync_selection_proof.rs b/consensus/types/src/sync_committee/sync_selection_proof.rs similarity index 92% rename from consensus/types/src/sync_selection_proof.rs rename to consensus/types/src/sync_committee/sync_selection_proof.rs index b1e9e8186f..7efc6c4c76 100644 --- a/consensus/types/src/sync_selection_proof.rs +++ b/consensus/types/src/sync_committee/sync_selection_proof.rs @@ -1,16 +1,20 @@ -use crate::consts::altair::{ - SYNC_COMMITTEE_SUBNET_COUNT, TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE, -}; -use crate::{ - ChainSpec, Domain, EthSpec, Fork, Hash256, PublicKey, SecretKey, Signature, SignedRoot, Slot, - SyncAggregatorSelectionData, -}; +use std::cmp; + +use bls::{PublicKey, SecretKey, Signature}; use ethereum_hashing::hash; use safe_arith::{ArithError, SafeArith}; use serde::{Deserialize, Serialize}; use ssz::Encode; use ssz_types::typenum::Unsigned; -use std::cmp; + +use crate::{ + core::{ + ChainSpec, Domain, EthSpec, Hash256, SignedRoot, Slot, + consts::altair::{SYNC_COMMITTEE_SUBNET_COUNT, TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE}, + }, + fork::Fork, + sync_committee::SyncAggregatorSelectionData, +}; #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] diff --git a/consensus/types/src/sync_subnet_id.rs b/consensus/types/src/sync_committee/sync_subnet_id.rs similarity index 92% rename from consensus/types/src/sync_subnet_id.rs rename to consensus/types/src/sync_committee/sync_subnet_id.rs index 3d0d853fca..fb58146178 100644 --- a/consensus/types/src/sync_subnet_id.rs +++ b/consensus/types/src/sync_committee/sync_subnet_id.rs @@ -1,13 +1,16 @@ //! Identifies each sync committee subnet by an integer identifier. -use crate::EthSpec; -use crate::consts::altair::SYNC_COMMITTEE_SUBNET_COUNT; +use std::{ + collections::HashSet, + fmt::{self, Display}, + ops::{Deref, DerefMut}, + sync::LazyLock, +}; + use safe_arith::{ArithError, SafeArith}; use serde::{Deserialize, Serialize}; use ssz_types::typenum::Unsigned; -use std::collections::HashSet; -use std::fmt::{self, Display}; -use std::ops::{Deref, DerefMut}; -use std::sync::LazyLock; + +use crate::core::{EthSpec, consts::altair::SYNC_COMMITTEE_SUBNET_COUNT}; static SYNC_SUBNET_ID_TO_STRING: LazyLock> = LazyLock::new(|| { let mut v = Vec::with_capacity(SYNC_COMMITTEE_SUBNET_COUNT as usize); diff --git a/consensus/types/src/test_utils/generate_deterministic_keypairs.rs b/consensus/types/src/test_utils/generate_deterministic_keypairs.rs index f30afda257..5ccd748c25 100644 --- a/consensus/types/src/test_utils/generate_deterministic_keypairs.rs +++ b/consensus/types/src/test_utils/generate_deterministic_keypairs.rs @@ -1,7 +1,8 @@ -use crate::*; +use std::path::PathBuf; + +use bls::Keypair; use eth2_interop_keypairs::{keypair, keypairs_from_yaml_file}; use rayon::prelude::*; -use std::path::PathBuf; use tracing::debug; /// Generates `validator_count` keypairs where the secret key is derived solely from the index of diff --git a/consensus/types/src/test_utils/generate_random_block_and_blobs.rs b/consensus/types/src/test_utils/generate_random_block_and_blobs.rs index 8f4908291e..cf7b5df891 100644 --- a/consensus/types/src/test_utils/generate_random_block_and_blobs.rs +++ b/consensus/types/src/test_utils/generate_random_block_and_blobs.rs @@ -1,11 +1,16 @@ +use bls::Signature; +use kzg::{KzgCommitment, KzgProof}; use rand::Rng; -use kzg::{KzgCommitment, KzgProof}; - -use crate::beacon_block_body::KzgCommitments; -use crate::*; - -use super::*; +use crate::{ + block::{BeaconBlock, SignedBeaconBlock}, + core::{EthSpec, MainnetEthSpec}, + data::{Blob, BlobSidecar, BlobsList}, + execution::FullPayload, + fork::{ForkName, map_fork_name}, + kzg_ext::{KzgCommitments, KzgProofs}, + test_utils::TestRandom, +}; type BlobsBundle = (KzgCommitments, KzgProofs, BlobsList); @@ -73,6 +78,7 @@ pub fn generate_blobs(n_blobs: usize) -> Result, Stri mod test { use super::*; use rand::rng; + use ssz_types::FixedVector; #[test] fn test_verify_blob_inclusion_proof() { diff --git a/consensus/types/src/test_utils/mod.rs b/consensus/types/src/test_utils/mod.rs index 37d58d4342..c4409b4392 100644 --- a/consensus/types/src/test_utils/mod.rs +++ b/consensus/types/src/test_utils/mod.rs @@ -1,17 +1,5 @@ #![allow(clippy::arithmetic_side_effects)] -use std::fmt::Debug; - -pub use rand::{RngCore, SeedableRng}; -pub use rand_xorshift::XorShiftRng; - -pub use generate_deterministic_keypairs::generate_deterministic_keypair; -pub use generate_deterministic_keypairs::generate_deterministic_keypairs; -pub use generate_deterministic_keypairs::load_keypairs_from_yaml; -use ssz::{Decode, Encode, ssz_encode}; -pub use test_random::{TestRandom, test_random_instance}; -use tree_hash::TreeHash; - #[macro_use] mod macros; mod generate_deterministic_keypairs; @@ -19,6 +7,18 @@ mod generate_deterministic_keypairs; mod generate_random_block_and_blobs; mod test_random; +pub use generate_deterministic_keypairs::generate_deterministic_keypair; +pub use generate_deterministic_keypairs::generate_deterministic_keypairs; +pub use generate_deterministic_keypairs::load_keypairs_from_yaml; +pub use test_random::{TestRandom, test_random_instance}; + +pub use rand::{RngCore, SeedableRng}; +pub use rand_xorshift::XorShiftRng; + +use ssz::{Decode, Encode, ssz_encode}; +use std::fmt::Debug; +use tree_hash::TreeHash; + pub fn test_ssz_tree_hash_pair(v1: &T, v2: &U) where T: TreeHash + Encode + Decode + Debug + PartialEq, diff --git a/consensus/types/src/test_utils/test_random/address.rs b/consensus/types/src/test_utils/test_random/address.rs index 421801ce53..2f601cb91e 100644 --- a/consensus/types/src/test_utils/test_random/address.rs +++ b/consensus/types/src/test_utils/test_random/address.rs @@ -1,7 +1,7 @@ -use super::*; +use crate::{core::Address, test_utils::TestRandom}; impl TestRandom for Address { - fn random_for_test(rng: &mut impl RngCore) -> Self { + fn random_for_test(rng: &mut impl rand::RngCore) -> Self { let mut key_bytes = vec![0; 20]; rng.fill_bytes(&mut key_bytes); Address::from_slice(&key_bytes[..]) diff --git a/consensus/types/src/test_utils/test_random/aggregate_signature.rs b/consensus/types/src/test_utils/test_random/aggregate_signature.rs index 772f284431..f9f3dd9567 100644 --- a/consensus/types/src/test_utils/test_random/aggregate_signature.rs +++ b/consensus/types/src/test_utils/test_random/aggregate_signature.rs @@ -1,7 +1,9 @@ -use super::*; +use bls::{AggregateSignature, Signature}; + +use crate::test_utils::TestRandom; impl TestRandom for AggregateSignature { - fn random_for_test(rng: &mut impl RngCore) -> Self { + fn random_for_test(rng: &mut impl rand::RngCore) -> Self { let signature = Signature::random_for_test(rng); let mut aggregate_signature = AggregateSignature::infinity(); aggregate_signature.add_assign(&signature); diff --git a/consensus/types/src/test_utils/test_random/bitfield.rs b/consensus/types/src/test_utils/test_random/bitfield.rs index e335ac7fe8..3bc0d37c62 100644 --- a/consensus/types/src/test_utils/test_random/bitfield.rs +++ b/consensus/types/src/test_utils/test_random/bitfield.rs @@ -1,8 +1,10 @@ -use super::*; use smallvec::smallvec; +use ssz_types::{BitList, BitVector, typenum::Unsigned}; + +use crate::test_utils::TestRandom; impl TestRandom for BitList { - fn random_for_test(rng: &mut impl RngCore) -> Self { + fn random_for_test(rng: &mut impl rand::RngCore) -> Self { let initial_len = std::cmp::max(1, N::to_usize().div_ceil(8)); let mut raw_bytes = smallvec![0; initial_len]; rng.fill_bytes(&mut raw_bytes); @@ -23,7 +25,7 @@ impl TestRandom for BitList { } impl TestRandom for BitVector { - fn random_for_test(rng: &mut impl RngCore) -> Self { + fn random_for_test(rng: &mut impl rand::RngCore) -> Self { let mut raw_bytes = smallvec![0; std::cmp::max(1, N::to_usize().div_ceil(8))]; rng.fill_bytes(&mut raw_bytes); // If N isn't divisible by 8 diff --git a/consensus/types/src/test_utils/test_random/hash256.rs b/consensus/types/src/test_utils/test_random/hash256.rs index 21d443c0e2..4d7570fb55 100644 --- a/consensus/types/src/test_utils/test_random/hash256.rs +++ b/consensus/types/src/test_utils/test_random/hash256.rs @@ -1,7 +1,7 @@ -use super::*; +use crate::{core::Hash256, test_utils::TestRandom}; impl TestRandom for Hash256 { - fn random_for_test(rng: &mut impl RngCore) -> Self { + fn random_for_test(rng: &mut impl rand::RngCore) -> Self { let mut key_bytes = vec![0; 32]; rng.fill_bytes(&mut key_bytes); Hash256::from_slice(&key_bytes[..]) diff --git a/consensus/types/src/test_utils/test_random/kzg_commitment.rs b/consensus/types/src/test_utils/test_random/kzg_commitment.rs index a4030f2b6a..31e316a198 100644 --- a/consensus/types/src/test_utils/test_random/kzg_commitment.rs +++ b/consensus/types/src/test_utils/test_random/kzg_commitment.rs @@ -1,4 +1,6 @@ -use super::*; +use kzg::KzgCommitment; + +use crate::test_utils::TestRandom; impl TestRandom for KzgCommitment { fn random_for_test(rng: &mut impl rand::RngCore) -> Self { diff --git a/consensus/types/src/test_utils/test_random/kzg_proof.rs b/consensus/types/src/test_utils/test_random/kzg_proof.rs index 7e771ca566..4465d5ab39 100644 --- a/consensus/types/src/test_utils/test_random/kzg_proof.rs +++ b/consensus/types/src/test_utils/test_random/kzg_proof.rs @@ -1,8 +1,9 @@ -use super::*; -use kzg::BYTES_PER_COMMITMENT; +use kzg::{BYTES_PER_COMMITMENT, KzgProof}; + +use crate::test_utils::TestRandom; impl TestRandom for KzgProof { - fn random_for_test(rng: &mut impl RngCore) -> Self { + fn random_for_test(rng: &mut impl rand::RngCore) -> Self { let mut bytes = [0; BYTES_PER_COMMITMENT]; rng.fill_bytes(&mut bytes); Self(bytes) diff --git a/consensus/types/src/test_utils/test_random/mod.rs b/consensus/types/src/test_utils/test_random/mod.rs new file mode 100644 index 0000000000..41812593fa --- /dev/null +++ b/consensus/types/src/test_utils/test_random/mod.rs @@ -0,0 +1,15 @@ +mod address; +mod aggregate_signature; +mod bitfield; +mod hash256; +mod kzg_commitment; +mod kzg_proof; +mod public_key; +mod public_key_bytes; +mod secret_key; +mod signature; +mod signature_bytes; +mod test_random; +mod uint256; + +pub use test_random::{TestRandom, test_random_instance}; diff --git a/consensus/types/src/test_utils/test_random/public_key.rs b/consensus/types/src/test_utils/test_random/public_key.rs index d33e9ac704..9d287c23d7 100644 --- a/consensus/types/src/test_utils/test_random/public_key.rs +++ b/consensus/types/src/test_utils/test_random/public_key.rs @@ -1,7 +1,9 @@ -use super::*; +use bls::{PublicKey, SecretKey}; + +use crate::test_utils::TestRandom; impl TestRandom for PublicKey { - fn random_for_test(rng: &mut impl RngCore) -> Self { + fn random_for_test(rng: &mut impl rand::RngCore) -> Self { SecretKey::random_for_test(rng).public_key() } } diff --git a/consensus/types/src/test_utils/test_random/public_key_bytes.rs b/consensus/types/src/test_utils/test_random/public_key_bytes.rs index 6e5cafc4f0..587c3baf8f 100644 --- a/consensus/types/src/test_utils/test_random/public_key_bytes.rs +++ b/consensus/types/src/test_utils/test_random/public_key_bytes.rs @@ -1,9 +1,9 @@ -use bls::PUBLIC_KEY_BYTES_LEN; +use bls::{PUBLIC_KEY_BYTES_LEN, PublicKey, PublicKeyBytes}; -use super::*; +use crate::test_utils::TestRandom; impl TestRandom for PublicKeyBytes { - fn random_for_test(rng: &mut impl RngCore) -> Self { + fn random_for_test(rng: &mut impl rand::RngCore) -> Self { //50-50 chance for signature to be "valid" or invalid if bool::random_for_test(rng) { //valid signature diff --git a/consensus/types/src/test_utils/test_random/secret_key.rs b/consensus/types/src/test_utils/test_random/secret_key.rs index da1614aa24..a8295d968a 100644 --- a/consensus/types/src/test_utils/test_random/secret_key.rs +++ b/consensus/types/src/test_utils/test_random/secret_key.rs @@ -1,7 +1,9 @@ -use super::*; +use bls::SecretKey; + +use crate::test_utils::TestRandom; impl TestRandom for SecretKey { - fn random_for_test(_rng: &mut impl RngCore) -> Self { + fn random_for_test(_rng: &mut impl rand::RngCore) -> Self { // TODO: Not deterministic generation. Using `SecretKey::deserialize` results in // `BlstError(BLST_BAD_ENCODING)`, need to debug with blst source on what encoding expects. SecretKey::random() diff --git a/consensus/types/src/test_utils/test_random/signature.rs b/consensus/types/src/test_utils/test_random/signature.rs index 8bc0d71110..006aba9650 100644 --- a/consensus/types/src/test_utils/test_random/signature.rs +++ b/consensus/types/src/test_utils/test_random/signature.rs @@ -1,7 +1,9 @@ -use super::*; +use bls::Signature; + +use crate::test_utils::TestRandom; impl TestRandom for Signature { - fn random_for_test(_rng: &mut impl RngCore) -> Self { + fn random_for_test(_rng: &mut impl rand::RngCore) -> Self { // TODO: `SecretKey::random_for_test` does not return a deterministic signature. Since this // signature will not pass verification we could just return the generator point or the // generator point multiplied by a random scalar if we want disctint signatures. diff --git a/consensus/types/src/test_utils/test_random/signature_bytes.rs b/consensus/types/src/test_utils/test_random/signature_bytes.rs index 2117a48232..6992e57467 100644 --- a/consensus/types/src/test_utils/test_random/signature_bytes.rs +++ b/consensus/types/src/test_utils/test_random/signature_bytes.rs @@ -1,9 +1,9 @@ -use bls::SIGNATURE_BYTES_LEN; +use bls::{SIGNATURE_BYTES_LEN, Signature, SignatureBytes}; -use super::*; +use crate::test_utils::TestRandom; impl TestRandom for SignatureBytes { - fn random_for_test(rng: &mut impl RngCore) -> Self { + fn random_for_test(rng: &mut impl rand::RngCore) -> Self { //50-50 chance for signature to be "valid" or invalid if bool::random_for_test(rng) { //valid signature diff --git a/consensus/types/src/test_utils/test_random.rs b/consensus/types/src/test_utils/test_random/test_random.rs similarity index 90% rename from consensus/types/src/test_utils/test_random.rs rename to consensus/types/src/test_utils/test_random/test_random.rs index 7c8f86e14d..f31be97c03 100644 --- a/consensus/types/src/test_utils/test_random.rs +++ b/consensus/types/src/test_utils/test_random/test_random.rs @@ -1,23 +1,9 @@ -use crate::*; -use rand::RngCore; -use rand::SeedableRng; +use std::{marker::PhantomData, sync::Arc}; + +use rand::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; use smallvec::{SmallVec, smallvec}; -use std::marker::PhantomData; -use std::sync::Arc; - -mod address; -mod aggregate_signature; -mod bitfield; -mod hash256; -mod kzg_commitment; -mod kzg_proof; -mod public_key; -mod public_key_bytes; -mod secret_key; -mod signature; -mod signature_bytes; -mod uint256; +use ssz_types::{VariableList, typenum::Unsigned}; pub fn test_random_instance() -> T { let mut rng = XorShiftRng::from_seed([0x42; 16]); diff --git a/consensus/types/src/test_utils/test_random/uint256.rs b/consensus/types/src/test_utils/test_random/uint256.rs index 30077f0e0f..eccf476595 100644 --- a/consensus/types/src/test_utils/test_random/uint256.rs +++ b/consensus/types/src/test_utils/test_random/uint256.rs @@ -1,7 +1,7 @@ -use super::*; +use crate::{core::Uint256, test_utils::TestRandom}; impl TestRandom for Uint256 { - fn random_for_test(rng: &mut impl RngCore) -> Self { + fn random_for_test(rng: &mut impl rand::RngCore) -> Self { let mut key_bytes = [0; 32]; rng.fill_bytes(&mut key_bytes); Self::from_le_slice(&key_bytes[..]) diff --git a/consensus/types/src/validator/mod.rs b/consensus/types/src/validator/mod.rs new file mode 100644 index 0000000000..8a67407821 --- /dev/null +++ b/consensus/types/src/validator/mod.rs @@ -0,0 +1,9 @@ +mod proposer_preparation_data; +mod validator; +mod validator_registration_data; +mod validator_subscription; + +pub use proposer_preparation_data::ProposerPreparationData; +pub use validator::{Validator, is_compounding_withdrawal_credential}; +pub use validator_registration_data::{SignedValidatorRegistrationData, ValidatorRegistrationData}; +pub use validator_subscription::ValidatorSubscription; diff --git a/consensus/types/src/proposer_preparation_data.rs b/consensus/types/src/validator/proposer_preparation_data.rs similarity index 95% rename from consensus/types/src/proposer_preparation_data.rs rename to consensus/types/src/validator/proposer_preparation_data.rs index 477fb3b9d1..8ef675de4f 100644 --- a/consensus/types/src/proposer_preparation_data.rs +++ b/consensus/types/src/validator/proposer_preparation_data.rs @@ -1,6 +1,7 @@ -use crate::*; use serde::{Deserialize, Serialize}; +use crate::core::Address; + /// A proposer preparation, created when a validator prepares the beacon node for potential proposers /// by supplying information required when proposing blocks for the given validators. #[derive(PartialEq, Debug, Serialize, Deserialize, Clone)] diff --git a/consensus/types/src/validator.rs b/consensus/types/src/validator/validator.rs similarity index 97% rename from consensus/types/src/validator.rs rename to consensus/types/src/validator/validator.rs index dec8bba627..7898ab9073 100644 --- a/consensus/types/src/validator.rs +++ b/consensus/types/src/validator/validator.rs @@ -1,13 +1,19 @@ -use crate::context_deserialize; -use crate::{ - Address, BeaconState, ChainSpec, Checkpoint, Epoch, EthSpec, FixedBytesExtended, ForkName, - Hash256, PublicKeyBytes, test_utils::TestRandom, -}; +use bls::PublicKeyBytes; +use context_deserialize::context_deserialize; +use fixed_bytes::FixedBytesExtended; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + attestation::Checkpoint, + core::{Address, ChainSpec, Epoch, EthSpec, Hash256}, + fork::ForkName, + state::BeaconState, + test_utils::TestRandom, +}; + /// Information about a `BeaconChain` validator. /// /// Spec v0.12.1 diff --git a/consensus/types/src/validator_registration_data.rs b/consensus/types/src/validator/validator_registration_data.rs similarity index 93% rename from consensus/types/src/validator_registration_data.rs rename to consensus/types/src/validator/validator_registration_data.rs index 345771074c..a0a1df7dc5 100644 --- a/consensus/types/src/validator_registration_data.rs +++ b/consensus/types/src/validator/validator_registration_data.rs @@ -1,8 +1,10 @@ -use crate::*; +use bls::{PublicKeyBytes, Signature}; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use tree_hash_derive::TreeHash; +use crate::core::{Address, ChainSpec, SignedRoot}; + /// Validator registration, for use in interacting with servers implementing the builder API. #[derive(PartialEq, Debug, Serialize, Deserialize, Clone, Encode, Decode)] pub struct SignedValidatorRegistrationData { diff --git a/consensus/types/src/validator_subscription.rs b/consensus/types/src/validator/validator_subscription.rs similarity index 93% rename from consensus/types/src/validator_subscription.rs rename to consensus/types/src/validator/validator_subscription.rs index 62932638ec..92fb200e10 100644 --- a/consensus/types/src/validator_subscription.rs +++ b/consensus/types/src/validator/validator_subscription.rs @@ -1,7 +1,8 @@ -use crate::*; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; +use crate::{attestation::CommitteeIndex, core::Slot}; + /// A validator subscription, created when a validator subscribes to a slot to perform optional aggregation /// duties. #[derive(PartialEq, Debug, Serialize, Deserialize, Clone, Encode, Decode, Eq, PartialOrd, Ord)] diff --git a/consensus/types/src/withdrawal/mod.rs b/consensus/types/src/withdrawal/mod.rs new file mode 100644 index 0000000000..bac80d00be --- /dev/null +++ b/consensus/types/src/withdrawal/mod.rs @@ -0,0 +1,9 @@ +mod pending_partial_withdrawal; +mod withdrawal; +mod withdrawal_credentials; +mod withdrawal_request; + +pub use pending_partial_withdrawal::PendingPartialWithdrawal; +pub use withdrawal::{Withdrawal, Withdrawals}; +pub use withdrawal_credentials::WithdrawalCredentials; +pub use withdrawal_request::WithdrawalRequest; diff --git a/consensus/types/src/pending_partial_withdrawal.rs b/consensus/types/src/withdrawal/pending_partial_withdrawal.rs similarity index 85% rename from consensus/types/src/pending_partial_withdrawal.rs rename to consensus/types/src/withdrawal/pending_partial_withdrawal.rs index e9b10f79b5..cd866369a4 100644 --- a/consensus/types/src/pending_partial_withdrawal.rs +++ b/consensus/types/src/withdrawal/pending_partial_withdrawal.rs @@ -1,11 +1,11 @@ -use crate::context_deserialize; -use crate::test_utils::TestRandom; -use crate::{Epoch, ForkName}; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{core::Epoch, fork::ForkName, test_utils::TestRandom}; + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, diff --git a/consensus/types/src/withdrawal.rs b/consensus/types/src/withdrawal/withdrawal.rs similarity index 73% rename from consensus/types/src/withdrawal.rs rename to consensus/types/src/withdrawal/withdrawal.rs index ef4a1f285d..d75bd4f501 100644 --- a/consensus/types/src/withdrawal.rs +++ b/consensus/types/src/withdrawal/withdrawal.rs @@ -1,10 +1,16 @@ -use crate::test_utils::TestRandom; -use crate::*; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; +use ssz_types::VariableList; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{ + core::{Address, EthSpec}, + fork::ForkName, + test_utils::TestRandom, +}; + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, @@ -21,6 +27,8 @@ pub struct Withdrawal { pub amount: u64, } +pub type Withdrawals = VariableList::MaxWithdrawalsPerPayload>; + #[cfg(test)] mod tests { use super::*; diff --git a/consensus/types/src/withdrawal_credentials.rs b/consensus/types/src/withdrawal/withdrawal_credentials.rs similarity index 91% rename from consensus/types/src/withdrawal_credentials.rs rename to consensus/types/src/withdrawal/withdrawal_credentials.rs index 52d51ed559..b732222ca1 100644 --- a/consensus/types/src/withdrawal_credentials.rs +++ b/consensus/types/src/withdrawal/withdrawal_credentials.rs @@ -1,5 +1,6 @@ -use crate::*; -use bls::get_withdrawal_credentials; +use bls::{PublicKey, get_withdrawal_credentials}; + +use crate::core::{Address, ChainSpec, Hash256}; pub struct WithdrawalCredentials(Hash256); @@ -27,7 +28,7 @@ impl From for Hash256 { #[cfg(test)] mod test { use super::*; - use crate::test_utils::generate_deterministic_keypair; + use crate::{EthSpec, MainnetEthSpec, test_utils::generate_deterministic_keypair}; use std::str::FromStr; #[test] diff --git a/consensus/types/src/withdrawal_request.rs b/consensus/types/src/withdrawal/withdrawal_request.rs similarity index 87% rename from consensus/types/src/withdrawal_request.rs rename to consensus/types/src/withdrawal/withdrawal_request.rs index c08921a68c..98a40016f9 100644 --- a/consensus/types/src/withdrawal_request.rs +++ b/consensus/types/src/withdrawal/withdrawal_request.rs @@ -1,12 +1,13 @@ -use crate::context_deserialize; -use crate::test_utils::TestRandom; -use crate::{Address, ForkName, PublicKeyBytes}; +use bls::PublicKeyBytes; +use context_deserialize::context_deserialize; use serde::{Deserialize, Serialize}; use ssz::Encode; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; +use crate::{core::Address, fork::ForkName, test_utils::TestRandom}; + #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive( Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom, diff --git a/consensus/types/src/beacon_state/committee_cache/tests.rs b/consensus/types/tests/committee_cache.rs similarity index 97% rename from consensus/types/src/beacon_state/committee_cache/tests.rs rename to consensus/types/tests/committee_cache.rs index 1d2ca4ccdb..751ef05d29 100644 --- a/consensus/types/src/beacon_state/committee_cache/tests.rs +++ b/consensus/types/tests/committee_cache.rs @@ -1,9 +1,14 @@ #![cfg(test)] -use crate::test_utils::*; -use beacon_chain::test_utils::{BeaconChainHarness, EphemeralHarnessType}; -use beacon_chain::types::*; use std::sync::LazyLock; + +use beacon_chain::test_utils::{BeaconChainHarness, EphemeralHarnessType}; +use bls::Keypair; +use fixed_bytes::FixedBytesExtended; +use milhouse::Vector; use swap_or_not_shuffle::shuffle_list; +use types::*; + +use crate::test_utils::generate_deterministic_keypairs; pub const VALIDATOR_COUNT: usize = 16; diff --git a/consensus/types/src/beacon_state/tests.rs b/consensus/types/tests/state.rs similarity index 97% rename from consensus/types/src/beacon_state/tests.rs rename to consensus/types/tests/state.rs index e5b05a4a5b..63ab3b8084 100644 --- a/consensus/types/src/beacon_state/tests.rs +++ b/consensus/types/tests/state.rs @@ -1,15 +1,17 @@ #![cfg(test)] -use crate::test_utils::*; -use beacon_chain::test_utils::{BeaconChainHarness, EphemeralHarnessType}; -use beacon_chain::types::{ - BeaconState, BeaconStateAltair, BeaconStateBase, BeaconStateError, ChainSpec, Domain, Epoch, - EthSpec, FixedBytesExtended, Hash256, Keypair, MainnetEthSpec, MinimalEthSpec, RelativeEpoch, - Slot, Vector, test_utils::TestRandom, -}; -use ssz::Encode; use std::ops::Mul; use std::sync::LazyLock; + +use beacon_chain::test_utils::{BeaconChainHarness, EphemeralHarnessType}; +use bls::Keypair; +use fixed_bytes::FixedBytesExtended; +use milhouse::Vector; +use rand::SeedableRng; +use rand_xorshift::XorShiftRng; +use ssz::Encode; use swap_or_not_shuffle::compute_shuffled_index; +use types::test_utils::{TestRandom, generate_deterministic_keypairs}; +use types::*; pub const MAX_VALIDATOR_COUNT: usize = 129; pub const SLOT_OFFSET: Slot = Slot::new(1); diff --git a/scripts/local_testnet/README.md b/scripts/local_testnet/README.md index 9d9844c4c4..6260f91019 100644 --- a/scripts/local_testnet/README.md +++ b/scripts/local_testnet/README.md @@ -21,7 +21,7 @@ cd ./scripts/local_testnet ``` It will build a Lighthouse docker image from the root of the directory and will take an approximately 12 minutes to complete. Once built, the testing will be started automatically. You will see a list of services running and "Started!" at the end. -You can also select your own Lighthouse docker image to use by specifying it in `network_params.yml` under the `cl_image` key. +You can also select your own Lighthouse docker image to use by specifying it in `network_params.yaml` under the `cl_image` key. Full configuration reference for Kurtosis is specified [here](https://github.com/ethpandaops/ethereum-package?tab=readme-ov-file#configuration). To view all running services: diff --git a/scripts/local_testnet/network_params.yaml b/scripts/local_testnet/network_params.yaml index cdfacbced4..a048674e63 100644 --- a/scripts/local_testnet/network_params.yaml +++ b/scripts/local_testnet/network_params.yaml @@ -1,19 +1,37 @@ # Full configuration reference [here](https://github.com/ethpandaops/ethereum-package?tab=readme-ov-file#configuration). participants: - - el_type: geth - el_image: ethereum/client-go:latest - cl_type: lighthouse + - cl_type: lighthouse cl_image: lighthouse:local + el_type: geth + el_image: ethereum/client-go:latest + supernode: true cl_extra_params: - --target-peers=3 - count: 4 + count: 2 + - cl_type: lighthouse + cl_image: lighthouse:local + el_type: geth + el_image: ethereum/client-go:latest + supernode: false + cl_extra_params: + - --target-peers=3 + count: 2 network_params: - electra_fork_epoch: 0 - seconds_per_slot: 3 -global_log_level: debug + fulu_fork_epoch: 0 + seconds_per_slot: 6 snooper_enabled: false +global_log_level: debug additional_services: - dora - spamoor - prometheus_grafana - tempo +spamoor_params: + image: ethpandaops/spamoor:master + spammers: + - scenario: eoatx + config: + throughput: 200 + - scenario: blobs + config: + throughput: 20 \ No newline at end of file diff --git a/scripts/local_testnet/network_params_das.yaml b/scripts/local_testnet/network_params_das.yaml deleted file mode 100644 index e3bc513153..0000000000 --- a/scripts/local_testnet/network_params_das.yaml +++ /dev/null @@ -1,41 +0,0 @@ -participants: - - cl_type: lighthouse - cl_image: lighthouse:local - el_type: geth - el_image: ethpandaops/geth:master - supernode: true - cl_extra_params: - # Note: useful for testing range sync (only produce block if the node is in sync to prevent forking) - - --sync-tolerance-epochs=0 - - --target-peers=3 - count: 2 - - cl_type: lighthouse - cl_image: lighthouse:local - el_type: geth - el_image: ethpandaops/geth:master - supernode: false - cl_extra_params: - # Note: useful for testing range sync (only produce block if the node is in sync to prevent forking) - - --sync-tolerance-epochs=0 - - --target-peers=3 - count: 2 -network_params: - electra_fork_epoch: 0 - fulu_fork_epoch: 1 - seconds_per_slot: 6 -snooper_enabled: false -global_log_level: debug -additional_services: - - dora - - spamoor - - prometheus_grafana - - tempo -spamoor_params: - image: ethpandaops/spamoor:master - spammers: - - scenario: eoatx - config: - throughput: 200 - - scenario: blobs - config: - throughput: 20 \ No newline at end of file diff --git a/scripts/local_testnet/start_local_testnet.sh b/scripts/local_testnet/start_local_testnet.sh index 442e6fd98d..8d8b33526d 100755 --- a/scripts/local_testnet/start_local_testnet.sh +++ b/scripts/local_testnet/start_local_testnet.sh @@ -78,6 +78,11 @@ if [ "$RUN_ASSERTOOR_TESTS" = true ]; then echo "Assertoor has been added to $NETWORK_PARAMS_FILE." fi +if [ "$KEEP_ENCLAVE" = false ]; then + # Stop local testnet + kurtosis enclave rm -f $ENCLAVE_NAME 2>/dev/null || true +fi + if [ "$BUILD_IMAGE" = true ]; then echo "Building Lighthouse Docker image." ROOT_DIR="$SCRIPT_DIR/../.." @@ -86,11 +91,6 @@ else echo "Not rebuilding Lighthouse Docker image." fi -if [ "$KEEP_ENCLAVE" = false ]; then - # Stop local testnet - kurtosis enclave rm -f $ENCLAVE_NAME 2>/dev/null || true -fi - kurtosis run --enclave $ENCLAVE_NAME github.com/ethpandaops/ethereum-package@$ETHEREUM_PKG_VERSION --args-file $NETWORK_PARAMS_FILE echo "Started!" diff --git a/scripts/tests/checkpoint-sync-config-devnet.yaml b/scripts/tests/checkpoint-sync-config-devnet.yaml deleted file mode 100644 index 2392011ed3..0000000000 --- a/scripts/tests/checkpoint-sync-config-devnet.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Kurtosis config file to checkpoint sync to a running devnet supported by ethPandaOps and `ethereum-package`. -participants: - - cl_type: lighthouse - cl_image: lighthouse:local - el_type: geth - el_image: ethpandaops/geth:master - cl_extra_params: - - --disable-backfill-rate-limiting - supernode: true - - cl_type: lighthouse - cl_image: lighthouse:local - el_type: geth - el_image: ethpandaops/geth:master - cl_extra_params: - - --disable-backfill-rate-limiting - supernode: false - -checkpoint_sync_enabled: true -checkpoint_sync_url: "https://checkpoint-sync.fusaka-devnet-3.ethpandaops.io" - -global_log_level: debug - -network_params: - network: fusaka-devnet-3 diff --git a/validator_client/lighthouse_validator_store/src/lib.rs b/validator_client/lighthouse_validator_store/src/lib.rs index d10fecb32e..dc8fb07b65 100644 --- a/validator_client/lighthouse_validator_store/src/lib.rs +++ b/validator_client/lighthouse_validator_store/src/lib.rs @@ -15,7 +15,7 @@ use std::marker::PhantomData; use std::path::Path; use std::sync::Arc; use task_executor::TaskExecutor; -use tracing::{error, info, warn}; +use tracing::{error, info, instrument, warn}; use types::{ AbstractExecPayload, Address, AggregateAndProof, Attestation, BeaconBlock, BlindedPayload, ChainSpec, ContributionAndProof, Domain, Epoch, EthSpec, Fork, Graffiti, Hash256, @@ -242,6 +242,7 @@ impl LighthouseValidatorStore { /// Returns a `SigningMethod` for `validator_pubkey` *only if* that validator is considered safe /// by doppelganger protection. + #[instrument(skip_all, level = "debug")] fn doppelganger_checked_signing_method( &self, validator_pubkey: PublicKeyBytes, @@ -745,6 +746,7 @@ impl ValidatorStore for LighthouseValidatorS } } + #[instrument(skip_all)] async fn sign_attestation( &self, validator_pubkey: PublicKeyBytes, diff --git a/validator_client/signing_method/Cargo.toml b/validator_client/signing_method/Cargo.toml index 3e1a48142f..2defd25caa 100644 --- a/validator_client/signing_method/Cargo.toml +++ b/validator_client/signing_method/Cargo.toml @@ -12,6 +12,7 @@ parking_lot = { workspace = true } reqwest = { workspace = true } serde = { workspace = true } task_executor = { workspace = true } +tracing = { workspace = true } types = { workspace = true } url = { workspace = true } validator_metrics = { workspace = true } diff --git a/validator_client/signing_method/src/lib.rs b/validator_client/signing_method/src/lib.rs index c535415b1e..7e0f2c02f7 100644 --- a/validator_client/signing_method/src/lib.rs +++ b/validator_client/signing_method/src/lib.rs @@ -10,6 +10,7 @@ use reqwest::{Client, header::ACCEPT}; use std::path::PathBuf; use std::sync::Arc; use task_executor::TaskExecutor; +use tracing::instrument; use types::*; use url::Url; use web3signer::{ForkInfo, MessageType, SigningRequest, SigningResponse}; @@ -131,6 +132,7 @@ impl SigningMethod { } /// Return the signature of `signable_message`, with respect to the `signing_context`. + #[instrument(skip_all, level = "debug")] pub async fn get_signature>( &self, signable_message: SignableMessage<'_, E, Payload>, diff --git a/validator_client/slashing_protection/src/slashing_database.rs b/validator_client/slashing_protection/src/slashing_database.rs index ce32299a51..00677212a3 100644 --- a/validator_client/slashing_protection/src/slashing_database.rs +++ b/validator_client/slashing_protection/src/slashing_database.rs @@ -11,6 +11,7 @@ use rusqlite::{OptionalExtension, Transaction, TransactionBehavior, params}; use std::fs::File; use std::path::Path; use std::time::Duration; +use tracing::instrument; use types::{AttestationData, BeaconBlockHeader, Epoch, Hash256, PublicKeyBytes, SignedRoot, Slot}; type Pool = r2d2::Pool; @@ -639,6 +640,7 @@ impl SlashingDatabase { /// to prevent concurrent checks and inserts from resulting in slashable data being inserted. /// /// This is the safe, externally-callable interface for checking attestations. + #[instrument(skip_all, level = "debug")] pub fn check_and_insert_attestation( &self, validator_pubkey: &PublicKeyBytes, diff --git a/validator_client/validator_services/src/attestation_service.rs b/validator_client/validator_services/src/attestation_service.rs index da6e8f3588..b2b8bc81e2 100644 --- a/validator_client/validator_services/src/attestation_service.rs +++ b/validator_client/validator_services/src/attestation_service.rs @@ -8,7 +8,7 @@ use std::ops::Deref; use std::sync::Arc; use task_executor::TaskExecutor; use tokio::time::{Duration, Instant, sleep, sleep_until}; -use tracing::{debug, error, info, trace, warn}; +use tracing::{Instrument, Span, debug, error, info, info_span, instrument, trace, warn}; use tree_hash::TreeHash; use types::{Attestation, AttestationData, ChainSpec, CommitteeIndex, EthSpec, Slot}; use validator_store::{Error as ValidatorStoreError, ValidatorStore}; @@ -180,8 +180,9 @@ impl AttestationService Result<(), String> { let slot = self.slot_clock.now().ok_or("Failed to read slot clock")?; let duration_to_next_slot = self @@ -189,6 +190,53 @@ impl AttestationService = self.duties_service.attesters(slot).into_iter().collect(); + let attestation_service = self.clone(); + + let attestation_data_handle = self + .inner + .executor + .spawn_handle( + async move { + let attestation_data = attestation_service + .beacon_nodes + .first_success(|beacon_node| async move { + let _timer = validator_metrics::start_timer_vec( + &validator_metrics::ATTESTATION_SERVICE_TIMES, + &[validator_metrics::ATTESTATIONS_HTTP_GET], + ); + beacon_node + .get_validator_attestation_data(slot, 0) + .await + .map_err(|e| format!("Failed to produce attestation data: {:?}", e)) + .map(|result| result.data) + }) + .await + .map_err(|e| e.to_string())?; + + attestation_service + .sign_and_publish_attestations( + slot, + &attestation_duties, + attestation_data.clone(), + ) + .await + .map_err(|e| { + crit!( + error = format!("{:?}", e), + slot = slot.as_u64(), + "Error during attestation routine" + ); + e + })?; + Ok::(attestation_data) + }, + "unaggregated attestation production", + ) + .ok_or("Failed to spawn attestation data task")?; + // If a validator needs to publish an aggregate attestation, they must do so at 2/3 // through the slot. This delay triggers at this time let aggregate_production_instant = Instant::now() @@ -196,7 +244,7 @@ impl AttestationService> = self + let aggregate_duties_by_committee_index: HashMap> = self .duties_service .attesters(slot) .into_iter() @@ -207,24 +255,45 @@ impl AttestationService data, + Ok(Some(Err(err))) => { + error!(?err, "Attestation production failed"); + return; + } + Ok(None) | Err(_) => { + info!("Aborting attestation production due to shutdown"); + return; + } + }; + + // For each committee index for this slot: + // Create and publish `SignedAggregateAndProof` for all aggregating validators. + aggregate_duties_by_committee_index.into_iter().for_each( + |(committee_index, validator_duties)| { + let attestation_service = attestation_service_clone.clone(); + let attestation_data = attestation_data.clone(); + executor.spawn_ignoring_error( + attestation_service.handle_aggregates( + slot, + committee_index, + validator_duties, + aggregate_production_instant, + attestation_data, + ), + "aggregate publish", + ); + }, + ) + }, + "attestation and aggregate publish", + ); // Schedule pruning of the slashing protection database once all unaggregated // attestations have (hopefully) been signed, i.e. at the same time as aggregate @@ -234,108 +303,76 @@ impl AttestationService, aggregate_production_instant: Instant, + attestation_data: AttestationData, ) -> Result<(), ()> { - let attestations_timer = validator_metrics::start_timer_vec( - &validator_metrics::ATTESTATION_SERVICE_TIMES, - &[validator_metrics::ATTESTATIONS], - ); - - // There's not need to produce `Attestation` or `SignedAggregateAndProof` if we do not have + // There's not need to produce `SignedAggregateAndProof` if we do not have // any validators for the given `slot` and `committee_index`. if validator_duties.is_empty() { return Ok(()); } - // Step 1. - // - // Download, sign and publish an `Attestation` for each validator. - let attestation_opt = self - .produce_and_publish_attestations(slot, committee_index, &validator_duties) + // Wait until the `aggregation_production_instant` (2/3rds + // of the way though the slot). As verified in the + // `delay_triggers_when_in_the_past` test, this code will still run + // even if the instant has already elapsed. + sleep_until(aggregate_production_instant).await; + + // Start the metrics timer *after* we've done the delay. + let _aggregates_timer = validator_metrics::start_timer_vec( + &validator_metrics::ATTESTATION_SERVICE_TIMES, + &[validator_metrics::AGGREGATES], + ); + + // Download, sign and publish a `SignedAggregateAndProof` for each + // validator that is elected to aggregate for this `slot` and + // `committee_index`. + self.produce_and_publish_aggregates(&attestation_data, committee_index, &validator_duties) .await .map_err(move |e| { crit!( error = format!("{:?}", e), committee_index, slot = slot.as_u64(), - "Error during attestation routine" + "Error during aggregate attestation routine" ) })?; - drop(attestations_timer); - - // Step 2. - // - // If an attestation was produced, make an aggregate. - if let Some(attestation_data) = attestation_opt { - // First, wait until the `aggregation_production_instant` (2/3rds - // of the way though the slot). As verified in the - // `delay_triggers_when_in_the_past` test, this code will still run - // even if the instant has already elapsed. - sleep_until(aggregate_production_instant).await; - - // Start the metrics timer *after* we've done the delay. - let _aggregates_timer = validator_metrics::start_timer_vec( - &validator_metrics::ATTESTATION_SERVICE_TIMES, - &[validator_metrics::AGGREGATES], - ); - - // Then download, sign and publish a `SignedAggregateAndProof` for each - // validator that is elected to aggregate for this `slot` and - // `committee_index`. - self.produce_and_publish_aggregates( - &attestation_data, - committee_index, - &validator_duties, - ) - .await - .map_err(move |e| { - crit!( - error = format!("{:?}", e), - committee_index, - slot = slot.as_u64(), - "Error during attestation routine" - ) - })?; - } - Ok(()) } - /// Performs the first step of the attesting process: downloading `Attestation` objects, - /// signing them and returning them to the validator. + /// Performs the main steps of the attesting process: signing and publishing to the BN. /// - /// https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/validator.md#attesting + /// https://github.com/ethereum/consensus-specs/blob/master/specs/phase0/validator.md#attesting /// /// ## Detail /// /// The given `validator_duties` should already be filtered to only contain those that match - /// `slot` and `committee_index`. Critical errors will be logged if this is not the case. - /// - /// Only one `Attestation` is downloaded from the BN. It is then cloned and signed by each - /// validator and the list of individually-signed `Attestation` objects is returned to the BN. - async fn produce_and_publish_attestations( + /// `slot`. Critical errors will be logged if this is not the case. + #[instrument(skip_all, fields(%slot, %attestation_data.beacon_block_root))] + async fn sign_and_publish_attestations( &self, slot: Slot, - committee_index: CommitteeIndex, validator_duties: &[DutyAndProof], - ) -> Result, String> { + attestation_data: AttestationData, + ) -> Result<(), String> { + let _attestations_timer = validator_metrics::start_timer_vec( + &validator_metrics::ATTESTATION_SERVICE_TIMES, + &[validator_metrics::ATTESTATIONS], + ); + if validator_duties.is_empty() { - return Ok(None); + return Ok(()); } let current_epoch = self @@ -344,101 +381,90 @@ impl AttestationService(attestation_data, &self.chain_spec) { - crit!( - validator = ?duty.pubkey, - duty_slot = %duty.slot, - attestation_slot = %attestation_data.slot, - duty_index = duty.committee_index, - attestation_index = attestation_data.index, - "Inconsistent validator duties during signing" - ); - return None; - } - - let mut attestation = match Attestation::empty_for_signing( - duty.committee_index, - duty.committee_length as usize, - attestation_data.slot, - attestation_data.beacon_block_root, - attestation_data.source, - attestation_data.target, - &self.chain_spec, - ) { - Ok(attestation) => attestation, - Err(err) => { + // Ensure that the attestation matches the duties. + if !duty.match_attestation_data::(attestation_data, &self.chain_spec) { crit!( validator = ?duty.pubkey, - ?duty, - ?err, - "Invalid validator duties during signing" + duty_slot = %duty.slot, + attestation_slot = %attestation_data.slot, + duty_index = duty.committee_index, + attestation_index = attestation_data.index, + "Inconsistent validator duties during signing" ); return None; } - }; - match self - .validator_store - .sign_attestation( - duty.pubkey, - duty.validator_committee_index as usize, - &mut attestation, - current_epoch, - ) - .await - { - Ok(()) => Some((attestation, duty.validator_index)), - Err(ValidatorStoreError::UnknownPubkey(pubkey)) => { - // A pubkey can be missing when a validator was recently - // removed via the API. - warn!( - info = "a validator may have recently been removed from this VC", - pubkey = ?pubkey, - validator = ?duty.pubkey, - committee_index = committee_index, - slot = slot.as_u64(), - "Missing pubkey for attestation" - ); - None - } - Err(e) => { - crit!( - error = ?e, - validator = ?duty.pubkey, - committee_index, - slot = slot.as_u64(), - "Failed to sign attestation" - ); - None + let mut attestation = match Attestation::empty_for_signing( + duty.committee_index, + duty.committee_length as usize, + attestation_data.slot, + attestation_data.beacon_block_root, + attestation_data.source, + attestation_data.target, + &self.chain_spec, + ) { + Ok(attestation) => attestation, + Err(err) => { + crit!( + validator = ?duty.pubkey, + ?duty, + ?err, + "Invalid validator duties during signing" + ); + return None; + } + }; + + match self + .validator_store + .sign_attestation( + duty.pubkey, + duty.validator_committee_index as usize, + &mut attestation, + current_epoch, + ) + .await + { + Ok(()) => Some((attestation, duty.validator_index)), + Err(ValidatorStoreError::UnknownPubkey(pubkey)) => { + // A pubkey can be missing when a validator was recently + // removed via the API. + warn!( + info = "a validator may have recently been removed from this VC", + pubkey = ?pubkey, + validator = ?duty.pubkey, + slot = slot.as_u64(), + "Missing pubkey for attestation" + ); + None + } + Err(e) => { + crit!( + error = ?e, + validator = ?duty.pubkey, + slot = slot.as_u64(), + "Failed to sign attestation" + ); + None + } } } + .instrument(Span::current()) }); // Execute all the futures in parallel, collecting any successful results. let (ref attestations, ref validator_indices): (Vec<_>, Vec<_>) = join_all(signing_futures) + .instrument(info_span!( + "sign_attestations", + count = validator_duties.len() + )) .await .into_iter() .flatten() @@ -446,7 +472,7 @@ impl AttestationService AttestationService(single_attestations, fork_name) .await }) + .instrument(info_span!( + "publish_attestations", + count = attestations.len() + )) .await { Ok(()) => info!( @@ -507,7 +537,7 @@ impl AttestationService AttestationService AttestationService AttestationService AttestationService { diff --git a/validator_client/validator_services/src/block_service.rs b/validator_client/validator_services/src/block_service.rs index c111b1f22e..8ec53d3f40 100644 --- a/validator_client/validator_services/src/block_service.rs +++ b/validator_client/validator_services/src/block_service.rs @@ -1,5 +1,4 @@ use beacon_node_fallback::{ApiTopic, BeaconNodeFallback, Error as FallbackError, Errors}; -use bls::SignatureBytes; use eth2::{BeaconNodeHttpClient, StatusCode}; use graffiti_file::{GraffitiFile, determine_graffiti}; use logging::crit; @@ -11,7 +10,7 @@ use std::sync::Arc; use std::time::Duration; use task_executor::TaskExecutor; use tokio::sync::mpsc; -use tracing::{debug, error, info, trace, warn}; +use tracing::{Instrument, debug, error, info, info_span, instrument, trace, warn}; use types::{BlockType, ChainSpec, EthSpec, Graffiti, PublicKeyBytes, Slot}; use validator_store::{Error as ValidatorStoreError, SignedBlock, UnsignedBlock, ValidatorStore}; @@ -298,7 +297,7 @@ impl BlockService { self.inner.executor.spawn( async move { let result = service - .publish_block(slot, validator_pubkey, builder_boost_factor) + .get_validator_block_and_publish_block(slot, validator_pubkey, builder_boost_factor) .await; match result { @@ -320,6 +319,7 @@ impl BlockService { } #[allow(clippy::too_many_arguments)] + #[instrument(skip_all, fields(%slot, ?validator_pubkey))] async fn sign_and_publish_block( &self, proposer_fallback: ProposerFallback, @@ -333,6 +333,7 @@ impl BlockService { let res = self .validator_store .sign_block(*validator_pubkey, unsigned_block, slot) + .instrument(info_span!("sign_block")) .await; let signed_block = match res { @@ -389,7 +390,12 @@ impl BlockService { Ok(()) } - async fn publish_block( + #[instrument( + name = "block_proposal_duty_cycle", + skip_all, + fields(%slot, ?validator_pubkey) + )] + async fn get_validator_block_and_publish_block( self, slot: Slot, validator_pubkey: PublicKeyBytes, @@ -442,33 +448,80 @@ impl BlockService { info!(slot = slot.as_u64(), "Requesting unsigned block"); - // Request block from first responsive beacon node. + // Request an SSZ block from all beacon nodes in order, returning on the first successful response. + // If all nodes fail, run a second pass falling back to JSON. // - // Try the proposer nodes last, since it's likely that they don't have a + // Proposer nodes will always be tried last during each pass since it's likely that they don't have a // great view of attestations on the network. - let unsigned_block = proposer_fallback + let ssz_block_response = proposer_fallback .request_proposers_last(|beacon_node| async move { let _get_timer = validator_metrics::start_timer_vec( &validator_metrics::BLOCK_SERVICE_TIMES, &[validator_metrics::BEACON_BLOCK_HTTP_GET], ); - Self::get_validator_block( - &beacon_node, - slot, - randao_reveal_ref, - graffiti, - proposer_index, - builder_boost_factor, - ) - .await - .map_err(|e| { - BlockError::Recoverable(format!( - "Error from beacon node when producing block: {:?}", - e - )) - }) + beacon_node + .get_validator_blocks_v3_ssz::( + slot, + randao_reveal_ref, + graffiti.as_ref(), + builder_boost_factor, + ) + .await }) - .await?; + .await; + + let block_response = match ssz_block_response { + Ok((ssz_block_response, _metadata)) => ssz_block_response, + Err(e) => { + warn!( + slot = slot.as_u64(), + error = %e, + "SSZ block production failed, falling back to JSON" + ); + + proposer_fallback + .request_proposers_last(|beacon_node| async move { + let _get_timer = validator_metrics::start_timer_vec( + &validator_metrics::BLOCK_SERVICE_TIMES, + &[validator_metrics::BEACON_BLOCK_HTTP_GET], + ); + let (json_block_response, _metadata) = beacon_node + .get_validator_blocks_v3::( + slot, + randao_reveal_ref, + graffiti.as_ref(), + builder_boost_factor, + ) + .await + .map_err(|e| { + BlockError::Recoverable(format!( + "Error from beacon node when producing block: {:?}", + e + )) + })?; + + Ok(json_block_response.data) + }) + .await + .map_err(BlockError::from)? + } + }; + + let (block_proposer, unsigned_block) = match block_response { + eth2::types::ProduceBlockV3Response::Full(block) => { + (block.block().proposer_index(), UnsignedBlock::Full(block)) + } + eth2::types::ProduceBlockV3Response::Blinded(block) => { + (block.proposer_index(), UnsignedBlock::Blinded(block)) + } + }; + + info!(slot = slot.as_u64(), "Received unsigned block"); + if proposer_index != Some(block_proposer) { + return Err(BlockError::Recoverable( + "Proposer index does not match block proposer. Beacon chain re-orged".to_string(), + )); + } self_ref .sign_and_publish_block( @@ -483,6 +536,7 @@ impl BlockService { Ok(()) } + #[instrument(skip_all)] async fn publish_signed_block_contents( &self, signed_block: &SignedBlock, @@ -517,70 +571,6 @@ impl BlockService { } Ok::<_, BlockError>(()) } - - async fn get_validator_block( - beacon_node: &BeaconNodeHttpClient, - slot: Slot, - randao_reveal_ref: &SignatureBytes, - graffiti: Option, - proposer_index: Option, - builder_boost_factor: Option, - ) -> Result, BlockError> { - let block_response = match beacon_node - .get_validator_blocks_v3_ssz::( - slot, - randao_reveal_ref, - graffiti.as_ref(), - builder_boost_factor, - ) - .await - { - Ok((ssz_block_response, _)) => ssz_block_response, - Err(e) => { - warn!( - slot = slot.as_u64(), - error = %e, - "Beacon node does not support SSZ in block production, falling back to JSON" - ); - - let (json_block_response, _) = beacon_node - .get_validator_blocks_v3::( - slot, - randao_reveal_ref, - graffiti.as_ref(), - builder_boost_factor, - ) - .await - .map_err(|e| { - BlockError::Recoverable(format!( - "Error from beacon node when producing block: {:?}", - e - )) - })?; - - // Extract ProduceBlockV3Response (data field of the struct ForkVersionedResponse) - json_block_response.data - } - }; - - let (block_proposer, unsigned_block) = match block_response { - eth2::types::ProduceBlockV3Response::Full(block) => { - (block.block().proposer_index(), UnsignedBlock::Full(block)) - } - eth2::types::ProduceBlockV3Response::Blinded(block) => { - (block.proposer_index(), UnsignedBlock::Blinded(block)) - } - }; - - info!(slot = slot.as_u64(), "Received unsigned block"); - if proposer_index != Some(block_proposer) { - return Err(BlockError::Recoverable( - "Proposer index does not match block proposer. Beacon chain re-orged".to_string(), - )); - } - - Ok::<_, BlockError>(unsigned_block) - } } /// Wrapper for values we want to log about a block we signed, for easy extraction from the possible diff --git a/validator_client/validator_services/src/sync_committee_service.rs b/validator_client/validator_services/src/sync_committee_service.rs index 02f9f24c8a..5f6b1cb710 100644 --- a/validator_client/validator_services/src/sync_committee_service.rs +++ b/validator_client/validator_services/src/sync_committee_service.rs @@ -11,7 +11,7 @@ use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use task_executor::TaskExecutor; use tokio::time::{Duration, Instant, sleep, sleep_until}; -use tracing::{debug, error, info, trace, warn}; +use tracing::{Instrument, debug, error, info, info_span, instrument, trace, warn}; use types::{ ChainSpec, EthSpec, Hash256, PublicKeyBytes, Slot, SyncCommitteeSubscription, SyncContributionData, SyncDuty, SyncSelectionProof, SyncSubnetId, @@ -208,7 +208,8 @@ impl SyncCommitteeService SyncCommitteeService SyncCommitteeService SyncCommitteeService SyncCommitteeService SyncCommitteeService SyncCommitteeService SyncCommitteeService SyncCommitteeService SyncCommitteeService