Rust-libp2p: How to connect by node ID / try hole punching?

Hey there,

I tried the examples for rust’s libp2p (rust-libp2p/examples at master · libp2p/rust-libp2p · GitHub), but all of them are working with 2 terminals locally.

I tried to set up an example where I have 2 servers with open ports so it should be possible to connect to each other, but I really don’t know where to get started to make the following happening:

  • Create a peer ID on node 1
  • Let node 2 dial to that peer ID and set up a p2p connection (using whatever works, hole punching, relay etc)

I tried to modify the example code of the chat with no luck, I only was able to talk to the IP address of the other node directly by means of cargo run --example chat -- /ip4/<external ip>/tcp/4001.

I assume since it’s a p2p library with a DHT, that this ought to be possible. I’m just a little stuck in the docs and code that is available as of now. I hope this makes our intentions clear, thanks a lot in advance.

Hi Peter,

I assume since it’s a p2p library with a DHT, that this ought to be possible. I’m just a little stuck in the docs and code that is available as of now. I hope this makes our intentions clear, thanks a lot in advance.

Do I understand correctly that you would like Peer 2 to discover Peer 1 without directly pointing Peer 2 at Peer 1 via -- /ip4/<external ip>/tcp/4001?

rust-libp2p does support different discovery mechanisms, e.g. mdns and Kademlia but the simple chat example does not leverage either.

In case you want to play around with the DHT I would recommend the following:

  1. Apply the diff below.

    diff --git a/examples/distributed-key-value-store.rs b/examples/distributed-key-value-store.rs
    index 412e933f..a15806e1 100644
    --- a/examples/distributed-key-value-store.rs
    +++ b/examples/distributed-key-value-store.rs
    @@ -55,6 +55,7 @@ use libp2p::kad::{
        record::Key,
    };
    use libp2p::{
    +    Multiaddr,
        NetworkBehaviour,
        PeerId,
        Swarm,
    @@ -156,6 +157,13 @@ fn main() -> Result<(), Box<dyn Error>> {
            Swarm::new(transport, behaviour, local_peer_id)
        };
    
    +    // Reach out to another node if specified
    +    if let Some(to_dial) = std::env::args().nth(1) {
    +        let addr: Multiaddr = to_dial.parse()?;
    +        Swarm::dial_addr(&mut swarm, addr)?;
    +        println!("Dialed {:?}", to_dial)
    +    }
    +
        // Read full lines from stdin
        let mut stdin = io::BufReader::new(io::stdin()).lines();
    
  2. Spin up 3 nodes with open ports and your patched repository.

  3. Run cargo run --example distributed-key-value-store on Node 1.

  4. Run cargo run --example distributed-key-value-store -- /ip4/<external-ip-node-1>/tcp/<port-node-1> on Node 2

  5. Run cargo run --example distributed-key-value-store -- /ip4/<external-ip-node-1>/tcp/<port-node-1> on Node 3

  6. Enter PUT my_key my_value on Node 2.

  7. Enter GET my_key on Node 3.

What is going to happen is that Node 2 will discover Node 3 through Node 1 via the Kademlia DHT. Thus Node 3 will be able to read a key published by Node 2.

Let me know if the above is of some help.

Hi mxinden,

Thanks for your fast reply! I appreciate it. The example you provided is working great over here, but that’s not what I meant.

We wanted to connect 2 nodes by their peerId, where libp2p is taking care of how that should happen (be it a peer to peer connection, or through a relay if needed, which we provide then).

So for instance, if you take the chat example: It works between 2 nodes on the same network, but connecting to an external node I only managed to do after opening the port that the chat example was listening on, and dialing to that ip address.

It would be nice that if we could instead of manually dialing an IP address, we could dial the peerId belonging to that IP address instead, in where libp2p is either trying to open a connection through hole punching, or if not possible, use a relay instead (We set up a server with this node: GitHub - libp2p/js-libp2p-relay-server: An out of the box libp2p relay server with HOP, which I assumed was for such relay)

We wanted to connect 2 nodes by their peerId, where libp2p is taking care of how that should happen

If you want two nodes to be able to connect by their peer id only, you need some kind of out-of-band mechanism to go from a peer id to a network address. rust-libp2p provides two and a half mechanisms for this today:

  • MDNS (which would only work on your local network, i.e. your IP broadcast domain)
  • Kademlia (which requires both nodes to have at least one other node in common, see Node 1 in my example above)
  • GossipSub (again, the two nodes would need to have at least one other node in common)

in where libp2p is either trying to open a connection through hole punching, or if not possible, use a relay instead

Rust lib2p does not support hole punching (yet). But you can use the circuit relay v1 implementation. On master branch for now, but likely to be released to crates.io this week).

As next steps for you to explore, I would suggest:

Does this help @peterwilli?