Skip to content

Commit 3d294c7

Browse files
authored
Merge pull request #634 from libp2p/fix/protect-useful-peers
fix: protect useful peers in low buckets
2 parents c38060f + 6c1d38b commit 3d294c7

File tree

2 files changed

+55
-12
lines changed

2 files changed

+55
-12
lines changed

dht.go

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,23 @@ var (
4242
baseLogger = logger.Desugar()
4343
)
4444

45-
// BaseConnMgrScore is the base of the score set on the connection manager "kbucket" tag.
46-
// It is added with the common prefix length between two peer IDs.
47-
const BaseConnMgrScore = 5
45+
const (
46+
// BaseConnMgrScore is the base of the score set on the connection
47+
// manager "kbucket" tag. It is added with the common prefix length
48+
// between two peer IDs.
49+
baseConnMgrScore = 5
50+
51+
// UsefulConnMgrScore is given to peers that are among the first peers
52+
// to respond to a query.
53+
//
54+
// This score is given to peers the first time they're useful and lasts
55+
// until we disconnect from the peer.
56+
usefulConnMgrScore = 20
57+
58+
// UsefulConnMgrProtectedBuckets is the number of buckets where useful
59+
// peers are _protected_, instead of just given the useful score.
60+
usefulConnMgrProtectedBuckets = 2
61+
)
4862

4963
type mode int
5064

@@ -58,11 +72,17 @@ const (
5872
kad2 protocol.ID = "/kad/2.0.0"
5973
)
6074

75+
const (
76+
dhtUsefulTag = "dht-useful"
77+
kbucketTag = "kbucket"
78+
)
79+
6180
// IpfsDHT is an implementation of Kademlia with S/Kademlia modifications.
6281
// It is used to implement the base Routing module.
6382
type IpfsDHT struct {
64-
host host.Host // the network services we need
65-
self peer.ID // Local peer (yourself)
83+
host host.Host // the network services we need
84+
self peer.ID // Local peer (yourself)
85+
selfKey kb.ID
6686
peerstore peerstore.Peerstore // Peer Registry
6787

6888
datastore ds.Datastore // Local data
@@ -250,6 +270,7 @@ func makeDHT(ctx context.Context, h host.Host, cfg config) (*IpfsDHT, error) {
250270
dht := &IpfsDHT{
251271
datastore: cfg.datastore,
252272
self: h.ID(),
273+
selfKey: kb.ConvertPeerID(h.ID()),
253274
peerstore: h.Peerstore(),
254275
host: h,
255276
strmap: make(map[peer.ID]*messageSender),
@@ -337,17 +358,22 @@ func makeRtRefreshManager(dht *IpfsDHT, cfg config, maxLastSuccessfulOutboundThr
337358
}
338359

339360
func makeRoutingTable(dht *IpfsDHT, cfg config, maxLastSuccessfulOutboundThreshold time.Duration) (*kb.RoutingTable, error) {
340-
self := kb.ConvertPeerID(dht.host.ID())
341-
342-
rt, err := kb.NewRoutingTable(cfg.bucketSize, self, time.Minute, dht.host.Peerstore(), maxLastSuccessfulOutboundThreshold)
361+
rt, err := kb.NewRoutingTable(cfg.bucketSize, dht.selfKey, time.Minute, dht.host.Peerstore(), maxLastSuccessfulOutboundThreshold)
343362
cmgr := dht.host.ConnManager()
344363

345364
rt.PeerAdded = func(p peer.ID) {
346-
commonPrefixLen := kb.CommonPrefixLen(self, kb.ConvertPeerID(p))
347-
cmgr.TagPeer(p, "kbucket", BaseConnMgrScore+commonPrefixLen)
365+
// We tag our closest peers with higher and higher scores so we
366+
// stay connected to our nearest neighbors.
367+
//
368+
// We _also_ (elsewhere) protect useful peers in the furthest
369+
// buckets (our "core" routing nodes) and give high scores to
370+
// all other useful peers.
371+
commonPrefixLen := kb.CommonPrefixLen(dht.selfKey, kb.ConvertPeerID(p))
372+
cmgr.TagPeer(p, kbucketTag, baseConnMgrScore+commonPrefixLen)
348373
}
349374
rt.PeerRemoved = func(p peer.ID) {
350-
cmgr.UntagPeer(p, "kbucket")
375+
cmgr.Unprotect(p, dhtUsefulTag)
376+
cmgr.UntagPeer(p, kbucketTag)
351377

352378
// try to fix the RT
353379
dht.fixRTIfNeeded()

query.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,24 @@ func (dht *IpfsDHT) runQuery(ctx context.Context, target string, queryFn queryFn
174174
}
175175

176176
func (q *query) recordPeerIsValuable(p peer.ID) {
177-
q.dht.routingTable.UpdateLastUsefulAt(p, time.Now())
177+
if !q.dht.routingTable.UpdateLastUsefulAt(p, time.Now()) {
178+
// not in routing table
179+
return
180+
}
181+
182+
// Protect useful peers, when they're actually useful. This will last
183+
// through disconnects. However, we'll still evict them if they keep
184+
// disconnecting from us.
185+
//
186+
// Restrict to buckets 0, 1 (75% of requests, max 40 peers), so we don't
187+
// protect _too_ many peers.
188+
commonPrefixLen := kb.CommonPrefixLen(q.dht.selfKey, kb.ConvertPeerID(p))
189+
cmgr := q.dht.host.ConnManager()
190+
if commonPrefixLen < usefulConnMgrProtectedBuckets {
191+
cmgr.Protect(p, dhtUsefulTag)
192+
} else {
193+
cmgr.TagPeer(p, dhtUsefulTag, usefulConnMgrScore)
194+
}
178195
}
179196

180197
func (q *query) recordValuablePeers() {

0 commit comments

Comments
 (0)