Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions p2p/discover/lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,7 @@ func (it *lookup) startQueries() bool {

// The first query returns nodes from the local table.
if it.queries == -1 {
it.tab.mutex.Lock()
closest := it.tab.closest(it.result.target, bucketSize, false)
it.tab.mutex.Unlock()
closest := it.tab.findnodeByID(it.result.target, bucketSize, false)
// Avoid finishing the lookup too quickly if table is empty. It'd be better to wait
// for the table to fill in this case, but there is no good mechanism for that
// yet.
Expand Down Expand Up @@ -150,11 +148,14 @@ func (it *lookup) query(n *node, reply chan<- []*node) {
} else if len(r) == 0 {
fails++
it.tab.db.UpdateFindFails(n.ID(), n.IP(), fails)
it.tab.log.Trace("Findnode failed", "id", n.ID(), "failcount", fails, "results", len(r), "err", err)
if fails >= maxFindnodeFailures {
it.tab.log.Trace("Too many findnode failures, dropping", "id", n.ID(), "failcount", fails)
// Remove the node from the local table if it fails to return anything useful too
// many times, but only if there are enough other nodes in the bucket.
dropped := false
if fails >= maxFindnodeFailures && it.tab.bucketLen(n.ID()) >= bucketSize/2 {
dropped = true
it.tab.delete(n)
}
it.tab.log.Trace("FINDNODE failed", "id", n.ID(), "failcount", fails, "dropped", dropped, "err", err)
} else if fails > 0 {
// Reset failure counter because it counts _consecutive_ failures.
it.tab.db.UpdateFindFails(n.ID(), n.IP(), 0)
Expand Down
11 changes: 0 additions & 11 deletions p2p/discover/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,6 @@ func (e encPubkey) id() enode.ID {
return enode.ID(crypto.Keccak256Hash(e[:]))
}

// recoverNodeKey computes the public key used to sign the
// given hash from the signature.
func recoverNodeKey(hash, sig []byte) (key encPubkey, err error) {
pubkey, err := crypto.Ecrecover(hash, sig)
if err != nil {
return key, err
}
copy(key[:], pubkey[1:])
return key, nil
}

func wrapNode(n *enode.Node) *node {
return &node{Node: *n}
}
Expand Down
45 changes: 33 additions & 12 deletions p2p/discover/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ func (tab *Table) nextRevalidateTime() time.Duration {
}

// copyLiveNodes adds nodes from the table to the database if they have been in the table
// longer then minTableTime.
// longer than seedMinTableTime.
func (tab *Table) copyLiveNodes() {
tab.mutex.Lock()
defer tab.mutex.Unlock()
Expand All @@ -394,22 +394,35 @@ func (tab *Table) copyLiveNodes() {
}
}

// closest returns the n nodes in the table that are closest to the
// given id. The caller must hold tab.mutex.
func (tab *Table) closest(target enode.ID, nresults int, checklive bool) *nodesByDistance {
// This is a very wasteful way to find the closest nodes but
// obviously correct. I believe that tree-based buckets would make
// this easier to implement efficiently.
close := &nodesByDistance{target: target}
// findnodeByID returns the n nodes in the table that are closest to the given id.
// This is used by the FINDNODE/v4 handler.
//
// The preferLive parameter says whether the caller wants liveness-checked results. If
// preferLive is true and the table contains any verified nodes, the result will not
// contain unverified nodes. However, if there are no verified nodes at all, the result
// will contain unverified nodes.
func (tab *Table) findnodeByID(target enode.ID, nresults int, preferLive bool) *nodesByDistance {
tab.mutex.Lock()
defer tab.mutex.Unlock()

// Scan all buckets. There might be a better way to do this, but there aren't that many
// buckets, so this solution should be fine. The worst-case complexity of this loop
// is O(tab.len() * nresults).
nodes := &nodesByDistance{target: target}
liveNodes := &nodesByDistance{target: target}
for _, b := range &tab.buckets {
for _, n := range b.entries {
if checklive && n.livenessChecks == 0 {
continue
nodes.push(n, nresults)
if preferLive && n.livenessChecks > 0 {
liveNodes.push(n, nresults)
}
close.push(n, nresults)
}
}
return close

if preferLive && len(liveNodes.entries) > 0 {
return liveNodes
}
return nodes
}

// len returns the number of nodes in the table.
Expand All @@ -423,6 +436,14 @@ func (tab *Table) len() (n int) {
return n
}

// bucketLen returns the number of nodes in the bucket for the given ID.
func (tab *Table) bucketLen(id enode.ID) int {
tab.mutex.Lock()
defer tab.mutex.Unlock()

return len(tab.bucket(id).entries)
}

// bucket returns the bucket for the given node ID hash.
func (tab *Table) bucket(id enode.ID) *bucket {
d := enode.LogDist(tab.self().ID(), id)
Expand Down
4 changes: 2 additions & 2 deletions p2p/discover/table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ func checkIPLimitInvariant(t *testing.T, tab *Table) {
}
}

func TestTable_closest(t *testing.T) {
func TestTable_findnodeByID(t *testing.T) {
t.Parallel()

test := func(test *closeTest) bool {
Expand All @@ -213,7 +213,7 @@ func TestTable_closest(t *testing.T) {
fillTable(tab, test.All)

// check that closest(Target, N) returns nodes
result := tab.closest(test.Target, test.N, false).entries
result := tab.findnodeByID(test.Target, test.N, false).entries
if hasDuplicates(result) {
t.Errorf("result contains duplicates")
return false
Expand Down
15 changes: 8 additions & 7 deletions p2p/discover/v4_lookup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"testing"

"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/p2p/discover/v4wire"
"github.com/XinFinOrg/XDPoSChain/p2p/enode"
"github.com/XinFinOrg/XDPoSChain/p2p/enr"
)
Expand Down Expand Up @@ -135,15 +136,15 @@ func TestUDPv4_LookupIteratorClose(t *testing.T) {

func serveTestnet(test *udpTest, testnet *preminedTestnet) {
for done := false; !done; {
done = test.waitPacketOut(func(p packetV4, to *net.UDPAddr, hash []byte) {
done = test.waitPacketOut(func(p v4wire.Packet, to *net.UDPAddr, hash []byte) {
n, key := testnet.nodeByAddr(to)
switch p.(type) {
case *pingV4:
test.packetInFrom(nil, key, to, &pongV4{Expiration: futureExp, ReplyTok: hash})
case *findnodeV4:
case *v4wire.Ping:
test.packetInFrom(nil, key, to, &v4wire.Pong{Expiration: futureExp, ReplyTok: hash})
case *v4wire.Findnode:
dist := enode.LogDist(n.ID(), testnet.target.id())
nodes := testnet.nodesAtDistance(dist - 1)
test.packetInFrom(nil, key, to, &neighborsV4{Expiration: futureExp, Nodes: nodes})
test.packetInFrom(nil, key, to, &v4wire.Neighbors{Expiration: futureExp, Nodes: nodes})
}
})
}
Expand Down Expand Up @@ -270,8 +271,8 @@ func (tn *preminedTestnet) nodeByAddr(addr *net.UDPAddr) (*enode.Node, *ecdsa.Pr
return tn.node(dist, index), key
}

func (tn *preminedTestnet) nodesAtDistance(dist int) []rpcNode {
result := make([]rpcNode, len(tn.dists[dist]))
func (tn *preminedTestnet) nodesAtDistance(dist int) []v4wire.Node {
result := make([]v4wire.Node, len(tn.dists[dist]))
for i := range result {
result[i] = nodeToRPC(wrapNode(tn.node(dist, i)))
}
Expand Down
Loading
Loading