Repository: tangle-network/gadget (language: rust)

Issue:
[TASK] Add dualstack (IPv4/IPv6) support for libp2p
Unlike IPv4, IPv6 is usually not NAT'd because of a far larger address space. For this reason, direct p2p connections are much more likely when using IPv6 than IPv4.

A "dualstack" is where a node simultaneously binds to ipv4 and ipv6 addresses (e.g., `0.0.0.0` and `::`). By doing this, we will allow peers whose networks support IPv6 facilitate direct connections to each other.

This will only require a small change in the `network/setup.rs` file.


Discussion / hints:

Fix patch:
diff --git a/metadata/tangle.scale b/metadata/tangle.scale
index bfde1ad92..aee413fcb 100644
Binary files a/metadata/tangle.scale and b/metadata/tangle.scale differ
diff --git a/shell/src/network/gossip.rs b/shell/src/network/gossip.rs
index 54eff0303..24974e367 100644
--- a/shell/src/network/gossip.rs
+++ b/shell/src/network/gossip.rs
@@ -386,8 +386,7 @@ mod tests {
     use crate::config::{KeystoreConfig, ShellConfig, SubxtConfig};
     use crate::network::setup::setup_libp2p_network;
     use crate::shell::wait_for_connection_to_bootnodes;
-    use gadget_common::config::Network;
-    use gadget_common::prelude::{DebugLogger, GadgetProtocolMessage, WorkManager};
+    use gadget_common::prelude::{DebugLogger, GadgetProtocolMessage, Network, WorkManager};
     use gadget_core::job_manager::WorkManagerInterface;
     use sp_core::{ecdsa, Pair};
 
diff --git a/shell/src/network/setup.rs b/shell/src/network/setup.rs
index 53da2d3b0..2bdbe31d6 100644
--- a/shell/src/network/setup.rs
+++ b/shell/src/network/setup.rs
@@ -13,6 +13,8 @@ use sp_core::ecdsa;
 use std::collections::HashMap;
 use std::error::Error;
 use std::io;
+use std::net::IpAddr;
+use std::str::FromStr;
 use std::sync::atomic::AtomicU32;
 use std::sync::Arc;
 use std::time::Duration;
@@ -20,6 +22,7 @@ use tokio::select;
 use tokio::sync::{Mutex, RwLock};
 use tokio::task::JoinHandle;
 
+#[allow(clippy::collapsible_else_if)]
 pub async fn setup_libp2p_network(
     identity: libp2p::identity::Keypair,
     config: &ShellConfig,
@@ -142,9 +145,29 @@ pub async fn setup_libp2p_network(
         );
     }
 
-    swarm
-        .listen_on(format!("/ip4/{}/udp/{}/quic-v1", config.bind_ip, config.bind_port).parse()?)?;
-    swarm.listen_on(format!("/ip4/{}/tcp/{}", config.bind_ip, config.bind_port).parse()?)?;
+    let bind_ip = config.bind_ip;
+
+    let mut ips_to_bind_to = vec![bind_ip];
+
+    if let IpAddr::V6(v6_addr) = bind_ip {
+        if v6_addr.is_loopback() {
+            ips_to_bind_to.push(IpAddr::from_str("127.0.0.1").unwrap());
+        } else {
+            ips_to_bind_to.push(IpAddr::from_str("0.0.0.0").unwrap());
+        }
+    } else {
+        if bind_ip.is_loopback() {
+            ips_to_bind_to.push(IpAddr::from_str("::1").unwrap());
+        } else {
+            ips_to_bind_to.push(IpAddr::from_str("::").unwrap());
+        }
+    }
+
+    for addr in ips_to_bind_to {
+        let ip_label = if addr.is_ipv4() { "ip4" } else { "ip6" };
+        swarm.listen_on(format!("/{ip_label}/{addr}/udp/{}/quic-v1", config.bind_port).parse()?)?;
+        swarm.listen_on(format!("/{ip_label}/{addr}/tcp/{}", config.bind_port).parse()?)?;
+    }
 
     // Dial all bootnodes
     for bootnode in &config.bootnodes {


Test patch: