p2p/discover: avoid dropping unverified nodes when table is almost empty (#21396)

This change improves discovery behavior in small networks. Very small
networks would often fail to bootstrap because all member nodes were
dropping table content due to findnode failure. The check is now changed
to avoid dropping nodes on findnode failure when their bucket is almost
empty. It also relaxes the liveness check requirement for FINDNODE/v4
response nodes, returning unverified nodes as results when there aren't
any verified nodes yet.

The "findnode failed" log now reports whether the node was dropped
instead of the number of results. The value of the "results" was
always zero by definition.

Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
timcooijmans
2020-08-24 14:42:39 +02:00
committed by GitHub
parent bdde616f23
commit 7b5107b73f
5 changed files with 140 additions and 25 deletions

View File

@ -324,7 +324,16 @@ func (t *UDPv4) findnode(toid enode.ID, toaddr *net.UDPAddr, target v4wire.Pubke
Target: target,
Expiration: uint64(time.Now().Add(expiration).Unix()),
})
return nodes, <-rm.errc
// Ensure that callers don't see a timeout if the node actually responded. Since
// findnode can receive more than one neighbors response, the reply matcher will be
// active until the remote node sends enough nodes. If the remote end doesn't have
// enough nodes the reply matcher will time out waiting for the second reply, but
// there's no need for an error in that case.
err := <-rm.errc
if err == errTimeout && rm.reply != nil {
err = nil
}
return nodes, err
}
// RequestENR sends enrRequest to the given node and waits for a response.
@ -453,9 +462,9 @@ func (t *UDPv4) loop() {
if p.from == r.from && p.ptype == r.data.Kind() && p.ip.Equal(r.ip) {
ok, requestDone := p.callback(r.data)
matched = matched || ok
p.reply = r.data
// Remove the matcher if callback indicates that all replies have been received.
if requestDone {
p.reply = r.data
p.errc <- nil
plist.Remove(el)
}
@ -715,9 +724,7 @@ func (t *UDPv4) handleFindnode(h *packetHandlerV4, from *net.UDPAddr, fromID eno
// Determine closest nodes.
target := enode.ID(crypto.Keccak256Hash(req.Target[:]))
t.tab.mutex.Lock()
closest := t.tab.closest(target, bucketSize, true).entries
t.tab.mutex.Unlock()
closest := t.tab.findnodeByID(target, bucketSize, true).entries
// Send neighbors in chunks with at most maxNeighbors per packet
// to stay below the packet size limit.