2015-01-16 12:52:12 -08:00
|
|
|
package dht
|
|
|
|
|
|
|
|
import (
|
2016-09-30 10:24:03 -07:00
|
|
|
"context"
|
2015-01-16 12:52:12 -08:00
|
|
|
"crypto/rand"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
2016-08-21 17:18:58 +01:00
|
|
|
u "github.com/ipfs/go-ipfs-util"
|
2016-10-05 12:34:28 -07:00
|
|
|
peer "github.com/libp2p/go-libp2p-peer"
|
2019-01-23 11:18:24 +11:00
|
|
|
peerstore "github.com/libp2p/go-libp2p-peerstore"
|
2016-09-02 20:21:23 +01:00
|
|
|
routing "github.com/libp2p/go-libp2p-routing"
|
2015-01-16 12:52:12 -08:00
|
|
|
)
|
|
|
|
|
2015-01-23 04:36:18 -08:00
|
|
|
// BootstrapConfig specifies parameters used bootstrapping the DHT.
|
2015-01-16 12:52:12 -08:00
|
|
|
//
|
2015-01-23 04:36:18 -08:00
|
|
|
// Note there is a tradeoff between the bootstrap period and the
|
|
|
|
// number of queries. We could support a higher period with less
|
|
|
|
// queries.
|
|
|
|
type BootstrapConfig struct {
|
|
|
|
Queries int // how many queries to run per period
|
2018-07-28 15:54:01 +02:00
|
|
|
Period time.Duration // how often to run periodic bootstrap.
|
|
|
|
Timeout time.Duration // how long to wait for a bootstrap query to run
|
2015-01-23 04:36:18 -08:00
|
|
|
}
|
2015-01-16 12:52:12 -08:00
|
|
|
|
2015-01-23 04:36:18 -08:00
|
|
|
var DefaultBootstrapConfig = BootstrapConfig{
|
|
|
|
// For now, this is set to 1 query.
|
|
|
|
// We are currently more interested in ensuring we have a properly formed
|
|
|
|
// DHT than making sure our dht minimizes traffic. Once we are more certain
|
|
|
|
// of our implementation's robustness, we should lower this down to 8 or 4.
|
|
|
|
Queries: 1,
|
2015-01-16 12:52:12 -08:00
|
|
|
|
2018-07-31 18:19:24 -04:00
|
|
|
// For now, this is set to 5 minutes, which is a medium period. We are
|
2015-01-23 04:36:18 -08:00
|
|
|
// We are currently more interested in ensuring we have a properly formed
|
2015-03-19 04:01:15 -07:00
|
|
|
// DHT than making sure our dht minimizes traffic.
|
|
|
|
Period: time.Duration(5 * time.Minute),
|
2015-01-16 12:52:12 -08:00
|
|
|
|
2015-03-19 04:01:15 -07:00
|
|
|
Timeout: time.Duration(10 * time.Second),
|
2015-01-23 04:36:18 -08:00
|
|
|
}
|
|
|
|
|
2019-01-23 11:18:24 +11:00
|
|
|
// A method in the IpfsRouting interface. It calls BootstrapWithConfig with
|
|
|
|
// the default bootstrap config.
|
2015-02-05 07:53:53 -08:00
|
|
|
func (dht *IpfsDHT) Bootstrap(ctx context.Context) error {
|
2019-01-23 11:18:24 +11:00
|
|
|
return dht.BootstrapWithConfig(ctx, DefaultBootstrapConfig)
|
|
|
|
}
|
2015-02-05 07:53:53 -08:00
|
|
|
|
2019-01-23 11:18:24 +11:00
|
|
|
// Runs cfg.Queries bootstrap queries every cfg.Period.
|
|
|
|
func (dht *IpfsDHT) BootstrapWithConfig(ctx context.Context, cfg BootstrapConfig) error {
|
|
|
|
if cfg.Queries <= 0 {
|
|
|
|
return fmt.Errorf("invalid number of queries: %d", cfg.Queries)
|
|
|
|
}
|
|
|
|
go func() {
|
2018-06-29 12:32:09 -04:00
|
|
|
for {
|
2019-01-23 11:18:24 +11:00
|
|
|
err := dht.runBootstrap(ctx, cfg)
|
|
|
|
if err != nil {
|
|
|
|
log.Warningf("error bootstrapping: %s", err)
|
|
|
|
}
|
2018-06-29 12:32:09 -04:00
|
|
|
select {
|
|
|
|
case <-time.After(cfg.Period):
|
2019-01-23 11:18:24 +11:00
|
|
|
case <-ctx.Done():
|
2018-06-29 12:32:09 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2019-01-23 11:18:24 +11:00
|
|
|
}()
|
|
|
|
return nil
|
|
|
|
}
|
2016-12-08 11:58:06 +02:00
|
|
|
|
2019-01-23 11:18:24 +11:00
|
|
|
func newRandomPeerId() peer.ID {
|
|
|
|
id := make([]byte, 32) // SHA256 is the default. TODO: Use a more canonical way to generate random IDs.
|
|
|
|
rand.Read(id)
|
|
|
|
id = u.Hash(id) // TODO: Feed this directly into the multihash instead of hashing it.
|
|
|
|
return peer.ID(id)
|
2015-01-16 12:52:12 -08:00
|
|
|
}
|
|
|
|
|
2019-01-23 11:18:24 +11:00
|
|
|
// Traverse the DHT toward the given ID.
|
|
|
|
func (dht *IpfsDHT) walk(ctx context.Context, target peer.ID) (peerstore.PeerInfo, error) {
|
|
|
|
// TODO: Extract the query action (traversal logic?) inside FindPeer,
|
|
|
|
// don't actually call through the FindPeer machinery, which can return
|
|
|
|
// things out of the peer store etc.
|
|
|
|
return dht.FindPeer(ctx, target)
|
|
|
|
}
|
2015-01-20 07:38:20 -08:00
|
|
|
|
2019-01-23 11:18:24 +11:00
|
|
|
// Traverse the DHT toward a random ID.
|
|
|
|
func (dht *IpfsDHT) randomWalk(ctx context.Context) error {
|
|
|
|
id := newRandomPeerId()
|
|
|
|
p, err := dht.walk(ctx, id)
|
|
|
|
switch err {
|
|
|
|
case routing.ErrNotFound:
|
|
|
|
return nil
|
|
|
|
case nil:
|
2019-01-23 16:28:23 +11:00
|
|
|
// We found a peer from a randomly generated ID. This should be very
|
|
|
|
// unlikely.
|
|
|
|
log.Warningf("random walk toward %s actually found peer: %s", id, p)
|
2019-01-23 11:18:24 +11:00
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
return err
|
2016-12-08 19:37:07 +02:00
|
|
|
}
|
2015-01-16 12:52:12 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// runBootstrap builds up list of peers by requesting random peer IDs
|
2015-01-23 04:36:18 -08:00
|
|
|
func (dht *IpfsDHT) runBootstrap(ctx context.Context, cfg BootstrapConfig) error {
|
2015-01-18 00:58:34 -08:00
|
|
|
bslog := func(msg string) {
|
|
|
|
log.Debugf("DHT %s dhtRunBootstrap %s -- routing table size: %d", dht.self, msg, dht.routingTable.Size())
|
|
|
|
}
|
|
|
|
bslog("start")
|
|
|
|
defer bslog("end")
|
|
|
|
defer log.EventBegin(ctx, "dhtRunBootstrap").Done()
|
2015-01-16 12:52:12 -08:00
|
|
|
|
2019-01-23 11:18:24 +11:00
|
|
|
doQuery := func(n int, target string, f func(context.Context) error) error {
|
|
|
|
log.Debugf("Bootstrapping query (%d/%d) to %s", n, cfg.Queries, target)
|
2018-06-09 08:48:33 -07:00
|
|
|
ctx, cancel := context.WithTimeout(ctx, cfg.Timeout)
|
|
|
|
defer cancel()
|
2019-01-23 11:18:24 +11:00
|
|
|
return f(ctx)
|
2015-01-16 12:52:12 -08:00
|
|
|
}
|
|
|
|
|
2019-01-23 11:18:24 +11:00
|
|
|
// Do all but one of the bootstrap queries as random walks.
|
|
|
|
for i := 1; i < cfg.Queries; i++ {
|
|
|
|
err := doQuery(i, "random ID", dht.randomWalk)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-01-16 12:52:12 -08:00
|
|
|
}
|
|
|
|
|
2018-06-09 08:48:33 -07:00
|
|
|
// Find self to distribute peer info to our neighbors.
|
2019-01-23 11:18:24 +11:00
|
|
|
return doQuery(cfg.Queries, fmt.Sprintf("self: %s", dht.self), func(ctx context.Context) error {
|
|
|
|
_, err := dht.walk(ctx, dht.self)
|
|
|
|
return err
|
|
|
|
})
|
2015-01-16 12:52:12 -08:00
|
|
|
}
|