Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions bin/florestad/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,11 @@ pub struct Cli {
pub zmq_address: Option<String>,

#[arg(long, value_name = "address[:<port>]")]
/// A node to connect to
/// A node to connect to. May be specified multiple times.
///
/// If this option is provided, we'll connect **only** to this node. It should be an ipv4
/// address in the format `<address>[:<port>]`.
pub connect: Option<String>,
/// If this option is provided, we'll connect **only** to the listed nodes. Each value
/// should be an ipv4/ipv6/hostname address in the format `<address>[:<port>]`.
pub connect: Vec<String>,

#[arg(long, value_name = "address[:<port>]")]
/// The address where our json-rpc server should listen to, in the format `<address>[:<port>]`
Expand Down
2 changes: 1 addition & 1 deletion bin/florestad/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ fn main() {

let config = Config {
datadir,
disable_dns_seeds: params.connect.is_some() || params.disable_dns_seeds,
disable_dns_seeds: !params.connect.is_empty() || params.disable_dns_seeds,
network: params.network,
debug: params.debug,
cfilters: !params.no_cfilters,
Expand Down
10 changes: 5 additions & 5 deletions crates/floresta-node/src/florestad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,10 @@ pub struct Config {
/// is the address that we'll listen for incoming connections.
pub zmq_address: Option<String>,

/// A node to connect to
/// Nodes to connect to
///
/// If this option is provided, we'll connect **only** to this node.
pub connect: Option<String>,
/// If non-empty, we'll connect **only** to these nodes.
pub connect: Vec<String>,

#[cfg(feature = "json-rpc")]
/// The address our json-rpc should listen to
Expand Down Expand Up @@ -238,7 +238,7 @@ impl Config {
filters_start_height: None,
#[cfg(feature = "zmq-server")]
zmq_address: None,
connect: None,
connect: Vec::new(),
#[cfg(feature = "json-rpc")]
json_rpc_address: None,
log_to_stdout: false,
Expand Down Expand Up @@ -412,7 +412,7 @@ impl Florestad {
pow_fraud_proofs: false,
proxy,
datadir: datadir.into(),
fixed_peer: self.config.connect.clone(),
fixed_peers: self.config.connect.clone(),
compact_filters: self.config.cfilters,
assume_utreexo: self.config.assumeutreexo_value.clone().or(assume_utreexo),
backfill: self.config.backfill,
Expand Down
10 changes: 5 additions & 5 deletions crates/floresta-wire/src/p2p_wire/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ pub struct UtreexoNodeConfig {
/// needing to download the whole chain. It will download ~1GB of filters, and then
/// download the blocks that match the filters.
pub compact_filters: bool,
/// Fixed peers to connect to. Defaults to None.
/// Fixed peers to connect to. Defaults to an empty list.
///
/// If you want to connect to a specific peer, you can set this to a string with the
/// format `ip:port`. For example, `localhost:8333`.
pub fixed_peer: Option<String>,
/// Each entry is `host[:port]`, where `host` is an IPv4 address, a bracketed IPv6 address (`[::1]`), or a hostname;
/// `port` is optional and defaults to the network's default port (for example, `"localhost"` or `"127.0.0.1:8333"`).
pub fixed_peers: Vec<String>,
/// Maximum ban score. Defaults to 100.
///
/// If a peer misbehaves, we increase its ban score. If the ban score reaches this value,
Expand Down Expand Up @@ -75,7 +75,7 @@ impl Default for UtreexoNodeConfig {
network: Network::Bitcoin,
pow_fraud_proofs: false,
compact_filters: false,
fixed_peer: None,
fixed_peers: Vec::new(),
max_banscore: 100,
datadir: ".floresta-node".into(),
proxy: None,
Expand Down
4 changes: 2 additions & 2 deletions crates/floresta-wire/src/p2p_wire/node/chain_selector_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -823,8 +823,8 @@ where
return true;
}

if self.fixed_peer.is_some() && connected_peers >= 1 {
return true;
if self.has_fixed_peers() {
return connected_peers >= 1;
}

connected_peers >= ChainSelector::MAX_OUTGOING_PEERS
Expand Down
62 changes: 37 additions & 25 deletions crates/floresta-wire/src/p2p_wire/node/conn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,11 @@ where

/// Create a new outgoing connection, selecting an appropriate peer address.
///
/// If a fixed peer is set via the `--connect` CLI argument, its connection
/// kind will always be coerced to [`ConnectionKind::Manual`]. Otherwise,
/// an address is selected from the [`AddressMan`] based on the required
/// [`ServiceFlags`] for the given `connection_kind`.
/// If fixed peers are set via the `--connect` CLI argument, their connection
/// kind will always be coerced to [`ConnectionKind::Manual`] and the first
/// not-yet-connected fixed peer is selected. Otherwise, an address is
/// selected from the [`AddressMan`] based on the required [`ServiceFlags`]
/// for the given `connection_kind`.
///
/// If no address is available and the kind is not [`ConnectionKind::Manual`],
/// hardcoded addresses are loaded into the [`AddressMan`] as a fallback.
Expand All @@ -74,28 +75,36 @@ where
mut conn_kind: ConnectionKind,
) -> Result<(), WireError> {
// Set the fixed peer's connection kind to manual, if set.
if self.fixed_peer.is_some() {
if self.has_fixed_peers() {
conn_kind = ConnectionKind::Manual;
}

// Get the peer's `ServiceFlags`.
let required_services = match conn_kind {
ConnectionKind::Regular(services) => services,
_ => ServiceFlags::NONE,
ConnectionKind::Feeler | ConnectionKind::Extra | ConnectionKind::Manual => {
ServiceFlags::NONE
}
};

// Get the fixed peer's `peer_id` and `LocalAddress` if set,
// or fetch a new address from the address manager.
let candidate_peer = self
.fixed_peer
.as_ref()
.map(|addr| (0, addr.clone()))
.or_else(|| {
self.address_man.get_address_to_connect(
required_services,
matches!(conn_kind, ConnectionKind::Feeler),
)
});
// Pick the first fixed peer that we are not already connected to, or
// fall back to fetching a new address from the address manager when no
// fixed peers were configured.
let candidate_peer = if self.has_fixed_peers() {
self.fixed_peers
.iter()
.find(|addr| {
self.peers
.values()
.all(|p| p.address != addr.get_net_address() || p.port != addr.get_port())
})
.map(|addr| (0, addr.clone()))
} else {
self.address_man.get_address_to_connect(
required_services,
matches!(conn_kind, ConnectionKind::Feeler),
)
};

// Load hardcoded addresses to the address manager if no fixed or manual peers exist.
let Some((peer_id, peer_address)) = candidate_peer else {
Expand Down Expand Up @@ -142,7 +151,7 @@ where

pub(crate) fn open_feeler_connection(&mut self) -> Result<(), WireError> {
// No feeler if `--connect` is set
if self.fixed_peer.is_some() {
if self.has_fixed_peers() {
return Ok(());
}

Expand Down Expand Up @@ -552,7 +561,7 @@ where
/// can't find a Utreexo peer in a context we need them. This function
/// won't do anything if `--connect` was used
fn maybe_use_hardcoded_addresses(&mut self) {
if self.fixed_peer.is_some() {
if self.has_fixed_peers() {
return;
}

Expand Down Expand Up @@ -612,11 +621,14 @@ where

let connection_kind = ConnectionKind::Regular(required_service);

// If the user passes in a `--connect` cli argument, we only connect with
// that particular peer.
if self.fixed_peer.is_some() {
if self.peers.is_empty() {
self.create_connection(connection_kind)?;
// If the user passes in `--connect` cli arguments, we only connect with
// those peers. Try to (re)connect as many as we are missing.
if self.has_fixed_peers() {
let missing = self.fixed_peers.len().saturating_sub(self.peers.len());
for _ in 0..missing {
if let Err(e) = self.create_connection(connection_kind) {
debug!("Failed to connect to fixed peer: {e:?}");
}
}
return Ok(());
}
Expand Down
19 changes: 12 additions & 7 deletions crates/floresta-wire/src/p2p_wire/node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod user_req;
use core::fmt::Debug;
use core::net::IpAddr;
use std::collections::HashMap;
use std::collections::HashSet;
use std::ops::Deref;
use std::ops::DerefMut;
use std::path::PathBuf;
Expand Down Expand Up @@ -270,7 +271,7 @@ pub struct NodeCommon<Chain: ChainBackend> {

// 4. Networking Configuration
pub(crate) socks5: Option<Socks5StreamBuilder>,
pub(crate) fixed_peer: Option<LocalAddress>,
pub(crate) fixed_peers: Vec<LocalAddress>,

// 5. Time and Event Tracking
pub(crate) inflight: HashMap<InflightRequests, (u32, Instant)>,
Expand Down Expand Up @@ -348,11 +349,15 @@ where
let (node_tx, node_rx) = unbounded_channel();
let socks5 = config.proxy.map(Socks5StreamBuilder::new);

let fixed_peer = config
.fixed_peer
.as_ref()
.map(|address| Self::resolve_connect_host(address, Self::get_port(config.network)))
.transpose()?;
// Dedup the resolved fixed peers so we don't open multiple connections to the same host.
let mut seen = HashSet::new();
let mut fixed_peers = Vec::with_capacity(config.fixed_peers.len());
for address in &config.fixed_peers {
let resolved = Self::resolve_connect_host(address, Self::get_port(config.network))?;
if seen.insert((resolved.get_net_address(), resolved.get_port())) {
fixed_peers.push(resolved);
}
}

Ok(UtreexoNode {
common: NodeCommon {
Expand Down Expand Up @@ -386,7 +391,7 @@ where
datadir: config.datadir.clone(),
max_banscore: config.max_banscore,
socks5,
fixed_peer,
fixed_peers,
config,
kill_signal,
added_peers: Vec::new(),
Expand Down
8 changes: 8 additions & 0 deletions crates/floresta-wire/src/p2p_wire/node/peer_man.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ where
Some((id, peer))
}

/// Whether the node was configured with a fixed peer list (via `--connect`).
///
/// When this is true, we connect *only* to those peers and skip the usual
/// peer-discovery paths (address manager, feelers, hardcoded fallback).
pub(crate) fn has_fixed_peers(&self) -> bool {
!self.fixed_peers.is_empty()
}

/// Returns how many connected peers we have.
///
/// This function will only count peers that completed handshake and are ready
Expand Down
7 changes: 7 additions & 0 deletions crates/floresta-wire/src/p2p_wire/node/sync_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,13 @@ where
/// - we have enough peers to download blocks from (at most `MAX_OUTGOING_PEERS`)
/// - if some of peers are too slow, and potentially stalling our block download (TODO)
fn check_connections(&mut self) -> Result<(), WireError> {
// With `--connect`, the user pins us to a specific peer set; don't search
// for extra utreexo or network peers. `maybe_open_connection` still
// reconnects any fixed peer that has dropped.
if self.has_fixed_peers() {
return self.maybe_open_connection(service_flags::UTREEXO.into());
}

let total_peers = self.connected_peers();
let utreexo_peers = self
.peer_by_service
Expand Down
2 changes: 1 addition & 1 deletion crates/floresta-wire/src/p2p_wire/tests/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ pub async fn setup_node(

// Add a fixed peer to avoid opening real P2P connections
if i == 0 {
node.fixed_peer = Some(to_addr_v2(peer.address).into());
node.fixed_peers = vec![to_addr_v2(peer.address).into()];
}

node.peers.insert(peer_id, peer);
Expand Down
Loading