Can't add a peer in Kad DHT `UnroutablePeer` event

When contacting a peer it unleash a UnroutablePeer event.

This are the steps that i follow:

  • start first peer
  • second node has the first as boot address
  • start second peer
    What happens is that in the first node this is what happens:
[INFO]peer id:	12D3KooWCPyxtinDT6yBBk9TEuaH1YFER6kfKAh1VEN9DHQSqzyc
adding local node into DHT result: false
listen on result: true
is connected to a peer: false
Dialed "/ip4/10.0.0.11/tcp/8000"
Searching for the closest peers to PeerId("12D3KooWBeXS6GZEDMwjf9prXq8RFHAQ6JQqwL6LTCwpeR89Epxw")
Query finished with no closest peers.
External addr /ip4/10.0.0.11/tcp/8000
External addr /ip4/10.0.0.11/tcp/8000
Listening on /ip4/127.0.0.1/tcp/8000
Listening on /ip4/10.0.0.11/tcp/8000
Other event: UnroutablePeer {
    peer: PeerId(
        "12D3KooWARTkYv6S4J3NGBt1ZGdZs46bSCPHfLSPtKP1Ct1gqMem",
    ),
}
Other event: InboundRequest {
    request: FindNode {
        num_closer_peers: 0,
    },
}
Other event: InboundRequest {
    request: FindNode {
        num_closer_peers: 0,
    },
}
Other event: UnroutablePeer {
    peer: PeerId(
        "12D3KooWARTkYv6S4J3NGBt1ZGdZs46bSCPHfLSPtKP1Ct1gqMem",
    ),
}
Other event: InboundRequest {
    request: FindNode {
        num_closer_peers: 0,
    },
}

This is my implementation:

use crate::utils;
use async_std::future;
use async_std::task::{self, block_on};
use futures::StreamExt;
use libp2p::kad::record::store::MemoryStore;
use libp2p::kad::{
    GetClosestPeersError, Kademlia, KademliaBucketInserts, KademliaConfig, KademliaEvent,
    QueryResult,
};
use libp2p::swarm::AddressScore;
use libp2p::{
    development_transport, identity,
    swarm::{Swarm, SwarmEvent},
    Multiaddr, PeerId,
};
use std::task::{Context, Poll};
use std::{error::Error, str::FromStr, time::Duration};

