2014-09-17 07:19:40 -07:00
|
|
|
package dht
|
|
|
|
|
|
|
|
import (
|
2016-09-30 10:24:03 -07:00
|
|
|
"context"
|
2019-04-24 08:29:00 -07:00
|
|
|
"errors"
|
2014-09-18 19:30:04 -07:00
|
|
|
"sync"
|
|
|
|
|
2019-05-26 23:33:15 +01:00
|
|
|
"github.com/libp2p/go-libp2p-core/network"
|
|
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
|
|
|
2016-08-21 17:18:58 +01:00
|
|
|
logging "github.com/ipfs/go-log"
|
2016-09-02 20:21:23 +01:00
|
|
|
todoctr "github.com/ipfs/go-todocounter"
|
2016-08-21 17:18:58 +01:00
|
|
|
process "github.com/jbenet/goprocess"
|
|
|
|
ctxproc "github.com/jbenet/goprocess/context"
|
2019-04-26 12:55:45 -07:00
|
|
|
kb "github.com/libp2p/go-libp2p-kbucket"
|
2019-05-26 23:33:15 +01:00
|
|
|
|
|
|
|
pstore "github.com/libp2p/go-libp2p-core/peerstore"
|
|
|
|
"github.com/libp2p/go-libp2p-core/routing"
|
2016-10-05 12:34:28 -07:00
|
|
|
queue "github.com/libp2p/go-libp2p-peerstore/queue"
|
2016-09-02 20:21:23 +01:00
|
|
|
notif "github.com/libp2p/go-libp2p-routing/notifications"
|
2014-09-17 07:19:40 -07:00
|
|
|
)
|
|
|
|
|
2019-04-24 08:29:00 -07:00
|
|
|
// ErrNoPeersQueried is returned when we failed to connect to any peers.
|
|
|
|
var ErrNoPeersQueried = errors.New("failed to query any peers")
|
|
|
|
|
2014-09-19 05:23:57 -07:00
|
|
|
var maxQueryConcurrency = AlphaValue
|
2014-09-18 19:30:04 -07:00
|
|
|
|
2014-09-17 07:19:40 -07:00
|
|
|
type dhtQuery struct {
|
2015-01-01 12:45:39 -08:00
|
|
|
dht *IpfsDHT
|
2016-09-30 10:13:57 -07:00
|
|
|
key string // the key we're querying for
|
2015-01-01 12:45:39 -08:00
|
|
|
qfunc queryFunc // the function to execute per peer
|
|
|
|
concurrency int // the concurrency parameter
|
2014-09-18 19:30:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
type dhtQueryResult struct {
|
2019-05-26 23:33:15 +01:00
|
|
|
peer *peer.AddrInfo // FindPeer
|
|
|
|
closerPeers []*peer.AddrInfo // *
|
2019-03-14 11:07:15 +11:00
|
|
|
success bool
|
2017-03-05 12:08:20 -08:00
|
|
|
|
2019-05-26 23:33:15 +01:00
|
|
|
finalSet *peer.Set
|
|
|
|
queriedSet *peer.Set
|
2014-09-18 19:30:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// constructs query
|
2016-09-30 10:13:57 -07:00
|
|
|
func (dht *IpfsDHT) newQuery(k string, f queryFunc) *dhtQuery {
|
2014-09-18 19:30:04 -07:00
|
|
|
return &dhtQuery{
|
|
|
|
key: k,
|
2015-01-01 12:45:39 -08:00
|
|
|
dht: dht,
|
2014-09-18 19:30:04 -07:00
|
|
|
qfunc: f,
|
|
|
|
concurrency: maxQueryConcurrency,
|
|
|
|
}
|
2014-09-17 07:19:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// QueryFunc is a function that runs a particular query with a given peer.
|
|
|
|
// It returns either:
|
|
|
|
// - the value
|
|
|
|
// - a list of peers potentially better able to serve the query
|
|
|
|
// - an error
|
2014-12-19 12:19:56 -08:00
|
|
|
type queryFunc func(context.Context, peer.ID) (*dhtQueryResult, error)
|
2014-09-18 19:30:04 -07:00
|
|
|
|
|
|
|
// Run runs the query at hand. pass in a list of peers to use first.
|
2014-12-19 12:19:56 -08:00
|
|
|
func (q *dhtQuery) Run(ctx context.Context, peers []peer.ID) (*dhtQueryResult, error) {
|
2019-04-26 12:55:45 -07:00
|
|
|
if len(peers) == 0 {
|
|
|
|
logger.Warning("Running query with no peers!")
|
|
|
|
return nil, kb.ErrLookupFailure
|
|
|
|
}
|
|
|
|
|
2015-03-07 02:44:20 -08:00
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil, ctx.Err()
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
2015-02-03 12:18:30 -08:00
|
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
|
|
defer cancel()
|
|
|
|
|
2015-03-07 03:20:29 -08:00
|
|
|
runner := newQueryRunner(q)
|
|
|
|
return runner.Run(ctx, peers)
|
2014-09-18 19:30:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
type dhtQueryRunner struct {
|
2015-01-05 04:35:54 -08:00
|
|
|
query *dhtQuery // query to run
|
2019-05-26 23:33:15 +01:00
|
|
|
peersSeen *peer.Set // all peers queried. prevent querying same peer 2x
|
|
|
|
peersQueried *peer.Set // peers successfully connected to and queried
|
2019-01-24 18:04:52 +00:00
|
|
|
peersDialed *dialQueue // peers we have dialed to
|
2015-01-05 04:35:54 -08:00
|
|
|
peersToQuery *queue.ChanQueue // peers remaining to be queried
|
|
|
|
peersRemaining todoctr.Counter // peersToQuery + currently processing
|
2014-09-18 19:30:04 -07:00
|
|
|
|
2015-01-05 04:35:54 -08:00
|
|
|
result *dhtQueryResult // query result
|
2014-09-18 19:30:04 -07:00
|
|
|
|
2015-01-05 04:35:54 -08:00
|
|
|
rateLimit chan struct{} // processing semaphore
|
2015-09-14 17:33:03 -07:00
|
|
|
log logging.EventLogger
|
2014-09-18 19:30:04 -07:00
|
|
|
|
2015-11-04 21:49:20 -08:00
|
|
|
runCtx context.Context
|
|
|
|
|
2015-03-07 03:20:29 -08:00
|
|
|
proc process.Process
|
2014-09-18 19:30:04 -07:00
|
|
|
sync.RWMutex
|
|
|
|
}
|
|
|
|
|
2015-03-07 03:20:29 -08:00
|
|
|
func newQueryRunner(q *dhtQuery) *dhtQueryRunner {
|
|
|
|
proc := process.WithParent(process.Background())
|
2015-09-08 21:15:53 -07:00
|
|
|
ctx := ctxproc.OnClosingContext(proc)
|
2019-01-24 18:04:52 +00:00
|
|
|
peersToQuery := queue.NewChanQueue(ctx, queue.NewXORDistancePQ(string(q.key)))
|
|
|
|
r := &dhtQueryRunner{
|
2014-09-18 19:30:04 -07:00
|
|
|
query: q,
|
|
|
|
peersRemaining: todoctr.NewSyncCounter(),
|
2019-05-26 23:33:15 +01:00
|
|
|
peersSeen: peer.NewSet(),
|
|
|
|
peersQueried: peer.NewSet(),
|
2014-09-18 19:30:04 -07:00
|
|
|
rateLimit: make(chan struct{}, q.concurrency),
|
2019-01-24 18:04:52 +00:00
|
|
|
peersToQuery: peersToQuery,
|
2015-03-07 03:20:29 -08:00
|
|
|
proc: proc,
|
2014-09-18 19:30:04 -07:00
|
|
|
}
|
2019-01-31 12:56:28 +00:00
|
|
|
dq, err := newDialQueue(&dqParams{
|
|
|
|
ctx: ctx,
|
|
|
|
target: q.key,
|
|
|
|
in: peersToQuery,
|
|
|
|
dialFn: r.dialPeer,
|
|
|
|
config: dqDefaultConfig(),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
r.peersDialed = dq
|
2019-01-24 18:04:52 +00:00
|
|
|
return r
|
2014-09-18 19:30:04 -07:00
|
|
|
}
|
|
|
|
|
2015-03-07 03:20:29 -08:00
|
|
|
func (r *dhtQueryRunner) Run(ctx context.Context, peers []peer.ID) (*dhtQueryResult, error) {
|
2020-02-27 17:47:15 -08:00
|
|
|
// Make sure to clean everything up when we return from this function.
|
|
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
|
|
defer cancel()
|
|
|
|
|
2019-02-01 17:46:46 +11:00
|
|
|
r.log = logger
|
2015-11-04 21:49:20 -08:00
|
|
|
r.runCtx = ctx
|
2015-01-05 04:35:54 -08:00
|
|
|
|
2014-09-18 19:30:04 -07:00
|
|
|
// setup concurrency rate limiting
|
|
|
|
for i := 0; i < r.query.concurrency; i++ {
|
|
|
|
r.rateLimit <- struct{}{}
|
2014-09-17 07:19:40 -07:00
|
|
|
}
|
|
|
|
|
2014-09-18 19:30:04 -07:00
|
|
|
// add all the peers we got first.
|
|
|
|
for _, p := range peers {
|
2015-03-07 03:20:29 -08:00
|
|
|
r.addPeerToQuery(p)
|
2015-03-07 02:44:20 -08:00
|
|
|
}
|
|
|
|
|
2019-03-13 21:25:36 +00:00
|
|
|
// start the dial queue only after we've added the initial set of peers.
|
|
|
|
// this is to avoid race conditions that could cause the peersRemaining todoctr
|
|
|
|
// to be done too early if the initial dial fails before others make it into the queue.
|
|
|
|
r.peersDialed.Start()
|
|
|
|
|
2014-09-18 19:41:41 -07:00
|
|
|
// go do this thing.
|
2015-03-07 03:20:29 -08:00
|
|
|
// do it as a child proc to make sure Run exits
|
2014-12-24 02:13:38 -08:00
|
|
|
// ONLY AFTER spawn workers has exited.
|
2015-03-07 03:20:29 -08:00
|
|
|
r.proc.Go(r.spawnWorkers)
|
2014-09-18 19:41:41 -07:00
|
|
|
|
|
|
|
// wait until they're done.
|
2019-03-14 11:07:15 +11:00
|
|
|
var err error
|
2014-09-19 08:07:56 -07:00
|
|
|
|
2015-03-07 03:20:29 -08:00
|
|
|
// now, if the context finishes, close the proc.
|
|
|
|
// we have to do it here because the logic before is setup, which
|
|
|
|
// should run without closing the proc.
|
2015-07-05 09:35:06 +07:00
|
|
|
ctxproc.CloseAfterContext(r.proc, ctx)
|
2015-03-07 03:20:29 -08:00
|
|
|
|
2014-09-17 07:19:40 -07:00
|
|
|
select {
|
2014-09-18 19:30:04 -07:00
|
|
|
case <-r.peersRemaining.Done():
|
2015-03-07 03:20:29 -08:00
|
|
|
r.proc.Close()
|
2019-04-24 08:29:00 -07:00
|
|
|
if r.peersQueried.Size() == 0 {
|
|
|
|
err = ErrNoPeersQueried
|
|
|
|
} else {
|
|
|
|
err = routing.ErrNotFound
|
2014-09-18 19:30:04 -07:00
|
|
|
}
|
|
|
|
|
2015-03-07 03:20:29 -08:00
|
|
|
case <-r.proc.Closed():
|
2019-02-15 14:46:42 +11:00
|
|
|
err = r.runCtx.Err()
|
2014-09-19 08:07:56 -07:00
|
|
|
}
|
2014-09-18 19:30:04 -07:00
|
|
|
|
2019-04-24 08:29:00 -07:00
|
|
|
r.RLock()
|
|
|
|
defer r.RUnlock()
|
|
|
|
|
2014-09-19 08:07:56 -07:00
|
|
|
if r.result != nil && r.result.success {
|
|
|
|
return r.result, nil
|
2014-09-18 19:30:04 -07:00
|
|
|
}
|
|
|
|
|
2017-03-05 12:08:20 -08:00
|
|
|
return &dhtQueryResult{
|
2018-06-06 09:49:03 -07:00
|
|
|
finalSet: r.peersSeen,
|
|
|
|
queriedSet: r.peersQueried,
|
2017-03-05 12:08:20 -08:00
|
|
|
}, err
|
2014-09-18 19:30:04 -07:00
|
|
|
}
|
|
|
|
|
2015-03-07 03:20:29 -08:00
|
|
|
func (r *dhtQueryRunner) addPeerToQuery(next peer.ID) {
|
2014-11-21 08:03:11 -08:00
|
|
|
// if new peer is ourselves...
|
2015-01-01 12:45:39 -08:00
|
|
|
if next == r.query.dht.self {
|
2015-01-05 04:35:54 -08:00
|
|
|
r.log.Debug("addPeerToQuery skip self")
|
2014-11-21 08:03:11 -08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-12-14 00:50:49 +00:00
|
|
|
if !r.peersSeen.TryAdd(next) {
|
2014-09-18 19:30:04 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-11-04 21:49:20 -08:00
|
|
|
notif.PublishQueryEvent(r.runCtx, ¬if.QueryEvent{
|
|
|
|
Type: notif.AddingPeer,
|
|
|
|
ID: next,
|
|
|
|
})
|
|
|
|
|
2014-09-18 19:30:04 -07:00
|
|
|
r.peersRemaining.Increment(1)
|
|
|
|
select {
|
|
|
|
case r.peersToQuery.EnqChan <- next:
|
2015-03-07 03:20:29 -08:00
|
|
|
case <-r.proc.Closing():
|
2014-09-17 07:19:40 -07:00
|
|
|
}
|
2014-09-18 19:30:04 -07:00
|
|
|
}
|
|
|
|
|
2015-03-07 03:20:29 -08:00
|
|
|
func (r *dhtQueryRunner) spawnWorkers(proc process.Process) {
|
2014-09-18 19:30:04 -07:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-r.peersRemaining.Done():
|
|
|
|
return
|
|
|
|
|
2015-03-07 03:20:29 -08:00
|
|
|
case <-r.proc.Closing():
|
2014-09-18 19:30:04 -07:00
|
|
|
return
|
|
|
|
|
2015-09-21 09:55:25 -07:00
|
|
|
case <-r.rateLimit:
|
2019-01-29 16:47:42 +00:00
|
|
|
ch := r.peersDialed.Consume()
|
2015-09-21 09:55:25 -07:00
|
|
|
select {
|
2019-01-29 20:49:04 +00:00
|
|
|
case p, ok := <-ch:
|
|
|
|
if !ok {
|
|
|
|
// this signals context cancellation.
|
2019-01-29 16:47:42 +00:00
|
|
|
return
|
|
|
|
}
|
2015-09-21 09:55:25 -07:00
|
|
|
// do it as a child func to make sure Run exits
|
|
|
|
// ONLY AFTER spawn workers has exited.
|
|
|
|
proc.Go(func(proc process.Process) {
|
|
|
|
r.queryPeer(proc, p)
|
|
|
|
})
|
|
|
|
case <-r.proc.Closing():
|
|
|
|
return
|
|
|
|
case <-r.peersRemaining.Done():
|
|
|
|
return
|
2014-09-18 19:41:41 -07:00
|
|
|
}
|
2014-09-18 19:30:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-09-17 07:19:40 -07:00
|
|
|
|
2019-01-24 18:04:52 +00:00
|
|
|
func (r *dhtQueryRunner) dialPeer(ctx context.Context, p peer.ID) error {
|
|
|
|
// short-circuit if we're already connected.
|
2019-05-26 23:33:15 +01:00
|
|
|
if r.query.dht.host.Network().Connectedness(p) == network.Connected {
|
2019-01-24 18:04:52 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-02-01 17:46:46 +11:00
|
|
|
logger.Debug("not connected. dialing.")
|
2019-01-24 18:04:52 +00:00
|
|
|
notif.PublishQueryEvent(r.runCtx, ¬if.QueryEvent{
|
|
|
|
Type: notif.DialingPeer,
|
|
|
|
ID: p,
|
|
|
|
})
|
|
|
|
|
2019-05-26 23:33:15 +01:00
|
|
|
pi := peer.AddrInfo{ID: p}
|
2019-01-24 18:04:52 +00:00
|
|
|
if err := r.query.dht.host.Connect(ctx, pi); err != nil {
|
2019-02-01 17:46:46 +11:00
|
|
|
logger.Debugf("error connecting: %s", err)
|
2019-01-24 18:04:52 +00:00
|
|
|
notif.PublishQueryEvent(r.runCtx, ¬if.QueryEvent{
|
|
|
|
Type: notif.QueryError,
|
|
|
|
Extra: err.Error(),
|
|
|
|
ID: p,
|
|
|
|
})
|
|
|
|
|
2019-02-26 19:30:59 -07:00
|
|
|
// This peer is dropping out of the race.
|
|
|
|
r.peersRemaining.Decrement(1)
|
2019-01-24 18:04:52 +00:00
|
|
|
return err
|
|
|
|
}
|
2019-02-01 17:46:46 +11:00
|
|
|
logger.Debugf("connected. dial success.")
|
2019-01-24 18:04:52 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-03-07 03:20:29 -08:00
|
|
|
func (r *dhtQueryRunner) queryPeer(proc process.Process, p peer.ID) {
|
2014-10-21 01:18:38 -07:00
|
|
|
// ok let's do this!
|
|
|
|
|
2015-03-07 03:20:29 -08:00
|
|
|
// create a context from our proc.
|
2015-09-08 21:15:53 -07:00
|
|
|
ctx := ctxproc.OnClosingContext(proc)
|
2015-03-07 03:20:29 -08:00
|
|
|
|
2014-10-21 01:18:38 -07:00
|
|
|
// make sure we do this when we exit
|
|
|
|
defer func() {
|
2018-03-25 11:38:08 +02:00
|
|
|
// signal we're done processing peer p
|
2014-10-21 01:18:38 -07:00
|
|
|
r.peersRemaining.Decrement(1)
|
|
|
|
r.rateLimit <- struct{}{}
|
|
|
|
}()
|
|
|
|
|
2014-09-18 19:30:04 -07:00
|
|
|
// finally, run the query against this peer
|
2015-03-07 03:20:29 -08:00
|
|
|
res, err := r.query.qfunc(ctx, p)
|
2014-09-18 19:30:04 -07:00
|
|
|
|
2018-06-06 09:49:03 -07:00
|
|
|
r.peersQueried.Add(p)
|
|
|
|
|
2014-09-18 19:30:04 -07:00
|
|
|
if err != nil {
|
2019-02-01 17:46:46 +11:00
|
|
|
logger.Debugf("ERROR worker for: %v %v", p, err)
|
2014-09-18 19:30:04 -07:00
|
|
|
} else if res.success {
|
2019-02-01 17:46:46 +11:00
|
|
|
logger.Debugf("SUCCESS worker for: %v %s", p, res)
|
2014-09-18 19:30:04 -07:00
|
|
|
r.Lock()
|
|
|
|
r.result = res
|
|
|
|
r.Unlock()
|
2019-03-12 20:08:07 +11:00
|
|
|
if res.peer != nil {
|
|
|
|
r.query.dht.peerstore.AddAddrs(res.peer.ID, res.peer.Addrs, pstore.TempAddrTTL)
|
|
|
|
}
|
2015-03-07 03:20:29 -08:00
|
|
|
go r.proc.Close() // signal to everyone that we're done.
|
2014-12-24 02:13:38 -08:00
|
|
|
// must be async, as we're one of the children, and Close blocks.
|
2014-09-18 19:30:04 -07:00
|
|
|
|
2014-12-23 03:14:30 -08:00
|
|
|
} else if len(res.closerPeers) > 0 {
|
2019-02-01 17:46:46 +11:00
|
|
|
logger.Debugf("PEERS CLOSER -- worker for: %v (%d closer peers)", p, len(res.closerPeers))
|
2014-09-18 19:30:04 -07:00
|
|
|
for _, next := range res.closerPeers {
|
2018-03-25 11:38:08 +02:00
|
|
|
if next.ID == r.query.dht.self { // don't add self.
|
2019-02-01 17:46:46 +11:00
|
|
|
logger.Debugf("PEERS CLOSER -- worker for: %v found self", p)
|
2015-01-16 02:13:00 -08:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2014-12-19 12:19:56 -08:00
|
|
|
// add their addresses to the dialer's peerstore
|
2016-06-01 15:51:39 -07:00
|
|
|
r.query.dht.peerstore.AddAddrs(next.ID, next.Addrs, pstore.TempAddrTTL)
|
2015-03-07 03:20:29 -08:00
|
|
|
r.addPeerToQuery(next.ID)
|
2019-02-01 17:46:46 +11:00
|
|
|
logger.Debugf("PEERS CLOSER -- worker for: %v added %v (%v)", p, next.ID, next.Addrs)
|
2014-09-18 19:30:04 -07:00
|
|
|
}
|
2014-12-23 03:14:30 -08:00
|
|
|
} else {
|
2019-02-01 17:46:46 +11:00
|
|
|
logger.Debugf("QUERY worker for: %v - not found, and no closer peers.", p)
|
2014-09-17 07:19:40 -07:00
|
|
|
}
|
|
|
|
}
|