@@ -2,6 +2,7 @@ package provider
2
2
3
3
import (
4
4
"context"
5
+ "errors"
5
6
"fmt"
6
7
"strconv"
7
8
"sync"
@@ -98,6 +99,8 @@ type SweepingProvider struct {
98
99
99
100
keyStore * datastore.KeyStore
100
101
102
+ replicationFactor int
103
+
101
104
provideQueue * queue.ProvideQueue
102
105
provideRunning sync.Mutex
103
106
@@ -129,7 +132,7 @@ type SweepingProvider struct {
129
132
// FIXME: remove me
130
133
func (s * SweepingProvider ) SatisfyLinter () {
131
134
s .vanillaProvide ([]byte {})
132
- s .closestPeersToKey ("" )
135
+ s .exploreSwarm ("" )
133
136
s .measureInitialPrefixLen ()
134
137
}
135
138
@@ -396,6 +399,126 @@ func (s *SweepingProvider) vanillaProvide(k mh.Multihash) (bitstr.Key, error) {
396
399
return coveredPrefix , s .sendProviderRecords (keysAllocations , addrInfo )
397
400
}
398
401
402
+ // exploreSwarm finds all peers whose kademlia identifier matches `prefix` in
403
+ // the DHT swarm, and organizes them in keyspace regions.
404
+ //
405
+ // A region is identified by a keyspace prefix, and contains all the peers
406
+ // matching this prefix. A region always has at least s.replicationFactor
407
+ // peers. Regions are non-overlapping.
408
+ //
409
+ // If there less than s.replicationFactor peers match `prefix`, explore
410
+ // shorter prefixes until at least s.replicationFactor peers are included in
411
+ // the region.
412
+ //
413
+ // The returned `coveredPrefix` represents the keyspace prefix covered by all
414
+ // returned regions combined. It is different to the supplied `prefix` if there
415
+ // aren't enough peers matching `prefix`.
416
+ func (s * SweepingProvider ) exploreSwarm (prefix bitstr.Key ) (regions []keyspace.Region , coveredPrefix bitstr.Key , err error ) {
417
+ peers , err := s .closestPeersToPrefix (prefix )
418
+ if err != nil {
419
+ return nil , "" , fmt .Errorf ("exploreSwarm '%s': %w" , prefix , err )
420
+ }
421
+ if len (peers ) == 0 {
422
+ return nil , "" , fmt .Errorf ("no peers found when exploring prefix %s" , prefix )
423
+ }
424
+ regions , coveredPrefix = keyspace .RegionsFromPeers (peers , s .replicationFactor , s .order )
425
+ return regions , coveredPrefix , nil
426
+ }
427
+
428
+ // maxPrefixSearches is the maximum number of GetClosestPeers operations that
429
+ // are allowed to explore a prefix, preventing an infinite loop, since the exit
430
+ // condition depends on the network topology.
431
+ //
432
+ // A lower bound estimate on the number of fresh peers returned by GCP is
433
+ // replicationFactor/2. Hence, 64 GCP are expected to return at least
434
+ // 32*replicatonFactor peers, which should be more than enough, even if the
435
+ // supplied prefix is too short.
436
+ const maxPrefixSearches = 64
437
+
438
+ // closestPeersToPrefix returns at least s.replicationFactor peers
439
+ // corresponding to the branch of the network peers trie matching the provided
440
+ // prefix. In the case there aren't enough peers matching the provided prefix,
441
+ // it will find and return the closest peers to the prefix, even if they don't
442
+ // exactly match it.
443
+ func (s * SweepingProvider ) closestPeersToPrefix (prefix bitstr.Key ) ([]peer.ID , error ) {
444
+ allClosestPeers := make (map [peer.ID ]struct {})
445
+
446
+ nextPrefix := prefix
447
+ startTime := time .Now ()
448
+ coveredPrefixesStack := []bitstr.Key {}
449
+
450
+ i := 0
451
+ // Go down the trie to fully cover prefix.
452
+ exploration:
453
+ for {
454
+ if i == maxPrefixSearches {
455
+ return nil , errors .New ("closestPeersToPrefix needed more than maxPrefixSearches iterations" )
456
+ }
457
+ if ! s .connectivity .IsOnline () {
458
+ return nil , errors .New ("provider: node is offline" )
459
+ }
460
+ i ++
461
+ fullKey := keyspace .FirstFullKeyWithPrefix (nextPrefix , s .order )
462
+ closestPeers , err := s .closestPeersToKey (fullKey )
463
+ if err != nil {
464
+ // We only get an err if something really bad happened, e.g no peers in
465
+ // routing table, invalid key, etc.
466
+ return nil , err
467
+ }
468
+ if len (closestPeers ) == 0 {
469
+ return nil , errors .New ("dht lookup did not return any peers" )
470
+ }
471
+ coveredPrefix , coveredPeers := keyspace .ShortestCoveredPrefix (fullKey , closestPeers )
472
+ for _ , p := range coveredPeers {
473
+ allClosestPeers [p ] = struct {}{}
474
+ }
475
+
476
+ coveredPrefixLen := len (coveredPrefix )
477
+ if i == 1 {
478
+ if coveredPrefixLen <= len (prefix ) && coveredPrefix == prefix [:coveredPrefixLen ] && len (allClosestPeers ) >= s .replicationFactor {
479
+ // Exit early if the prefix is fully covered at the first request and
480
+ // we have enough (at least replicationFactor) peers.
481
+ break exploration
482
+ }
483
+ } else {
484
+ latestPrefix := coveredPrefixesStack [len (coveredPrefixesStack )- 1 ]
485
+ for coveredPrefixLen <= len (latestPrefix ) && coveredPrefix [:coveredPrefixLen - 1 ] == latestPrefix [:coveredPrefixLen - 1 ] {
486
+ // Pop latest prefix from stack, because current prefix is
487
+ // complementary.
488
+ // e.g latestPrefix=0010, currentPrefix=0011. latestPrefix is
489
+ // replaced by 001, unless 000 was also in the stack, etc.
490
+ coveredPrefixesStack = coveredPrefixesStack [:len (coveredPrefixesStack )- 1 ]
491
+ coveredPrefix = coveredPrefix [:len (coveredPrefix )- 1 ]
492
+ coveredPrefixLen = len (coveredPrefix )
493
+
494
+ if len (coveredPrefixesStack ) == 0 {
495
+ if coveredPrefixLen <= len (prefix ) && len (allClosestPeers ) >= s .replicationFactor {
496
+ break exploration
497
+ }
498
+ // Not enough peers -> add coveredPrefix to stack and continue.
499
+ break
500
+ }
501
+ if coveredPrefixLen == 0 {
502
+ logger .Error ("coveredPrefixLen==0, coveredPrefixStack " , coveredPrefixesStack )
503
+ break exploration
504
+ }
505
+ latestPrefix = coveredPrefixesStack [len (coveredPrefixesStack )- 1 ]
506
+ }
507
+ }
508
+ // Push coveredPrefix to stack
509
+ coveredPrefixesStack = append (coveredPrefixesStack , coveredPrefix )
510
+ // Flip last bit of last covered prefix
511
+ nextPrefix = keyspace .FlipLastBit (coveredPrefixesStack [len (coveredPrefixesStack )- 1 ])
512
+ }
513
+
514
+ peers := make ([]peer.ID , 0 , len (allClosestPeers ))
515
+ for p := range allClosestPeers {
516
+ peers = append (peers , p )
517
+ }
518
+ logger .Debugf ("Region %s exploration required %d requests to discover %d peers in %s" , prefix , i , len (allClosestPeers ), time .Since (startTime ))
519
+ return peers , nil
520
+ }
521
+
399
522
// closestPeersToKey returns a valid peer ID sharing a long common prefix with
400
523
// the provided key. Note that the returned peer IDs aren't random, they are
401
524
// taken from a static list of preimages.
0 commit comments