async fn run_async(peer_mod: String, port: String) -> Result<(), Box<dyn Error>> {
    env_logger::init();

    // loading boot peer id
    let boot_key = utils::load_keypair("./keypairs/boot.bin".to_string());
    let boot_peer_id = boot_key.public().to_peer_id();

    // Create a random key for ourselves.
    let local_key = if peer_mod.eq(&format!("boot")) {
        utils::load_keypair("./keypairs/boot.bin".to_string())
    } else if peer_mod.eq(&format!("w-boot")) {
        let local_key = utils::generate_keypair();
        utils::store_keypair(&local_key, "./keypairs/boot.bin".to_string());
        local_key
    } else {
        utils::generate_keypair()
    };

    let local_peer_id = PeerId::from(local_key.public());
    println!("[INFO]peer id:\t{}", local_peer_id.to_string());
    utils::log_local(format!("[INFO]peer id:\t{}", local_peer_id.to_string()));

    // Set up a an encrypted DNS-enabled TCP Transport over the Mplex protocol
    let transport = development_transport(local_key).await?;

    // Create a swarm to manage peers and events.
    let mut swarm = {
        // Create a Kademlia behaviour.
        let mut cfg = KademliaConfig::default();
        cfg.set_query_timeout(Duration::from_secs(5 * 60))
            .set_kbucket_inserts(KademliaBucketInserts::OnConnected)
            .set_connection_idle_timeout(Duration::from_secs(5 * 60))
            .set_provider_publication_interval(Some(Duration::from_secs(60)));
        let store = MemoryStore::new(local_peer_id);
        let mut behaviour = Kademlia::with_config(local_peer_id, store, cfg);

        let bootaddr = Multiaddr::from_str(
            "/ip4/10.0.0.11/tcp/8000/p2p/12D3KooWCPyxtinDT6yBBk9TEuaH1YFER6kfKAh1VEN9DHQSqzyc",
        )?;
        behaviour.add_address(
            &PeerId::from_str(&boot_peer_id.to_string())?,
            bootaddr.clone(),
        );

        println!("adding local node into DHT result: {:?}", behaviour.bootstrap().is_ok());

        Swarm::new(transport, behaviour, local_peer_id)
    };

    // CHANGE THIS PORT
    let addr = format!("/ip4/{}/tcp/{}", "0.0.0.0", port);
    let addr = addr.parse::<Multiaddr>().unwrap();
    let result = swarm.listen_on(addr).is_ok();
    println!("listen on result: {:?}", result);

    println!(
        "is connected to a peer: {:?}",
        swarm.is_connected(&boot_peer_id)
    );

    // Reach out to another node if specified
    let addr = Multiaddr::from_str("/ip4/10.0.0.11/tcp/8000").unwrap();
    swarm.dial(addr.clone()).unwrap();
    //dial_addr(&mut swarm, addr)?;
    println!("Dialed {:?}", addr);

    // Order Kademlia to search for a peer.
    let to_search: PeerId = identity::Keypair::generate_ed25519().public().into();
    println!("Searching for the closest peers to {:?}", to_search);
    swarm.behaviour_mut().get_closest_peers(to_search);

    // get all peers
    for bucket in swarm.behaviour_mut().kbuckets() {
        println!("the bucket has :{} peers", bucket.num_entries());
        for peer in bucket.iter() {
            println!("{:?}", peer.node.key);
        }
    }

    let addr = Multiaddr::from_str(&format!("/ip4/10.0.0.11/tcp/{}", port)).unwrap();
    let score = AddressScore::Infinite;
    let _result = swarm.add_external_address(addr, score);

    let mut listening = false;
    let future_poll = future::poll_fn(move |cx: &mut Context<'_>| -> Poll<()> {
        loop {
            match swarm.poll_next_unpin(cx) {
                Poll::Ready(Some(event)) => {
                    match event {
                        SwarmEvent::Behaviour(event) => match event {
                            KademliaEvent::RoutingUpdated {
                                peer,
                                is_new_peer,
                                addresses,
                                bucket_range: _,
                                old_peer: _,
                            } => {
                                println!("===\nRouting Table has been updated");
                                if is_new_peer {
                                    println!("#new peer: {}#", peer);
                                } else {
                                    println!("#old peer: {}#", peer);
                                }

                                for addr in addresses.iter() {
                                    println!("\t\t{}", addr)
                                }
                        
                            }
                            KademliaEvent::OutboundQueryCompleted { 
                                result: QueryResult::GetClosestPeers(result),
                                .. 
                            } => {
                                    match result {
                                        Ok(ok) => {
                                            if !ok.peers.is_empty() {
                                                println!("Query finished with closest peers: {:#?}", ok.peers)
                                            } else {
                                                // The example is considered failed as there
                                                // should always be at least 1 reachable peer.
                                                println!("Query finished with no closest peers.")
                                            }
                                        }
                                        Err(GetClosestPeersError::Timeout { peers, .. }) => {
                                            if !peers.is_empty() {
                                                println!("Query timed out with closest peers: {:#?}", peers)
                                            } else {
                                                // The example is considered failed as there
                                                // should always be at least 1 reachable peer.
                                                println!("Query timed out with no closest peers.");
                                            }
                                        }
                                }
                            }
                            _=> println!("Other event: {:#?}", event)
                            //KademliaEvent::InboundRequest { request } => todo!(),
                            //KademliaEvent::OutboundQueryCompleted { id, result, stats } => todo!(),
                            //KademliaEvent::UnroutablePeer { peer } => todo!(),
                            //KademliaEvent::RoutablePeer { peer, address } => todo!(),
                            //KademliaEvent::PendingRoutablePeer { peer, address } => todo!(),

                        },
                        _ => (),
                    }
                }
                Poll::Ready(None) => return Poll::Ready(()),
                Poll::Pending => {
                    if !listening {
                        for addr in Swarm::external_addresses(&swarm) {
                            println!("External addr {}", addr.addr);
                        }
                        
                        for addr in Swarm::listeners(&swarm) {
                            println!("Listening on {}", addr);
                            listening = true;
                        }
                    }
                    return Poll::Pending;
                }
            }
        }
    });

    future_poll.await;
    Ok(())
}

pub fn run(peer_mod: String, port: String) {
    block_on(run_async(peer_mod, port)).unwrap();
}

What should i do?

I would suggest using libp2p-identify in combination with libp2p-kad:

I’ll try right away, I’ll tell you if I succeed. Thanks

Do I need to specify in Kad config that the adding of peers is manual?

No, you can leave KademliaBucketInserts as is.

1 Like