node: let others know their address

Include the node's IP address in the Node's announcements.  Source it
from the service's configuration which can assumed to be populated with
default values when available.

Signed-off-by: Slack Coder <slackcoder@server.ky>
This commit is contained in:
Slack Coder 2022-11-07 07:50:09 -05:00
parent f3d867af4d
commit a9b38dc1ae
5 changed files with 91 additions and 53 deletions

View File

@ -14,6 +14,7 @@ type Reactor = nakamoto_net_poll::Reactor<net::TcpStream>;
struct Options { struct Options {
connect: Vec<Address>, connect: Vec<Address>,
listen: Vec<net::SocketAddr>, listen: Vec<net::SocketAddr>,
external_addresses: Vec<Address>,
} }
impl Options { impl Options {
@ -22,6 +23,7 @@ impl Options {
let mut parser = lexopt::Parser::from_env(); let mut parser = lexopt::Parser::from_env();
let mut connect = Vec::new(); let mut connect = Vec::new();
let mut external_addresses = Vec::new();
let mut listen = Vec::new(); let mut listen = Vec::new();
while let Some(arg) = parser.next()? { while let Some(arg) = parser.next()? {
@ -34,6 +36,10 @@ impl Options {
let addr = parser.value()?.parse()?; let addr = parser.value()?.parse()?;
listen.push(addr); listen.push(addr);
} }
Long("external-address") => {
let addr = parser.value()?.parse()?;
external_addresses.push(addr);
}
Long("help") => { Long("help") => {
println!("usage: radicle-node [--connect <addr>].."); println!("usage: radicle-node [--connect <addr>]..");
process::exit(0); process::exit(0);
@ -41,7 +47,11 @@ impl Options {
_ => return Err(arg.unexpected()), _ => return Err(arg.unexpected()),
} }
} }
Ok(Self { connect, listen }) Ok(Self {
connect,
listen,
external_addresses,
})
} }
} }
@ -66,6 +76,7 @@ fn main() -> anyhow::Result<()> {
let config = client::Config { let config = client::Config {
service: service::Config { service: service::Config {
connect: options.connect, connect: options.connect,
external_addresses: options.external_addresses,
..service::Config::default() ..service::Config::default()
}, },
listen: options.listen, listen: options.listen,

View File

@ -945,29 +945,22 @@ where
} }
} }
fn next_connections( fn choose_addresses(&mut self) -> Vec<Address> {
rng: &Rng,
sessions: &Sessions,
address_pool: &mut dyn Iterator<Item = (NodeId, address::KnownAddress)>,
) -> Vec<Address> {
let mut initializing: Vec<Address> = Vec::new(); let mut initializing: Vec<Address> = Vec::new();
let mut negotiated: HashMap<NodeId, &Session> = HashMap::new(); let mut negotiated: HashMap<NodeId, &Session> = HashMap::new();
for (_, s) in sessions.iter() { for s in self.sessions.values() {
if s.link != Link::Outbound { if !s.link.is_outbound() {
continue; continue;
} }
match s.state { match s.state {
session::State::Initial => { session::State::Initial => {
initializing.push(s.addr.into()); initializing.push(s.addr.into());
} }
session::State::Negotiated { .. } => { session::State::Negotiated { id, .. } => {
let node_id = s negotiated.insert(id, s);
.node_id()
.expect("negotiated sessions must have a node ID");
negotiated.insert(node_id, s);
} }
_ => continue, session::State::Disconnected { .. } => {}
}; }
} }
let wanted = TARGET_OUTBOUND_PEERS let wanted = TARGET_OUTBOUND_PEERS
@ -977,33 +970,23 @@ where
return Vec::new(); return Vec::new();
} }
// All nodes are considered equal self.addresses
let mut address_pool: Vec<_> = address_pool .entries()
.unwrap()
.filter(|(node_id, s)| { .filter(|(node_id, s)| {
!initializing.contains(&s.addr) && !negotiated.contains_key(node_id) !initializing.contains(&s.addr) && !negotiated.contains_key(node_id)
}) })
.take(wanted) .take(wanted)
.collect(); .map(|(_, s)| s.addr)
.collect()
let mut next = Vec::new();
loop {
if address_pool.is_empty() {
break;
}
let i = rng.usize(0..address_pool.len());
let (_, addr) = address_pool.swap_remove(i);
next.push(addr.addr);
}
next
} }
fn maintain_connections(&mut self) { fn maintain_connections(&mut self) {
let mut address_pool = self let addrs = self.choose_addresses();
.addresses if addrs.is_empty() {
.entries() debug!("No eligible peers available to connect to");
.expect("address store be accessible"); }
let next = Self::next_connections(&self.rng, &self.sessions, &mut address_pool); for addr in addrs {
for addr in next {
self.reactor.connect(addr.clone()); self.reactor.connect(addr.clone());
} }
} }
@ -1267,7 +1250,7 @@ mod gossip {
pub fn node(timestamp: Timestamp, config: &Config) -> NodeAnnouncement { pub fn node(timestamp: Timestamp, config: &Config) -> NodeAnnouncement {
let features = node::Features::SEED; let features = node::Features::SEED;
let alias = config.alias(); let alias = config.alias();
let addresses = vec![]; // TODO let addresses = config.external_addresses.clone();
NodeAnnouncement { NodeAnnouncement {
features, features,

View File

@ -46,6 +46,8 @@ pub struct Config {
/// Peers to connect to on startup. /// Peers to connect to on startup.
/// Connections to these peers will be maintained. /// Connections to these peers will be maintained.
pub connect: Vec<Address>, pub connect: Vec<Address>,
/// Specify the node's public addresses
pub external_addresses: Vec<Address>,
/// Peer-to-peer network. /// Peer-to-peer network.
pub network: Network, pub network: Network,
/// Project tracking policy. /// Project tracking policy.
@ -62,6 +64,7 @@ impl Default for Config {
fn default() -> Self { fn default() -> Self {
Self { Self {
connect: Vec::default(), connect: Vec::default(),
external_addresses: vec![],
network: Network::default(), network: Network::default(),
project_tracking: ProjectTracking::default(), project_tracking: ProjectTracking::default(),
remote_tracking: RemoteTracking::default(), remote_tracking: RemoteTracking::default(),

View File

@ -75,7 +75,8 @@ where
let mut rng = fastrand::Rng::new(); let mut rng = fastrand::Rng::new();
let signer = MockSigner::new(&mut rng); let signer = MockSigner::new(&mut rng);
Self::config(name, Config::default(), ip, storage, signer, rng) let addrs = address::Book::memory().unwrap();
Self::config(name, Config::default(), ip, storage, addrs, signer, rng)
} }
} }
@ -89,13 +90,13 @@ where
config: Config, config: Config,
ip: impl Into<net::IpAddr>, ip: impl Into<net::IpAddr>,
storage: S, storage: S,
addrs: address::Book,
signer: G, signer: G,
rng: fastrand::Rng, rng: fastrand::Rng,
) -> Self { ) -> Self {
let local_time = LocalTime::now(); let local_time = LocalTime::now();
let clock = RefClock::from(local_time); let clock = RefClock::from(local_time);
let routing = routing::Table::memory().unwrap(); let routing = routing::Table::memory().unwrap();
let addrs = address::Book::memory().unwrap();
let service = Service::new(config, clock, routing, storage, addrs, signer, rng.clone()); let service = Service::new(config, clock, routing, storage, addrs, signer, rng.clone());
let ip = ip.into(); let ip = ip.into();
let local_addr = net::SocketAddr::new(ip, rng.u16(..)); let local_addr = net::SocketAddr::new(ip, rng.u16(..));

View File

@ -4,6 +4,7 @@ use std::sync::Arc;
use crossbeam_channel as chan; use crossbeam_channel as chan;
use nakamoto_net as nakamoto; use nakamoto_net as nakamoto;
use crate::address;
use crate::collections::{HashMap, HashSet}; use crate::collections::{HashMap, HashSet};
use crate::crypto::test::signer::MockSigner; use crate::crypto::test::signer::MockSigner;
use crate::prelude::{LocalDuration, Timestamp}; use crate::prelude::{LocalDuration, Timestamp};
@ -169,6 +170,7 @@ fn test_persistent_peer_connect() {
config, config,
[7, 7, 7, 7], [7, 7, 7, 7],
MockStorage::empty(), MockStorage::empty(),
address::Book::memory().unwrap(),
MockSigner::new(&mut rng), MockSigner::new(&mut rng),
rng, rng,
); );
@ -235,6 +237,7 @@ fn test_tracking() {
}, },
[7, 7, 7, 7], [7, 7, 7, 7],
MockStorage::empty(), MockStorage::empty(),
address::Book::memory().unwrap(),
MockSigner::default(), MockSigner::default(),
fastrand::Rng::new(), fastrand::Rng::new(),
); );
@ -428,10 +431,19 @@ fn test_refs_announcement_relay() {
let bob = { let bob = {
let mut rng = fastrand::Rng::new(); let mut rng = fastrand::Rng::new();
let addresses = address::Book::memory().unwrap();
let signer = MockSigner::new(&mut rng); let signer = MockSigner::new(&mut rng);
let storage = fixtures::storage(tmp.path().join("bob"), &signer).unwrap(); let storage = fixtures::storage(tmp.path().join("bob"), &signer).unwrap();
Peer::config("bob", Config::default(), [9, 9, 9, 9], storage, signer, rng) Peer::config(
"bob",
Config::default(),
[9, 9, 9, 9],
storage,
addresses,
signer,
rng,
)
}; };
let bob_inv = bob.inventory().unwrap(); let bob_inv = bob.inventory().unwrap();
@ -593,6 +605,7 @@ fn test_persistent_peer_reconnect() {
}, },
[7, 7, 7, 7], [7, 7, 7, 7],
MockStorage::empty(), MockStorage::empty(),
address::Book::memory().unwrap(),
MockSigner::default(), MockSigner::default(),
fastrand::Rng::new(), fastrand::Rng::new(),
); );
@ -648,21 +661,49 @@ fn test_persistent_peer_reconnect() {
assert_matches!(alice.outbox().next(), None); assert_matches!(alice.outbox().next(), None);
} }
fn add_peer<A: address::Store>(address_book: &mut A, peer: &Peer<MockStorage, MockSigner>) {
let known_address = address::KnownAddress::new(peer.address(), address::Source::Peer);
address_book
.insert(
&peer.node_id(),
radicle::node::Features::NONE,
peer.name,
LocalTime::now().as_secs(),
Some(known_address),
)
.unwrap();
}
#[test] #[test]
fn test_maintain_connections() { fn test_maintain_connections() {
let mut alice = Peer::new("alice", [7, 7, 7, 7], MockStorage::empty());
let connected = vec![ let connected = vec![
Peer::new("connected 1", [8, 8, 8, 1], MockStorage::empty()), Peer::new("connected", [8, 8, 8, 1], MockStorage::empty()),
Peer::new("connected 2", [8, 8, 8, 2], MockStorage::empty()), Peer::new("connected", [8, 8, 8, 2], MockStorage::empty()),
Peer::new("connected 3", [8, 8, 8, 3], MockStorage::empty()), Peer::new("connected", [8, 8, 8, 3], MockStorage::empty()),
]; ];
let mut unconnected = vec![ let mut unconnected = vec![
Peer::new("new 1", [9, 9, 9, 1], MockStorage::empty()), Peer::new("unconnected", [9, 9, 9, 1], MockStorage::empty()),
Peer::new("new 2", [9, 9, 9, 2], MockStorage::empty()), Peer::new("unconnected", [9, 9, 9, 2], MockStorage::empty()),
Peer::new("new 3", [9, 9, 9, 3], MockStorage::empty()), Peer::new("unconnected", [9, 9, 9, 3], MockStorage::empty()),
]; ];
let mut address_book = address::Book::memory().unwrap();
for peer in &unconnected {
add_peer(&mut address_book, peer);
}
let mut alice = Peer::config(
"alice",
Config {
project_tracking: ProjectTracking::Allowed(HashSet::default()),
..Config::default()
},
[7, 7, 7, 7],
MockStorage::empty(),
address_book,
MockSigner::default(),
fastrand::Rng::new(),
);
for peer in connected.iter() { for peer in connected.iter() {
alice.connect_to(peer); alice.connect_to(peer);
} }
@ -672,14 +713,10 @@ fn test_maintain_connections() {
"alice should be connected to all peers" "alice should be connected to all peers"
); );
for peer in unconnected.iter() {
alice.receive(&connected[0].addr(), peer.node_announcement());
}
for peer in connected.iter() { for peer in connected.iter() {
alice.disconnected( alice.disconnected(
&peer.addr(), &peer.addr(),
&nakamoto::DisconnectReason::Protocol(DisconnectReason::User), &nakamoto::DisconnectReason::Protocol(DisconnectReason::Error(session::Error::Timeout)),
); );
let addr = alice let addr = alice
@ -692,7 +729,10 @@ fn test_maintain_connections() {
assert!(addr != peer.addr()); assert!(addr != peer.addr());
unconnected.retain(|p| p.addr() != addr); unconnected.retain(|p| p.addr() != addr);
} }
assert!(unconnected.is_empty()); assert!(
unconnected.is_empty(),
"alice should connect to all unconnected peers"
);
} }
#[test] #[test]