From b8b50ac1c1404826759c844d877e65c4a9dfc446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bierlein?= Date: Mon, 29 Apr 2024 13:37:03 +0200 Subject: [PATCH 1/4] Add behaviors for hole punching --- Cargo.lock | 134 +++++++++++++++++++++++++++++++++++++++++++++++++---- Cargo.toml | 7 ++- src/p2p.rs | 7 +++ 3 files changed, 137 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fa80e78..a9ed375 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -313,7 +313,7 @@ dependencies = [ "dashmap", "futures", "futures-utils-wasm", - "lru", + "lru 0.12.3", "reqwest 0.12.4", "serde_json", "tokio", @@ -964,6 +964,19 @@ dependencies = [ "rustc_version 0.4.0", ] +[[package]] +name = "asynchronous-codec" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057f2c32adbb2fc158e22fb38433c8e9bbf76b75a4732c7c0cbaf695fb65568" +dependencies = [ + "bytes", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite", +] + [[package]] name = "asynchronous-codec" version = "0.7.0" @@ -3338,8 +3351,10 @@ dependencies = [ "getrandom", "instant", "libp2p-allow-block-list", + "libp2p-autonat", "libp2p-connection-limits", "libp2p-core", + "libp2p-dcutr", "libp2p-dns", "libp2p-gossipsub", "libp2p-identify", @@ -3350,6 +3365,7 @@ dependencies = [ "libp2p-noise", "libp2p-ping", "libp2p-quic", + "libp2p-relay", "libp2p-request-response", "libp2p-swarm", "libp2p-tcp", @@ -3373,6 +3389,27 @@ dependencies = [ "void", ] +[[package]] +name = "libp2p-autonat" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95151726170e41b591735bf95c42b888fe4aa14f65216a9fbf0edcc04510586" +dependencies = [ + "async-trait", + "asynchronous-codec 0.6.2", + "futures", + "futures-timer", + "instant", + "libp2p-core", + "libp2p-identity", + "libp2p-request-response", + "libp2p-swarm", + "quick-protobuf", + "quick-protobuf-codec 0.2.0", + "rand", + "tracing", +] + [[package]] name = "libp2p-connection-limits" version = "0.3.1" @@ -3413,6 +3450,29 @@ dependencies = [ "void", ] +[[package]] +name = "libp2p-dcutr" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f7bb7fa2b9e6cad9c30a6f67e3ff5c1e4b658c62b6375e35861a85f9c97bf3" +dependencies = [ + "asynchronous-codec 0.6.2", + "either", + "futures", + "futures-bounded", + "futures-timer", + "instant", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "lru 0.11.1", + "quick-protobuf", + "quick-protobuf-codec 0.2.0", + "thiserror", + "tracing", + "void", +] + [[package]] name = "libp2p-dns" version = "0.41.1" @@ -3435,7 +3495,7 @@ version = "0.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d665144a616dadebdc5fff186b1233488cdcd8bfb1223218ff084b6d052c94f7" dependencies = [ - "asynchronous-codec", + "asynchronous-codec 0.7.0", "base64 0.21.7", "byteorder", "bytes", @@ -3451,7 +3511,7 @@ dependencies = [ "libp2p-swarm", "prometheus-client", "quick-protobuf", - "quick-protobuf-codec", + "quick-protobuf-codec 0.3.1", "rand", "regex", "sha2", @@ -3466,7 +3526,7 @@ version = "0.44.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20499a945d2f0221fdc6269b3848892c0f370d2ee3e19c7f65a29d8f860f6126" dependencies = [ - "asynchronous-codec", + "asynchronous-codec 0.7.0", "either", "futures", "futures-bounded", @@ -3474,9 +3534,9 @@ dependencies = [ "libp2p-core", "libp2p-identity", "libp2p-swarm", - "lru", + "lru 0.12.3", "quick-protobuf", - "quick-protobuf-codec", + "quick-protobuf-codec 0.3.1", "smallvec", "thiserror", "tracing", @@ -3508,7 +3568,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cc5767727d062c4eac74dd812c998f0e488008e82cce9c33b463d38423f9ad2" dependencies = [ "arrayvec", - "asynchronous-codec", + "asynchronous-codec 0.7.0", "bytes", "either", "fnv", @@ -3520,7 +3580,7 @@ dependencies = [ "libp2p-identity", "libp2p-swarm", "quick-protobuf", - "quick-protobuf-codec", + "quick-protobuf-codec 0.3.1", "rand", "sha2", "smallvec", @@ -3560,11 +3620,13 @@ dependencies = [ "futures", "instant", "libp2p-core", + "libp2p-dcutr", "libp2p-gossipsub", "libp2p-identify", "libp2p-identity", "libp2p-kad", "libp2p-ping", + "libp2p-relay", "libp2p-swarm", "pin-project 1.1.5", "prometheus-client", @@ -3576,7 +3638,7 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecd0545ce077f6ea5434bcb76e8d0fe942693b4380aaad0d34a358c2bd05793" dependencies = [ - "asynchronous-codec", + "asynchronous-codec 0.7.0", "bytes", "curve25519-dalek", "futures", @@ -3638,6 +3700,31 @@ dependencies = [ "tracing", ] +[[package]] +name = "libp2p-relay" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aadb213ffc8e1a6f2b9c48dcf0fc07bf370f2ea4db7981813d45e50671c8d9d" +dependencies = [ + "asynchronous-codec 0.7.0", + "bytes", + "either", + "futures", + "futures-bounded", + "futures-timer", + "instant", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "quick-protobuf", + "quick-protobuf-codec 0.3.1", + "rand", + "static_assertions", + "thiserror", + "tracing", + "void", +] + [[package]] name = "libp2p-request-response" version = "0.26.1" @@ -3825,6 +3912,15 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "lru" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21" +dependencies = [ + "hashbrown 0.14.3", +] + [[package]] name = "lru" version = "0.12.3" @@ -3952,6 +4048,7 @@ dependencies = [ "futures", "futures-ticker", "futures-util", + "itertools 0.12.1", "libp2p", "mini-moka", "once_cell", @@ -4849,13 +4946,26 @@ dependencies = [ "byteorder", ] +[[package]] +name = "quick-protobuf-codec" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ededb1cd78531627244d51dd0c7139fbe736c7d57af0092a76f0ffb2f56e98" +dependencies = [ + "asynchronous-codec 0.6.2", + "bytes", + "quick-protobuf", + "thiserror", + "unsigned-varint 0.7.2", +] + [[package]] name = "quick-protobuf-codec" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15a0580ab32b169745d7a39db2ba969226ca16738931be152a3209b409de2474" dependencies = [ - "asynchronous-codec", + "asynchronous-codec 0.7.0", "bytes", "quick-protobuf", "thiserror", @@ -6700,6 +6810,10 @@ name = "unsigned-varint" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" +dependencies = [ + "asynchronous-codec 0.6.2", + "bytes", +] [[package]] name = "unsigned-varint" diff --git a/Cargo.toml b/Cargo.toml index 4cae18b..c4c920d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,11 @@ libp2p = { version = "0.53.2", features = [ "ping", "dns", "request-response", - "cbor" + "cbor", + "quic", + "relay", + "autonat", + "dcutr" ] } tokio = { version = "1.36.0", features = ["full"] } eyre = "0.6.12" @@ -93,6 +97,7 @@ opentelemetry-stdout = { version = "0.3.0", features = ["logs", "metrics", "trac opentelemetry-prometheus = "0.15.0" prometheus = "0.13.3" built = "0.7.2" +itertools = "0.12.1" [profile.dev.package.sqlx-macros] diff --git a/src/p2p.rs b/src/p2p.rs index 5747652..9a627c3 100644 --- a/src/p2p.rs +++ b/src/p2p.rs @@ -36,6 +36,9 @@ pub struct MintpoolBehaviour { identify: libp2p::identify::Behaviour, ping: libp2p::ping::Behaviour, request_response: request_response::cbor::Behaviour, + relay: libp2p::relay::Behaviour, + autonat: libp2p::autonat::Behaviour, + dcutr: libp2p::dcutr::Behaviour, } pub struct SwarmController { @@ -102,6 +105,7 @@ impl SwarmController { noise::Config::new, yamux::Config::default, )? + .with_quic() .with_dns()? .with_behaviour(|key| { let mut b = @@ -136,6 +140,9 @@ impl SwarmController { )], request_response::Config::default(), ), + relay: libp2p::relay::Behaviour::new(peer_id, Default::default()), + autonat: libp2p::autonat::Behaviour::new(peer_id, Default::default()), + dcutr: libp2p::dcutr::Behaviour::new(peer_id), } })? .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(60))) From 65297870362ccd1a892ad47f6feafb175b59b68c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bierlein?= Date: Mon, 29 Apr 2024 20:51:39 +0200 Subject: [PATCH 2/4] Test automatic relay --- Cargo.lock | 18 +++++++ Cargo.toml | 1 + src/p2p.rs | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 155 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9ed375..c4f6a90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3725,6 +3725,23 @@ dependencies = [ "void", ] +[[package]] +name = "libp2p-relay-manager" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8ce24a6522b4b58c82d72cf9f364732f354d8daaecdd359329aa6a84708428d" +dependencies = [ + "anyhow", + "futures", + "futures-timer", + "getrandom", + "libp2p", + "log", + "rand", + "thiserror", + "void", +] + [[package]] name = "libp2p-request-response" version = "0.26.1" @@ -4050,6 +4067,7 @@ dependencies = [ "futures-util", "itertools 0.12.1", "libp2p", + "libp2p-relay-manager", "mini-moka", "once_cell", "opentelemetry", diff --git a/Cargo.toml b/Cargo.toml index c4c920d..3e34a9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ libp2p = { version = "0.53.2", features = [ "autonat", "dcutr" ] } +libp2p-relay-manager = "0.2.2" tokio = { version = "1.36.0", features = ["full"] } eyre = "0.6.12" regex = "1.10.4" diff --git a/src/p2p.rs b/src/p2p.rs index 9a627c3..5dde28a 100644 --- a/src/p2p.rs +++ b/src/p2p.rs @@ -6,6 +6,7 @@ use futures_ticker::Ticker; use libp2p::core::ConnectedPoint; use libp2p::futures::StreamExt; use libp2p::gossipsub::Version; +use libp2p::identify::Event; use libp2p::identity::Keypair; use libp2p::kad::store::MemoryStore; use libp2p::kad::GetProvidersOk::FoundProviders; @@ -14,8 +15,10 @@ use libp2p::multiaddr::Protocol; use libp2p::request_response::{InboundRequestId, Message, ProtocolSupport, ResponseChannel}; use libp2p::swarm::{ConnectionId, NetworkBehaviour, NetworkInfo, SwarmEvent}; use libp2p::{ - gossipsub, kad, noise, request_response, tcp, yamux, Multiaddr, PeerId, StreamProtocol, + autonat, dcutr, gossipsub, kad, noise, relay, request_response, tcp, yamux, Multiaddr, PeerId, + StreamProtocol, }; + use rand::prelude::SliceRandom; use serde::{Deserialize, Serialize}; use sha256::digest; @@ -37,6 +40,8 @@ pub struct MintpoolBehaviour { ping: libp2p::ping::Behaviour, request_response: request_response::cbor::Behaviour, relay: libp2p::relay::Behaviour, + relay_client: libp2p::relay::client::Behaviour, + relay_manager: libp2p_relay_manager::Behaviour, autonat: libp2p::autonat::Behaviour, dcutr: libp2p::dcutr::Behaviour, } @@ -85,7 +90,10 @@ impl SwarmController { max_peers: config.peer_limit, local_mode: !config.connect_external, premint_names: config.premint_names(), - discover_ticker: Ticker::new(Duration::from_secs(60)), + discover_ticker: Ticker::new_with_next( + Duration::from_secs(60), + Duration::from_secs(10), + ), } } @@ -107,7 +115,8 @@ impl SwarmController { )? .with_quic() .with_dns()? - .with_behaviour(|key| { + .with_relay_client(noise::Config::new, yamux::Config::default)? + .with_behaviour(|key, client| { let mut b = kad::Behaviour::new(peer_id, MemoryStore::new(key.public().to_peer_id())); b.set_mode(Some(kad::Mode::Server)); @@ -141,7 +150,24 @@ impl SwarmController { request_response::Config::default(), ), relay: libp2p::relay::Behaviour::new(peer_id, Default::default()), - autonat: libp2p::autonat::Behaviour::new(peer_id, Default::default()), + relay_client: client, + relay_manager: libp2p_relay_manager::Behaviour::new( + libp2p_relay_manager::Config { + auto_connect: true, + auto_relay: true, + limit: Some(5), + backoff: Duration::from_secs(15), + }, + ), + autonat: libp2p::autonat::Behaviour::new( + peer_id, + libp2p::autonat::Config { + boot_delay: Duration::from_secs(15), + only_global_ips: false, + use_connected: true, + ..Default::default() + }, + ), dcutr: libp2p::dcutr::Behaviour::new(peer_id), } })? @@ -237,7 +263,11 @@ impl SwarmController { match event { SwarmEvent::NewListenAddr { address, .. } => { let pid = self.swarm.local_peer_id(); - let local_address = address.with(Protocol::P2p(*pid)).to_string(); + let local_address = if address.iter().any(|p| p == Protocol::P2pCircuit) { + address.to_string() + } else { + address.with(Protocol::P2p(*pid)).to_string() + }; tracing::info!(local_address = local_address, "Started listening"); } @@ -319,6 +349,15 @@ impl SwarmController { } } + SwarmEvent::Behaviour(MintpoolBehaviourEvent::RelayClient(event)) => { + match self.handle_relay_client_event(event).await { + Ok(_) => {} + Err(err) => { + tracing::error!("Error handling relay client event: {:?}", err); + } + } + } + SwarmEvent::Dialing { peer_id, .. } => { tracing::info!("Dialing: {:?}", peer_id) } @@ -343,6 +382,57 @@ impl SwarmController { } } + SwarmEvent::Behaviour(MintpoolBehaviourEvent::Identify(event)) => match event { + Event::Received { peer_id, info } => { + let is_relay = info.protocols.contains(&libp2p::relay::HOP_PROTOCOL_NAME); + + if is_relay { + tracing::info!("Discovered relay peer: {:?}", info); + + for addr in info.listen_addrs { + self.swarm + .behaviour_mut() + .relay_manager + .add_address(peer_id, addr); + } + } + } + _ => { + tracing::info!("Identify event: {:?}", event); + } + }, + + SwarmEvent::Behaviour(MintpoolBehaviourEvent::Ping(event)) => { + match event.result { + Ok(rtt) => { + self.swarm.behaviour_mut().relay_manager.set_peer_rtt( + event.peer, + event.connection, + rtt, + ); + } + _ => {} + } + tracing::info!("Ping event: {:?}", event); + } + + SwarmEvent::Behaviour(MintpoolBehaviourEvent::Relay(event)) => { + tracing::info!("Relay event: {:?}", event); + } + + SwarmEvent::Behaviour(MintpoolBehaviourEvent::Autonat(event)) => { + match self.handle_autonat_event(event).await { + Ok(_) => {} + Err(err) => { + tracing::error!("Error handling autonat event: {:?}", err); + } + } + } + + SwarmEvent::Behaviour(MintpoolBehaviourEvent::Dcutr(event)) => { + tracing::info!("Dcutr event: {:?}", event); + } + other => { tracing::debug!("Unhandled swarm event: {:?}", other) } @@ -610,7 +700,7 @@ impl SwarmController { event: request_response::Event, ) -> eyre::Result<()> { match event { - request_response::Event::Message { peer, message } => match message { + request_response::Event::Message { message, .. } => match message { Message::Request { request_id, request, @@ -652,6 +742,46 @@ impl SwarmController { Ok(()) } + async fn handle_relay_client_event(&mut self, event: relay::client::Event) -> eyre::Result<()> { + match event { + relay::client::Event::ReservationReqAccepted { relay_peer_id, .. } => { + tracing::info!("Relay reservation request accepted: {:?}", relay_peer_id); + self.swarm + .behaviour_mut() + .kad + .start_providing(RecordKey::new(&"mintpool::gossip"))?; + } + + other => { + tracing::info!("Relay client event: {:?}", other); + } + } + + Ok(()) + } + + async fn handle_autonat_event(&mut self, event: autonat::Event) -> eyre::Result<()> { + if let autonat::Event::StatusChanged { old, new } = event { + tracing::info!("Autonat status changed: {:?} -> {:?}", old, new); + match new { + autonat::NatStatus::Private => { + tracing::info!("Autonat status is private"); + if let Some(peer) = self.swarm.behaviour_mut().relay_manager.random_select() { + tracing::info!("Relay peer: {:?}", peer); + } + } + autonat::NatStatus::Public(multiaddr) => { + tracing::info!("Autonat status is public: {}", multiaddr); + } + autonat::NatStatus::Unknown => { + tracing::info!("Autonat status is unknown"); + } + } + } + + Ok(()) + } + // Makes a Response for a request to sync from another node async fn make_sync_response( &mut self, From 25c93d061e540efbd3d157cf9a74bd800f1666ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bierlein?= Date: Mon, 29 Apr 2024 21:08:06 +0200 Subject: [PATCH 3/4] Use debug logging for pings --- src/p2p.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p2p.rs b/src/p2p.rs index 5dde28a..cb28c9d 100644 --- a/src/p2p.rs +++ b/src/p2p.rs @@ -413,7 +413,7 @@ impl SwarmController { } _ => {} } - tracing::info!("Ping event: {:?}", event); + tracing::debug!("Ping event: {:?}", event); } SwarmEvent::Behaviour(MintpoolBehaviourEvent::Relay(event)) => { From fe984953b2d21062a4c51beb752a494c0d25a3ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bierlein?= Date: Mon, 29 Apr 2024 21:17:51 +0200 Subject: [PATCH 4/4] Update tcp settings --- src/p2p.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/p2p.rs b/src/p2p.rs index cb28c9d..c84ab24 100644 --- a/src/p2p.rs +++ b/src/p2p.rs @@ -39,11 +39,11 @@ pub struct MintpoolBehaviour { identify: libp2p::identify::Behaviour, ping: libp2p::ping::Behaviour, request_response: request_response::cbor::Behaviour, - relay: libp2p::relay::Behaviour, - relay_client: libp2p::relay::client::Behaviour, + relay: relay::Behaviour, + relay_client: relay::client::Behaviour, relay_manager: libp2p_relay_manager::Behaviour, - autonat: libp2p::autonat::Behaviour, - dcutr: libp2p::dcutr::Behaviour, + autonat: autonat::Behaviour, + dcutr: dcutr::Behaviour, } pub struct SwarmController { @@ -109,7 +109,7 @@ impl SwarmController { let swarm = libp2p::SwarmBuilder::with_existing_identity(id_keys) .with_tokio() .with_tcp( - tcp::Config::default(), + tcp::Config::default().port_reuse(true).nodelay(true), noise::Config::new, yamux::Config::default, )?