libp2p / go-libp2p-kad-dht

A Kademlia DHT implementation on go-libp2p
https://github.com/libp2p/specs/tree/master/kad-dht
MIT License
517 stars 221 forks source link

DHT Bootstrap Example #827

Closed phillebaba closed 1 year ago

phillebaba commented 1 year ago

Currently there is no example for how to bootstrap DHT with peers. The common example found in blogs and other projects is to iterate through bootstrap peers and connect to them individually.

var wg sync.WaitGroup
for _, peerAddr := range bootstrapPeers {
    peerinfo, _ := peer.AddrInfoFromP2pAddr(peerAddr)

    wg.Add(1)
    go func() {
      defer wg.Done()
      if err := host.Connect(ctx, *peerinfo); err != nil {
          log.Printf("Error while connecting to node %q: %-v", peerinfo, err)
      } else {
          log.Printf("Connection established with bootstrap node: %q", *peerinfo)
      }
    }()
}
wg.Wait()

https://medium.com/rahasak/libp2p-pubsub-peer-discovery-with-kademlia-dht-c8b131550ac7

Having a look at the other implementations of libp2p it seems like it is more common to specify the peers in the BootstrapPeersFunc. This obviously has the benefit of configuring the DHT to be able to recover if connection to all peers is lost.

bootstrapPeerOpt := dht.BootstrapPeersFunc(func() []peer.AddrInfo {
  return []peer.AddrInfo{somePeer}
})
dhtOpts = append(dhtOpts, bootstrapPeerOpt)
kdht, err := dht.New(ctx, host, dhtOpts...)
if err != nil {
  return err
}
errCh := kdht.RefreshRoutingTable()
err = <-errCh
if err != nil {
  return err
}

I wanted to check if this would be a better way of bootstrapping the DHT. If that is the case it might be good to create an example to influence others to do it in a similar way.

guillaumemichel commented 1 year ago

Bootstrapping in the context of go-libp2p-kad-dht essentially means to fill the IpfsDHT's routing table with the peers already connected to the libp2p node.

The BootstrapPeersFunc function defines how bootstrap peers are selected, and is set as argument to the IpfsDHT.

https://github.com/libp2p/go-libp2p-kad-dht/blob/0239a32c4a99c2e782a504a40673dc8d0afd9de8/dht_options.go#L275-L282

The IpfsDHT then uses the BootstrapPeersFunc function when the routing table is empty as follow:

https://github.com/libp2p/go-libp2p-kad-dht/blob/0239a32c4a99c2e782a504a40673dc8d0afd9de8/dht.go#L490-L530

It is equivalent to the first code snippet that you provided. The libp2p node will try to connect to the provided peer.AddrInfos, and refresh the routing table immediately after connecting to the provided peers.

The two code snippets that you provided are equivalent, so you can use the one you are the most comfortable with. Note that the following bit isn't necessary in the second code snippet, for a routing table refresh is run right after the BootstrapPeersFunc anyway.

errCh := kdht.RefreshRoutingTable()
err = <-errCh
if err != nil {
  return err
}
phillebaba commented 1 year ago

Thanks for the response. That last piece of code is there to block until the routing table has refreshed. With the first example this is not an issue as the wait group would block until all bootstrap peers are connected to. In my use case other functionality relies on the instance being connected so I want to block until it is complete. A simple alternative to that could be to wait for the routing table size to be greater than zero.

for {
  if kdht.RoutingTable().Size() > 0 {
    break
  }
  time.Sleep(5*time.Seconds)
}
guillaumemichel commented 1 year ago

Yes exactly!

Closing this issue. Feel free to reopen if you want to follow up.