Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

findnode(self) should return multiple peers #968

Merged
merged 1 commit into from
Apr 3, 2024

Conversation

guillaumemichel
Copy link
Contributor

When receiving a kademlia FIND_NODE request for its own peer id, a go-libp2p-kad-dht node will respond with its own peer record only. According to libp2p kademlia spec, it should reply with the k closest nodes.

The libp2p Kademlia DHT offers the following types of operations:

  • Peer routing
    • Finding the closest nodes to a given key via FIND_NODE.
  1. Upon a response:
    1. If successful the response will contain the k closest nodes the peer knows to the key Key. Add them to the candidate list Pn, except for those that have already been queried.

Depending on libp2p/specs#609, nodes shouldn't even include their own peer records in the response (already known to the requester). This will be implemented in a future PR.

@guillaumemichel guillaumemichel requested a review from a team as a code owner March 28, 2024 09:41
@guillaumemichel guillaumemichel changed the title findnode(self) now returns multiple peers findnode(self) should return multiple peers Apr 2, 2024
Copy link
Member

@sukunrt sukunrt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a test for this?

handlers.go Show resolved Hide resolved
}
if !found {
closest = append(closest, targetPid)
closest = dht.betterPeersToQuery(pmes, from, dht.bucketSize)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dht.betterPeersToQuery errors if the list contains self.
https://github.com/libp2p/go-libp2p-kad-dht/blob/master/dht.go#L765-L769

		if clp == dht.self {
			logger.Error("BUG betterPeersToQuery: attempted to return self! this shouldn't happen...")
			return nil
		}
		// Dont send a peer back themselves
		if clp == from {
			continue
		}

It also handles the case that we don't inform the peer about itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This snippet handles the output of routingTable.NearestPeers, which never contains itself.

The peer adds it own peer id, only if its own peer id is the target of the FIND_NODE request at

// if looking for self... special case where we send it on CloserPeers.
targetPid := peer.ID(pmes.GetKey())
closest = dht.betterPeersToQuery(pmes, from, dht.bucketSize)
// Never tell a peer about itself.
if targetPid != from {
// Add the target peer to the set of closest peers if
// not already present in our routing table.
//
// Later, when we lookup known addresses for all peers
// in this set, we'll prune this peer if we don't
// _actually_ know where it is.
found := false
for _, p := range closest {
if targetPid == p {
found = true
break
}
}
if !found {
closest = append(closest, targetPid)
}
}

Comment on lines +282 to +284
if !found {
closest = append(closest, targetPid)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we do this? If the peerID isn't found, should we return it in findPeer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dht.betterPeersToQuery never includes itself (nor from) in the results.

Currently if A sends FIND_NODE(B) to B, B will reply [B], and we now want it to return [B, C, D, ...]. In this case targetPid is our own peer id, it wasn't returned by dht.betterPeersToQuery, so we need to add it at this step.

We will be able to get rid of this when the response to FIND_NODE(self) will be not include self.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if the target node doesnt exist we will include it in the output, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Peers without addresses are excluded from the response

closestinfos := pstore.PeerInfos(dht.peerstore, closest)
// possibly an over-allocation but this array is temporary anyways.
withAddresses := make([]peer.AddrInfo, 0, len(closestinfos))
for _, pi := range closestinfos {
if len(pi.Addrs) > 0 {
withAddresses = append(withAddresses, pi)
}
}
resp.CloserPeers = pb.PeerInfosToPBPeers(dht.host.Network(), withAddresses)

@guillaumemichel guillaumemichel merged commit 7fc6a1d into master Apr 3, 2024
9 checks passed
@guillaumemichel guillaumemichel deleted the fix/findnode_self_response branch April 3, 2024 07:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants