Merge branch 'eip4844' into deneb-free-blobs

This commit is contained in:
Diva M 2023-03-15 12:26:30 -05:00
commit 4a39e43f96
No known key found for this signature in database
GPG Key ID: 1BAE5E01126680FE
57 changed files with 1401 additions and 513 deletions

View File

@ -203,6 +203,9 @@ pub enum ProduceBlockVerification {
pub struct PrePayloadAttributes { pub struct PrePayloadAttributes {
pub proposer_index: u64, pub proposer_index: u64,
pub prev_randao: Hash256, pub prev_randao: Hash256,
/// The parent block number is not part of the payload attributes sent to the EL, but *is*
/// sent to builders via SSE.
pub parent_block_number: u64,
} }
/// Define whether a forkchoiceUpdate needs to be checked for an override (`Yes`) or has already /// Define whether a forkchoiceUpdate needs to be checked for an override (`Yes`) or has already
@ -3977,16 +3980,21 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
proposer as u64 proposer as u64
}; };
// Get the `prev_randao` value. // Get the `prev_randao` and parent block number.
let prev_randao = if proposer_head == parent_block_root { let head_block_number = cached_head.head_block_number()?;
cached_head.parent_random() let (prev_randao, parent_block_number) = if proposer_head == parent_block_root {
(
cached_head.parent_random()?,
head_block_number.saturating_sub(1),
)
} else { } else {
cached_head.head_random() (cached_head.head_random()?, head_block_number)
}?; };
Ok(Some(PrePayloadAttributes { Ok(Some(PrePayloadAttributes {
proposer_index, proposer_index,
prev_randao, prev_randao,
parent_block_number,
})) }))
} }
@ -5116,6 +5124,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
proposal_slot: prepare_slot, proposal_slot: prepare_slot,
proposer_index: proposer, proposer_index: proposer,
parent_block_root: head_root, parent_block_root: head_root,
parent_block_number: pre_payload_attributes.parent_block_number,
parent_block_hash: forkchoice_update_params.head_hash.unwrap_or_default(), parent_block_hash: forkchoice_update_params.head_hash.unwrap_or_default(),
payload_attributes: payload_attributes.into(), payload_attributes: payload_attributes.into(),
}, },

View File

@ -167,6 +167,17 @@ impl<E: EthSpec> CachedHead<E> {
.map(|payload| payload.prev_randao()) .map(|payload| payload.prev_randao())
} }
/// Returns the execution block number of the block at the head of the chain.
///
/// Returns an error if the chain is prior to Bellatrix.
pub fn head_block_number(&self) -> Result<u64, BeaconStateError> {
self.snapshot
.beacon_block
.message()
.execution_payload()
.map(|payload| payload.block_number())
}
/// Returns the active validator count for the current epoch of the head state. /// Returns the active validator count for the current epoch of the head state.
/// ///
/// Should only return `None` if the caches have not been built on the head state (this should /// Should only return `None` if the caches have not been built on the head state (this should

View File

@ -65,43 +65,43 @@ impl<T: EthSpec> ServerSentEventHandler<T> {
EventKind::Attestation(_) => self EventKind::Attestation(_) => self
.attestation_tx .attestation_tx
.send(kind) .send(kind)
.map(|count| log_count(count, "attestation")), .map(|count| log_count("attestation", count)),
EventKind::Block(_) => self EventKind::Block(_) => self
.block_tx .block_tx
.send(kind) .send(kind)
.map(|count| log_count(count, "block")), .map(|count| log_count("block", count)),
EventKind::FinalizedCheckpoint(_) => self EventKind::FinalizedCheckpoint(_) => self
.finalized_tx .finalized_tx
.send(kind) .send(kind)
.map(|count| log_count(count, "finalized checkpoint")), .map(|count| log_count("finalized checkpoint", count)),
EventKind::Head(_) => self EventKind::Head(_) => self
.head_tx .head_tx
.send(kind) .send(kind)
.map(|count| log_count(count, "head")), .map(|count| log_count("head", count)),
EventKind::VoluntaryExit(_) => self EventKind::VoluntaryExit(_) => self
.exit_tx .exit_tx
.send(kind) .send(kind)
.map(|count| log_count(count, "exit")), .map(|count| log_count("exit", count)),
EventKind::ChainReorg(_) => self EventKind::ChainReorg(_) => self
.chain_reorg_tx .chain_reorg_tx
.send(kind) .send(kind)
.map(|count| log_count(count, "chain reorg")), .map(|count| log_count("chain reorg", count)),
EventKind::ContributionAndProof(_) => self EventKind::ContributionAndProof(_) => self
.contribution_tx .contribution_tx
.send(kind) .send(kind)
.map(|count| log_count(count, "contribution and proof")), .map(|count| log_count("contribution and proof", count)),
EventKind::PayloadAttributes(_) => self EventKind::PayloadAttributes(_) => self
.payload_attributes_tx .payload_attributes_tx
.send(kind) .send(kind)
.map(|count| log_count(count, "payload attributes")), .map(|count| log_count("payload attributes", count)),
EventKind::LateHead(_) => self EventKind::LateHead(_) => self
.late_head .late_head
.send(kind) .send(kind)
.map(|count| log_count(count, "late head")), .map(|count| log_count("late head", count)),
EventKind::BlockReward(_) => self EventKind::BlockReward(_) => self
.block_reward_tx .block_reward_tx
.send(kind) .send(kind)
.map(|count| log_count(count, "block reward")), .map(|count| log_count("block reward", count)),
}; };
if let Err(SendError(event)) = result { if let Err(SendError(event)) = result {
trace!(self.log, "No receivers registered to listen for event"; "event" => ?event); trace!(self.log, "No receivers registered to listen for event"; "event" => ?event);

View File

@ -1,4 +1,3 @@
#![recursion_limit = "128"] // For lazy-static
pub mod attestation_rewards; pub mod attestation_rewards;
pub mod attestation_verification; pub mod attestation_verification;
mod attester_cache; mod attester_cache;

View File

@ -468,7 +468,7 @@ where
builder_threshold: Option<u128>, builder_threshold: Option<u128>,
) -> Self { ) -> Self {
// Get a random unused port // Get a random unused port
let port = unused_port::unused_tcp_port().unwrap(); let port = unused_port::unused_tcp4_port().unwrap();
let builder_url = SensitiveUrl::parse(format!("http://127.0.0.1:{port}").as_str()).unwrap(); let builder_url = SensitiveUrl::parse(format!("http://127.0.0.1:{port}").as_str()).unwrap();
let spec = self.spec.clone().expect("cannot build without spec"); let spec = self.spec.clone().expect("cannot build without spec");

View File

@ -46,9 +46,18 @@ impl<T: BeaconChainTypes> Client<T> {
self.http_metrics_listen_addr self.http_metrics_listen_addr
} }
/// Returns the port of the client's libp2p stack, if it was started. /// Returns the ipv4 port of the client's libp2p stack, if it was started.
pub fn libp2p_listen_port(&self) -> Option<u16> { pub fn libp2p_listen_ipv4_port(&self) -> Option<u16> {
self.network_globals.as_ref().map(|n| n.listen_port_tcp()) self.network_globals
.as_ref()
.and_then(|n| n.listen_port_tcp4())
}
/// Returns the ipv6 port of the client's libp2p stack, if it was started.
pub fn libp2p_listen_ipv6_port(&self) -> Option<u16> {
self.network_globals
.as_ref()
.and_then(|n| n.listen_port_tcp6())
} }
/// Returns the list of libp2p addresses the client is listening to. /// Returns the list of libp2p addresses the client is listening to.

View File

@ -57,7 +57,7 @@ use types::{
mod block_hash; mod block_hash;
mod engine_api; mod engine_api;
mod engines; pub mod engines;
mod keccak; mod keccak;
mod metrics; mod metrics;
pub mod payload_cache; pub mod payload_cache;
@ -342,6 +342,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
.map_err(Error::InvalidJWTSecret) .map_err(Error::InvalidJWTSecret)
} else { } else {
// Create a new file and write a randomly generated secret to it if file does not exist // Create a new file and write a randomly generated secret to it if file does not exist
warn!(log, "No JWT found on disk. Generating"; "path" => %secret_file.display());
std::fs::File::options() std::fs::File::options()
.write(true) .write(true)
.create_new(true) .create_new(true)

View File

@ -1,4 +1,3 @@
#![recursion_limit = "256"]
//! This crate contains a HTTP server which serves the endpoints listed here: //! This crate contains a HTTP server which serves the endpoints listed here:
//! //!
//! https://github.com/ethereum/beacon-APIs //! https://github.com/ethereum/beacon-APIs
@ -72,7 +71,8 @@ use warp::Reply;
use warp::{http::Response, Filter}; use warp::{http::Response, Filter};
use warp_utils::{ use warp_utils::{
query::multi_key_query, query::multi_key_query,
task::{blocking_json_task, blocking_task}, task::{blocking_json_task, blocking_response_task},
uor::UnifyingOrFilter,
}; };
const API_PREFIX: &str = "eth"; const API_PREFIX: &str = "eth";
@ -1126,7 +1126,7 @@ pub fn serve<T: BeaconChainTypes>(
log: Logger| async move { log: Logger| async move {
publish_blocks::publish_block(None, block, chain, &network_tx, log) publish_blocks::publish_block(None, block, chain, &network_tx, log)
.await .await
.map(|()| warp::reply()) .map(|()| warp::reply().into_response())
}, },
); );
@ -1146,7 +1146,7 @@ pub fn serve<T: BeaconChainTypes>(
log: Logger| async move { log: Logger| async move {
publish_blocks::publish_blinded_block(block, chain, &network_tx, log) publish_blocks::publish_blinded_block(block, chain, &network_tx, log)
.await .await
.map(|()| warp::reply()) .map(|()| warp::reply().into_response())
}, },
); );
@ -1252,7 +1252,7 @@ pub fn serve<T: BeaconChainTypes>(
|block_id: BlockId, |block_id: BlockId,
chain: Arc<BeaconChain<T>>, chain: Arc<BeaconChain<T>>,
accept_header: Option<api_types::Accept>| { accept_header: Option<api_types::Accept>| {
blocking_task(move || { blocking_response_task(move || {
let (block, execution_optimistic) = block_id.blinded_block(&chain)?; let (block, execution_optimistic) = block_id.blinded_block(&chain)?;
let fork_name = block let fork_name = block
.fork_name(&chain.spec) .fork_name(&chain.spec)
@ -1764,7 +1764,7 @@ pub fn serve<T: BeaconChainTypes>(
.and(eth1_service_filter.clone()) .and(eth1_service_filter.clone())
.and_then( .and_then(
|accept_header: Option<api_types::Accept>, eth1_service: eth1::Service| { |accept_header: Option<api_types::Accept>, eth1_service: eth1::Service| {
blocking_task(move || match accept_header { blocking_response_task(move || match accept_header {
Some(api_types::Accept::Json) | None => { Some(api_types::Accept::Json) | None => {
let snapshot = eth1_service.get_deposit_snapshot(); let snapshot = eth1_service.get_deposit_snapshot();
Ok( Ok(
@ -1983,7 +1983,7 @@ pub fn serve<T: BeaconChainTypes>(
state_id: StateId, state_id: StateId,
accept_header: Option<api_types::Accept>, accept_header: Option<api_types::Accept>,
chain: Arc<BeaconChain<T>>| { chain: Arc<BeaconChain<T>>| {
blocking_task(move || match accept_header { blocking_response_task(move || match accept_header {
Some(api_types::Accept::Ssz) => { Some(api_types::Accept::Ssz) => {
// We can ignore the optimistic status for the "fork" since it's a // We can ignore the optimistic status for the "fork" since it's a
// specification constant that doesn't change across competing heads of the // specification constant that doesn't change across competing heads of the
@ -1996,7 +1996,9 @@ pub fn serve<T: BeaconChainTypes>(
.status(200) .status(200)
.header("Content-Type", "application/octet-stream") .header("Content-Type", "application/octet-stream")
.body(state.as_ssz_bytes().into()) .body(state.as_ssz_bytes().into())
.map(|resp| add_consensus_version_header(resp, fork_name)) .map(|resp: warp::reply::Response| {
add_consensus_version_header(resp, fork_name)
})
.map_err(|e| { .map_err(|e| {
warp_utils::reject::custom_server_error(format!( warp_utils::reject::custom_server_error(format!(
"failed to create response: {}", "failed to create response: {}",
@ -2159,7 +2161,7 @@ pub fn serve<T: BeaconChainTypes>(
.and(warp::path::end()) .and(warp::path::end())
.and(network_globals.clone()) .and(network_globals.clone())
.and_then(|network_globals: Arc<NetworkGlobals<T::EthSpec>>| { .and_then(|network_globals: Arc<NetworkGlobals<T::EthSpec>>| {
blocking_task(move || match *network_globals.sync_state.read() { blocking_response_task(move || match *network_globals.sync_state.read() {
SyncState::SyncingFinalized { .. } SyncState::SyncingFinalized { .. }
| SyncState::SyncingHead { .. } | SyncState::SyncingHead { .. }
| SyncState::SyncTransition | SyncState::SyncTransition
@ -2426,7 +2428,7 @@ pub fn serve<T: BeaconChainTypes>(
build_block_contents::build_block_contents(fork_name, chain, block); build_block_contents::build_block_contents(fork_name, chain, block);
fork_versioned_response(endpoint_version, fork_name, block_contents?) fork_versioned_response(endpoint_version, fork_name, block_contents?)
.map(|response| warp::reply::json(&response)) .map(|response| warp::reply::json(&response).into_response())
}, },
); );
@ -2483,7 +2485,7 @@ pub fn serve<T: BeaconChainTypes>(
// Pose as a V2 endpoint so we return the fork `version`. // Pose as a V2 endpoint so we return the fork `version`.
fork_versioned_response(V2, fork_name, block) fork_versioned_response(V2, fork_name, block)
.map(|response| warp::reply::json(&response)) .map(|response| warp::reply::json(&response).into_response())
}, },
); );
@ -2856,7 +2858,7 @@ pub fn serve<T: BeaconChainTypes>(
)) ))
})?; })?;
Ok::<_, warp::reject::Rejection>(warp::reply::json(&())) Ok::<_, warp::reject::Rejection>(warp::reply::json(&()).into_response())
}, },
); );
@ -2965,7 +2967,7 @@ pub fn serve<T: BeaconChainTypes>(
builder builder
.post_builder_validators(&filtered_registration_data) .post_builder_validators(&filtered_registration_data)
.await .await
.map(|resp| warp::reply::json(&resp)) .map(|resp| warp::reply::json(&resp).into_response())
.map_err(|e| { .map_err(|e| {
warn!( warn!(
log, log,
@ -3227,7 +3229,7 @@ pub fn serve<T: BeaconChainTypes>(
.and(warp::path::end()) .and(warp::path::end())
.and(chain_filter.clone()) .and(chain_filter.clone())
.and_then(|chain: Arc<BeaconChain<T>>| { .and_then(|chain: Arc<BeaconChain<T>>| {
blocking_task(move || { blocking_response_task(move || {
Ok::<_, warp::Rejection>(warp::reply::json(&api_types::GenericResponseRef::from( Ok::<_, warp::Rejection>(warp::reply::json(&api_types::GenericResponseRef::from(
chain chain
.canonical_head .canonical_head
@ -3346,7 +3348,7 @@ pub fn serve<T: BeaconChainTypes>(
.and(warp::path::end()) .and(warp::path::end())
.and(chain_filter.clone()) .and(chain_filter.clone())
.and_then(|state_id: StateId, chain: Arc<BeaconChain<T>>| { .and_then(|state_id: StateId, chain: Arc<BeaconChain<T>>| {
blocking_task(move || { blocking_response_task(move || {
// This debug endpoint provides no indication of optimistic status. // This debug endpoint provides no indication of optimistic status.
let (state, _execution_optimistic) = state_id.state(&chain)?; let (state, _execution_optimistic) = state_id.state(&chain)?;
Response::builder() Response::builder()
@ -3482,9 +3484,10 @@ pub fn serve<T: BeaconChainTypes>(
.and(chain_filter.clone()) .and(chain_filter.clone())
.and_then(|chain: Arc<BeaconChain<T>>| async move { .and_then(|chain: Arc<BeaconChain<T>>| async move {
let merge_readiness = chain.check_merge_readiness().await; let merge_readiness = chain.check_merge_readiness().await;
Ok::<_, warp::reject::Rejection>(warp::reply::json(&api_types::GenericResponse::from( Ok::<_, warp::reject::Rejection>(
merge_readiness, warp::reply::json(&api_types::GenericResponse::from(merge_readiness))
))) .into_response(),
)
}); });
// GET lighthouse/beacon/blobs_sidecars/{block_id} // GET lighthouse/beacon/blobs_sidecars/{block_id}
@ -3530,7 +3533,7 @@ pub fn serve<T: BeaconChainTypes>(
.and_then( .and_then(
|topics_res: Result<api_types::EventQuery, warp::Rejection>, |topics_res: Result<api_types::EventQuery, warp::Rejection>,
chain: Arc<BeaconChain<T>>| { chain: Arc<BeaconChain<T>>| {
blocking_task(move || { blocking_response_task(move || {
let topics = topics_res?; let topics = topics_res?;
// for each topic subscribed spawn a new subscription // for each topic subscribed spawn a new subscription
let mut receivers = Vec::with_capacity(topics.topics.len()); let mut receivers = Vec::with_capacity(topics.topics.len());
@ -3597,109 +3600,111 @@ pub fn serve<T: BeaconChainTypes>(
); );
// Define the ultimate set of routes that will be provided to the server. // Define the ultimate set of routes that will be provided to the server.
// Use `uor` rather than `or` in order to simplify types (see `UnifyingOrFilter`).
let routes = warp::get() let routes = warp::get()
.and( .and(
get_beacon_genesis get_beacon_genesis
.boxed() .uor(get_beacon_state_root)
.or(get_beacon_state_root.boxed()) .uor(get_beacon_state_fork)
.or(get_beacon_state_fork.boxed()) .uor(get_beacon_state_finality_checkpoints)
.or(get_beacon_state_finality_checkpoints.boxed()) .uor(get_beacon_state_validator_balances)
.or(get_beacon_state_validator_balances.boxed()) .uor(get_beacon_state_validators_id)
.or(get_beacon_state_validators_id.boxed()) .uor(get_beacon_state_validators)
.or(get_beacon_state_validators.boxed()) .uor(get_beacon_state_committees)
.or(get_beacon_state_committees.boxed()) .uor(get_beacon_state_sync_committees)
.or(get_beacon_state_sync_committees.boxed()) .uor(get_beacon_state_randao)
.or(get_beacon_state_randao.boxed()) .uor(get_beacon_headers)
.or(get_beacon_headers.boxed()) .uor(get_beacon_headers_block_id)
.or(get_beacon_headers_block_id.boxed()) .uor(get_beacon_block)
.or(get_beacon_block.boxed()) .uor(get_beacon_block_attestations)
.or(get_beacon_block_attestations.boxed()) .uor(get_beacon_blinded_block)
.or(get_beacon_blinded_block.boxed()) .uor(get_beacon_block_root)
.or(get_beacon_block_root.boxed()) .uor(get_beacon_pool_attestations)
.or(get_beacon_pool_attestations.boxed()) .uor(get_beacon_pool_attester_slashings)
.or(get_beacon_pool_attester_slashings.boxed()) .uor(get_beacon_pool_proposer_slashings)
.or(get_beacon_pool_proposer_slashings.boxed()) .uor(get_beacon_pool_voluntary_exits)
.or(get_beacon_pool_voluntary_exits.boxed()) .uor(get_beacon_pool_bls_to_execution_changes)
.or(get_beacon_pool_bls_to_execution_changes.boxed()) .uor(get_beacon_deposit_snapshot)
.or(get_beacon_deposit_snapshot.boxed()) .uor(get_beacon_rewards_blocks)
.or(get_beacon_rewards_blocks.boxed()) .uor(get_config_fork_schedule)
.or(get_config_fork_schedule.boxed()) .uor(get_config_spec)
.or(get_config_spec.boxed()) .uor(get_config_deposit_contract)
.or(get_config_deposit_contract.boxed()) .uor(get_debug_beacon_states)
.or(get_debug_beacon_states.boxed()) .uor(get_debug_beacon_heads)
.or(get_debug_beacon_heads.boxed()) .uor(get_node_identity)
.or(get_node_identity.boxed()) .uor(get_node_version)
.or(get_node_version.boxed()) .uor(get_node_syncing)
.or(get_node_syncing.boxed()) .uor(get_node_health)
.or(get_node_health.boxed()) .uor(get_node_peers_by_id)
.or(get_node_peers_by_id.boxed()) .uor(get_node_peers)
.or(get_node_peers.boxed()) .uor(get_node_peer_count)
.or(get_node_peer_count.boxed()) .uor(get_validator_duties_proposer)
.or(get_validator_duties_proposer.boxed()) .uor(get_validator_blocks)
.or(get_validator_blocks.boxed()) .uor(get_validator_blinded_blocks)
.or(get_validator_blinded_blocks.boxed()) .uor(get_validator_attestation_data)
.or(get_validator_attestation_data.boxed()) .uor(get_validator_aggregate_attestation)
.or(get_validator_aggregate_attestation.boxed()) .uor(get_validator_sync_committee_contribution)
.or(get_validator_sync_committee_contribution.boxed()) .uor(get_lighthouse_health)
.or(get_lighthouse_health.boxed()) .uor(get_lighthouse_ui_health)
.or(get_lighthouse_ui_health.boxed()) .uor(get_lighthouse_ui_validator_count)
.or(get_lighthouse_ui_validator_count.boxed()) .uor(get_lighthouse_syncing)
.or(get_lighthouse_syncing.boxed()) .uor(get_lighthouse_nat)
.or(get_lighthouse_nat.boxed()) .uor(get_lighthouse_peers)
.or(get_lighthouse_peers.boxed()) .uor(get_lighthouse_peers_connected)
.or(get_lighthouse_peers_connected.boxed()) .uor(get_lighthouse_proto_array)
.or(get_lighthouse_proto_array.boxed()) .uor(get_lighthouse_validator_inclusion_global)
.or(get_lighthouse_validator_inclusion_global.boxed()) .uor(get_lighthouse_validator_inclusion)
.or(get_lighthouse_validator_inclusion.boxed()) .uor(get_lighthouse_eth1_syncing)
.or(get_lighthouse_eth1_syncing.boxed()) .uor(get_lighthouse_eth1_block_cache)
.or(get_lighthouse_eth1_block_cache.boxed()) .uor(get_lighthouse_eth1_deposit_cache)
.or(get_lighthouse_eth1_deposit_cache.boxed()) .uor(get_lighthouse_beacon_states_ssz)
.or(get_lighthouse_beacon_states_ssz.boxed()) .uor(get_lighthouse_staking)
.or(get_lighthouse_staking.boxed()) .uor(get_lighthouse_database_info)
.or(get_lighthouse_database_info.boxed()) .uor(get_lighthouse_block_rewards)
.or(get_lighthouse_block_rewards.boxed()) .uor(get_lighthouse_attestation_performance)
.or(get_lighthouse_attestation_performance.boxed()) .uor(get_lighthouse_block_packing_efficiency)
.or(get_lighthouse_block_packing_efficiency.boxed()) .uor(get_lighthouse_merge_readiness)
.or(get_lighthouse_merge_readiness.boxed()) .uor(get_lighthouse_blobs_sidecars.boxed())
.or(get_lighthouse_blobs_sidecars.boxed()) .uor(get_events)
.or(get_events.boxed())
.recover(warp_utils::reject::handle_rejection), .recover(warp_utils::reject::handle_rejection),
) )
.boxed() .boxed()
.or(warp::post().and( .uor(
warp::post().and(
post_beacon_blocks post_beacon_blocks
.boxed() .uor(post_beacon_blinded_blocks)
.or(post_beacon_blinded_blocks.boxed()) .uor(post_beacon_pool_attestations)
.or(post_beacon_pool_attestations.boxed()) .uor(post_beacon_pool_attester_slashings)
.or(post_beacon_pool_attester_slashings.boxed()) .uor(post_beacon_pool_proposer_slashings)
.or(post_beacon_pool_proposer_slashings.boxed()) .uor(post_beacon_pool_voluntary_exits)
.or(post_beacon_pool_voluntary_exits.boxed()) .uor(post_beacon_pool_sync_committees)
.or(post_beacon_pool_sync_committees.boxed()) .uor(post_beacon_pool_bls_to_execution_changes)
.or(post_beacon_pool_bls_to_execution_changes.boxed()) .uor(post_beacon_rewards_attestations)
.or(post_beacon_rewards_attestations.boxed()) .uor(post_beacon_rewards_sync_committee)
.or(post_beacon_rewards_sync_committee.boxed()) .uor(post_validator_duties_attester)
.or(post_validator_duties_attester.boxed()) .uor(post_validator_duties_sync)
.or(post_validator_duties_sync.boxed()) .uor(post_validator_aggregate_and_proofs)
.or(post_validator_aggregate_and_proofs.boxed()) .uor(post_validator_contribution_and_proofs)
.or(post_validator_contribution_and_proofs.boxed()) .uor(post_validator_beacon_committee_subscriptions)
.or(post_validator_beacon_committee_subscriptions.boxed()) .uor(post_validator_sync_committee_subscriptions)
.or(post_validator_sync_committee_subscriptions.boxed()) .uor(post_validator_prepare_beacon_proposer)
.or(post_validator_prepare_beacon_proposer.boxed()) .uor(post_validator_register_validator)
.or(post_validator_register_validator.boxed()) .uor(post_lighthouse_liveness)
.or(post_lighthouse_liveness.boxed()) .uor(post_lighthouse_database_reconstruct)
.or(post_lighthouse_database_reconstruct.boxed()) .uor(post_lighthouse_database_historical_blocks)
.or(post_lighthouse_database_historical_blocks.boxed()) .uor(post_lighthouse_block_rewards)
.or(post_lighthouse_block_rewards.boxed()) .uor(post_lighthouse_ui_validator_metrics)
.or(post_lighthouse_ui_validator_metrics.boxed()) .uor(post_lighthouse_ui_validator_info)
.or(post_lighthouse_ui_validator_info.boxed())
.recover(warp_utils::reject::handle_rejection), .recover(warp_utils::reject::handle_rejection),
)) ),
)
.recover(warp_utils::reject::handle_rejection) .recover(warp_utils::reject::handle_rejection)
.with(slog_logging(log.clone())) .with(slog_logging(log.clone()))
.with(prometheus_metrics()) .with(prometheus_metrics())
// Add a `Server` header. // Add a `Server` header.
.map(|reply| warp::reply::with_header(reply, "Server", &version_with_platform())) .map(|reply| warp::reply::with_header(reply, "Server", &version_with_platform()))
.with(cors_builder.build()); .with(cors_builder.build())
.boxed();
let http_socket: SocketAddr = SocketAddr::new(config.listen_addr, config.listen_port); let http_socket: SocketAddr = SocketAddr::new(config.listen_addr, config.listen_port);
let http_server: HttpServer = match config.tls_config { let http_server: HttpServer = match config.tls_config {

View File

@ -4,7 +4,7 @@ use serde::Serialize;
use types::{ use types::{
ExecutionOptimisticForkVersionedResponse, ForkName, ForkVersionedResponse, InconsistentFork, ExecutionOptimisticForkVersionedResponse, ForkName, ForkVersionedResponse, InconsistentFork,
}; };
use warp::reply::{self, Reply, WithHeader}; use warp::reply::{self, Reply, Response};
pub const V1: EndpointVersion = EndpointVersion(1); pub const V1: EndpointVersion = EndpointVersion(1);
pub const V2: EndpointVersion = EndpointVersion(2); pub const V2: EndpointVersion = EndpointVersion(2);
@ -48,8 +48,8 @@ pub fn execution_optimistic_fork_versioned_response<T: Serialize>(
} }
/// Add the `Eth-Consensus-Version` header to a response. /// Add the `Eth-Consensus-Version` header to a response.
pub fn add_consensus_version_header<T: Reply>(reply: T, fork_name: ForkName) -> WithHeader<T> { pub fn add_consensus_version_header<T: Reply>(reply: T, fork_name: ForkName) -> Response {
reply::with_header(reply, CONSENSUS_VERSION_HEADER, fork_name.to_string()) reply::with_header(reply, CONSENSUS_VERSION_HEADER, fork_name.to_string()).into_response()
} }
pub fn inconsistent_fork_rejection(error: InconsistentFork) -> warp::reject::Rejection { pub fn inconsistent_fork_rejection(error: InconsistentFork) -> warp::reject::Rejection {

View File

@ -127,7 +127,7 @@ pub async fn create_api_server<T: BeaconChainTypes>(
log: Logger, log: Logger,
) -> ApiServer<T::EthSpec, impl Future<Output = ()>> { ) -> ApiServer<T::EthSpec, impl Future<Output = ()>> {
// Get a random unused port. // Get a random unused port.
let port = unused_port::unused_tcp_port().unwrap(); let port = unused_port::unused_tcp4_port().unwrap();
create_api_server_on_port(chain, log, port).await create_api_server_on_port(chain, log, port).await
} }
@ -148,8 +148,8 @@ pub async fn create_api_server_on_port<T: BeaconChainTypes>(
let enr = EnrBuilder::new("v4").build(&enr_key).unwrap(); let enr = EnrBuilder::new("v4").build(&enr_key).unwrap();
let network_globals = Arc::new(NetworkGlobals::new( let network_globals = Arc::new(NetworkGlobals::new(
enr.clone(), enr.clone(),
TCP_PORT, Some(TCP_PORT),
UDP_PORT, None,
meta_data, meta_data,
vec![], vec![],
&log, &log,

View File

@ -1,5 +1,4 @@
#![cfg(not(debug_assertions))] // Tests are too slow in debug. #![cfg(not(debug_assertions))] // Tests are too slow in debug.
#![recursion_limit = "256"]
pub mod common; pub mod common;
pub mod fork_tests; pub mod fork_tests;

View File

@ -112,7 +112,7 @@ impl ApiTester {
pub async fn new_from_config(config: ApiTesterConfig) -> Self { pub async fn new_from_config(config: ApiTesterConfig) -> Self {
// Get a random unused port // Get a random unused port
let spec = config.spec; let spec = config.spec;
let port = unused_port::unused_tcp_port().unwrap(); let port = unused_port::unused_tcp4_port().unwrap();
let beacon_url = SensitiveUrl::parse(format!("http://127.0.0.1:{port}").as_str()).unwrap(); let beacon_url = SensitiveUrl::parse(format!("http://127.0.0.1:{port}").as_str()).unwrap();
let harness = Arc::new( let harness = Arc::new(

View File

@ -1,3 +1,4 @@
use crate::listen_addr::{ListenAddr, ListenAddress};
use crate::rpc::config::OutboundRateLimiterConfig; use crate::rpc::config::OutboundRateLimiterConfig;
use crate::types::GossipKind; use crate::types::GossipKind;
use crate::{Enr, PeerIdSerialized}; use crate::{Enr, PeerIdSerialized};
@ -12,6 +13,7 @@ use libp2p::gossipsub::{
use libp2p::Multiaddr; use libp2p::Multiaddr;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::net::{Ipv4Addr, Ipv6Addr};
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
@ -57,24 +59,24 @@ pub struct Config {
/// Data directory where node's keyfile is stored /// Data directory where node's keyfile is stored
pub network_dir: PathBuf, pub network_dir: PathBuf,
/// IP address to listen on. /// IP addresses to listen on.
pub listen_address: std::net::IpAddr, listen_addresses: ListenAddress,
/// The TCP port that libp2p listens on.
pub libp2p_port: u16,
/// UDP port that discovery listens on.
pub discovery_port: u16,
/// The address to broadcast to peers about which address we are listening on. None indicates /// The address to broadcast to peers about which address we are listening on. None indicates
/// that no discovery address has been set in the CLI args. /// that no discovery address has been set in the CLI args.
pub enr_address: Option<std::net::IpAddr>, pub enr_address: (Option<Ipv4Addr>, Option<Ipv6Addr>),
/// The udp port to broadcast to peers in order to reach back for discovery. /// The udp4 port to broadcast to peers in order to reach back for discovery.
pub enr_udp_port: Option<u16>, pub enr_udp4_port: Option<u16>,
/// The tcp port to broadcast to peers in order to reach back for libp2p services. /// The tcp4 port to broadcast to peers in order to reach back for libp2p services.
pub enr_tcp_port: Option<u16>, pub enr_tcp4_port: Option<u16>,
/// The udp6 port to broadcast to peers in order to reach back for discovery.
pub enr_udp6_port: Option<u16>,
/// The tcp6 port to broadcast to peers in order to reach back for libp2p services.
pub enr_tcp6_port: Option<u16>,
/// Target number of connected peers. /// Target number of connected peers.
pub target_peers: usize, pub target_peers: usize,
@ -139,6 +141,105 @@ pub struct Config {
pub outbound_rate_limiter_config: Option<OutboundRateLimiterConfig>, pub outbound_rate_limiter_config: Option<OutboundRateLimiterConfig>,
} }
impl Config {
/// Sets the listening address to use an ipv4 address. The discv5 ip_mode and table filter are
/// adjusted accordingly to ensure addresses that are present in the enr are globally
/// reachable.
pub fn set_ipv4_listening_address(&mut self, addr: Ipv4Addr, tcp_port: u16, udp_port: u16) {
self.listen_addresses = ListenAddress::V4(ListenAddr {
addr,
udp_port,
tcp_port,
});
self.discv5_config.ip_mode = discv5::IpMode::Ip4;
self.discv5_config.table_filter = |enr| enr.ip4().as_ref().map_or(false, is_global_ipv4)
}
/// Sets the listening address to use an ipv6 address. The discv5 ip_mode and table filter is
/// adjusted accordingly to ensure addresses that are present in the enr are globally
/// reachable.
pub fn set_ipv6_listening_address(&mut self, addr: Ipv6Addr, tcp_port: u16, udp_port: u16) {
self.listen_addresses = ListenAddress::V6(ListenAddr {
addr,
udp_port,
tcp_port,
});
self.discv5_config.ip_mode = discv5::IpMode::Ip6 {
enable_mapped_addresses: false,
};
self.discv5_config.table_filter = |enr| enr.ip6().as_ref().map_or(false, is_global_ipv6)
}
/// Sets the listening address to use both an ipv4 and ipv6 address. The discv5 ip_mode and
/// table filter is adjusted accordingly to ensure addresses that are present in the enr are
/// globally reachable.
pub fn set_ipv4_ipv6_listening_addresses(
&mut self,
v4_addr: Ipv4Addr,
tcp4_port: u16,
udp4_port: u16,
v6_addr: Ipv6Addr,
tcp6_port: u16,
udp6_port: u16,
) {
self.listen_addresses = ListenAddress::DualStack(
ListenAddr {
addr: v4_addr,
udp_port: udp4_port,
tcp_port: tcp4_port,
},
ListenAddr {
addr: v6_addr,
udp_port: udp6_port,
tcp_port: tcp6_port,
},
);
self.discv5_config.ip_mode = discv5::IpMode::Ip6 {
enable_mapped_addresses: true,
};
self.discv5_config.table_filter = |enr| match (&enr.ip4(), &enr.ip6()) {
(None, None) => false,
(None, Some(ip6)) => is_global_ipv6(ip6),
(Some(ip4), None) => is_global_ipv4(ip4),
(Some(ip4), Some(ip6)) => is_global_ipv4(ip4) && is_global_ipv6(ip6),
};
}
pub fn set_listening_addr(&mut self, listen_addr: ListenAddress) {
match listen_addr {
ListenAddress::V4(ListenAddr {
addr,
udp_port,
tcp_port,
}) => self.set_ipv4_listening_address(addr, tcp_port, udp_port),
ListenAddress::V6(ListenAddr {
addr,
udp_port,
tcp_port,
}) => self.set_ipv6_listening_address(addr, tcp_port, udp_port),
ListenAddress::DualStack(
ListenAddr {
addr: ip4addr,
udp_port: udp4_port,
tcp_port: tcp4_port,
},
ListenAddr {
addr: ip6addr,
udp_port: udp6_port,
tcp_port: tcp6_port,
},
) => self.set_ipv4_ipv6_listening_addresses(
ip4addr, tcp4_port, udp4_port, ip6addr, tcp6_port, udp6_port,
),
}
}
pub fn listen_addrs(&self) -> &ListenAddress {
&self.listen_addresses
}
}
impl Default for Config { impl Default for Config {
/// Generate a default network configuration. /// Generate a default network configuration.
fn default() -> Self { fn default() -> Self {
@ -183,7 +284,7 @@ impl Default for Config {
.filter_rate_limiter(filter_rate_limiter) .filter_rate_limiter(filter_rate_limiter)
.filter_max_bans_per_ip(Some(5)) .filter_max_bans_per_ip(Some(5))
.filter_max_nodes_per_ip(Some(10)) .filter_max_nodes_per_ip(Some(10))
.table_filter(|enr| enr.ip4().map_or(false, |ip| is_global(&ip))) // Filter non-global IPs .table_filter(|enr| enr.ip4().map_or(false, |ip| is_global_ipv4(&ip))) // Filter non-global IPs
.ban_duration(Some(Duration::from_secs(3600))) .ban_duration(Some(Duration::from_secs(3600)))
.ping_interval(Duration::from_secs(300)) .ping_interval(Duration::from_secs(300))
.build(); .build();
@ -191,12 +292,16 @@ impl Default for Config {
// NOTE: Some of these get overridden by the corresponding CLI default values. // NOTE: Some of these get overridden by the corresponding CLI default values.
Config { Config {
network_dir, network_dir,
listen_address: "0.0.0.0".parse().expect("valid ip address"), listen_addresses: ListenAddress::V4(ListenAddr {
libp2p_port: 9000, addr: Ipv4Addr::UNSPECIFIED,
discovery_port: 9000, udp_port: 9000,
enr_address: None, tcp_port: 9000,
enr_udp_port: None, }),
enr_tcp_port: None, enr_address: (None, None),
enr_udp4_port: None,
enr_tcp4_port: None,
enr_udp6_port: None,
enr_tcp6_port: None,
target_peers: 50, target_peers: 50,
gs_config, gs_config,
discv5_config, discv5_config,
@ -363,7 +468,7 @@ pub fn gossipsub_config(network_load: u8, fork_context: Arc<ForkContext>) -> Gos
/// Helper function to determine if the IpAddr is a global address or not. The `is_global()` /// Helper function to determine if the IpAddr is a global address or not. The `is_global()`
/// function is not yet stable on IpAddr. /// function is not yet stable on IpAddr.
#[allow(clippy::nonminimal_bool)] #[allow(clippy::nonminimal_bool)]
fn is_global(addr: &std::net::Ipv4Addr) -> bool { fn is_global_ipv4(addr: &Ipv4Addr) -> bool {
// check if this address is 192.0.0.9 or 192.0.0.10. These addresses are the only two // check if this address is 192.0.0.9 or 192.0.0.10. These addresses are the only two
// globally routable addresses in the 192.0.0.0/24 range. // globally routable addresses in the 192.0.0.0/24 range.
if u32::from_be_bytes(addr.octets()) == 0xc0000009 if u32::from_be_bytes(addr.octets()) == 0xc0000009
@ -384,3 +489,60 @@ fn is_global(addr: &std::net::Ipv4Addr) -> bool {
// Make sure the address is not in 0.0.0.0/8 // Make sure the address is not in 0.0.0.0/8
&& addr.octets()[0] != 0 && addr.octets()[0] != 0
} }
/// NOTE: Docs taken from https://doc.rust-lang.org/stable/std/net/struct.Ipv6Addr.html#method.is_global
///
/// Returns true if the address appears to be globally reachable as specified by the IANA IPv6
/// Special-Purpose Address Registry. Whether or not an address is practically reachable will
/// depend on your network configuration.
///
/// Most IPv6 addresses are globally reachable; unless they are specifically defined as not
/// globally reachable.
///
/// Non-exhaustive list of notable addresses that are not globally reachable:
///
/// - The unspecified address (is_unspecified)
/// - The loopback address (is_loopback)
/// - IPv4-mapped addresses
/// - Addresses reserved for benchmarking
/// - Addresses reserved for documentation (is_documentation)
/// - Unique local addresses (is_unique_local)
/// - Unicast addresses with link-local scope (is_unicast_link_local)
// TODO: replace with [`Ipv6Addr::is_global`] once
// [Ip](https://github.com/rust-lang/rust/issues/27709) is stable.
pub const fn is_global_ipv6(addr: &Ipv6Addr) -> bool {
const fn is_documentation(addr: &Ipv6Addr) -> bool {
(addr.segments()[0] == 0x2001) && (addr.segments()[1] == 0xdb8)
}
const fn is_unique_local(addr: &Ipv6Addr) -> bool {
(addr.segments()[0] & 0xfe00) == 0xfc00
}
const fn is_unicast_link_local(addr: &Ipv6Addr) -> bool {
(addr.segments()[0] & 0xffc0) == 0xfe80
}
!(addr.is_unspecified()
|| addr.is_loopback()
// IPv4-mapped Address (`::ffff:0:0/96`)
|| matches!(addr.segments(), [0, 0, 0, 0, 0, 0xffff, _, _])
// IPv4-IPv6 Translat. (`64:ff9b:1::/48`)
|| matches!(addr.segments(), [0x64, 0xff9b, 1, _, _, _, _, _])
// Discard-Only Address Block (`100::/64`)
|| matches!(addr.segments(), [0x100, 0, 0, 0, _, _, _, _])
// IETF Protocol Assignments (`2001::/23`)
|| (matches!(addr.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200)
&& !(
// Port Control Protocol Anycast (`2001:1::1`)
u128::from_be_bytes(addr.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001
// Traversal Using Relays around NAT Anycast (`2001:1::2`)
|| u128::from_be_bytes(addr.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002
// AMT (`2001:3::/32`)
|| matches!(addr.segments(), [0x2001, 3, _, _, _, _, _, _])
// AS112-v6 (`2001:4:112::/48`)
|| matches!(addr.segments(), [0x2001, 4, 0x112, _, _, _, _, _])
// ORCHIDv2 (`2001:20::/28`)
|| matches!(addr.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F)
))
|| is_documentation(addr)
|| is_unique_local(addr)
|| is_unicast_link_local(addr))
}

View File

@ -145,16 +145,39 @@ pub fn create_enr_builder_from_config<T: EnrKey>(
enable_tcp: bool, enable_tcp: bool,
) -> EnrBuilder<T> { ) -> EnrBuilder<T> {
let mut builder = EnrBuilder::new("v4"); let mut builder = EnrBuilder::new("v4");
if let Some(enr_address) = config.enr_address { let (maybe_ipv4_address, maybe_ipv6_address) = &config.enr_address;
builder.ip(enr_address);
if let Some(ip) = maybe_ipv4_address {
builder.ip4(*ip);
} }
if let Some(udp_port) = config.enr_udp_port {
builder.udp4(udp_port); if let Some(ip) = maybe_ipv6_address {
builder.ip6(*ip);
} }
// we always give it our listening tcp port
if let Some(udp4_port) = config.enr_udp4_port {
builder.udp4(udp4_port);
}
if let Some(udp6_port) = config.enr_udp6_port {
builder.udp6(udp6_port);
}
if enable_tcp { if enable_tcp {
let tcp_port = config.enr_tcp_port.unwrap_or(config.libp2p_port); // If the ENR port is not set, and we are listening over that ip version, use the listening port instead.
builder.tcp4(tcp_port); let tcp4_port = config
.enr_tcp4_port
.or_else(|| config.listen_addrs().v4().map(|v4_addr| v4_addr.tcp_port));
if let Some(tcp4_port) = tcp4_port {
builder.tcp4(tcp4_port);
}
let tcp6_port = config
.enr_tcp6_port
.or_else(|| config.listen_addrs().v6().map(|v6_addr| v6_addr.tcp_port));
if let Some(tcp6_port) = tcp6_port {
builder.tcp6(tcp6_port);
}
} }
builder builder
} }

View File

@ -201,8 +201,13 @@ impl<TSpec: EthSpec> Discovery<TSpec> {
info!(log, "ENR Initialised"; "enr" => local_enr.to_base64(), "seq" => local_enr.seq(), "id"=> %local_enr.node_id(), info!(log, "ENR Initialised"; "enr" => local_enr.to_base64(), "seq" => local_enr.seq(), "id"=> %local_enr.node_id(),
"ip4" => ?local_enr.ip4(), "udp4"=> ?local_enr.udp4(), "tcp4" => ?local_enr.tcp6() "ip4" => ?local_enr.ip4(), "udp4"=> ?local_enr.udp4(), "tcp4" => ?local_enr.tcp6()
); );
let listen_socket = match config.listen_addrs() {
let listen_socket = SocketAddr::new(config.listen_address, config.discovery_port); crate::listen_addr::ListenAddress::V4(v4_addr) => v4_addr.udp_socket_addr(),
crate::listen_addr::ListenAddress::V6(v6_addr) => v6_addr.udp_socket_addr(),
crate::listen_addr::ListenAddress::DualStack(_v4_addr, v6_addr) => {
v6_addr.udp_socket_addr()
}
};
// convert the keypair into an ENR key // convert the keypair into an ENR key
let enr_key: CombinedKey = CombinedKey::from_libp2p(local_key)?; let enr_key: CombinedKey = CombinedKey::from_libp2p(local_key)?;
@ -1015,15 +1020,28 @@ impl<TSpec: EthSpec> NetworkBehaviour for Discovery<TSpec> {
*self.network_globals.local_enr.write() = enr; *self.network_globals.local_enr.write() = enr;
// A new UDP socket has been detected. // A new UDP socket has been detected.
// Build a multiaddr to report to libp2p // Build a multiaddr to report to libp2p
let mut address = Multiaddr::from(socket_addr.ip()); let addr = match socket_addr.ip() {
IpAddr::V4(v4_addr) => {
self.network_globals.listen_port_tcp4().map(|tcp4_port| {
Multiaddr::from(v4_addr).with(Protocol::Tcp(tcp4_port))
})
}
IpAddr::V6(v6_addr) => {
self.network_globals.listen_port_tcp6().map(|tcp6_port| {
Multiaddr::from(v6_addr).with(Protocol::Tcp(tcp6_port))
})
}
};
if let Some(address) = addr {
// NOTE: This doesn't actually track the external TCP port. More sophisticated NAT handling // NOTE: This doesn't actually track the external TCP port. More sophisticated NAT handling
// should handle this. // should handle this.
address.push(Protocol::Tcp(self.network_globals.listen_port_tcp()));
return Poll::Ready(NBAction::ReportObservedAddr { return Poll::Ready(NBAction::ReportObservedAddr {
address, address,
score: AddressScore::Finite(1), score: AddressScore::Finite(1),
}); });
} }
}
Discv5Event::EnrAdded { .. } Discv5Event::EnrAdded { .. }
| Discv5Event::TalkRequest(_) | Discv5Event::TalkRequest(_)
| Discv5Event::NodeInserted { .. } | Discv5Event::NodeInserted { .. }
@ -1087,7 +1105,6 @@ mod tests {
use enr::EnrBuilder; use enr::EnrBuilder;
use slog::{o, Drain}; use slog::{o, Drain};
use types::{BitVector, MinimalEthSpec, SubnetId}; use types::{BitVector, MinimalEthSpec, SubnetId};
use unused_port::unused_udp_port;
type E = MinimalEthSpec; type E = MinimalEthSpec;
@ -1105,17 +1122,15 @@ mod tests {
async fn build_discovery() -> Discovery<E> { async fn build_discovery() -> Discovery<E> {
let keypair = libp2p::identity::Keypair::generate_secp256k1(); let keypair = libp2p::identity::Keypair::generate_secp256k1();
let config = NetworkConfig { let mut config = NetworkConfig::default();
discovery_port: unused_udp_port().unwrap(), config.set_listening_addr(crate::ListenAddress::unused_v4_ports());
..Default::default()
};
let enr_key: CombinedKey = CombinedKey::from_libp2p(&keypair).unwrap(); let enr_key: CombinedKey = CombinedKey::from_libp2p(&keypair).unwrap();
let enr: Enr = build_enr::<E>(&enr_key, &config, &EnrForkId::default()).unwrap(); let enr: Enr = build_enr::<E>(&enr_key, &config, &EnrForkId::default()).unwrap();
let log = build_log(slog::Level::Debug, false); let log = build_log(slog::Level::Debug, false);
let globals = NetworkGlobals::new( let globals = NetworkGlobals::new(
enr, enr,
9000, Some(9000),
9000, None,
MetaData::V2(MetaDataV2 { MetaData::V2(MetaDataV2 {
seq_number: 0, seq_number: 0,
attnets: Default::default(), attnets: Default::default(),

View File

@ -10,12 +10,14 @@ pub mod service;
#[allow(clippy::mutable_key_type)] // PeerId in hashmaps are no longer permitted by clippy #[allow(clippy::mutable_key_type)] // PeerId in hashmaps are no longer permitted by clippy
pub mod discovery; pub mod discovery;
pub mod listen_addr;
pub mod metrics; pub mod metrics;
pub mod peer_manager; pub mod peer_manager;
pub mod rpc; pub mod rpc;
pub mod types; pub mod types;
pub use config::gossip_max_size; pub use config::gossip_max_size;
pub use listen_addr::*;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::str::FromStr; use std::str::FromStr;

View File

@ -0,0 +1,97 @@
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use libp2p::{multiaddr::Protocol, Multiaddr};
use serde::{Deserialize, Serialize};
/// A listening address composed by an Ip, an UDP port and a TCP port.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ListenAddr<Ip> {
pub addr: Ip,
pub udp_port: u16,
pub tcp_port: u16,
}
impl<Ip: Into<IpAddr> + Clone> ListenAddr<Ip> {
pub fn udp_socket_addr(&self) -> SocketAddr {
(self.addr.clone().into(), self.udp_port).into()
}
pub fn tcp_socket_addr(&self) -> SocketAddr {
(self.addr.clone().into(), self.tcp_port).into()
}
}
/// Types of listening addresses Lighthouse can accept.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ListenAddress {
V4(ListenAddr<Ipv4Addr>),
V6(ListenAddr<Ipv6Addr>),
DualStack(ListenAddr<Ipv4Addr>, ListenAddr<Ipv6Addr>),
}
impl ListenAddress {
/// Return the listening address over IpV4 if any.
pub fn v4(&self) -> Option<&ListenAddr<Ipv4Addr>> {
match self {
ListenAddress::V4(v4_addr) | ListenAddress::DualStack(v4_addr, _) => Some(v4_addr),
ListenAddress::V6(_) => None,
}
}
/// Return the listening address over IpV6 if any.
pub fn v6(&self) -> Option<&ListenAddr<Ipv6Addr>> {
match self {
ListenAddress::V6(v6_addr) | ListenAddress::DualStack(_, v6_addr) => Some(v6_addr),
ListenAddress::V4(_) => None,
}
}
/// Returns the TCP addresses.
pub fn tcp_addresses(&self) -> impl Iterator<Item = Multiaddr> + '_ {
let v4_multiaddr = self
.v4()
.map(|v4_addr| Multiaddr::from(v4_addr.addr).with(Protocol::Tcp(v4_addr.tcp_port)));
let v6_multiaddr = self
.v6()
.map(|v6_addr| Multiaddr::from(v6_addr.addr).with(Protocol::Tcp(v6_addr.tcp_port)));
v4_multiaddr.into_iter().chain(v6_multiaddr)
}
#[cfg(test)]
pub fn unused_v4_ports() -> Self {
ListenAddress::V4(ListenAddr {
addr: Ipv4Addr::UNSPECIFIED,
udp_port: unused_port::unused_udp4_port().unwrap(),
tcp_port: unused_port::unused_tcp4_port().unwrap(),
})
}
#[cfg(test)]
pub fn unused_v6_ports() -> Self {
ListenAddress::V6(ListenAddr {
addr: Ipv6Addr::UNSPECIFIED,
udp_port: unused_port::unused_udp6_port().unwrap(),
tcp_port: unused_port::unused_tcp6_port().unwrap(),
})
}
}
impl slog::KV for ListenAddress {
fn serialize(
&self,
_record: &slog::Record,
serializer: &mut dyn slog::Serializer,
) -> slog::Result {
if let Some(v4_addr) = self.v4() {
serializer.emit_arguments("ip4_address", &format_args!("{}", v4_addr.addr))?;
serializer.emit_u16("udp4_port", v4_addr.udp_port)?;
serializer.emit_u16("tcp4_port", v4_addr.tcp_port)?;
}
if let Some(v6_addr) = self.v6() {
serializer.emit_arguments("ip6_address", &format_args!("{}", v6_addr.addr))?;
serializer.emit_u16("udp6_port", v6_addr.udp_port)?;
serializer.emit_u16("tcp6_port", v6_addr.tcp_port)?;
}
slog::Result::Ok(())
}
}

View File

@ -159,7 +159,7 @@ pub fn check_nat() {
if NAT_OPEN.as_ref().map(|v| v.get()).unwrap_or(0) != 0 { if NAT_OPEN.as_ref().map(|v| v.get()).unwrap_or(0) != 0 {
return; return;
} }
if ADDRESS_UPDATE_COUNT.as_ref().map(|v| v.get()).unwrap_or(0) == 0 if ADDRESS_UPDATE_COUNT.as_ref().map(|v| v.get()).unwrap_or(0) != 0
|| NETWORK_INBOUND_PEERS.as_ref().map(|v| v.get()).unwrap_or(0) != 0_i64 || NETWORK_INBOUND_PEERS.as_ref().map(|v| v.get()).unwrap_or(0) != 0_i64
{ {
inc_counter(&NAT_OPEN); inc_counter(&NAT_OPEN);

View File

@ -165,8 +165,8 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
let meta_data = utils::load_or_build_metadata(&config.network_dir, &log); let meta_data = utils::load_or_build_metadata(&config.network_dir, &log);
let globals = NetworkGlobals::new( let globals = NetworkGlobals::new(
enr, enr,
config.libp2p_port, config.listen_addrs().v4().map(|v4_addr| v4_addr.tcp_port),
config.discovery_port, config.listen_addrs().v6().map(|v6_addr| v6_addr.tcp_port),
meta_data, meta_data,
config config
.trusted_peers .trusted_peers
@ -391,20 +391,9 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
async fn start(&mut self, config: &crate::NetworkConfig) -> error::Result<()> { async fn start(&mut self, config: &crate::NetworkConfig) -> error::Result<()> {
let enr = self.network_globals.local_enr(); let enr = self.network_globals.local_enr();
info!(self.log, "Libp2p Starting"; "peer_id" => %enr.peer_id(), "bandwidth_config" => format!("{}-{}", config.network_load, NetworkLoad::from(config.network_load).name)); info!(self.log, "Libp2p Starting"; "peer_id" => %enr.peer_id(), "bandwidth_config" => format!("{}-{}", config.network_load, NetworkLoad::from(config.network_load).name));
let discovery_string = if config.disable_discovery { debug!(self.log, "Attempting to open listening ports"; config.listen_addrs(), "discovery_enabled" => !config.disable_discovery);
"None".into()
} else {
config.discovery_port.to_string()
};
debug!(self.log, "Attempting to open listening ports"; "address" => ?config.listen_address, "tcp_port" => config.libp2p_port, "udp_port" => discovery_string);
let listen_multiaddr = {
let mut m = Multiaddr::from(config.listen_address);
m.push(MProtocol::Tcp(config.libp2p_port));
m
};
for listen_multiaddr in config.listen_addrs().tcp_addresses() {
match self.swarm.listen_on(listen_multiaddr.clone()) { match self.swarm.listen_on(listen_multiaddr.clone()) {
Ok(_) => { Ok(_) => {
let mut log_address = listen_multiaddr; let mut log_address = listen_multiaddr;
@ -421,6 +410,7 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
return Err("Libp2p was unable to listen on the given listen address.".into()); return Err("Libp2p was unable to listen on the given listen address.".into());
} }
}; };
}
// helper closure for dialing peers // helper closure for dialing peers
let mut dial = |mut multiaddr: Multiaddr| { let mut dial = |mut multiaddr: Multiaddr| {

View File

@ -7,7 +7,6 @@ use crate::EnrExt;
use crate::{Enr, GossipTopic, Multiaddr, PeerId}; use crate::{Enr, GossipTopic, Multiaddr, PeerId};
use parking_lot::RwLock; use parking_lot::RwLock;
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::atomic::{AtomicU16, Ordering};
use types::EthSpec; use types::EthSpec;
pub struct NetworkGlobals<TSpec: EthSpec> { pub struct NetworkGlobals<TSpec: EthSpec> {
@ -17,10 +16,10 @@ pub struct NetworkGlobals<TSpec: EthSpec> {
pub peer_id: RwLock<PeerId>, pub peer_id: RwLock<PeerId>,
/// Listening multiaddrs. /// Listening multiaddrs.
pub listen_multiaddrs: RwLock<Vec<Multiaddr>>, pub listen_multiaddrs: RwLock<Vec<Multiaddr>>,
/// The TCP port that the libp2p service is listening on /// The TCP port that the libp2p service is listening on over Ipv4.
pub listen_port_tcp: AtomicU16, listen_port_tcp4: Option<u16>,
/// The UDP port that the discovery service is listening on /// The TCP port that the libp2p service is listening on over Ipv6.
pub listen_port_udp: AtomicU16, listen_port_tcp6: Option<u16>,
/// The collection of known peers. /// The collection of known peers.
pub peers: RwLock<PeerDB<TSpec>>, pub peers: RwLock<PeerDB<TSpec>>,
// The local meta data of our node. // The local meta data of our node.
@ -36,8 +35,8 @@ pub struct NetworkGlobals<TSpec: EthSpec> {
impl<TSpec: EthSpec> NetworkGlobals<TSpec> { impl<TSpec: EthSpec> NetworkGlobals<TSpec> {
pub fn new( pub fn new(
enr: Enr, enr: Enr,
tcp_port: u16, listen_port_tcp4: Option<u16>,
udp_port: u16, listen_port_tcp6: Option<u16>,
local_metadata: MetaData<TSpec>, local_metadata: MetaData<TSpec>,
trusted_peers: Vec<PeerId>, trusted_peers: Vec<PeerId>,
log: &slog::Logger, log: &slog::Logger,
@ -46,8 +45,8 @@ impl<TSpec: EthSpec> NetworkGlobals<TSpec> {
local_enr: RwLock::new(enr.clone()), local_enr: RwLock::new(enr.clone()),
peer_id: RwLock::new(enr.peer_id()), peer_id: RwLock::new(enr.peer_id()),
listen_multiaddrs: RwLock::new(Vec::new()), listen_multiaddrs: RwLock::new(Vec::new()),
listen_port_tcp: AtomicU16::new(tcp_port), listen_port_tcp4,
listen_port_udp: AtomicU16::new(udp_port), listen_port_tcp6,
local_metadata: RwLock::new(local_metadata), local_metadata: RwLock::new(local_metadata),
peers: RwLock::new(PeerDB::new(trusted_peers, log)), peers: RwLock::new(PeerDB::new(trusted_peers, log)),
gossipsub_subscriptions: RwLock::new(HashSet::new()), gossipsub_subscriptions: RwLock::new(HashSet::new()),
@ -73,13 +72,13 @@ impl<TSpec: EthSpec> NetworkGlobals<TSpec> {
} }
/// Returns the libp2p TCP port that this node has been configured to listen on. /// Returns the libp2p TCP port that this node has been configured to listen on.
pub fn listen_port_tcp(&self) -> u16 { pub fn listen_port_tcp4(&self) -> Option<u16> {
self.listen_port_tcp.load(Ordering::Relaxed) self.listen_port_tcp4
} }
/// Returns the UDP discovery port that this node has been configured to listen on. /// Returns the UDP discovery port that this node has been configured to listen on.
pub fn listen_port_udp(&self) -> u16 { pub fn listen_port_tcp6(&self) -> Option<u16> {
self.listen_port_udp.load(Ordering::Relaxed) self.listen_port_tcp6
} }
/// Returns the number of libp2p connected peers. /// Returns the number of libp2p connected peers.
@ -137,8 +136,8 @@ impl<TSpec: EthSpec> NetworkGlobals<TSpec> {
let enr = discv5::enr::EnrBuilder::new("v4").build(&enr_key).unwrap(); let enr = discv5::enr::EnrBuilder::new("v4").build(&enr_key).unwrap();
NetworkGlobals::new( NetworkGlobals::new(
enr, enr,
9000, Some(9000),
9000, None,
MetaData::V2(MetaDataV2 { MetaData::V2(MetaDataV2 {
seq_number: 0, seq_number: 0,
attnets: Default::default(), attnets: Default::default(),

View File

@ -13,7 +13,7 @@ use tokio::runtime::Runtime;
use types::{ use types::{
ChainSpec, EnrForkId, Epoch, EthSpec, ForkContext, ForkName, Hash256, MinimalEthSpec, Slot, ChainSpec, EnrForkId, Epoch, EthSpec, ForkContext, ForkName, Hash256, MinimalEthSpec, Slot,
}; };
use unused_port::unused_tcp_port; use unused_port::unused_tcp4_port;
type E = MinimalEthSpec; type E = MinimalEthSpec;
type ReqId = usize; type ReqId = usize;
@ -78,11 +78,9 @@ pub fn build_config(port: u16, mut boot_nodes: Vec<Enr>) -> NetworkConfig {
.tempdir() .tempdir()
.unwrap(); .unwrap();
config.libp2p_port = port; // tcp port config.set_ipv4_listening_address(std::net::Ipv4Addr::UNSPECIFIED, port, port);
config.discovery_port = port; // udp port config.enr_udp4_port = Some(port);
config.enr_tcp_port = Some(port); config.enr_address = (Some(std::net::Ipv4Addr::LOCALHOST), None);
config.enr_udp_port = Some(port);
config.enr_address = Some("127.0.0.1".parse().unwrap());
config.boot_nodes_enr.append(&mut boot_nodes); config.boot_nodes_enr.append(&mut boot_nodes);
config.network_dir = path.into_path(); config.network_dir = path.into_path();
// Reduce gossipsub heartbeat parameters // Reduce gossipsub heartbeat parameters
@ -100,7 +98,7 @@ pub async fn build_libp2p_instance(
log: slog::Logger, log: slog::Logger,
fork_name: ForkName, fork_name: ForkName,
) -> Libp2pInstance { ) -> Libp2pInstance {
let port = unused_tcp_port().unwrap(); let port = unused_tcp4_port().unwrap();
let config = build_config(port, boot_nodes); let config = build_config(port, boot_nodes);
// launch libp2p service // launch libp2p service

View File

@ -36,7 +36,6 @@ const SMALL_CHAIN: u64 = 2;
const LONG_CHAIN: u64 = SLOTS_PER_EPOCH * 2; const LONG_CHAIN: u64 = SLOTS_PER_EPOCH * 2;
const TCP_PORT: u16 = 42; const TCP_PORT: u16 = 42;
const UDP_PORT: u16 = 42;
const SEQ_NUMBER: u64 = 0; const SEQ_NUMBER: u64 = 0;
/// The default time to wait for `BeaconProcessor` events. /// The default time to wait for `BeaconProcessor` events.
@ -177,8 +176,8 @@ impl TestRig {
let enr = EnrBuilder::new("v4").build(&enr_key).unwrap(); let enr = EnrBuilder::new("v4").build(&enr_key).unwrap();
let network_globals = Arc::new(NetworkGlobals::new( let network_globals = Arc::new(NetworkGlobals::new(
enr, enr,
TCP_PORT, Some(TCP_PORT),
UDP_PORT, None,
meta_data, meta_data,
vec![], vec![],
&log, &log,

View File

@ -20,13 +20,13 @@ pub struct UPnPConfig {
disable_discovery: bool, disable_discovery: bool,
} }
impl From<&NetworkConfig> for UPnPConfig { impl UPnPConfig {
fn from(config: &NetworkConfig) -> Self { pub fn from_config(config: &NetworkConfig) -> Option<Self> {
UPnPConfig { config.listen_addrs().v4().map(|v4_addr| UPnPConfig {
tcp_port: config.libp2p_port, tcp_port: v4_addr.tcp_port,
udp_port: config.discovery_port, udp_port: v4_addr.udp_port,
disable_discovery: config.disable_discovery, disable_discovery: config.disable_discovery,
} })
} }
} }

View File

@ -228,17 +228,22 @@ impl<T: BeaconChainTypes> NetworkService<T> {
let (network_senders, network_recievers) = NetworkSenders::new(); let (network_senders, network_recievers) = NetworkSenders::new();
// try and construct UPnP port mappings if required. // try and construct UPnP port mappings if required.
let upnp_config = crate::nat::UPnPConfig::from(config); if let Some(upnp_config) = crate::nat::UPnPConfig::from_config(config) {
let upnp_log = network_log.new(o!("service" => "UPnP")); let upnp_log = network_log.new(o!("service" => "UPnP"));
let upnp_network_send = network_senders.network_send(); let upnp_network_send = network_senders.network_send();
if config.upnp_enabled { if config.upnp_enabled {
executor.spawn_blocking( executor.spawn_blocking(
move || { move || {
crate::nat::construct_upnp_mappings(upnp_config, upnp_network_send, upnp_log) crate::nat::construct_upnp_mappings(
upnp_config,
upnp_network_send,
upnp_log,
)
}, },
"UPnP", "UPnP",
); );
} }
}
// get a reference to the beacon chain store // get a reference to the beacon chain store
let store = beacon_chain.store.clone(); let store = beacon_chain.store.clone();

View File

@ -61,10 +61,9 @@ mod tests {
); );
let mut config = NetworkConfig::default(); let mut config = NetworkConfig::default();
config.set_ipv4_listening_address(std::net::Ipv4Addr::UNSPECIFIED, 21212, 21212);
config.discv5_config.table_filter = |_| true; // Do not ignore local IPs config.discv5_config.table_filter = |_| true; // Do not ignore local IPs
config.libp2p_port = 21212;
config.upnp_enabled = false; config.upnp_enabled = false;
config.discovery_port = 21212;
config.boot_nodes_enr = enrs.clone(); config.boot_nodes_enr = enrs.clone();
runtime.block_on(async move { runtime.block_on(async move {
// Create a new network service which implicitly gets dropped at the // Create a new network service which implicitly gets dropped at the

View File

@ -78,7 +78,16 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
Arg::with_name("listen-address") Arg::with_name("listen-address")
.long("listen-address") .long("listen-address")
.value_name("ADDRESS") .value_name("ADDRESS")
.help("The address lighthouse will listen for UDP and TCP connections.") .help("The address lighthouse will listen for UDP and TCP connections. To listen \
over IpV4 and IpV6 set this flag twice with the different values.\n\
Examples:\n\
- --listen-address '0.0.0.0' will listen over Ipv4.\n\
- --listen-address '::' will listen over Ipv6.\n\
- --listen-address '0.0.0.0' --listen-address '::' will listen over both \
Ipv4 and Ipv6. The order of the given addresses is not relevant. However, \
multiple Ipv4, or multiple Ipv6 addresses will not be accepted.")
.multiple(true)
.max_values(2)
.default_value("0.0.0.0") .default_value("0.0.0.0")
.takes_value(true) .takes_value(true)
) )
@ -86,10 +95,21 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
Arg::with_name("port") Arg::with_name("port")
.long("port") .long("port")
.value_name("PORT") .value_name("PORT")
.help("The TCP/UDP port to listen on. The UDP port can be modified by the --discovery-port flag.") .help("The TCP/UDP port to listen on. The UDP port can be modified by the \
--discovery-port flag. If listening over both Ipv4 and Ipv6 the --port flag \
will apply to the Ipv4 address and --port6 to the Ipv6 address.")
.default_value("9000") .default_value("9000")
.takes_value(true), .takes_value(true),
) )
.arg(
Arg::with_name("port6")
.long("port6")
.value_name("PORT")
.help("The TCP/UDP port to listen on over IpV6 when listening over both Ipv4 and \
Ipv6. Defaults to 9090 when required.")
.default_value("9090")
.takes_value(true),
)
.arg( .arg(
Arg::with_name("discovery-port") Arg::with_name("discovery-port")
.long("discovery-port") .long("discovery-port")
@ -97,6 +117,15 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.help("The UDP port that discovery will listen on. Defaults to `port`") .help("The UDP port that discovery will listen on. Defaults to `port`")
.takes_value(true), .takes_value(true),
) )
.arg(
Arg::with_name("discovery-port6")
.long("discovery-port6")
.value_name("PORT")
.help("The UDP port that discovery will listen on over IpV6 if listening over \
both Ipv4 and IpV6. Defaults to `port6`")
.hidden(true) // TODO: implement dual stack via two sockets in discv5.
.takes_value(true),
)
.arg( .arg(
Arg::with_name("target-peers") Arg::with_name("target-peers")
.long("target-peers") .long("target-peers")
@ -137,27 +166,49 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
Arg::with_name("enr-udp-port") Arg::with_name("enr-udp-port")
.long("enr-udp-port") .long("enr-udp-port")
.value_name("PORT") .value_name("PORT")
.help("The UDP port of the local ENR. Set this only if you are sure other nodes can connect to your local node on this port.") .help("The UDP4 port of the local ENR. Set this only if you are sure other nodes \
can connect to your local node on this port over IpV4.")
.takes_value(true),
)
.arg(
Arg::with_name("enr-udp6-port")
.long("enr-udp6-port")
.value_name("PORT")
.help("The UDP6 port of the local ENR. Set this only if you are sure other nodes \
can connect to your local node on this port over IpV6.")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
Arg::with_name("enr-tcp-port") Arg::with_name("enr-tcp-port")
.long("enr-tcp-port") .long("enr-tcp-port")
.value_name("PORT") .value_name("PORT")
.help("The TCP port of the local ENR. Set this only if you are sure other nodes can connect to your local node on this port.\ .help("The TCP4 port of the local ENR. Set this only if you are sure other nodes \
The --port flag is used if this is not set.") can connect to your local node on this port over IpV4. The --port flag is \
used if this is not set.")
.takes_value(true),
)
.arg(
Arg::with_name("enr-tcp6-port")
.long("enr-tcp6-port")
.value_name("PORT")
.help("The TCP6 port of the local ENR. Set this only if you are sure other nodes \
can connect to your local node on this port over IpV6. The --port6 flag is \
used if this is not set.")
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
Arg::with_name("enr-address") Arg::with_name("enr-address")
.long("enr-address") .long("enr-address")
.value_name("ADDRESS") .value_name("ADDRESS")
.help("The IP address/ DNS address to broadcast to other peers on how to reach this node. \ .help("The IP address/ DNS address to broadcast to other peers on how to reach \
If a DNS address is provided, the enr-address is set to the IP address it resolves to and \ this node. If a DNS address is provided, the enr-address is set to the IP \
does not auto-update based on PONG responses in discovery. \ address it resolves to and does not auto-update based on PONG responses in \
Set this only if you are sure other nodes can connect to your local node on this address. \ discovery. Set this only if you are sure other nodes can connect to your \
Discovery will automatically find your external address, if possible.") local node on this address. This will update the `ip4` or `ip6` ENR fields \
accordingly. To update both, set this flag twice with the different values.")
.requires("enr-udp-port") .requires("enr-udp-port")
.multiple(true)
.max_values(2)
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
@ -165,7 +216,8 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.short("e") .short("e")
.long("enr-match") .long("enr-match")
.help("Sets the local ENR IP address and port to match those set for lighthouse. \ .help("Sets the local ENR IP address and port to match those set for lighthouse. \
Specifically, the IP address will be the value of --listen-address and the UDP port will be --discovery-port.") Specifically, the IP address will be the value of --listen-address and the \
UDP port will be --discovery-port.")
) )
.arg( .arg(
Arg::with_name("disable-enr-auto-update") Arg::with_name("disable-enr-auto-update")

View File

@ -11,13 +11,13 @@ use environment::RuntimeContext;
use execution_layer::DEFAULT_JWT_FILE; use execution_layer::DEFAULT_JWT_FILE;
use genesis::Eth1Endpoint; use genesis::Eth1Endpoint;
use http_api::TlsConfig; use http_api::TlsConfig;
use lighthouse_network::ListenAddress;
use lighthouse_network::{multiaddr::Protocol, Enr, Multiaddr, NetworkConfig, PeerIdSerialized}; use lighthouse_network::{multiaddr::Protocol, Enr, Multiaddr, NetworkConfig, PeerIdSerialized};
use sensitive_url::SensitiveUrl; use sensitive_url::SensitiveUrl;
use slog::{info, warn, Logger}; use slog::{info, warn, Logger};
use std::cmp; use std::cmp;
use std::cmp::max; use std::cmp::max;
use std::fmt::Debug; use std::fmt::Debug;
use std::fmt::Write;
use std::fs; use std::fs;
use std::net::Ipv6Addr; use std::net::Ipv6Addr;
use std::net::{IpAddr, Ipv4Addr, ToSocketAddrs}; use std::net::{IpAddr, Ipv4Addr, ToSocketAddrs};
@ -25,7 +25,6 @@ use std::path::{Path, PathBuf};
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration; use std::time::Duration;
use types::{Checkpoint, Epoch, EthSpec, Hash256, PublicKeyBytes, GRAFFITI_BYTES_LEN}; use types::{Checkpoint, Epoch, EthSpec, Hash256, PublicKeyBytes, GRAFFITI_BYTES_LEN};
use unused_port::{unused_tcp_port, unused_udp_port};
/// Gets the fully-initialized global client. /// Gets the fully-initialized global client.
/// ///
@ -79,13 +78,7 @@ pub fn get_config<E: EthSpec>(
let data_dir_ref = client_config.data_dir().clone(); let data_dir_ref = client_config.data_dir().clone();
set_network_config( set_network_config(&mut client_config.network, cli_args, &data_dir_ref, log)?;
&mut client_config.network,
cli_args,
&data_dir_ref,
log,
false,
)?;
/* /*
* Staking flag * Staking flag
@ -441,13 +434,6 @@ pub fn get_config<E: EthSpec>(
* Discovery address is set to localhost by default. * Discovery address is set to localhost by default.
*/ */
if cli_args.is_present("zero-ports") { if cli_args.is_present("zero-ports") {
if client_config.network.enr_address == Some(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))) {
client_config.network.enr_address = None
}
client_config.network.libp2p_port =
unused_tcp_port().map_err(|e| format!("Failed to get port for libp2p: {}", e))?;
client_config.network.discovery_port =
unused_udp_port().map_err(|e| format!("Failed to get port for discovery: {}", e))?;
client_config.http_api.listen_port = 0; client_config.http_api.listen_port = 0;
client_config.http_metrics.listen_port = 0; client_config.http_metrics.listen_port = 0;
} }
@ -798,13 +784,177 @@ pub fn get_config<E: EthSpec>(
Ok(client_config) Ok(client_config)
} }
/// Sets the network config from the command line arguments /// Gets the listening_addresses for lighthouse based on the cli options.
pub fn parse_listening_addresses(
cli_args: &ArgMatches,
log: &Logger,
) -> Result<ListenAddress, String> {
let listen_addresses_str = cli_args
.values_of("listen-address")
.expect("--listen_addresses has a default value");
let use_zero_ports = cli_args.is_present("zero-ports");
// parse the possible ips
let mut maybe_ipv4 = None;
let mut maybe_ipv6 = None;
for addr_str in listen_addresses_str {
let addr = addr_str.parse::<IpAddr>().map_err(|parse_error| {
format!("Failed to parse listen-address ({addr_str}) as an Ip address: {parse_error}")
})?;
match addr {
IpAddr::V4(v4_addr) => match &maybe_ipv4 {
Some(first_ipv4_addr) => {
return Err(format!(
"When setting the --listen-address option twice, use an IpV4 address and an Ipv6 address. \
Got two IpV4 addresses {first_ipv4_addr} and {v4_addr}"
));
}
None => maybe_ipv4 = Some(v4_addr),
},
IpAddr::V6(v6_addr) => match &maybe_ipv6 {
Some(first_ipv6_addr) => {
return Err(format!(
"When setting the --listen-address option twice, use an IpV4 address and an Ipv6 address. \
Got two IpV6 addresses {first_ipv6_addr} and {v6_addr}"
));
}
None => maybe_ipv6 = Some(v6_addr),
},
}
}
// parse the possible tcp ports
let port = cli_args
.value_of("port")
.expect("--port has a default value")
.parse::<u16>()
.map_err(|parse_error| format!("Failed to parse --port as an integer: {parse_error}"))?;
let port6 = cli_args
.value_of("port6")
.map(str::parse::<u16>)
.transpose()
.map_err(|parse_error| format!("Failed to parse --port6 as an integer: {parse_error}"))?
.unwrap_or(9090);
// parse the possible udp ports
let maybe_udp_port = cli_args
.value_of("discovery-port")
.map(str::parse::<u16>)
.transpose()
.map_err(|parse_error| {
format!("Failed to parse --discovery-port as an integer: {parse_error}")
})?;
let maybe_udp6_port = cli_args
.value_of("discovery-port6")
.map(str::parse::<u16>)
.transpose()
.map_err(|parse_error| {
format!("Failed to parse --discovery-port6 as an integer: {parse_error}")
})?;
// Now put everything together
let listening_addresses = match (maybe_ipv4, maybe_ipv6) {
(None, None) => {
// This should never happen unless clap is broken
return Err("No listening addresses provided".into());
}
(None, Some(ipv6)) => {
// A single ipv6 address was provided. Set the ports
if cli_args.is_present("port6") {
warn!(log, "When listening only over IpV6, use the --port flag. The value of --port6 will be ignored.")
}
// use zero ports if required. If not, use the given port.
let tcp_port = use_zero_ports
.then(unused_port::unused_tcp6_port)
.transpose()?
.unwrap_or(port);
if maybe_udp6_port.is_some() {
warn!(log, "When listening only over IpV6, use the --discovery-port flag. The value of --discovery-port6 will be ignored.")
}
// use zero ports if required. If not, use the specific udp port. If none given, use
// the tcp port.
let udp_port = use_zero_ports
.then(unused_port::unused_udp6_port)
.transpose()?
.or(maybe_udp_port)
.unwrap_or(port);
ListenAddress::V6(lighthouse_network::ListenAddr {
addr: ipv6,
udp_port,
tcp_port,
})
}
(Some(ipv4), None) => {
// A single ipv4 address was provided. Set the ports
// use zero ports if required. If not, use the given port.
let tcp_port = use_zero_ports
.then(unused_port::unused_tcp4_port)
.transpose()?
.unwrap_or(port);
// use zero ports if required. If not, use the specific udp port. If none given, use
// the tcp port.
let udp_port = use_zero_ports
.then(unused_port::unused_udp4_port)
.transpose()?
.or(maybe_udp_port)
.unwrap_or(port);
ListenAddress::V4(lighthouse_network::ListenAddr {
addr: ipv4,
udp_port,
tcp_port,
})
}
(Some(ipv4), Some(ipv6)) => {
let ipv4_tcp_port = use_zero_ports
.then(unused_port::unused_tcp4_port)
.transpose()?
.unwrap_or(port);
let ipv4_udp_port = use_zero_ports
.then(unused_port::unused_udp4_port)
.transpose()?
.or(maybe_udp_port)
.unwrap_or(ipv4_tcp_port);
// Defaults to 9090 when required
let ipv6_tcp_port = use_zero_ports
.then(unused_port::unused_tcp6_port)
.transpose()?
.unwrap_or(port6);
let ipv6_udp_port = use_zero_ports
.then(unused_port::unused_udp6_port)
.transpose()?
.or(maybe_udp6_port)
.unwrap_or(ipv6_tcp_port);
ListenAddress::DualStack(
lighthouse_network::ListenAddr {
addr: ipv4,
udp_port: ipv4_udp_port,
tcp_port: ipv4_tcp_port,
},
lighthouse_network::ListenAddr {
addr: ipv6,
udp_port: ipv6_udp_port,
tcp_port: ipv6_tcp_port,
},
)
}
};
Ok(listening_addresses)
}
/// Sets the network config from the command line arguments.
pub fn set_network_config( pub fn set_network_config(
config: &mut NetworkConfig, config: &mut NetworkConfig,
cli_args: &ArgMatches, cli_args: &ArgMatches,
data_dir: &Path, data_dir: &Path,
log: &Logger, log: &Logger,
use_listening_port_as_enr_port_by_default: bool,
) -> Result<(), String> { ) -> Result<(), String> {
// If a network dir has been specified, override the `datadir` definition. // If a network dir has been specified, override the `datadir` definition.
if let Some(dir) = cli_args.value_of("network-dir") { if let Some(dir) = cli_args.value_of("network-dir") {
@ -825,12 +975,7 @@ pub fn set_network_config(
config.shutdown_after_sync = true; config.shutdown_after_sync = true;
} }
if let Some(listen_address_str) = cli_args.value_of("listen-address") { config.set_listening_addr(parse_listening_addresses(cli_args, log)?);
let listen_address = listen_address_str
.parse()
.map_err(|_| format!("Invalid listen address: {:?}", listen_address_str))?;
config.listen_address = listen_address;
}
if let Some(target_peers_str) = cli_args.value_of("target-peers") { if let Some(target_peers_str) = cli_args.value_of("target-peers") {
config.target_peers = target_peers_str config.target_peers = target_peers_str
@ -838,21 +983,6 @@ pub fn set_network_config(
.map_err(|_| format!("Invalid number of target peers: {}", target_peers_str))?; .map_err(|_| format!("Invalid number of target peers: {}", target_peers_str))?;
} }
if let Some(port_str) = cli_args.value_of("port") {
let port = port_str
.parse::<u16>()
.map_err(|_| format!("Invalid port: {}", port_str))?;
config.libp2p_port = port;
config.discovery_port = port;
}
if let Some(port_str) = cli_args.value_of("discovery-port") {
let port = port_str
.parse::<u16>()
.map_err(|_| format!("Invalid port: {}", port_str))?;
config.discovery_port = port;
}
if let Some(value) = cli_args.value_of("network-load") { if let Some(value) = cli_args.value_of("network-load") {
let network_load = value let network_load = value
.parse::<u8>() .parse::<u8>()
@ -908,7 +1038,7 @@ pub fn set_network_config(
} }
if let Some(enr_udp_port_str) = cli_args.value_of("enr-udp-port") { if let Some(enr_udp_port_str) = cli_args.value_of("enr-udp-port") {
config.enr_udp_port = Some( config.enr_udp4_port = Some(
enr_udp_port_str enr_udp_port_str
.parse::<u16>() .parse::<u16>()
.map_err(|_| format!("Invalid discovery port: {}", enr_udp_port_str))?, .map_err(|_| format!("Invalid discovery port: {}", enr_udp_port_str))?,
@ -916,7 +1046,23 @@ pub fn set_network_config(
} }
if let Some(enr_tcp_port_str) = cli_args.value_of("enr-tcp-port") { if let Some(enr_tcp_port_str) = cli_args.value_of("enr-tcp-port") {
config.enr_tcp_port = Some( config.enr_tcp4_port = Some(
enr_tcp_port_str
.parse::<u16>()
.map_err(|_| format!("Invalid ENR TCP port: {}", enr_tcp_port_str))?,
);
}
if let Some(enr_udp_port_str) = cli_args.value_of("enr-udp6-port") {
config.enr_udp6_port = Some(
enr_udp_port_str
.parse::<u16>()
.map_err(|_| format!("Invalid discovery port: {}", enr_udp_port_str))?,
);
}
if let Some(enr_tcp_port_str) = cli_args.value_of("enr-tcp6-port") {
config.enr_tcp6_port = Some(
enr_tcp_port_str enr_tcp_port_str
.parse::<u16>() .parse::<u16>()
.map_err(|_| format!("Invalid ENR TCP port: {}", enr_tcp_port_str))?, .map_err(|_| format!("Invalid ENR TCP port: {}", enr_tcp_port_str))?,
@ -924,58 +1070,106 @@ pub fn set_network_config(
} }
if cli_args.is_present("enr-match") { if cli_args.is_present("enr-match") {
// Match the Ip and UDP port in the enr.
// set the enr address to localhost if the address is unspecified // set the enr address to localhost if the address is unspecified
if config.listen_address == IpAddr::V4(Ipv4Addr::UNSPECIFIED) { if let Some(ipv4_addr) = config.listen_addrs().v4().cloned() {
config.enr_address = Some(IpAddr::V4(Ipv4Addr::LOCALHOST)); let ipv4_enr_addr = if ipv4_addr.addr == Ipv4Addr::UNSPECIFIED {
} else if config.listen_address == IpAddr::V6(Ipv6Addr::UNSPECIFIED) { Ipv4Addr::LOCALHOST
config.enr_address = Some(IpAddr::V6(Ipv6Addr::LOCALHOST));
} else { } else {
config.enr_address = Some(config.listen_address); ipv4_addr.addr
} };
config.enr_udp_port = Some(config.discovery_port); config.enr_address.0 = Some(ipv4_enr_addr);
config.enr_udp4_port = Some(ipv4_addr.udp_port);
} }
if let Some(enr_address) = cli_args.value_of("enr-address") { if let Some(ipv6_addr) = config.listen_addrs().v6().cloned() {
let resolved_addr = match enr_address.parse::<IpAddr>() { let ipv6_enr_addr = if ipv6_addr.addr == Ipv6Addr::UNSPECIFIED {
Ok(addr) => addr, // // Input is an IpAddr Ipv6Addr::LOCALHOST
} else {
ipv6_addr.addr
};
config.enr_address.1 = Some(ipv6_enr_addr);
config.enr_udp6_port = Some(ipv6_addr.udp_port);
}
}
if let Some(enr_addresses) = cli_args.values_of("enr-address") {
let mut enr_ip4 = None;
let mut enr_ip6 = None;
let mut resolved_enr_ip4 = None;
let mut resolved_enr_ip6 = None;
for addr in enr_addresses {
match addr.parse::<IpAddr>() {
Ok(IpAddr::V4(v4_addr)) => {
if let Some(used) = enr_ip4.as_ref() {
warn!(log, "More than one Ipv4 ENR address provided"; "used" => %used, "ignored" => %v4_addr)
} else {
enr_ip4 = Some(v4_addr)
}
}
Ok(IpAddr::V6(v6_addr)) => {
if let Some(used) = enr_ip6.as_ref() {
warn!(log, "More than one Ipv6 ENR address provided"; "used" => %used, "ignored" => %v6_addr)
} else {
enr_ip6 = Some(v6_addr)
}
}
Err(_) => { Err(_) => {
let mut addr = enr_address.to_string(); // Try to resolve the address
// Appending enr-port to the dns hostname to appease `to_socket_addrs()` parsing.
// Since enr-update is disabled with a dns address, not setting the enr-udp-port // NOTE: From checking the `to_socket_addrs` code I don't think the port
// will make the node undiscoverable. // actually matters. Just use the udp port.
if let Some(enr_udp_port) =
config let port = match config.listen_addrs() {
.enr_udp_port ListenAddress::V4(v4_addr) => v4_addr.udp_port,
.or(if use_listening_port_as_enr_port_by_default { ListenAddress::V6(v6_addr) => v6_addr.udp_port,
Some(config.discovery_port) ListenAddress::DualStack(v4_addr, _v6_addr) => {
} else { // NOTE: slight preference for ipv4 that I don't think is of importance.
None v4_addr.udp_port
})
{
write!(addr, ":{}", enr_udp_port)
.map_err(|e| format!("Failed to write enr address {}", e))?;
} else {
return Err(
"enr-udp-port must be set for node to be discoverable with dns address"
.into(),
);
} }
// `to_socket_addr()` does the dns resolution
// Note: `to_socket_addrs()` is a blocking call
let resolved_addr = if let Ok(mut resolved_addrs) = addr.to_socket_addrs() {
// Pick the first ip from the list of resolved addresses
resolved_addrs
.next()
.map(|a| a.ip())
.ok_or("Resolved dns addr contains no entries")?
} else {
return Err(format!("Failed to parse enr-address: {}", enr_address));
}; };
let addr_str = format!("{addr}:{port}");
match addr_str.to_socket_addrs() {
Err(_e) => {
return Err(format!("Failed to parse or resolve address {addr}."))
}
Ok(resolved_addresses) => {
for socket_addr in resolved_addresses {
// Use the first ipv4 and first ipv6 addresses present.
// NOTE: this means that if two dns addresses are provided, we
// might end up using the ipv4 and ipv6 resolved addresses of just
// the first.
match socket_addr.ip() {
IpAddr::V4(v4_addr) => {
if resolved_enr_ip4.is_none() {
resolved_enr_ip4 = Some(v4_addr)
}
}
IpAddr::V6(v6_addr) => {
if resolved_enr_ip6.is_none() {
resolved_enr_ip6 = Some(v6_addr)
}
}
}
}
}
}
}
}
}
// The ENR addresses given as ips should take preference over any resolved address
let used_host_resolution = resolved_enr_ip4.is_some() || resolved_enr_ip6.is_some();
let ip4 = enr_ip4.or(resolved_enr_ip4);
let ip6 = enr_ip6.or(resolved_enr_ip6);
config.enr_address = (ip4, ip6);
if used_host_resolution {
config.discv5_config.enr_update = false; config.discv5_config.enr_update = false;
resolved_addr
} }
};
config.enr_address = Some(resolved_addr);
} }
if cli_args.is_present("disable-enr-auto-update") { if cli_args.is_present("disable-enr-auto-update") {

View File

@ -1,5 +1,4 @@
#![cfg(test)] #![cfg(test)]
#![recursion_limit = "512"]
use beacon_chain::StateSkipConfig; use beacon_chain::StateSkipConfig;
use node_test_rig::{ use node_test_rig::{

View File

@ -53,6 +53,14 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.takes_value(true) .takes_value(true)
.conflicts_with("network-dir") .conflicts_with("network-dir")
) )
.arg(
Arg::with_name("enr-udp6-port")
.long("enr-udp6-port")
.value_name("PORT")
.help("The UDP6 port of the local ENR. Set this only if you are sure other nodes \
can connect to your local node on this port over IpV6.")
.takes_value(true),
)
.arg( .arg(
Arg::with_name("enable-enr-auto-update") Arg::with_name("enable-enr-auto-update")
.short("x") .short("x")

View File

@ -1,7 +1,7 @@
use beacon_node::{get_data_dir, set_network_config}; use beacon_node::{get_data_dir, set_network_config};
use clap::ArgMatches; use clap::ArgMatches;
use eth2_network_config::Eth2NetworkConfig; use eth2_network_config::Eth2NetworkConfig;
use lighthouse_network::discv5::enr::EnrBuilder; use lighthouse_network::discovery::create_enr_builder_from_config;
use lighthouse_network::discv5::IpMode; use lighthouse_network::discv5::IpMode;
use lighthouse_network::discv5::{enr::CombinedKey, Discv5Config, Enr}; use lighthouse_network::discv5::{enr::CombinedKey, Discv5Config, Enr};
use lighthouse_network::{ use lighthouse_network::{
@ -57,12 +57,24 @@ impl<T: EthSpec> BootNodeConfig<T> {
let logger = slog_scope::logger(); let logger = slog_scope::logger();
set_network_config(&mut network_config, matches, &data_dir, &logger, true)?; set_network_config(&mut network_config, matches, &data_dir, &logger)?;
// Set the enr-udp-port to the default listening port if it was not specified. // Set the Enr UDP ports to the listening ports if not present.
if !matches.is_present("enr-udp-port") { if let Some(listening_addr_v4) = network_config.listen_addrs().v4() {
network_config.enr_udp_port = Some(network_config.discovery_port); network_config.enr_udp4_port = Some(
} network_config
.enr_udp4_port
.unwrap_or(listening_addr_v4.udp_port),
)
};
if let Some(listening_addr_v6) = network_config.listen_addrs().v6() {
network_config.enr_udp6_port = Some(
network_config
.enr_udp6_port
.unwrap_or(listening_addr_v6.udp_port),
)
};
// By default this is enabled. If it is not set, revert to false. // By default this is enabled. If it is not set, revert to false.
if !matches.is_present("enable-enr-auto-update") { if !matches.is_present("enable-enr-auto-update") {
@ -70,17 +82,29 @@ impl<T: EthSpec> BootNodeConfig<T> {
} }
// the address to listen on // the address to listen on
let listen_socket = let listen_socket = match network_config.listen_addrs().clone() {
SocketAddr::new(network_config.listen_address, network_config.discovery_port); lighthouse_network::ListenAddress::V4(v4_addr) => {
if listen_socket.is_ipv6() { // Set explicitly as ipv4 otherwise
network_config.discv5_config.ip_mode = IpMode::Ip4;
v4_addr.udp_socket_addr()
}
lighthouse_network::ListenAddress::V6(v6_addr) => {
// create ipv6 sockets and enable ipv4 mapped addresses.
network_config.discv5_config.ip_mode = IpMode::Ip6 {
enable_mapped_addresses: false,
};
v6_addr.udp_socket_addr()
}
lighthouse_network::ListenAddress::DualStack(_v4_addr, v6_addr) => {
// create ipv6 sockets and enable ipv4 mapped addresses. // create ipv6 sockets and enable ipv4 mapped addresses.
network_config.discv5_config.ip_mode = IpMode::Ip6 { network_config.discv5_config.ip_mode = IpMode::Ip6 {
enable_mapped_addresses: true, enable_mapped_addresses: true,
}; };
} else {
// Set explicitly as ipv4 otherwise v6_addr.udp_socket_addr()
network_config.discv5_config.ip_mode = IpMode::Ip4;
} }
};
let private_key = load_private_key(&network_config, &logger); let private_key = load_private_key(&network_config, &logger);
let local_key = CombinedKey::from_libp2p(&private_key)?; let local_key = CombinedKey::from_libp2p(&private_key)?;
@ -115,30 +139,8 @@ impl<T: EthSpec> BootNodeConfig<T> {
// Build the local ENR // Build the local ENR
let mut local_enr = { let mut local_enr = {
let mut builder = EnrBuilder::new("v4"); let enable_tcp = false;
// Set the enr address if specified. Set also the port. let mut builder = create_enr_builder_from_config(&network_config, enable_tcp);
// NOTE: if the port is specified but the the address is not, the port won't be
// set since it can't be known if it's an ipv6 or ipv4 udp port.
if let Some(enr_address) = network_config.enr_address {
match enr_address {
std::net::IpAddr::V4(ipv4_addr) => {
builder.ip4(ipv4_addr);
if let Some(port) = network_config.enr_udp_port {
builder.udp4(port);
}
}
std::net::IpAddr::V6(ipv6_addr) => {
builder.ip6(ipv6_addr);
if let Some(port) = network_config.enr_udp_port {
builder.udp6(port);
// We are enabling mapped addresses in the boot node in this case,
// so advertise an udp4 port as well.
builder.udp4(port);
}
}
}
};
// If we know of the ENR field, add it to the initial construction // If we know of the ENR field, add it to the initial construction
if let Some(enr_fork_bytes) = enr_fork { if let Some(enr_fork_bytes) = enr_fork {
builder.add_value("eth2", enr_fork_bytes.as_slice()); builder.add_value("eth2", enr_fork_bytes.as_slice());

View File

@ -1,4 +1,3 @@
#![recursion_limit = "256"]
extern crate proc_macro; extern crate proc_macro;
use proc_macro::TokenStream; use proc_macro::TokenStream;

View File

@ -921,6 +921,8 @@ pub struct SseExtendedPayloadAttributesGeneric<T> {
#[serde(with = "eth2_serde_utils::quoted_u64")] #[serde(with = "eth2_serde_utils::quoted_u64")]
pub proposer_index: u64, pub proposer_index: u64,
pub parent_block_root: Hash256, pub parent_block_root: Hash256,
#[serde(with = "eth2_serde_utils::quoted_u64")]
pub parent_block_number: u64,
pub parent_block_hash: ExecutionBlockHash, pub parent_block_hash: ExecutionBlockHash,
pub payload_attributes: T, pub payload_attributes: T,
} }
@ -958,6 +960,7 @@ impl ForkVersionDeserialize for SseExtendedPayloadAttributes {
proposal_slot: helper.proposal_slot, proposal_slot: helper.proposal_slot,
proposer_index: helper.proposer_index, proposer_index: helper.proposer_index,
parent_block_root: helper.parent_block_root, parent_block_root: helper.parent_block_root,
parent_block_number: helper.parent_block_number,
parent_block_hash: helper.parent_block_hash, parent_block_hash: helper.parent_block_hash,
payload_attributes: SsePayloadAttributes::deserialize_by_fork::<D>( payload_attributes: SsePayloadAttributes::deserialize_by_fork::<D>(
helper.payload_attributes, helper.payload_attributes,

View File

@ -6,14 +6,30 @@ pub enum Transport {
Udp, Udp,
} }
/// A convenience function for `unused_port(Transport::Tcp)`. #[derive(Copy, Clone)]
pub fn unused_tcp_port() -> Result<u16, String> { pub enum IpVersion {
unused_port(Transport::Tcp) Ipv4,
Ipv6,
} }
/// A convenience function for `unused_port(Transport::Tcp)`. /// A convenience wrapper over [`zero_port`].
pub fn unused_udp_port() -> Result<u16, String> { pub fn unused_tcp4_port() -> Result<u16, String> {
unused_port(Transport::Udp) zero_port(Transport::Tcp, IpVersion::Ipv4)
}
/// A convenience wrapper over [`zero_port`].
pub fn unused_udp4_port() -> Result<u16, String> {
zero_port(Transport::Udp, IpVersion::Ipv4)
}
/// A convenience wrapper over [`zero_port`].
pub fn unused_tcp6_port() -> Result<u16, String> {
zero_port(Transport::Tcp, IpVersion::Ipv6)
}
/// A convenience wrapper over [`zero_port`].
pub fn unused_udp6_port() -> Result<u16, String> {
zero_port(Transport::Udp, IpVersion::Ipv6)
} }
/// A bit of hack to find an unused port. /// A bit of hack to find an unused port.
@ -26,10 +42,15 @@ pub fn unused_udp_port() -> Result<u16, String> {
/// It is possible that users are unable to bind to the ports returned by this function as the OS /// It is possible that users are unable to bind to the ports returned by this function as the OS
/// has a buffer period where it doesn't allow binding to the same port even after the socket is /// has a buffer period where it doesn't allow binding to the same port even after the socket is
/// closed. We might have to use SO_REUSEADDR socket option from `std::net2` crate in that case. /// closed. We might have to use SO_REUSEADDR socket option from `std::net2` crate in that case.
pub fn unused_port(transport: Transport) -> Result<u16, String> { pub fn zero_port(transport: Transport, ipv: IpVersion) -> Result<u16, String> {
let localhost = match ipv {
IpVersion::Ipv4 => std::net::Ipv4Addr::LOCALHOST.into(),
IpVersion::Ipv6 => std::net::Ipv6Addr::LOCALHOST.into(),
};
let socket_addr = std::net::SocketAddr::new(localhost, 0);
let local_addr = match transport { let local_addr = match transport {
Transport::Tcp => { Transport::Tcp => {
let listener = TcpListener::bind("127.0.0.1:0").map_err(|e| { let listener = TcpListener::bind(socket_addr).map_err(|e| {
format!("Failed to create TCP listener to find unused port: {:?}", e) format!("Failed to create TCP listener to find unused port: {:?}", e)
})?; })?;
listener.local_addr().map_err(|e| { listener.local_addr().map_err(|e| {
@ -40,7 +61,7 @@ pub fn unused_port(transport: Transport) -> Result<u16, String> {
})? })?
} }
Transport::Udp => { Transport::Udp => {
let socket = UdpSocket::bind("127.0.0.1:0") let socket = UdpSocket::bind(socket_addr)
.map_err(|e| format!("Failed to create UDP socket to find unused port: {:?}", e))?; .map_err(|e| format!("Failed to create UDP socket to find unused port: {:?}", e))?;
socket.local_addr().map_err(|e| { socket.local_addr().map_err(|e| {
format!( format!(

View File

@ -6,3 +6,4 @@ pub mod metrics;
pub mod query; pub mod query;
pub mod reject; pub mod reject;
pub mod task; pub mod task;
pub mod uor;

View File

@ -1,4 +1,5 @@
use serde::Serialize; use serde::Serialize;
use warp::reply::{Reply, Response};
/// A convenience wrapper around `blocking_task`. /// A convenience wrapper around `blocking_task`.
pub async fn blocking_task<F, T>(func: F) -> Result<T, warp::Rejection> pub async fn blocking_task<F, T>(func: F) -> Result<T, warp::Rejection>
@ -8,16 +9,29 @@ where
{ {
tokio::task::spawn_blocking(func) tokio::task::spawn_blocking(func)
.await .await
.unwrap_or_else(|_| Err(warp::reject::reject())) // This should really be a 500 .unwrap_or_else(|_| Err(warp::reject::reject()))
}
/// A convenience wrapper around `blocking_task` that returns a `warp::reply::Response`.
///
/// Using this method consistently makes it possible to simplify types using `.unify()` or `.uor()`.
pub async fn blocking_response_task<F, T>(func: F) -> Result<Response, warp::Rejection>
where
F: FnOnce() -> Result<T, warp::Rejection> + Send + 'static,
T: Reply + Send + 'static,
{
blocking_task(func).await.map(Reply::into_response)
} }
/// A convenience wrapper around `blocking_task` for use with `warp` JSON responses. /// A convenience wrapper around `blocking_task` for use with `warp` JSON responses.
pub async fn blocking_json_task<F, T>(func: F) -> Result<warp::reply::Json, warp::Rejection> pub async fn blocking_json_task<F, T>(func: F) -> Result<Response, warp::Rejection>
where where
F: FnOnce() -> Result<T, warp::Rejection> + Send + 'static, F: FnOnce() -> Result<T, warp::Rejection> + Send + 'static,
T: Serialize + Send + 'static, T: Serialize + Send + 'static,
{ {
blocking_task(func) blocking_response_task(|| {
let response = func()?;
Ok(warp::reply::json(&response))
})
.await .await
.map(|resp| warp::reply::json(&resp))
} }

View File

@ -0,0 +1,25 @@
use warp::{filters::BoxedFilter, Filter, Rejection};
/// Mixin trait for `Filter` providing the unifying-or method.
pub trait UnifyingOrFilter: Filter<Error = Rejection> + Sized + Send + Sync + 'static
where
Self::Extract: Send,
{
/// Unifying `or`.
///
/// This is a shorthand for `self.or(other).unify().boxed()`, which is useful because it keeps
/// the filter type simple and prevents type-checker explosions.
fn uor<F>(self, other: F) -> BoxedFilter<Self::Extract>
where
F: Filter<Extract = Self::Extract, Error = Rejection> + Clone + Send + Sync + 'static,
{
self.or(other).unify().boxed()
}
}
impl<F> UnifyingOrFilter for F
where
F: Filter<Error = Rejection> + Sized + Send + Sync + 'static,
F::Extract: Send,
{
}

View File

@ -1698,7 +1698,6 @@ mod tests {
fn get_queued_attestations() -> Vec<QueuedAttestation> { fn get_queued_attestations() -> Vec<QueuedAttestation> {
(1..4) (1..4)
.into_iter()
.map(|i| QueuedAttestation { .map(|i| QueuedAttestation {
slot: Slot::new(i), slot: Slot::new(i),
attesting_indices: vec![], attesting_indices: vec![],

View File

@ -1,4 +1,3 @@
#![recursion_limit = "256"]
//! Provides procedural derive macros for the `Encode` and `Decode` traits of the `eth2_ssz` crate. //! Provides procedural derive macros for the `Encode` and `Decode` traits of the `eth2_ssz` crate.
//! //!
//! ## Attributes //! ## Attributes

View File

@ -106,7 +106,7 @@ fn tx_peek_blob_versioned_hashes<T: EthSpec>(
.safe_sub(blob_versioned_hashes_offset as usize)? .safe_sub(blob_versioned_hashes_offset as usize)?
.safe_div(32)?; .safe_div(32)?;
Ok((0..num_hashes).into_iter().map(move |i| { Ok((0..num_hashes).map(move |i| {
let next_version_hash_index = let next_version_hash_index =
(blob_versioned_hashes_offset as usize).safe_add(i.safe_mul(32)?)?; (blob_versioned_hashes_offset as usize).safe_add(i.safe_mul(32)?)?;
let bytes = opaque_tx let bytes = opaque_tx

View File

@ -1,4 +1,3 @@
#![recursion_limit = "256"]
use darling::FromDeriveInput; use darling::FromDeriveInput;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::quote; use quote::quote;

View File

@ -1,6 +1,4 @@
//! Ethereum 2.0 types //! Ethereum 2.0 types
// Required for big type-level numbers
#![recursion_limit = "128"]
// Clippy lint set up // Clippy lint set up
#![cfg_attr( #![cfg_attr(
not(test), not(test),

View File

@ -266,7 +266,7 @@ where
} }
/// Hashes the `self.serialize()` bytes. /// Hashes the `self.serialize()` bytes.
#[allow(clippy::derive_hash_xor_eq)] #[allow(clippy::derived_hash_with_manual_eq)]
impl<Pub, AggPub, Sig, AggSig> Hash for GenericAggregateSignature<Pub, AggPub, Sig, AggSig> impl<Pub, AggPub, Sig, AggSig> Hash for GenericAggregateSignature<Pub, AggPub, Sig, AggSig>
where where
Sig: TSignature<Pub>, Sig: TSignature<Pub>,

View File

@ -3,15 +3,14 @@ use lighthouse_network::{
discovery::{build_enr, CombinedKey, CombinedKeyExt, Keypair, ENR_FILENAME}, discovery::{build_enr, CombinedKey, CombinedKeyExt, Keypair, ENR_FILENAME},
NetworkConfig, NETWORK_KEY_FILENAME, NetworkConfig, NETWORK_KEY_FILENAME,
}; };
use std::fs;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
use std::net::IpAddr;
use std::path::PathBuf; use std::path::PathBuf;
use std::{fs, net::Ipv4Addr};
use types::{ChainSpec, EnrForkId, Epoch, EthSpec, Hash256}; use types::{ChainSpec, EnrForkId, Epoch, EthSpec, Hash256};
pub fn run<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> { pub fn run<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
let ip: IpAddr = clap_utils::parse_required(matches, "ip")?; let ip: Ipv4Addr = clap_utils::parse_required(matches, "ip")?;
let udp_port: u16 = clap_utils::parse_required(matches, "udp-port")?; let udp_port: u16 = clap_utils::parse_required(matches, "udp-port")?;
let tcp_port: u16 = clap_utils::parse_required(matches, "tcp-port")?; let tcp_port: u16 = clap_utils::parse_required(matches, "tcp-port")?;
let output_dir: PathBuf = clap_utils::parse_required(matches, "output-dir")?; let output_dir: PathBuf = clap_utils::parse_required(matches, "output-dir")?;
@ -25,12 +24,10 @@ pub fn run<T: EthSpec>(matches: &ArgMatches) -> Result<(), String> {
)); ));
} }
let config = NetworkConfig { let mut config = NetworkConfig::default();
enr_address: Some(ip), config.enr_address = (Some(ip), None);
enr_udp_port: Some(udp_port), config.enr_udp4_port = Some(udp_port);
enr_tcp_port: Some(tcp_port), config.enr_tcp6_port = Some(tcp_port);
..Default::default()
};
let local_keypair = Keypair::generate_secp256k1(); let local_keypair = Keypair::generate_secp256k1();
let enr_key = CombinedKey::from_libp2p(&local_keypair)?; let enr_key = CombinedKey::from_libp2p(&local_keypair)?;

View File

@ -1,5 +1,3 @@
#![recursion_limit = "256"]
mod metrics; mod metrics;
use beacon_node::ProductionBeaconNode; use beacon_node::ProductionBeaconNode;

View File

@ -8,7 +8,7 @@ use eth1::Eth1Endpoint;
use lighthouse_network::PeerId; use lighthouse_network::PeerId;
use std::fs::File; use std::fs::File;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::net::IpAddr; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use std::str::FromStr; use std::str::FromStr;
@ -16,7 +16,7 @@ use std::string::ToString;
use std::time::Duration; use std::time::Duration;
use tempfile::TempDir; use tempfile::TempDir;
use types::{Address, Checkpoint, Epoch, ExecutionBlockHash, ForkName, Hash256, MainnetEthSpec}; use types::{Address, Checkpoint, Epoch, ExecutionBlockHash, ForkName, Hash256, MainnetEthSpec};
use unused_port::{unused_tcp_port, unused_udp_port}; use unused_port::{unused_tcp4_port, unused_tcp6_port, unused_udp4_port, unused_udp6_port};
const DEFAULT_ETH1_ENDPOINT: &str = "http://localhost:8545/"; const DEFAULT_ETH1_ENDPOINT: &str = "http://localhost:8545/";
@ -851,37 +851,188 @@ fn network_shutdown_after_sync_disabled_flag() {
.with_config(|config| assert!(!config.network.shutdown_after_sync)); .with_config(|config| assert!(!config.network.shutdown_after_sync));
} }
#[test] #[test]
fn network_listen_address_flag() { fn network_listen_address_flag_v4() {
let addr = "127.0.0.2".parse::<IpAddr>().unwrap(); let addr = "127.0.0.2".parse::<Ipv4Addr>().unwrap();
CommandLineTest::new() CommandLineTest::new()
.flag("listen-address", Some("127.0.0.2")) .flag("listen-address", Some("127.0.0.2"))
.run_with_zero_port() .run_with_zero_port()
.with_config(|config| assert_eq!(config.network.listen_address, addr)); .with_config(|config| {
assert_eq!(
config.network.listen_addrs().v4().map(|addr| addr.addr),
Some(addr)
)
});
} }
#[test] #[test]
fn network_port_flag() { fn network_listen_address_flag_v6() {
let port = unused_tcp_port().expect("Unable to find unused port."); const ADDR: &str = "::1";
let addr = ADDR.parse::<Ipv6Addr>().unwrap();
CommandLineTest::new()
.flag("listen-address", Some(ADDR))
.run_with_zero_port()
.with_config(|config| {
assert_eq!(
config.network.listen_addrs().v6().map(|addr| addr.addr),
Some(addr)
)
});
}
#[test]
fn network_listen_address_flag_dual_stack() {
const V4_ADDR: &str = "127.0.0.1";
const V6_ADDR: &str = "::1";
let ipv6_addr = V6_ADDR.parse::<Ipv6Addr>().unwrap();
let ipv4_addr = V4_ADDR.parse::<Ipv4Addr>().unwrap();
CommandLineTest::new()
.flag("listen-address", Some(V6_ADDR))
.flag("listen-address", Some(V4_ADDR))
.run_with_zero_port()
.with_config(|config| {
assert_eq!(
config.network.listen_addrs().v6().map(|addr| addr.addr),
Some(ipv6_addr)
);
assert_eq!(
config.network.listen_addrs().v4().map(|addr| addr.addr),
Some(ipv4_addr)
)
});
}
#[test]
#[should_panic]
fn network_listen_address_flag_wrong_double_v4_value_config() {
// It's actually possible to listen over multiple sockets in libp2p over the same ip version.
// However this is not compatible with the single contactable address over each version in ENR.
// Because of this, it's important to test this is disallowed.
const V4_ADDR1: &str = "127.0.0.1";
const V4_ADDR2: &str = "0.0.0.0";
CommandLineTest::new()
.flag("listen-address", Some(V4_ADDR1))
.flag("listen-address", Some(V4_ADDR2))
.run_with_zero_port();
}
#[test]
#[should_panic]
fn network_listen_address_flag_wrong_double_v6_value_config() {
// It's actually possible to listen over multiple sockets in libp2p over the same ip version.
// However this is not compatible with the single contactable address over each version in ENR.
// Because of this, it's important to test this is disallowed.
const V6_ADDR1: &str = "::3";
const V6_ADDR2: &str = "::1";
CommandLineTest::new()
.flag("listen-address", Some(V6_ADDR1))
.flag("listen-address", Some(V6_ADDR2))
.run_with_zero_port();
}
#[test]
fn network_port_flag_over_ipv4() {
let port = unused_tcp4_port().expect("Unable to find unused port.");
CommandLineTest::new() CommandLineTest::new()
.flag("port", Some(port.to_string().as_str())) .flag("port", Some(port.to_string().as_str()))
.run() .run()
.with_config(|config| { .with_config(|config| {
assert_eq!(config.network.libp2p_port, port); assert_eq!(
assert_eq!(config.network.discovery_port, port); config
.network
.listen_addrs()
.v4()
.map(|listen_addr| (listen_addr.udp_port, listen_addr.tcp_port)),
Some((port, port))
);
}); });
} }
#[test] #[test]
fn network_port_and_discovery_port_flags() { fn network_port_flag_over_ipv6() {
let port1 = unused_tcp_port().expect("Unable to find unused port."); let port = unused_tcp6_port().expect("Unable to find unused port.");
let port2 = unused_udp_port().expect("Unable to find unused port.");
CommandLineTest::new() CommandLineTest::new()
.flag("port", Some(port1.to_string().as_str())) .flag("listen-address", Some("::1"))
.flag("discovery-port", Some(port2.to_string().as_str())) .flag("port", Some(port.to_string().as_str()))
.run() .run()
.with_config(|config| { .with_config(|config| {
assert_eq!(config.network.libp2p_port, port1); assert_eq!(
assert_eq!(config.network.discovery_port, port2); config
.network
.listen_addrs()
.v6()
.map(|listen_addr| (listen_addr.udp_port, listen_addr.tcp_port)),
Some((port, port))
);
}); });
} }
#[test]
fn network_port_and_discovery_port_flags_over_ipv4() {
let tcp4_port = unused_tcp4_port().expect("Unable to find unused port.");
let udp4_port = unused_udp4_port().expect("Unable to find unused port.");
CommandLineTest::new()
.flag("port", Some(tcp4_port.to_string().as_str()))
.flag("discovery-port", Some(udp4_port.to_string().as_str()))
.run()
.with_config(|config| {
assert_eq!(
config
.network
.listen_addrs()
.v4()
.map(|listen_addr| (listen_addr.tcp_port, listen_addr.udp_port)),
Some((tcp4_port, udp4_port))
);
});
}
#[test]
fn network_port_and_discovery_port_flags_over_ipv6() {
let tcp6_port = unused_tcp6_port().expect("Unable to find unused port.");
let udp6_port = unused_udp6_port().expect("Unable to find unused port.");
CommandLineTest::new()
.flag("listen-address", Some("::1"))
.flag("port", Some(tcp6_port.to_string().as_str()))
.flag("discovery-port", Some(udp6_port.to_string().as_str()))
.run()
.with_config(|config| {
assert_eq!(
config
.network
.listen_addrs()
.v6()
.map(|listen_addr| (listen_addr.tcp_port, listen_addr.udp_port)),
Some((tcp6_port, udp6_port))
);
});
}
#[test]
fn network_port_and_discovery_port_flags_over_ipv4_and_ipv6() {
let tcp4_port = unused_tcp4_port().expect("Unable to find unused port.");
let udp4_port = unused_udp4_port().expect("Unable to find unused port.");
let tcp6_port = unused_tcp6_port().expect("Unable to find unused port.");
let udp6_port = unused_udp6_port().expect("Unable to find unused port.");
CommandLineTest::new()
.flag("listen-address", Some("::1"))
.flag("listen-address", Some("127.0.0.1"))
.flag("port", Some(tcp4_port.to_string().as_str()))
.flag("discovery-port", Some(udp4_port.to_string().as_str()))
.flag("port6", Some(tcp6_port.to_string().as_str()))
.flag("discovery-port6", Some(udp6_port.to_string().as_str()))
.run()
.with_config(|config| {
assert_eq!(
config
.network
.listen_addrs()
.v4()
.map(|listen_addr| (listen_addr.tcp_port, listen_addr.udp_port)),
Some((tcp4_port, udp4_port))
);
assert_eq!(
config
.network
.listen_addrs()
.v6()
.map(|listen_addr| (listen_addr.tcp_port, listen_addr.udp_port)),
Some((tcp6_port, udp6_port))
);
});
}
#[test] #[test]
fn disable_discovery_flag() { fn disable_discovery_flag() {
CommandLineTest::new() CommandLineTest::new()
@ -986,7 +1137,6 @@ fn zero_ports_flag() {
CommandLineTest::new() CommandLineTest::new()
.run_with_zero_port() .run_with_zero_port()
.with_config(|config| { .with_config(|config| {
assert_eq!(config.network.enr_address, None);
assert_eq!(config.http_api.listen_port, 0); assert_eq!(config.http_api.listen_port, 0);
assert_eq!(config.http_metrics.listen_port, 0); assert_eq!(config.http_metrics.listen_port, 0);
}); });
@ -1003,67 +1153,171 @@ fn network_load_flag() {
// Tests for ENR flags. // Tests for ENR flags.
#[test] #[test]
fn enr_udp_port_flags() { fn enr_udp_port_flag() {
let port = unused_udp_port().expect("Unable to find unused port."); let port = unused_udp4_port().expect("Unable to find unused port.");
CommandLineTest::new() CommandLineTest::new()
.flag("enr-udp-port", Some(port.to_string().as_str())) .flag("enr-udp-port", Some(port.to_string().as_str()))
.run_with_zero_port() .run_with_zero_port()
.with_config(|config| assert_eq!(config.network.enr_udp_port, Some(port))); .with_config(|config| assert_eq!(config.network.enr_udp4_port, Some(port)));
} }
#[test] #[test]
fn enr_tcp_port_flags() { fn enr_tcp_port_flag() {
let port = unused_tcp_port().expect("Unable to find unused port."); let port = unused_tcp4_port().expect("Unable to find unused port.");
CommandLineTest::new() CommandLineTest::new()
.flag("enr-tcp-port", Some(port.to_string().as_str())) .flag("enr-tcp-port", Some(port.to_string().as_str()))
.run_with_zero_port() .run_with_zero_port()
.with_config(|config| assert_eq!(config.network.enr_tcp_port, Some(port))); .with_config(|config| assert_eq!(config.network.enr_tcp4_port, Some(port)));
} }
#[test] #[test]
fn enr_match_flag() { fn enr_udp6_port_flag() {
let addr = "127.0.0.2".parse::<IpAddr>().unwrap(); let port = unused_udp6_port().expect("Unable to find unused port.");
let port1 = unused_udp_port().expect("Unable to find unused port."); CommandLineTest::new()
let port2 = unused_udp_port().expect("Unable to find unused port."); .flag("enr-udp6-port", Some(port.to_string().as_str()))
.run_with_zero_port()
.with_config(|config| assert_eq!(config.network.enr_udp6_port, Some(port)));
}
#[test]
fn enr_tcp6_port_flag() {
let port = unused_tcp6_port().expect("Unable to find unused port.");
CommandLineTest::new()
.flag("enr-tcp6-port", Some(port.to_string().as_str()))
.run_with_zero_port()
.with_config(|config| assert_eq!(config.network.enr_tcp6_port, Some(port)));
}
#[test]
fn enr_match_flag_over_ipv4() {
let addr = "127.0.0.2".parse::<Ipv4Addr>().unwrap();
let udp4_port = unused_udp4_port().expect("Unable to find unused port.");
let tcp4_port = unused_tcp4_port().expect("Unable to find unused port.");
CommandLineTest::new() CommandLineTest::new()
.flag("enr-match", None) .flag("enr-match", None)
.flag("listen-address", Some("127.0.0.2")) .flag("listen-address", Some("127.0.0.2"))
.flag("discovery-port", Some(port1.to_string().as_str())) .flag("discovery-port", Some(udp4_port.to_string().as_str()))
.flag("port", Some(port2.to_string().as_str())) .flag("port", Some(tcp4_port.to_string().as_str()))
.run() .run()
.with_config(|config| { .with_config(|config| {
assert_eq!(config.network.listen_address, addr); assert_eq!(
assert_eq!(config.network.enr_address, Some(addr)); config.network.listen_addrs().v4().map(|listen_addr| (
assert_eq!(config.network.discovery_port, port1); listen_addr.addr,
assert_eq!(config.network.enr_udp_port, Some(port1)); listen_addr.udp_port,
listen_addr.tcp_port
)),
Some((addr, udp4_port, tcp4_port))
);
assert_eq!(config.network.enr_address, (Some(addr), None));
assert_eq!(config.network.enr_udp4_port, Some(udp4_port));
}); });
} }
#[test] #[test]
fn enr_address_flag() { fn enr_match_flag_over_ipv6() {
let addr = "192.167.1.1".parse::<IpAddr>().unwrap(); const ADDR: &str = "::1";
let port = unused_udp_port().expect("Unable to find unused port."); let addr = ADDR.parse::<Ipv6Addr>().unwrap();
let udp6_port = unused_udp6_port().expect("Unable to find unused port.");
let tcp6_port = unused_tcp6_port().expect("Unable to find unused port.");
CommandLineTest::new()
.flag("enr-match", None)
.flag("listen-address", Some(ADDR))
.flag("discovery-port", Some(udp6_port.to_string().as_str()))
.flag("port", Some(tcp6_port.to_string().as_str()))
.run()
.with_config(|config| {
assert_eq!(
config.network.listen_addrs().v6().map(|listen_addr| (
listen_addr.addr,
listen_addr.udp_port,
listen_addr.tcp_port
)),
Some((addr, udp6_port, tcp6_port))
);
assert_eq!(config.network.enr_address, (None, Some(addr)));
assert_eq!(config.network.enr_udp6_port, Some(udp6_port));
});
}
#[test]
fn enr_match_flag_over_ipv4_and_ipv6() {
const IPV6_ADDR: &str = "::1";
let ipv6_addr = IPV6_ADDR.parse::<Ipv6Addr>().unwrap();
let udp6_port = unused_udp6_port().expect("Unable to find unused port.");
let tcp6_port = unused_tcp6_port().expect("Unable to find unused port.");
const IPV4_ADDR: &str = "127.0.0.1";
let ipv4_addr = IPV4_ADDR.parse::<Ipv4Addr>().unwrap();
let udp4_port = unused_udp4_port().expect("Unable to find unused port.");
let tcp4_port = unused_tcp4_port().expect("Unable to find unused port.");
CommandLineTest::new()
.flag("enr-match", None)
.flag("listen-address", Some(IPV4_ADDR))
.flag("discovery-port", Some(udp4_port.to_string().as_str()))
.flag("port", Some(tcp4_port.to_string().as_str()))
.flag("listen-address", Some(IPV6_ADDR))
.flag("discovery-port6", Some(udp6_port.to_string().as_str()))
.flag("port6", Some(tcp6_port.to_string().as_str()))
.run()
.with_config(|config| {
assert_eq!(
config.network.listen_addrs().v6().map(|listen_addr| (
listen_addr.addr,
listen_addr.udp_port,
listen_addr.tcp_port
)),
Some((ipv6_addr, udp6_port, tcp6_port))
);
assert_eq!(
config.network.listen_addrs().v4().map(|listen_addr| (
listen_addr.addr,
listen_addr.udp_port,
listen_addr.tcp_port
)),
Some((ipv4_addr, udp4_port, tcp4_port))
);
assert_eq!(
config.network.enr_address,
(Some(ipv4_addr), Some(ipv6_addr))
);
assert_eq!(config.network.enr_udp6_port, Some(udp6_port));
assert_eq!(config.network.enr_udp4_port, Some(udp4_port));
});
}
#[test]
fn enr_address_flag_with_ipv4() {
let addr = "192.167.1.1".parse::<Ipv4Addr>().unwrap();
let port = unused_udp4_port().expect("Unable to find unused port.");
CommandLineTest::new() CommandLineTest::new()
.flag("enr-address", Some("192.167.1.1")) .flag("enr-address", Some("192.167.1.1"))
.flag("enr-udp-port", Some(port.to_string().as_str())) .flag("enr-udp-port", Some(port.to_string().as_str()))
.run_with_zero_port() .run_with_zero_port()
.with_config(|config| { .with_config(|config| {
assert_eq!(config.network.enr_address, Some(addr)); assert_eq!(config.network.enr_address, (Some(addr), None));
assert_eq!(config.network.enr_udp_port, Some(port)); assert_eq!(config.network.enr_udp4_port, Some(port));
});
}
#[test]
fn enr_address_flag_with_ipv6() {
let addr = "192.167.1.1".parse::<Ipv4Addr>().unwrap();
let port = unused_udp4_port().expect("Unable to find unused port.");
CommandLineTest::new()
.flag("enr-address", Some("192.167.1.1"))
.flag("enr-udp-port", Some(port.to_string().as_str()))
.run_with_zero_port()
.with_config(|config| {
assert_eq!(config.network.enr_address, (Some(addr), None));
assert_eq!(config.network.enr_udp4_port, Some(port));
}); });
} }
#[test] #[test]
fn enr_address_dns_flag() { fn enr_address_dns_flag() {
let addr = "127.0.0.1".parse::<IpAddr>().unwrap(); let addr = Ipv4Addr::LOCALHOST;
let ipv6addr = "::1".parse::<IpAddr>().unwrap(); let ipv6addr = Ipv6Addr::LOCALHOST;
let port = unused_udp_port().expect("Unable to find unused port."); let port = unused_udp4_port().expect("Unable to find unused port.");
CommandLineTest::new() CommandLineTest::new()
.flag("enr-address", Some("localhost")) .flag("enr-address", Some("localhost"))
.flag("enr-udp-port", Some(port.to_string().as_str())) .flag("enr-udp-port", Some(port.to_string().as_str()))
.run_with_zero_port() .run_with_zero_port()
.with_config(|config| { .with_config(|config| {
assert!( assert!(
config.network.enr_address == Some(addr) config.network.enr_address.0 == Some(addr)
|| config.network.enr_address == Some(ipv6addr) || config.network.enr_address.1 == Some(ipv6addr)
); );
assert_eq!(config.network.enr_udp_port, Some(port)); assert_eq!(config.network.enr_udp4_port, Some(port));
}); });
} }
#[test] #[test]
@ -1100,8 +1354,8 @@ fn http_address_ipv6_flag() {
} }
#[test] #[test]
fn http_port_flag() { fn http_port_flag() {
let port1 = unused_tcp_port().expect("Unable to find unused port."); let port1 = unused_tcp4_port().expect("Unable to find unused port.");
let port2 = unused_tcp_port().expect("Unable to find unused port."); let port2 = unused_tcp4_port().expect("Unable to find unused port.");
CommandLineTest::new() CommandLineTest::new()
.flag("http-port", Some(port1.to_string().as_str())) .flag("http-port", Some(port1.to_string().as_str()))
.flag("port", Some(port2.to_string().as_str())) .flag("port", Some(port2.to_string().as_str()))
@ -1215,8 +1469,8 @@ fn metrics_address_ipv6_flag() {
} }
#[test] #[test]
fn metrics_port_flag() { fn metrics_port_flag() {
let port1 = unused_tcp_port().expect("Unable to find unused port."); let port1 = unused_tcp4_port().expect("Unable to find unused port.");
let port2 = unused_tcp_port().expect("Unable to find unused port."); let port2 = unused_tcp4_port().expect("Unable to find unused port.");
CommandLineTest::new() CommandLineTest::new()
.flag("metrics", None) .flag("metrics", None)
.flag("metrics-port", Some(port1.to_string().as_str())) .flag("metrics-port", Some(port1.to_string().as_str()))

View File

@ -12,7 +12,7 @@ use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
use std::str::FromStr; use std::str::FromStr;
use tempfile::TempDir; use tempfile::TempDir;
use unused_port::unused_udp_port; use unused_port::unused_udp4_port;
const IP_ADDRESS: &str = "192.168.2.108"; const IP_ADDRESS: &str = "192.168.2.108";
@ -62,7 +62,7 @@ fn enr_address_arg() {
#[test] #[test]
fn port_flag() { fn port_flag() {
let port = unused_udp_port().unwrap(); let port = unused_udp4_port().unwrap();
CommandLineTest::new() CommandLineTest::new()
.flag("port", Some(port.to_string().as_str())) .flag("port", Some(port.to_string().as_str()))
.run_with_ip() .run_with_ip()
@ -122,7 +122,7 @@ fn boot_nodes_flag() {
#[test] #[test]
fn enr_port_flag() { fn enr_port_flag() {
let port = unused_udp_port().unwrap(); let port = unused_udp4_port().unwrap();
CommandLineTest::new() CommandLineTest::new()
.flag("enr-port", Some(port.to_string().as_str())) .flag("enr-port", Some(port.to_string().as_str()))
.run_with_ip() .run_with_ip()

View File

@ -3,7 +3,7 @@ use std::io::prelude::*;
use std::io::BufReader; use std::io::BufReader;
use std::process::{Child, Command, Stdio}; use std::process::{Child, Command, Stdio};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use unused_port::unused_tcp_port; use unused_port::unused_tcp4_port;
use web3::{transports::Http, Transport, Web3}; use web3::{transports::Http, Transport, Web3};
/// How long we will wait for ganache to indicate that it is ready. /// How long we will wait for ganache to indicate that it is ready.
@ -65,7 +65,7 @@ impl GanacheInstance {
/// Start a new `ganache` process, waiting until it indicates that it is ready to accept /// Start a new `ganache` process, waiting until it indicates that it is ready to accept
/// RPC connections. /// RPC connections.
pub fn new(chain_id: u64) -> Result<Self, String> { pub fn new(chain_id: u64) -> Result<Self, String> {
let port = unused_tcp_port()?; let port = unused_tcp4_port()?;
let binary = match cfg!(windows) { let binary = match cfg!(windows) {
true => "ganache.cmd", true => "ganache.cmd",
false => "ganache", false => "ganache",
@ -97,7 +97,7 @@ impl GanacheInstance {
} }
pub fn fork(&self) -> Result<Self, String> { pub fn fork(&self) -> Result<Self, String> {
let port = unused_tcp_port()?; let port = unused_tcp4_port()?;
let binary = match cfg!(windows) { let binary = match cfg!(windows) {
true => "ganache.cmd", true => "ganache.cmd",
false => "ganache", false => "ganache",

View File

@ -4,7 +4,7 @@ use sensitive_url::SensitiveUrl;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Child; use std::process::Child;
use tempfile::TempDir; use tempfile::TempDir;
use unused_port::unused_tcp_port; use unused_port::unused_tcp4_port;
pub const KEYSTORE_PASSWORD: &str = "testpwd"; pub const KEYSTORE_PASSWORD: &str = "testpwd";
pub const ACCOUNT1: &str = "7b8C3a386C0eea54693fFB0DA17373ffC9228139"; pub const ACCOUNT1: &str = "7b8C3a386C0eea54693fFB0DA17373ffC9228139";
@ -50,8 +50,8 @@ impl<E: GenericExecutionEngine> ExecutionEngine<E> {
pub fn new(engine: E) -> Self { pub fn new(engine: E) -> Self {
let datadir = E::init_datadir(); let datadir = E::init_datadir();
let jwt_secret_path = datadir.path().join(DEFAULT_JWT_FILE); let jwt_secret_path = datadir.path().join(DEFAULT_JWT_FILE);
let http_port = unused_tcp_port().unwrap(); let http_port = unused_tcp4_port().unwrap();
let http_auth_port = unused_tcp_port().unwrap(); let http_auth_port = unused_tcp4_port().unwrap();
let child = E::start_client(&datadir, http_port, http_auth_port, jwt_secret_path); let child = E::start_client(&datadir, http_port, http_auth_port, jwt_secret_path);
let provider = Provider::<Http>::try_from(format!("http://localhost:{}", http_port)) let provider = Provider::<Http>::try_from(format!("http://localhost:{}", http_port))
.expect("failed to instantiate ethers provider"); .expect("failed to instantiate ethers provider");

View File

@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
use std::process::{Child, Command, Output}; use std::process::{Child, Command, Output};
use std::{env, fs::File}; use std::{env, fs::File};
use tempfile::TempDir; use tempfile::TempDir;
use unused_port::unused_tcp_port; use unused_port::unused_tcp4_port;
const GETH_BRANCH: &str = "master"; const GETH_BRANCH: &str = "master";
const GETH_REPO_URL: &str = "https://github.com/ethereum/go-ethereum"; const GETH_REPO_URL: &str = "https://github.com/ethereum/go-ethereum";
@ -83,7 +83,7 @@ impl GenericExecutionEngine for GethEngine {
http_auth_port: u16, http_auth_port: u16,
jwt_secret_path: PathBuf, jwt_secret_path: PathBuf,
) -> Child { ) -> Child {
let network_port = unused_tcp_port().unwrap(); let network_port = unused_tcp4_port().unwrap();
Command::new(Self::binary_path()) Command::new(Self::binary_path())
.arg("--datadir") .arg("--datadir")

View File

@ -1,4 +1,3 @@
#![recursion_limit = "1024"]
/// This binary runs integration tests between Lighthouse and execution engines. /// This binary runs integration tests between Lighthouse and execution engines.
/// ///
/// It will first attempt to build any supported integration clients, then it will run tests. /// It will first attempt to build any supported integration clients, then it will run tests.

View File

@ -6,7 +6,7 @@ use std::fs::File;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::{Child, Command, Output}; use std::process::{Child, Command, Output};
use tempfile::TempDir; use tempfile::TempDir;
use unused_port::unused_tcp_port; use unused_port::unused_tcp4_port;
/// We've pinned the Nethermind version since our method of using the `master` branch to /// We've pinned the Nethermind version since our method of using the `master` branch to
/// find the latest tag isn't working. It appears Nethermind don't always tag on `master`. /// find the latest tag isn't working. It appears Nethermind don't always tag on `master`.
@ -88,7 +88,7 @@ impl GenericExecutionEngine for NethermindEngine {
http_auth_port: u16, http_auth_port: u16,
jwt_secret_path: PathBuf, jwt_secret_path: PathBuf,
) -> Child { ) -> Child {
let network_port = unused_tcp_port().unwrap(); let network_port = unused_tcp4_port().unwrap();
let genesis_json_path = datadir.path().join("genesis.json"); let genesis_json_path = datadir.path().join("genesis.json");
Command::new(Self::binary_path()) Command::new(Self::binary_path())

View File

@ -89,8 +89,9 @@ pub fn testing_client_config() -> ClientConfig {
let mut client_config = ClientConfig::default(); let mut client_config = ClientConfig::default();
// Setting ports to `0` means that the OS will choose some available port. // Setting ports to `0` means that the OS will choose some available port.
client_config.network.libp2p_port = 0; client_config
client_config.network.discovery_port = 0; .network
.set_ipv4_listening_address(std::net::Ipv4Addr::UNSPECIFIED, 0, 0);
client_config.network.upnp_enabled = false; client_config.network.upnp_enabled = false;
client_config.http_api.enabled = true; client_config.http_api.enabled = true;
client_config.http_api.listen_port = 0; client_config.http_api.listen_port = 0;

View File

@ -13,7 +13,7 @@ use node_test_rig::{
use rayon::prelude::*; use rayon::prelude::*;
use sensitive_url::SensitiveUrl; use sensitive_url::SensitiveUrl;
use std::cmp::max; use std::cmp::max;
use std::net::{IpAddr, Ipv4Addr}; use std::net::Ipv4Addr;
use std::time::Duration; use std::time::Duration;
use tokio::time::sleep; use tokio::time::sleep;
use types::{Epoch, EthSpec, MinimalEthSpec}; use types::{Epoch, EthSpec, MinimalEthSpec};
@ -149,7 +149,7 @@ pub fn run_eth1_sim(matches: &ArgMatches) -> Result<(), String> {
beacon_config.eth1.chain_id = Eth1Id::from(chain_id); beacon_config.eth1.chain_id = Eth1Id::from(chain_id);
beacon_config.network.target_peers = node_count - 1; beacon_config.network.target_peers = node_count - 1;
beacon_config.network.enr_address = Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); beacon_config.network.enr_address = (Some(Ipv4Addr::LOCALHOST), None);
if post_merge_sim { if post_merge_sim {
let el_config = execution_layer::Config { let el_config = execution_layer::Config {

View File

@ -58,10 +58,13 @@ impl<E: EthSpec> LocalNetwork<E> {
context: RuntimeContext<E>, context: RuntimeContext<E>,
mut beacon_config: ClientConfig, mut beacon_config: ClientConfig,
) -> Result<Self, String> { ) -> Result<Self, String> {
beacon_config.network.discovery_port = BOOTNODE_PORT; beacon_config.network.set_ipv4_listening_address(
beacon_config.network.libp2p_port = BOOTNODE_PORT; std::net::Ipv4Addr::UNSPECIFIED,
beacon_config.network.enr_udp_port = Some(BOOTNODE_PORT); BOOTNODE_PORT,
beacon_config.network.enr_tcp_port = Some(BOOTNODE_PORT); BOOTNODE_PORT,
);
beacon_config.network.enr_udp4_port = Some(BOOTNODE_PORT);
beacon_config.network.enr_tcp4_port = Some(BOOTNODE_PORT);
beacon_config.network.discv5_config.table_filter = |_| true; beacon_config.network.discv5_config.table_filter = |_| true;
let execution_node = if let Some(el_config) = &mut beacon_config.execution_layer { let execution_node = if let Some(el_config) = &mut beacon_config.execution_layer {
@ -132,10 +135,13 @@ impl<E: EthSpec> LocalNetwork<E> {
.enr() .enr()
.expect("bootnode must have a network"), .expect("bootnode must have a network"),
); );
beacon_config.network.discovery_port = BOOTNODE_PORT + count; beacon_config.network.set_ipv4_listening_address(
beacon_config.network.libp2p_port = BOOTNODE_PORT + count; std::net::Ipv4Addr::UNSPECIFIED,
beacon_config.network.enr_udp_port = Some(BOOTNODE_PORT + count); BOOTNODE_PORT + count,
beacon_config.network.enr_tcp_port = Some(BOOTNODE_PORT + count); BOOTNODE_PORT + count,
);
beacon_config.network.enr_udp4_port = Some(BOOTNODE_PORT + count);
beacon_config.network.enr_tcp4_port = Some(BOOTNODE_PORT + count);
beacon_config.network.discv5_config.table_filter = |_| true; beacon_config.network.discv5_config.table_filter = |_| true;
} }
if let Some(el_config) = &mut beacon_config.execution_layer { if let Some(el_config) = &mut beacon_config.execution_layer {

View File

@ -1,5 +1,3 @@
#![recursion_limit = "256"]
//! This crate provides a simluation that creates `n` beacon node and validator clients, each with //! This crate provides a simluation that creates `n` beacon node and validator clients, each with
//! `v` validators. A deposit contract is deployed at the start of the simulation using a local //! `v` validators. A deposit contract is deployed at the start of the simulation using a local
//! `ganache` instance (you must have `ganache` installed and avaliable on your path). All //! `ganache` instance (you must have `ganache` installed and avaliable on your path). All

View File

@ -7,7 +7,7 @@ use node_test_rig::{
}; };
use rayon::prelude::*; use rayon::prelude::*;
use std::cmp::max; use std::cmp::max;
use std::net::{IpAddr, Ipv4Addr}; use std::net::Ipv4Addr;
use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tokio::time::sleep; use tokio::time::sleep;
use types::{Epoch, EthSpec, MainnetEthSpec}; use types::{Epoch, EthSpec, MainnetEthSpec};
@ -91,7 +91,7 @@ pub fn run_no_eth1_sim(matches: &ArgMatches) -> Result<(), String> {
beacon_config.dummy_eth1_backend = true; beacon_config.dummy_eth1_backend = true;
beacon_config.sync_eth1_chain = true; beacon_config.sync_eth1_chain = true;
beacon_config.network.enr_address = Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); beacon_config.network.enr_address = (Some(Ipv4Addr::LOCALHOST), None);
let main_future = async { let main_future = async {
let network = LocalNetwork::new(context.clone(), beacon_config.clone()).await?; let network = LocalNetwork::new(context.clone(), beacon_config.clone()).await?;

View File

@ -8,7 +8,7 @@ use node_test_rig::{
}; };
use node_test_rig::{testing_validator_config, ClientConfig}; use node_test_rig::{testing_validator_config, ClientConfig};
use std::cmp::max; use std::cmp::max;
use std::net::{IpAddr, Ipv4Addr}; use std::net::Ipv4Addr;
use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::time::{Duration, SystemTime, UNIX_EPOCH};
use types::{Epoch, EthSpec}; use types::{Epoch, EthSpec};
@ -95,7 +95,7 @@ fn syncing_sim(
beacon_config.http_api.allow_sync_stalled = true; beacon_config.http_api.allow_sync_stalled = true;
beacon_config.network.enr_address = Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); beacon_config.network.enr_address = (Some(Ipv4Addr::LOCALHOST), None);
// Generate the directories and keystores required for the validator clients. // Generate the directories and keystores required for the validator clients.
let validator_indices = (0..num_validators).collect::<Vec<_>>(); let validator_indices = (0..num_validators).collect::<Vec<_>>();