mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
p2p/discover: WIP node refactoring
This commit is contained in:
parent
cc22e0cdf0
commit
b168d82446
10 changed files with 182 additions and 177 deletions
|
|
@ -29,16 +29,16 @@ import (
|
||||||
// not need to be an actual node identifier.
|
// not need to be an actual node identifier.
|
||||||
type lookup struct {
|
type lookup struct {
|
||||||
tab *Table
|
tab *Table
|
||||||
queryfunc func(*node) ([]*node, error)
|
queryfunc queryFunc
|
||||||
replyCh chan []*node
|
replyCh chan []*enode.Node
|
||||||
cancelCh <-chan struct{}
|
cancelCh <-chan struct{}
|
||||||
asked, seen map[enode.ID]bool
|
asked, seen map[enode.ID]bool
|
||||||
result nodesByDistance
|
result nodesByDistance
|
||||||
replyBuffer []*node
|
replyBuffer []*enode.Node
|
||||||
queries int
|
queries int
|
||||||
}
|
}
|
||||||
|
|
||||||
type queryFunc func(*node) ([]*node, error)
|
type queryFunc func(*enode.Node) ([]*enode.Node, error)
|
||||||
|
|
||||||
func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *lookup {
|
func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *lookup {
|
||||||
it := &lookup{
|
it := &lookup{
|
||||||
|
|
@ -47,7 +47,7 @@ func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *l
|
||||||
asked: make(map[enode.ID]bool),
|
asked: make(map[enode.ID]bool),
|
||||||
seen: make(map[enode.ID]bool),
|
seen: make(map[enode.ID]bool),
|
||||||
result: nodesByDistance{target: target},
|
result: nodesByDistance{target: target},
|
||||||
replyCh: make(chan []*node, alpha),
|
replyCh: make(chan []*enode.Node, alpha),
|
||||||
cancelCh: ctx.Done(),
|
cancelCh: ctx.Done(),
|
||||||
queries: -1,
|
queries: -1,
|
||||||
}
|
}
|
||||||
|
|
@ -61,7 +61,7 @@ func newLookup(ctx context.Context, tab *Table, target enode.ID, q queryFunc) *l
|
||||||
func (it *lookup) run() []*enode.Node {
|
func (it *lookup) run() []*enode.Node {
|
||||||
for it.advance() {
|
for it.advance() {
|
||||||
}
|
}
|
||||||
return unwrapNodes(it.result.entries)
|
return it.result.entries
|
||||||
}
|
}
|
||||||
|
|
||||||
// advance advances the lookup until any new nodes have been found.
|
// advance advances the lookup until any new nodes have been found.
|
||||||
|
|
@ -139,7 +139,7 @@ func (it *lookup) slowdown() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *lookup) query(n *node, reply chan<- []*node) {
|
func (it *lookup) query(n *enode.Node, reply chan<- []*enode.Node) {
|
||||||
r, err := it.queryfunc(n)
|
r, err := it.queryfunc(n)
|
||||||
if !errors.Is(err, errClosed) { // avoid recording failures on shutdown.
|
if !errors.Is(err, errClosed) { // avoid recording failures on shutdown.
|
||||||
success := len(r) > 0
|
success := len(r) > 0
|
||||||
|
|
@ -154,7 +154,7 @@ func (it *lookup) query(n *node, reply chan<- []*node) {
|
||||||
// lookupIterator performs lookup operations and iterates over all seen nodes.
|
// lookupIterator performs lookup operations and iterates over all seen nodes.
|
||||||
// When a lookup finishes, a new one is created through nextLookup.
|
// When a lookup finishes, a new one is created through nextLookup.
|
||||||
type lookupIterator struct {
|
type lookupIterator struct {
|
||||||
buffer []*node
|
buffer []*enode.Node
|
||||||
nextLookup lookupFunc
|
nextLookup lookupFunc
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel func()
|
cancel func()
|
||||||
|
|
@ -173,7 +173,7 @@ func (it *lookupIterator) Node() *enode.Node {
|
||||||
if len(it.buffer) == 0 {
|
if len(it.buffer) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return unwrapNode(it.buffer[0])
|
return it.buffer[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next moves to the next node.
|
// Next moves to the next node.
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,7 @@ type BucketNode struct {
|
||||||
Live bool `json:"live"`
|
Live bool `json:"live"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// node represents a host on the network.
|
// node is a node table entry.
|
||||||
// The fields of Node may not be modified.
|
|
||||||
type node struct {
|
type node struct {
|
||||||
*enode.Node
|
*enode.Node
|
||||||
revalList *revalidationList
|
revalList *revalidationList
|
||||||
|
|
@ -75,26 +74,10 @@ func (e encPubkey) id() enode.ID {
|
||||||
return enode.ID(crypto.Keccak256Hash(e[:]))
|
return enode.ID(crypto.Keccak256Hash(e[:]))
|
||||||
}
|
}
|
||||||
|
|
||||||
func wrapNode(n *enode.Node) *node {
|
|
||||||
return &node{Node: n}
|
|
||||||
}
|
|
||||||
|
|
||||||
func wrapNodes(ns []*enode.Node) []*node {
|
|
||||||
result := make([]*node, len(ns))
|
|
||||||
for i, n := range ns {
|
|
||||||
result[i] = wrapNode(n)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func unwrapNode(n *node) *enode.Node {
|
|
||||||
return n.Node
|
|
||||||
}
|
|
||||||
|
|
||||||
func unwrapNodes(ns []*node) []*enode.Node {
|
func unwrapNodes(ns []*node) []*enode.Node {
|
||||||
result := make([]*enode.Node, len(ns))
|
result := make([]*enode.Node, len(ns))
|
||||||
for i, n := range ns {
|
for i, n := range ns {
|
||||||
result[i] = unwrapNode(n)
|
result[i] = n.Node
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ const (
|
||||||
type Table struct {
|
type Table struct {
|
||||||
mutex sync.Mutex // protects buckets, bucket content, nursery, rand
|
mutex sync.Mutex // protects buckets, bucket content, nursery, rand
|
||||||
buckets [nBuckets]*bucket // index of known nodes by distance
|
buckets [nBuckets]*bucket // index of known nodes by distance
|
||||||
nursery []*node // bootstrap nodes
|
nursery []*enode.Node // bootstrap nodes
|
||||||
rand reseedingRandom // source of randomness, periodically reseeded
|
rand reseedingRandom // source of randomness, periodically reseeded
|
||||||
ips netutil.DistinctNetSet
|
ips netutil.DistinctNetSet
|
||||||
revalidation tableRevalidation
|
revalidation tableRevalidation
|
||||||
|
|
@ -108,13 +108,14 @@ type bucket struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type addNodeOp struct {
|
type addNodeOp struct {
|
||||||
node *node
|
node *enode.Node
|
||||||
isInbound bool
|
isInbound bool
|
||||||
|
forceSetLive bool // for tests
|
||||||
}
|
}
|
||||||
|
|
||||||
type trackRequestOp struct {
|
type trackRequestOp struct {
|
||||||
node *node
|
node *enode.Node
|
||||||
foundNodes []*node
|
foundNodes []*enode.Node
|
||||||
success bool
|
success bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,7 +187,7 @@ func (tab *Table) getNode(id enode.ID) *enode.Node {
|
||||||
b := tab.bucket(id)
|
b := tab.bucket(id)
|
||||||
for _, e := range b.entries {
|
for _, e := range b.entries {
|
||||||
if e.ID() == id {
|
if e.ID() == id {
|
||||||
return unwrapNode(e)
|
return e.Node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -202,7 +203,7 @@ func (tab *Table) close() {
|
||||||
// are used to connect to the network if the table is empty and there
|
// are used to connect to the network if the table is empty and there
|
||||||
// are no known nodes in the database.
|
// are no known nodes in the database.
|
||||||
func (tab *Table) setFallbackNodes(nodes []*enode.Node) error {
|
func (tab *Table) setFallbackNodes(nodes []*enode.Node) error {
|
||||||
nursery := make([]*node, 0, len(nodes))
|
nursery := make([]*enode.Node, 0, len(nodes))
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
if err := n.ValidateComplete(); err != nil {
|
if err := n.ValidateComplete(); err != nil {
|
||||||
return fmt.Errorf("bad bootstrap node %q: %v", n, err)
|
return fmt.Errorf("bad bootstrap node %q: %v", n, err)
|
||||||
|
|
@ -211,7 +212,7 @@ func (tab *Table) setFallbackNodes(nodes []*enode.Node) error {
|
||||||
tab.log.Error("Bootstrap node filtered by netrestrict", "id", n.ID(), "ip", n.IP())
|
tab.log.Error("Bootstrap node filtered by netrestrict", "id", n.ID(), "ip", n.IP())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
nursery = append(nursery, wrapNode(n))
|
nursery = append(nursery, n)
|
||||||
}
|
}
|
||||||
tab.nursery = nursery
|
tab.nursery = nursery
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -255,9 +256,9 @@ func (tab *Table) findnodeByID(target enode.ID, nresults int, preferLive bool) *
|
||||||
liveNodes := &nodesByDistance{target: target}
|
liveNodes := &nodesByDistance{target: target}
|
||||||
for _, b := range &tab.buckets {
|
for _, b := range &tab.buckets {
|
||||||
for _, n := range b.entries {
|
for _, n := range b.entries {
|
||||||
nodes.push(n, nresults)
|
nodes.push(n.Node, nresults)
|
||||||
if preferLive && n.isValidatedLive {
|
if preferLive && n.isValidatedLive {
|
||||||
liveNodes.push(n, nresults)
|
liveNodes.push(n.Node, nresults)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -309,8 +310,8 @@ func (tab *Table) len() (n int) {
|
||||||
// list.
|
// list.
|
||||||
//
|
//
|
||||||
// The caller must not hold tab.mutex.
|
// The caller must not hold tab.mutex.
|
||||||
func (tab *Table) addFoundNode(n *node) bool {
|
func (tab *Table) addFoundNode(n *enode.Node, forceSetLive bool) bool {
|
||||||
op := addNodeOp{node: n, isInbound: false}
|
op := addNodeOp{node: n, isInbound: false, forceSetLive: forceSetLive}
|
||||||
select {
|
select {
|
||||||
case tab.addNodeCh <- op:
|
case tab.addNodeCh <- op:
|
||||||
return <-tab.addNodeHandled
|
return <-tab.addNodeHandled
|
||||||
|
|
@ -327,7 +328,7 @@ func (tab *Table) addFoundNode(n *node) bool {
|
||||||
// repeatedly.
|
// repeatedly.
|
||||||
//
|
//
|
||||||
// The caller must not hold tab.mutex.
|
// The caller must not hold tab.mutex.
|
||||||
func (tab *Table) addInboundNode(n *node) bool {
|
func (tab *Table) addInboundNode(n *enode.Node) bool {
|
||||||
op := addNodeOp{node: n, isInbound: true}
|
op := addNodeOp{node: n, isInbound: true}
|
||||||
select {
|
select {
|
||||||
case tab.addNodeCh <- op:
|
case tab.addNodeCh <- op:
|
||||||
|
|
@ -337,7 +338,7 @@ func (tab *Table) addInboundNode(n *node) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tab *Table) trackRequest(n *node, success bool, foundNodes []*node) {
|
func (tab *Table) trackRequest(n *enode.Node, success bool, foundNodes []*enode.Node) {
|
||||||
op := trackRequestOp{n, foundNodes, success}
|
op := trackRequestOp{n, foundNodes, success}
|
||||||
select {
|
select {
|
||||||
case tab.trackRequestCh <- op:
|
case tab.trackRequestCh <- op:
|
||||||
|
|
@ -443,13 +444,14 @@ func (tab *Table) doRefresh(done chan struct{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tab *Table) loadSeedNodes() {
|
func (tab *Table) loadSeedNodes() {
|
||||||
seeds := wrapNodes(tab.db.QuerySeeds(seedCount, seedMaxAge))
|
seeds := tab.db.QuerySeeds(seedCount, seedMaxAge)
|
||||||
seeds = append(seeds, tab.nursery...)
|
seeds = append(seeds, tab.nursery...)
|
||||||
for i := range seeds {
|
for i := range seeds {
|
||||||
seed := seeds[i]
|
seed := seeds[i]
|
||||||
if tab.log.Enabled(context.Background(), log.LevelTrace) {
|
if tab.log.Enabled(context.Background(), log.LevelTrace) {
|
||||||
age := time.Since(tab.db.LastPongReceived(seed.ID(), seed.IP()))
|
age := time.Since(tab.db.LastPongReceived(seed.ID(), seed.IP()))
|
||||||
tab.log.Trace("Found seed node in database", "id", seed.ID(), "addr", seed.addr(), "age", age)
|
addr, _ := seed.UDPEndpoint()
|
||||||
|
tab.log.Trace("Found seed node in database", "id", seed.ID(), "addr", addr, "age", age)
|
||||||
}
|
}
|
||||||
tab.handleAddNode(addNodeOp{node: seed, isInbound: false})
|
tab.handleAddNode(addNodeOp{node: seed, isInbound: false})
|
||||||
}
|
}
|
||||||
|
|
@ -513,7 +515,7 @@ func (tab *Table) handleAddNode(req addNodeOp) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
b := tab.bucket(req.node.ID())
|
b := tab.bucket(req.node.ID())
|
||||||
n, _ := tab.bumpInBucket(b, req.node.Node, req.isInbound)
|
n, _ := tab.bumpInBucket(b, req.node, req.isInbound)
|
||||||
if n != nil {
|
if n != nil {
|
||||||
// Already in bucket.
|
// Already in bucket.
|
||||||
return false
|
return false
|
||||||
|
|
@ -529,15 +531,20 @@ func (tab *Table) handleAddNode(req addNodeOp) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to bucket.
|
// Add to bucket.
|
||||||
b.entries = append(b.entries, req.node)
|
wn := &node{Node: req.node}
|
||||||
b.replacements = deleteNode(b.replacements, req.node)
|
if req.forceSetLive {
|
||||||
tab.nodeAdded(b, req.node)
|
wn.livenessChecks = 1
|
||||||
|
wn.isValidatedLive = true
|
||||||
|
}
|
||||||
|
b.entries = append(b.entries, wn)
|
||||||
|
b.replacements = deleteNode(b.replacements, wn.ID())
|
||||||
|
tab.nodeAdded(b, wn)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// addReplacement adds n to the replacement cache of bucket b.
|
// addReplacement adds n to the replacement cache of bucket b.
|
||||||
func (tab *Table) addReplacement(b *bucket, n *node) {
|
func (tab *Table) addReplacement(b *bucket, n *enode.Node) {
|
||||||
if contains(b.replacements, n.ID()) {
|
if containsID(b.replacements, n.ID()) {
|
||||||
// TODO: update ENR
|
// TODO: update ENR
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -545,9 +552,9 @@ func (tab *Table) addReplacement(b *bucket, n *node) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
n.addedToTable = time.Now()
|
wn := &node{Node: n, addedToTable: time.Now()}
|
||||||
var removed *node
|
var removed *node
|
||||||
b.replacements, removed = pushNode(b.replacements, n, maxReplacements)
|
b.replacements, removed = pushNode(b.replacements, wn, maxReplacements)
|
||||||
if removed != nil {
|
if removed != nil {
|
||||||
tab.removeIP(b, removed.IP())
|
tab.removeIP(b, removed.IP())
|
||||||
}
|
}
|
||||||
|
|
@ -672,11 +679,15 @@ func (tab *Table) handleTrackRequest(op trackRequestOp) {
|
||||||
|
|
||||||
// Add found nodes.
|
// Add found nodes.
|
||||||
for _, n := range op.foundNodes {
|
for _, n := range op.foundNodes {
|
||||||
tab.handleAddNode(addNodeOp{n, false})
|
tab.handleAddNode(addNodeOp{n, false, false})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func contains(ns []*node, id enode.ID) bool {
|
type nodeType interface {
|
||||||
|
ID() enode.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func containsID[N nodeType](ns []N, id enode.ID) bool {
|
||||||
for _, n := range ns {
|
for _, n := range ns {
|
||||||
if n.ID() == id {
|
if n.ID() == id {
|
||||||
return true
|
return true
|
||||||
|
|
@ -697,23 +708,20 @@ func pushNode(list []*node, n *node, max int) ([]*node, *node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteNode removes n from list.
|
// deleteNode removes n from list.
|
||||||
func deleteNode(list []*node, n *node) []*node {
|
func deleteNode[N nodeType](list []N, id enode.ID) []N {
|
||||||
for i := range list {
|
return slices.DeleteFunc(list, func(n N) bool {
|
||||||
if list[i].ID() == n.ID() {
|
return n.ID() == id
|
||||||
return append(list[:i], list[i+1:]...)
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
return list
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// nodesByDistance is a list of nodes, ordered by distance to target.
|
// nodesByDistance is a list of nodes, ordered by distance to target.
|
||||||
type nodesByDistance struct {
|
type nodesByDistance struct {
|
||||||
entries []*node
|
entries []*enode.Node
|
||||||
target enode.ID
|
target enode.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// push adds the given node to the list, keeping the total size below maxElems.
|
// push adds the given node to the list, keeping the total size below maxElems.
|
||||||
func (h *nodesByDistance) push(n *node, maxElems int) {
|
func (h *nodesByDistance) push(n *enode.Node, maxElems int) {
|
||||||
ix := sort.Search(len(h.entries), func(i int) bool {
|
ix := sort.Search(len(h.entries), func(i int) bool {
|
||||||
return enode.DistCmp(h.target, h.entries[i].ID(), n.ID()) > 0
|
return enode.DistCmp(h.target, h.entries[i].ID(), n.ID()) > 0
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
"testing/quick"
|
"testing/quick"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -64,7 +65,7 @@ func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding
|
||||||
|
|
||||||
// Fill up the sender's bucket.
|
// Fill up the sender's bucket.
|
||||||
replacementNodeKey, _ := crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")
|
replacementNodeKey, _ := crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8")
|
||||||
replacementNode := wrapNode(enode.NewV4(&replacementNodeKey.PublicKey, net.IP{127, 0, 0, 1}, 99, 99))
|
replacementNode := enode.NewV4(&replacementNodeKey.PublicKey, net.IP{127, 0, 0, 1}, 99, 99)
|
||||||
last := fillBucket(tab, replacementNode.ID())
|
last := fillBucket(tab, replacementNode.ID())
|
||||||
tab.mutex.Lock()
|
tab.mutex.Lock()
|
||||||
nodeEvents := newNodeEventRecorder(128)
|
nodeEvents := newNodeEventRecorder(128)
|
||||||
|
|
@ -78,7 +79,7 @@ func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding
|
||||||
transport.dead[replacementNode.ID()] = !newNodeIsResponding
|
transport.dead[replacementNode.ID()] = !newNodeIsResponding
|
||||||
|
|
||||||
// Add replacement node to table.
|
// Add replacement node to table.
|
||||||
tab.addFoundNode(replacementNode)
|
tab.addFoundNode(replacementNode, false)
|
||||||
|
|
||||||
t.Log("last:", last.ID())
|
t.Log("last:", last.ID())
|
||||||
t.Log("replacement:", replacementNode.ID())
|
t.Log("replacement:", replacementNode.ID())
|
||||||
|
|
@ -115,11 +116,11 @@ func testPingReplace(t *testing.T, newNodeIsResponding, lastInBucketIsResponding
|
||||||
if l := len(bucket.entries); l != wantSize {
|
if l := len(bucket.entries); l != wantSize {
|
||||||
t.Errorf("wrong bucket size after revalidation: got %d, want %d", l, wantSize)
|
t.Errorf("wrong bucket size after revalidation: got %d, want %d", l, wantSize)
|
||||||
}
|
}
|
||||||
if ok := contains(bucket.entries, last.ID()); ok != lastInBucketIsResponding {
|
if ok := containsID(bucket.entries, last.ID()); ok != lastInBucketIsResponding {
|
||||||
t.Errorf("revalidated node found: %t, want: %t", ok, lastInBucketIsResponding)
|
t.Errorf("revalidated node found: %t, want: %t", ok, lastInBucketIsResponding)
|
||||||
}
|
}
|
||||||
wantNewEntry := newNodeIsResponding && !lastInBucketIsResponding
|
wantNewEntry := newNodeIsResponding && !lastInBucketIsResponding
|
||||||
if ok := contains(bucket.entries, replacementNode.ID()); ok != wantNewEntry {
|
if ok := containsID(bucket.entries, replacementNode.ID()); ok != wantNewEntry {
|
||||||
t.Errorf("replacement node found: %t, want: %t", ok, wantNewEntry)
|
t.Errorf("replacement node found: %t, want: %t", ok, wantNewEntry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -153,7 +154,7 @@ func TestTable_IPLimit(t *testing.T) {
|
||||||
|
|
||||||
for i := 0; i < tableIPLimit+1; i++ {
|
for i := 0; i < tableIPLimit+1; i++ {
|
||||||
n := nodeAtDistance(tab.self().ID(), i, net.IP{172, 0, 1, byte(i)})
|
n := nodeAtDistance(tab.self().ID(), i, net.IP{172, 0, 1, byte(i)})
|
||||||
tab.addFoundNode(n)
|
tab.addFoundNode(n, false)
|
||||||
}
|
}
|
||||||
if tab.len() > tableIPLimit {
|
if tab.len() > tableIPLimit {
|
||||||
t.Errorf("too many nodes in table")
|
t.Errorf("too many nodes in table")
|
||||||
|
|
@ -171,7 +172,7 @@ func TestTable_BucketIPLimit(t *testing.T) {
|
||||||
d := 3
|
d := 3
|
||||||
for i := 0; i < bucketIPLimit+1; i++ {
|
for i := 0; i < bucketIPLimit+1; i++ {
|
||||||
n := nodeAtDistance(tab.self().ID(), d, net.IP{172, 0, 1, byte(i)})
|
n := nodeAtDistance(tab.self().ID(), d, net.IP{172, 0, 1, byte(i)})
|
||||||
tab.addFoundNode(n)
|
tab.addFoundNode(n, false)
|
||||||
}
|
}
|
||||||
if tab.len() > bucketIPLimit {
|
if tab.len() > bucketIPLimit {
|
||||||
t.Errorf("too many nodes in table")
|
t.Errorf("too many nodes in table")
|
||||||
|
|
@ -232,7 +233,7 @@ func TestTable_findnodeByID(t *testing.T) {
|
||||||
// check that the result nodes have minimum distance to target.
|
// check that the result nodes have minimum distance to target.
|
||||||
for _, b := range tab.buckets {
|
for _, b := range tab.buckets {
|
||||||
for _, n := range b.entries {
|
for _, n := range b.entries {
|
||||||
if contains(result, n.ID()) {
|
if containsID(result, n.ID()) {
|
||||||
continue // don't run the check below for nodes in result
|
continue // don't run the check below for nodes in result
|
||||||
}
|
}
|
||||||
farthestResult := result[len(result)-1].ID()
|
farthestResult := result[len(result)-1].ID()
|
||||||
|
|
@ -255,7 +256,7 @@ func TestTable_findnodeByID(t *testing.T) {
|
||||||
type closeTest struct {
|
type closeTest struct {
|
||||||
Self enode.ID
|
Self enode.ID
|
||||||
Target enode.ID
|
Target enode.ID
|
||||||
All []*node
|
All []*enode.Node
|
||||||
N int
|
N int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -268,8 +269,7 @@ func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value {
|
||||||
for _, id := range gen([]enode.ID{}, rand).([]enode.ID) {
|
for _, id := range gen([]enode.ID{}, rand).([]enode.ID) {
|
||||||
r := new(enr.Record)
|
r := new(enr.Record)
|
||||||
r.Set(enr.IP(genIP(rand)))
|
r.Set(enr.IP(genIP(rand)))
|
||||||
n := wrapNode(enode.SignNull(r, id))
|
n := enode.SignNull(r, id)
|
||||||
n.livenessChecks = 1
|
|
||||||
t.All = append(t.All, n)
|
t.All = append(t.All, n)
|
||||||
}
|
}
|
||||||
return reflect.ValueOf(t)
|
return reflect.ValueOf(t)
|
||||||
|
|
@ -284,16 +284,16 @@ func TestTable_addInboundNode(t *testing.T) {
|
||||||
// Insert two nodes.
|
// Insert two nodes.
|
||||||
n1 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 1})
|
n1 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 1})
|
||||||
n2 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 2})
|
n2 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 2})
|
||||||
tab.addFoundNode(n1)
|
tab.addFoundNode(n1, false)
|
||||||
tab.addFoundNode(n2)
|
tab.addFoundNode(n2, false)
|
||||||
checkBucketContent(t, tab, []*enode.Node{n1.Node, n2.Node})
|
checkBucketContent(t, tab, []*enode.Node{n1, n2})
|
||||||
|
|
||||||
// Add a changed version of n2. The bucket should be updated.
|
// Add a changed version of n2. The bucket should be updated.
|
||||||
newrec := n2.Record()
|
newrec := n2.Record()
|
||||||
newrec.Set(enr.IP{99, 99, 99, 99})
|
newrec.Set(enr.IP{99, 99, 99, 99})
|
||||||
n2v2 := enode.SignNull(newrec, n2.ID())
|
n2v2 := enode.SignNull(newrec, n2.ID())
|
||||||
tab.addInboundNode(wrapNode(n2v2))
|
tab.addInboundNode(n2v2)
|
||||||
checkBucketContent(t, tab, []*enode.Node{n1.Node, n2v2})
|
checkBucketContent(t, tab, []*enode.Node{n1, n2v2})
|
||||||
|
|
||||||
// Try updating n2 without sequence number change. The update is accepted
|
// Try updating n2 without sequence number change. The update is accepted
|
||||||
// because it's inbound.
|
// because it's inbound.
|
||||||
|
|
@ -301,8 +301,8 @@ func TestTable_addInboundNode(t *testing.T) {
|
||||||
newrec.Set(enr.IP{100, 100, 100, 100})
|
newrec.Set(enr.IP{100, 100, 100, 100})
|
||||||
newrec.SetSeq(n2.Seq())
|
newrec.SetSeq(n2.Seq())
|
||||||
n2v3 := enode.SignNull(newrec, n2.ID())
|
n2v3 := enode.SignNull(newrec, n2.ID())
|
||||||
tab.addInboundNode(wrapNode(n2v3))
|
tab.addInboundNode(n2v3)
|
||||||
checkBucketContent(t, tab, []*enode.Node{n1.Node, n2v3})
|
checkBucketContent(t, tab, []*enode.Node{n1, n2v3})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTable_addFoundNode(t *testing.T) {
|
func TestTable_addFoundNode(t *testing.T) {
|
||||||
|
|
@ -314,16 +314,16 @@ func TestTable_addFoundNode(t *testing.T) {
|
||||||
// Insert two nodes.
|
// Insert two nodes.
|
||||||
n1 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 1})
|
n1 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 1})
|
||||||
n2 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 2})
|
n2 := nodeAtDistance(tab.self().ID(), 256, net.IP{88, 77, 66, 2})
|
||||||
tab.addFoundNode(n1)
|
tab.addFoundNode(n1, false)
|
||||||
tab.addFoundNode(n2)
|
tab.addFoundNode(n2, false)
|
||||||
checkBucketContent(t, tab, []*enode.Node{n1.Node, n2.Node})
|
checkBucketContent(t, tab, []*enode.Node{n1, n2})
|
||||||
|
|
||||||
// Add a changed version of n2. The bucket should be updated.
|
// Add a changed version of n2. The bucket should be updated.
|
||||||
newrec := n2.Record()
|
newrec := n2.Record()
|
||||||
newrec.Set(enr.IP{99, 99, 99, 99})
|
newrec.Set(enr.IP{99, 99, 99, 99})
|
||||||
n2v2 := enode.SignNull(newrec, n2.ID())
|
n2v2 := enode.SignNull(newrec, n2.ID())
|
||||||
tab.addFoundNode(wrapNode(n2v2))
|
tab.addFoundNode(n2v2, false)
|
||||||
checkBucketContent(t, tab, []*enode.Node{n1.Node, n2v2})
|
checkBucketContent(t, tab, []*enode.Node{n1, n2v2})
|
||||||
|
|
||||||
// Try updating n2 without a sequence number change.
|
// Try updating n2 without a sequence number change.
|
||||||
// The update should not be accepted.
|
// The update should not be accepted.
|
||||||
|
|
@ -331,8 +331,8 @@ func TestTable_addFoundNode(t *testing.T) {
|
||||||
newrec.Set(enr.IP{100, 100, 100, 100})
|
newrec.Set(enr.IP{100, 100, 100, 100})
|
||||||
newrec.SetSeq(n2.Seq())
|
newrec.SetSeq(n2.Seq())
|
||||||
n2v3 := enode.SignNull(newrec, n2.ID())
|
n2v3 := enode.SignNull(newrec, n2.ID())
|
||||||
tab.addFoundNode(wrapNode(n2v3))
|
tab.addFoundNode(n2v3, false)
|
||||||
checkBucketContent(t, tab, []*enode.Node{n1.Node, n2v2})
|
checkBucketContent(t, tab, []*enode.Node{n1, n2v2})
|
||||||
}
|
}
|
||||||
|
|
||||||
// This test checks that discv4 nodes can update their own endpoint via PING.
|
// This test checks that discv4 nodes can update their own endpoint via PING.
|
||||||
|
|
@ -345,13 +345,13 @@ func TestTable_addInboundNodeUpdateV4Accept(t *testing.T) {
|
||||||
// Add a v4 node.
|
// Add a v4 node.
|
||||||
key, _ := crypto.HexToECDSA("dd3757a8075e88d0f2b1431e7d3c5b1562e1c0aab9643707e8cbfcc8dae5cfe3")
|
key, _ := crypto.HexToECDSA("dd3757a8075e88d0f2b1431e7d3c5b1562e1c0aab9643707e8cbfcc8dae5cfe3")
|
||||||
n1 := enode.NewV4(&key.PublicKey, net.IP{88, 77, 66, 1}, 9000, 9000)
|
n1 := enode.NewV4(&key.PublicKey, net.IP{88, 77, 66, 1}, 9000, 9000)
|
||||||
tab.addInboundNode(wrapNode(n1))
|
tab.addInboundNode(n1)
|
||||||
checkBucketContent(t, tab, []*enode.Node{n1})
|
checkBucketContent(t, tab, []*enode.Node{n1})
|
||||||
|
|
||||||
// Add an updated version with changed IP.
|
// Add an updated version with changed IP.
|
||||||
// The update will be accepted because it is inbound.
|
// The update will be accepted because it is inbound.
|
||||||
n1v2 := enode.NewV4(&key.PublicKey, net.IP{99, 99, 99, 99}, 9000, 9000)
|
n1v2 := enode.NewV4(&key.PublicKey, net.IP{99, 99, 99, 99}, 9000, 9000)
|
||||||
tab.addInboundNode(wrapNode(n1v2))
|
tab.addInboundNode(n1v2)
|
||||||
checkBucketContent(t, tab, []*enode.Node{n1v2})
|
checkBucketContent(t, tab, []*enode.Node{n1v2})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -366,13 +366,13 @@ func TestTable_addFoundNodeV4UpdateReject(t *testing.T) {
|
||||||
// Add a v4 node.
|
// Add a v4 node.
|
||||||
key, _ := crypto.HexToECDSA("dd3757a8075e88d0f2b1431e7d3c5b1562e1c0aab9643707e8cbfcc8dae5cfe3")
|
key, _ := crypto.HexToECDSA("dd3757a8075e88d0f2b1431e7d3c5b1562e1c0aab9643707e8cbfcc8dae5cfe3")
|
||||||
n1 := enode.NewV4(&key.PublicKey, net.IP{88, 77, 66, 1}, 9000, 9000)
|
n1 := enode.NewV4(&key.PublicKey, net.IP{88, 77, 66, 1}, 9000, 9000)
|
||||||
tab.addFoundNode(wrapNode(n1))
|
tab.addFoundNode(n1, false)
|
||||||
checkBucketContent(t, tab, []*enode.Node{n1})
|
checkBucketContent(t, tab, []*enode.Node{n1})
|
||||||
|
|
||||||
// Add an updated version with changed IP.
|
// Add an updated version with changed IP.
|
||||||
// The update won't be accepted because it isn't inbound.
|
// The update won't be accepted because it isn't inbound.
|
||||||
n1v2 := enode.NewV4(&key.PublicKey, net.IP{99, 99, 99, 99}, 9000, 9000)
|
n1v2 := enode.NewV4(&key.PublicKey, net.IP{99, 99, 99, 99}, 9000, 9000)
|
||||||
tab.addFoundNode(wrapNode(n1v2))
|
tab.addFoundNode(n1v2, false)
|
||||||
checkBucketContent(t, tab, []*enode.Node{n1})
|
checkBucketContent(t, tab, []*enode.Node{n1})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -413,8 +413,8 @@ func TestTable_revalidateSyncRecord(t *testing.T) {
|
||||||
var r enr.Record
|
var r enr.Record
|
||||||
r.Set(enr.IP(net.IP{127, 0, 0, 1}))
|
r.Set(enr.IP(net.IP{127, 0, 0, 1}))
|
||||||
id := enode.ID{1}
|
id := enode.ID{1}
|
||||||
n1 := wrapNode(enode.SignNull(&r, id))
|
n1 := enode.SignNull(&r, id)
|
||||||
tab.addFoundNode(n1)
|
tab.addFoundNode(n1, false)
|
||||||
|
|
||||||
// Update the node record.
|
// Update the node record.
|
||||||
r.Set(enr.WithEntry("foo", "bar"))
|
r.Set(enr.WithEntry("foo", "bar"))
|
||||||
|
|
@ -437,7 +437,7 @@ func TestNodesPush(t *testing.T) {
|
||||||
n1 := nodeAtDistance(target, 255, intIP(1))
|
n1 := nodeAtDistance(target, 255, intIP(1))
|
||||||
n2 := nodeAtDistance(target, 254, intIP(2))
|
n2 := nodeAtDistance(target, 254, intIP(2))
|
||||||
n3 := nodeAtDistance(target, 253, intIP(3))
|
n3 := nodeAtDistance(target, 253, intIP(3))
|
||||||
perm := [][]*node{
|
perm := [][]*enode.Node{
|
||||||
{n3, n2, n1},
|
{n3, n2, n1},
|
||||||
{n3, n1, n2},
|
{n3, n1, n2},
|
||||||
{n2, n3, n1},
|
{n2, n3, n1},
|
||||||
|
|
@ -452,7 +452,7 @@ func TestNodesPush(t *testing.T) {
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
list.push(n, 3)
|
list.push(n, 3)
|
||||||
}
|
}
|
||||||
if !slicesEqual(list.entries, perm[0], nodeIDEqual) {
|
if !slices.EqualFunc(list.entries, perm[0], nodeIDEqual) {
|
||||||
t.Fatal("not equal")
|
t.Fatal("not equal")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -463,13 +463,13 @@ func TestNodesPush(t *testing.T) {
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
list.push(n, 2)
|
list.push(n, 2)
|
||||||
}
|
}
|
||||||
if !slicesEqual(list.entries, perm[0][:2], nodeIDEqual) {
|
if !slices.EqualFunc(list.entries, perm[0][:2], nodeIDEqual) {
|
||||||
t.Fatal("not equal")
|
t.Fatal("not equal")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func nodeIDEqual(n1, n2 *node) bool {
|
func nodeIDEqual[N nodeType](n1, n2 N) bool {
|
||||||
return n1.ID() == n2.ID()
|
return n1.ID() == n2.ID()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,18 +56,18 @@ func newInactiveTestTable(t transport, cfg Config) (*Table, *enode.DB) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// nodeAtDistance creates a node for which enode.LogDist(base, n.id) == ld.
|
// nodeAtDistance creates a node for which enode.LogDist(base, n.id) == ld.
|
||||||
func nodeAtDistance(base enode.ID, ld int, ip net.IP) *node {
|
func nodeAtDistance(base enode.ID, ld int, ip net.IP) *enode.Node {
|
||||||
var r enr.Record
|
var r enr.Record
|
||||||
r.Set(enr.IP(ip))
|
r.Set(enr.IP(ip))
|
||||||
r.Set(enr.UDP(30303))
|
r.Set(enr.UDP(30303))
|
||||||
return wrapNode(enode.SignNull(&r, idAtDistance(base, ld)))
|
return enode.SignNull(&r, idAtDistance(base, ld))
|
||||||
}
|
}
|
||||||
|
|
||||||
// nodesAtDistance creates n nodes for which enode.LogDist(base, node.ID()) == ld.
|
// nodesAtDistance creates n nodes for which enode.LogDist(base, node.ID()) == ld.
|
||||||
func nodesAtDistance(base enode.ID, ld int, n int) []*enode.Node {
|
func nodesAtDistance(base enode.ID, ld int, n int) []*enode.Node {
|
||||||
results := make([]*enode.Node, n)
|
results := make([]*enode.Node, n)
|
||||||
for i := range results {
|
for i := range results {
|
||||||
results[i] = unwrapNode(nodeAtDistance(base, ld, intIP(i)))
|
results[i] = nodeAtDistance(base, ld, intIP(i))
|
||||||
}
|
}
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +110,7 @@ func fillBucket(tab *Table, id enode.ID) (last *node) {
|
||||||
b := tab.bucket(id)
|
b := tab.bucket(id)
|
||||||
for len(b.entries) < bucketSize {
|
for len(b.entries) < bucketSize {
|
||||||
node := nodeAtDistance(tab.self().ID(), ld, intIP(ld))
|
node := nodeAtDistance(tab.self().ID(), ld, intIP(ld))
|
||||||
if !tab.addFoundNode(node) {
|
if !tab.addFoundNode(node, false) {
|
||||||
panic("node not added")
|
panic("node not added")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -119,13 +119,11 @@ func fillBucket(tab *Table, id enode.ID) (last *node) {
|
||||||
|
|
||||||
// fillTable adds nodes the table to the end of their corresponding bucket
|
// fillTable adds nodes the table to the end of their corresponding bucket
|
||||||
// if the bucket is not full. The caller must not hold tab.mutex.
|
// if the bucket is not full. The caller must not hold tab.mutex.
|
||||||
func fillTable(tab *Table, nodes []*node, setLive bool) {
|
func fillTable(tab *Table, nodes []*enode.Node, setLive bool) {
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
if setLive {
|
if setLive {
|
||||||
n.livenessChecks = 1
|
|
||||||
n.isValidatedLive = true
|
|
||||||
}
|
}
|
||||||
tab.addFoundNode(n)
|
tab.addFoundNode(n, setLive)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,7 +217,7 @@ func (t *pingRecorder) RequestENR(n *enode.Node) (*enode.Node, error) {
|
||||||
return t.records[n.ID()], nil
|
return t.records[n.ID()], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasDuplicates(slice []*node) bool {
|
func hasDuplicates(slice []*enode.Node) bool {
|
||||||
seen := make(map[enode.ID]bool, len(slice))
|
seen := make(map[enode.ID]bool, len(slice))
|
||||||
for i, e := range slice {
|
for i, e := range slice {
|
||||||
if e == nil {
|
if e == nil {
|
||||||
|
|
@ -261,14 +259,14 @@ func nodeEqual(n1 *enode.Node, n2 *enode.Node) bool {
|
||||||
return n1.ID() == n2.ID() && n1.IP().Equal(n2.IP())
|
return n1.ID() == n2.ID() && n1.IP().Equal(n2.IP())
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortByID(nodes []*enode.Node) {
|
func sortByID[N nodeType](nodes []N) {
|
||||||
slices.SortFunc(nodes, func(a, b *enode.Node) int {
|
slices.SortFunc(nodes, func(a, b N) int {
|
||||||
return bytes.Compare(a.ID().Bytes(), b.ID().Bytes())
|
return bytes.Compare(a.ID().Bytes(), b.ID().Bytes())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortedByDistanceTo(distbase enode.ID, slice []*node) bool {
|
func sortedByDistanceTo(distbase enode.ID, slice []*enode.Node) bool {
|
||||||
return slices.IsSortedFunc(slice, func(a, b *node) int {
|
return slices.IsSortedFunc(slice, func(a, b *enode.Node) int {
|
||||||
return enode.DistCmp(distbase, a.ID(), b.ID())
|
return enode.DistCmp(distbase, a.ID(), b.ID())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ package discover
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net/netip"
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -40,7 +40,7 @@ func TestUDPv4_Lookup(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seed table with initial node.
|
// Seed table with initial node.
|
||||||
fillTable(test.table, []*node{wrapNode(lookupTestnet.node(256, 0))}, true)
|
fillTable(test.table, []*enode.Node{lookupTestnet.node(256, 0)}, true)
|
||||||
|
|
||||||
// Start the lookup.
|
// Start the lookup.
|
||||||
resultC := make(chan []*enode.Node, 1)
|
resultC := make(chan []*enode.Node, 1)
|
||||||
|
|
@ -70,9 +70,9 @@ func TestUDPv4_LookupIterator(t *testing.T) {
|
||||||
defer test.close()
|
defer test.close()
|
||||||
|
|
||||||
// Seed table with initial nodes.
|
// Seed table with initial nodes.
|
||||||
bootnodes := make([]*node, len(lookupTestnet.dists[256]))
|
bootnodes := make([]*enode.Node, len(lookupTestnet.dists[256]))
|
||||||
for i := range lookupTestnet.dists[256] {
|
for i := range lookupTestnet.dists[256] {
|
||||||
bootnodes[i] = wrapNode(lookupTestnet.node(256, i))
|
bootnodes[i] = lookupTestnet.node(256, i)
|
||||||
}
|
}
|
||||||
fillTable(test.table, bootnodes, true)
|
fillTable(test.table, bootnodes, true)
|
||||||
go serveTestnet(test, lookupTestnet)
|
go serveTestnet(test, lookupTestnet)
|
||||||
|
|
@ -105,9 +105,9 @@ func TestUDPv4_LookupIteratorClose(t *testing.T) {
|
||||||
defer test.close()
|
defer test.close()
|
||||||
|
|
||||||
// Seed table with initial nodes.
|
// Seed table with initial nodes.
|
||||||
bootnodes := make([]*node, len(lookupTestnet.dists[256]))
|
bootnodes := make([]*enode.Node, len(lookupTestnet.dists[256]))
|
||||||
for i := range lookupTestnet.dists[256] {
|
for i := range lookupTestnet.dists[256] {
|
||||||
bootnodes[i] = wrapNode(lookupTestnet.node(256, i))
|
bootnodes[i] = lookupTestnet.node(256, i)
|
||||||
}
|
}
|
||||||
fillTable(test.table, bootnodes, true)
|
fillTable(test.table, bootnodes, true)
|
||||||
go serveTestnet(test, lookupTestnet)
|
go serveTestnet(test, lookupTestnet)
|
||||||
|
|
@ -136,7 +136,7 @@ func TestUDPv4_LookupIteratorClose(t *testing.T) {
|
||||||
|
|
||||||
func serveTestnet(test *udpTest, testnet *preminedTestnet) {
|
func serveTestnet(test *udpTest, testnet *preminedTestnet) {
|
||||||
for done := false; !done; {
|
for done := false; !done; {
|
||||||
done = test.waitPacketOut(func(p v4wire.Packet, to *net.UDPAddr, hash []byte) {
|
done = test.waitPacketOut(func(p v4wire.Packet, to netip.AddrPort, hash []byte) {
|
||||||
n, key := testnet.nodeByAddr(to)
|
n, key := testnet.nodeByAddr(to)
|
||||||
switch p.(type) {
|
switch p.(type) {
|
||||||
case *v4wire.Ping:
|
case *v4wire.Ping:
|
||||||
|
|
@ -158,10 +158,10 @@ func checkLookupResults(t *testing.T, tn *preminedTestnet, results []*enode.Node
|
||||||
for _, e := range results {
|
for _, e := range results {
|
||||||
t.Logf(" ld=%d, %x", enode.LogDist(tn.target.id(), e.ID()), e.ID().Bytes())
|
t.Logf(" ld=%d, %x", enode.LogDist(tn.target.id(), e.ID()), e.ID().Bytes())
|
||||||
}
|
}
|
||||||
if hasDuplicates(wrapNodes(results)) {
|
if hasDuplicates(results) {
|
||||||
t.Errorf("result set contains duplicate entries")
|
t.Errorf("result set contains duplicate entries")
|
||||||
}
|
}
|
||||||
if !sortedByDistanceTo(tn.target.id(), wrapNodes(results)) {
|
if !sortedByDistanceTo(tn.target.id(), results) {
|
||||||
t.Errorf("result set not sorted by distance to target")
|
t.Errorf("result set not sorted by distance to target")
|
||||||
}
|
}
|
||||||
wantNodes := tn.closest(len(results))
|
wantNodes := tn.closest(len(results))
|
||||||
|
|
@ -264,9 +264,10 @@ func (tn *preminedTestnet) node(dist, index int) *enode.Node {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tn *preminedTestnet) nodeByAddr(addr *net.UDPAddr) (*enode.Node, *ecdsa.PrivateKey) {
|
func (tn *preminedTestnet) nodeByAddr(addr netip.AddrPort) (*enode.Node, *ecdsa.PrivateKey) {
|
||||||
dist := int(addr.IP[1])<<8 + int(addr.IP[2])
|
ip := addr.Addr().As4()
|
||||||
index := int(addr.IP[3])
|
dist := int(ip[1])<<8 + int(ip[2])
|
||||||
|
index := int(ip[3])
|
||||||
key := tn.dists[dist][index]
|
key := tn.dists[dist][index]
|
||||||
return tn.node(dist, index), key
|
return tn.node(dist, index), key
|
||||||
}
|
}
|
||||||
|
|
@ -274,7 +275,7 @@ func (tn *preminedTestnet) nodeByAddr(addr *net.UDPAddr) (*enode.Node, *ecdsa.Pr
|
||||||
func (tn *preminedTestnet) nodesAtDistance(dist int) []v4wire.Node {
|
func (tn *preminedTestnet) nodesAtDistance(dist int) []v4wire.Node {
|
||||||
result := make([]v4wire.Node, len(tn.dists[dist]))
|
result := make([]v4wire.Node, len(tn.dists[dist]))
|
||||||
for i := range result {
|
for i := range result {
|
||||||
result[i] = nodeToRPC(wrapNode(tn.node(dist, i)))
|
result[i] = nodeToRPC(tn.node(dist, i))
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -45,6 +46,7 @@ var (
|
||||||
errClockWarp = errors.New("reply deadline too far in the future")
|
errClockWarp = errors.New("reply deadline too far in the future")
|
||||||
errClosed = errors.New("socket closed")
|
errClosed = errors.New("socket closed")
|
||||||
errLowPort = errors.New("low port")
|
errLowPort = errors.New("low port")
|
||||||
|
errNoUDPEndpoint = errors.New("node has no UDP endpoint")
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -290,20 +292,25 @@ func (t *UDPv4) newRandomLookup(ctx context.Context) *lookup {
|
||||||
func (t *UDPv4) newLookup(ctx context.Context, targetKey encPubkey) *lookup {
|
func (t *UDPv4) newLookup(ctx context.Context, targetKey encPubkey) *lookup {
|
||||||
target := enode.ID(crypto.Keccak256Hash(targetKey[:]))
|
target := enode.ID(crypto.Keccak256Hash(targetKey[:]))
|
||||||
ekey := v4wire.Pubkey(targetKey)
|
ekey := v4wire.Pubkey(targetKey)
|
||||||
it := newLookup(ctx, t.tab, target, func(n *node) ([]*node, error) {
|
it := newLookup(ctx, t.tab, target, func(n *enode.Node) ([]*enode.Node, error) {
|
||||||
return t.findnode(n.ID(), n.addr(), ekey)
|
addr, ok := n.UDPEndpoint()
|
||||||
|
if !ok {
|
||||||
|
return nil, errNoUDPEndpoint
|
||||||
|
}
|
||||||
|
return t.findnode(n.ID(), addr, ekey)
|
||||||
})
|
})
|
||||||
return it
|
return it
|
||||||
}
|
}
|
||||||
|
|
||||||
// findnode sends a findnode request to the given node and waits until
|
// findnode sends a findnode request to the given node and waits until
|
||||||
// the node has sent up to k neighbors.
|
// the node has sent up to k neighbors.
|
||||||
func (t *UDPv4) findnode(toid enode.ID, toaddr *net.UDPAddr, target v4wire.Pubkey) ([]*node, error) {
|
func (t *UDPv4) findnode(toid enode.ID, toAddrPort netip.AddrPort, target v4wire.Pubkey) ([]*enode.Node, error) {
|
||||||
|
toaddr := &net.UDPAddr{IP: toAddrPort.Addr().AsSlice(), Port: int(toAddrPort.Port())}
|
||||||
t.ensureBond(toid, toaddr)
|
t.ensureBond(toid, toaddr)
|
||||||
|
|
||||||
// Add a matcher for 'neighbours' replies to the pending reply queue. The matcher is
|
// Add a matcher for 'neighbours' replies to the pending reply queue. The matcher is
|
||||||
// active until enough nodes have been received.
|
// active until enough nodes have been received.
|
||||||
nodes := make([]*node, 0, bucketSize)
|
nodes := make([]*enode.Node, 0, bucketSize)
|
||||||
nreceived := 0
|
nreceived := 0
|
||||||
rm := t.pending(toid, toaddr.IP, v4wire.NeighborsPacket, func(r v4wire.Packet) (matched bool, requestDone bool) {
|
rm := t.pending(toid, toaddr.IP, v4wire.NeighborsPacket, func(r v4wire.Packet) (matched bool, requestDone bool) {
|
||||||
reply := r.(*v4wire.Neighbors)
|
reply := r.(*v4wire.Neighbors)
|
||||||
|
|
@ -579,7 +586,7 @@ func (t *UDPv4) ensureBond(toid enode.ID, toaddr *net.UDPAddr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *UDPv4) nodeFromRPC(sender *net.UDPAddr, rn v4wire.Node) (*node, error) {
|
func (t *UDPv4) nodeFromRPC(sender *net.UDPAddr, rn v4wire.Node) (*enode.Node, error) {
|
||||||
if rn.UDP <= 1024 {
|
if rn.UDP <= 1024 {
|
||||||
return nil, errLowPort
|
return nil, errLowPort
|
||||||
}
|
}
|
||||||
|
|
@ -593,12 +600,12 @@ func (t *UDPv4) nodeFromRPC(sender *net.UDPAddr, rn v4wire.Node) (*node, error)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
n := wrapNode(enode.NewV4(key, rn.IP, int(rn.TCP), int(rn.UDP)))
|
n := enode.NewV4(key, rn.IP, int(rn.TCP), int(rn.UDP))
|
||||||
err = n.ValidateComplete()
|
err = n.ValidateComplete()
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func nodeToRPC(n *node) v4wire.Node {
|
func nodeToRPC(n *enode.Node) v4wire.Node {
|
||||||
var key ecdsa.PublicKey
|
var key ecdsa.PublicKey
|
||||||
var ekey v4wire.Pubkey
|
var ekey v4wire.Pubkey
|
||||||
if err := n.Load((*enode.Secp256k1)(&key)); err == nil {
|
if err := n.Load((*enode.Secp256k1)(&key)); err == nil {
|
||||||
|
|
@ -670,7 +677,7 @@ func (t *UDPv4) handlePing(h *packetHandlerV4, from *net.UDPAddr, fromID enode.I
|
||||||
})
|
})
|
||||||
|
|
||||||
// Ping back if our last pong on file is too far in the past.
|
// Ping back if our last pong on file is too far in the past.
|
||||||
n := wrapNode(enode.NewV4(h.senderKey, from.IP, int(req.From.TCP), from.Port))
|
n := enode.NewV4(h.senderKey, from.IP, int(req.From.TCP), from.Port)
|
||||||
if time.Since(t.db.LastPongReceived(n.ID(), from.IP)) > bondExpiration {
|
if time.Since(t.db.LastPongReceived(n.ID(), from.IP)) > bondExpiration {
|
||||||
t.sendPing(fromID, from, func() {
|
t.sendPing(fromID, from, func() {
|
||||||
t.tab.addInboundNode(n)
|
t.tab.addInboundNode(n)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -55,7 +56,7 @@ type udpTest struct {
|
||||||
udp *UDPv4
|
udp *UDPv4
|
||||||
sent [][]byte
|
sent [][]byte
|
||||||
localkey, remotekey *ecdsa.PrivateKey
|
localkey, remotekey *ecdsa.PrivateKey
|
||||||
remoteaddr *net.UDPAddr
|
remoteaddr netip.AddrPort
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUDPTest(t *testing.T) *udpTest {
|
func newUDPTest(t *testing.T) *udpTest {
|
||||||
|
|
@ -64,7 +65,7 @@ func newUDPTest(t *testing.T) *udpTest {
|
||||||
pipe: newpipe(),
|
pipe: newpipe(),
|
||||||
localkey: newkey(),
|
localkey: newkey(),
|
||||||
remotekey: newkey(),
|
remotekey: newkey(),
|
||||||
remoteaddr: &net.UDPAddr{IP: net.IP{10, 0, 1, 99}, Port: 30303},
|
remoteaddr: netip.AddrPortFrom(netip.MustParseAddr("10.0.1.99"), 30303),
|
||||||
}
|
}
|
||||||
|
|
||||||
test.db, _ = enode.OpenDB("")
|
test.db, _ = enode.OpenDB("")
|
||||||
|
|
@ -92,7 +93,7 @@ func (test *udpTest) packetIn(wantError error, data v4wire.Packet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// handles a packet as if it had been sent to the transport by the key/endpoint.
|
// handles a packet as if it had been sent to the transport by the key/endpoint.
|
||||||
func (test *udpTest) packetInFrom(wantError error, key *ecdsa.PrivateKey, addr *net.UDPAddr, data v4wire.Packet) {
|
func (test *udpTest) packetInFrom(wantError error, key *ecdsa.PrivateKey, addr netip.AddrPort, data v4wire.Packet) {
|
||||||
test.t.Helper()
|
test.t.Helper()
|
||||||
|
|
||||||
enc, _, err := v4wire.Encode(key, data)
|
enc, _, err := v4wire.Encode(key, data)
|
||||||
|
|
@ -100,7 +101,8 @@ func (test *udpTest) packetInFrom(wantError error, key *ecdsa.PrivateKey, addr *
|
||||||
test.t.Errorf("%s encode error: %v", data.Name(), err)
|
test.t.Errorf("%s encode error: %v", data.Name(), err)
|
||||||
}
|
}
|
||||||
test.sent = append(test.sent, enc)
|
test.sent = append(test.sent, enc)
|
||||||
if err = test.udp.handlePacket(addr, enc); err != wantError {
|
udpaddr := &net.UDPAddr{IP: addr.Addr().AsSlice(), Port: int(addr.Port())}
|
||||||
|
if err = test.udp.handlePacket(udpaddr, enc); err != wantError {
|
||||||
test.t.Errorf("error mismatch: got %q, want %q", err, wantError)
|
test.t.Errorf("error mismatch: got %q, want %q", err, wantError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -236,7 +238,7 @@ func TestUDPv4_findnodeTimeout(t *testing.T) {
|
||||||
test := newUDPTest(t)
|
test := newUDPTest(t)
|
||||||
defer test.close()
|
defer test.close()
|
||||||
|
|
||||||
toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222}
|
toaddr := netip.AddrPortFrom(netip.MustParseAddr("1.2.3.4"), 2222)
|
||||||
toid := enode.ID{1, 2, 3, 4}
|
toid := enode.ID{1, 2, 3, 4}
|
||||||
target := v4wire.Pubkey{4, 5, 6, 7}
|
target := v4wire.Pubkey{4, 5, 6, 7}
|
||||||
result, err := test.udp.findnode(toid, toaddr, target)
|
result, err := test.udp.findnode(toid, toaddr, target)
|
||||||
|
|
@ -261,25 +263,24 @@ func TestUDPv4_findnode(t *testing.T) {
|
||||||
for i := 0; i < numCandidates; i++ {
|
for i := 0; i < numCandidates; i++ {
|
||||||
key := newkey()
|
key := newkey()
|
||||||
ip := net.IP{10, 13, 0, byte(i)}
|
ip := net.IP{10, 13, 0, byte(i)}
|
||||||
n := wrapNode(enode.NewV4(&key.PublicKey, ip, 0, 2000))
|
n := enode.NewV4(&key.PublicKey, ip, 0, 2000)
|
||||||
// Ensure half of table content isn't verified live yet.
|
// Ensure half of table content isn't verified live yet.
|
||||||
if i > numCandidates/2 {
|
if i > numCandidates/2 {
|
||||||
n.isValidatedLive = true
|
|
||||||
live[n.ID()] = true
|
live[n.ID()] = true
|
||||||
}
|
}
|
||||||
|
test.table.addFoundNode(n, live[n.ID()])
|
||||||
nodes.push(n, numCandidates)
|
nodes.push(n, numCandidates)
|
||||||
}
|
}
|
||||||
fillTable(test.table, nodes.entries, false)
|
|
||||||
|
|
||||||
// ensure there's a bond with the test node,
|
// ensure there's a bond with the test node,
|
||||||
// findnode won't be accepted otherwise.
|
// findnode won't be accepted otherwise.
|
||||||
remoteID := v4wire.EncodePubkey(&test.remotekey.PublicKey).ID()
|
remoteID := v4wire.EncodePubkey(&test.remotekey.PublicKey).ID()
|
||||||
test.table.db.UpdateLastPongReceived(remoteID, test.remoteaddr.IP, time.Now())
|
test.table.db.UpdateLastPongReceived(remoteID, test.remoteaddr.Addr().AsSlice(), time.Now())
|
||||||
|
|
||||||
// check that closest neighbors are returned.
|
// check that closest neighbors are returned.
|
||||||
expected := test.table.findnodeByID(testTarget.ID(), bucketSize, true)
|
expected := test.table.findnodeByID(testTarget.ID(), bucketSize, true)
|
||||||
test.packetIn(nil, &v4wire.Findnode{Target: testTarget, Expiration: futureExp})
|
test.packetIn(nil, &v4wire.Findnode{Target: testTarget, Expiration: futureExp})
|
||||||
waitNeighbors := func(want []*node) {
|
waitNeighbors := func(want []*enode.Node) {
|
||||||
test.waitPacketOut(func(p *v4wire.Neighbors, to *net.UDPAddr, hash []byte) {
|
test.waitPacketOut(func(p *v4wire.Neighbors, to *net.UDPAddr, hash []byte) {
|
||||||
if len(p.Nodes) != len(want) {
|
if len(p.Nodes) != len(want) {
|
||||||
t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), len(want))
|
t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), len(want))
|
||||||
|
|
@ -309,10 +310,10 @@ func TestUDPv4_findnodeMultiReply(t *testing.T) {
|
||||||
defer test.close()
|
defer test.close()
|
||||||
|
|
||||||
rid := enode.PubkeyToIDV4(&test.remotekey.PublicKey)
|
rid := enode.PubkeyToIDV4(&test.remotekey.PublicKey)
|
||||||
test.table.db.UpdateLastPingReceived(rid, test.remoteaddr.IP, time.Now())
|
test.table.db.UpdateLastPingReceived(rid, test.remoteaddr.Addr().AsSlice(), time.Now())
|
||||||
|
|
||||||
// queue a pending findnode request
|
// queue a pending findnode request
|
||||||
resultc, errc := make(chan []*node, 1), make(chan error, 1)
|
resultc, errc := make(chan []*enode.Node, 1), make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
rid := encodePubkey(&test.remotekey.PublicKey).id()
|
rid := encodePubkey(&test.remotekey.PublicKey).id()
|
||||||
ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget)
|
ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget)
|
||||||
|
|
@ -332,11 +333,11 @@ func TestUDPv4_findnodeMultiReply(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// send the reply as two packets.
|
// send the reply as two packets.
|
||||||
list := []*node{
|
list := []*enode.Node{
|
||||||
wrapNode(enode.MustParse("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304")),
|
enode.MustParse("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304"),
|
||||||
wrapNode(enode.MustParse("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303")),
|
enode.MustParse("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303"),
|
||||||
wrapNode(enode.MustParse("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17")),
|
enode.MustParse("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17"),
|
||||||
wrapNode(enode.MustParse("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303")),
|
enode.MustParse("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303"),
|
||||||
}
|
}
|
||||||
rpclist := make([]v4wire.Node, len(list))
|
rpclist := make([]v4wire.Node, len(list))
|
||||||
for i := range list {
|
for i := range list {
|
||||||
|
|
@ -381,8 +382,8 @@ func TestUDPv4_pingMatchIP(t *testing.T) {
|
||||||
test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp})
|
test.packetIn(nil, &v4wire.Ping{From: testRemote, To: testLocalAnnounced, Version: 4, Expiration: futureExp})
|
||||||
test.waitPacketOut(func(*v4wire.Pong, *net.UDPAddr, []byte) {})
|
test.waitPacketOut(func(*v4wire.Pong, *net.UDPAddr, []byte) {})
|
||||||
|
|
||||||
test.waitPacketOut(func(p *v4wire.Ping, to *net.UDPAddr, hash []byte) {
|
test.waitPacketOut(func(p *v4wire.Ping, to netip.AddrPort, hash []byte) {
|
||||||
wrongAddr := &net.UDPAddr{IP: net.IP{33, 44, 1, 2}, Port: 30000}
|
wrongAddr := netip.MustParseAddrPort("33.44.1.2:30000")
|
||||||
test.packetInFrom(errUnsolicitedReply, test.remotekey, wrongAddr, &v4wire.Pong{
|
test.packetInFrom(errUnsolicitedReply, test.remotekey, wrongAddr, &v4wire.Pong{
|
||||||
ReplyTok: hash,
|
ReplyTok: hash,
|
||||||
To: testLocalAnnounced,
|
To: testLocalAnnounced,
|
||||||
|
|
@ -408,7 +409,8 @@ func TestUDPv4_successfulPing(t *testing.T) {
|
||||||
}
|
}
|
||||||
wantTo := v4wire.Endpoint{
|
wantTo := v4wire.Endpoint{
|
||||||
// The mirrored UDP address is the UDP packet sender
|
// The mirrored UDP address is the UDP packet sender
|
||||||
IP: test.remoteaddr.IP, UDP: uint16(test.remoteaddr.Port),
|
IP: test.remoteaddr.Addr().AsSlice(),
|
||||||
|
UDP: test.remoteaddr.Port(),
|
||||||
// The mirrored TCP port is the one from the ping packet
|
// The mirrored TCP port is the one from the ping packet
|
||||||
TCP: testRemote.TCP,
|
TCP: testRemote.TCP,
|
||||||
}
|
}
|
||||||
|
|
@ -424,8 +426,8 @@ func TestUDPv4_successfulPing(t *testing.T) {
|
||||||
}
|
}
|
||||||
wantTo := v4wire.Endpoint{
|
wantTo := v4wire.Endpoint{
|
||||||
// The mirrored UDP address is the UDP packet sender.
|
// The mirrored UDP address is the UDP packet sender.
|
||||||
IP: test.remoteaddr.IP,
|
IP: test.remoteaddr.Addr().AsSlice(),
|
||||||
UDP: uint16(test.remoteaddr.Port),
|
UDP: test.remoteaddr.Port(),
|
||||||
TCP: 0,
|
TCP: 0,
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(p.To, wantTo) {
|
if !reflect.DeepEqual(p.To, wantTo) {
|
||||||
|
|
@ -442,10 +444,10 @@ func TestUDPv4_successfulPing(t *testing.T) {
|
||||||
if n.ID() != rid {
|
if n.ID() != rid {
|
||||||
t.Errorf("node has wrong ID: got %v, want %v", n.ID(), rid)
|
t.Errorf("node has wrong ID: got %v, want %v", n.ID(), rid)
|
||||||
}
|
}
|
||||||
if !n.IP().Equal(test.remoteaddr.IP) {
|
if !n.IP().Equal(test.remoteaddr.Addr().AsSlice()) {
|
||||||
t.Errorf("node has wrong IP: got %v, want: %v", n.IP(), test.remoteaddr.IP)
|
t.Errorf("node has wrong IP: got %v, want: %v", n.IP(), test.remoteaddr.Addr())
|
||||||
}
|
}
|
||||||
if n.UDP() != test.remoteaddr.Port {
|
if n.UDP() != int(test.remoteaddr.Port()) {
|
||||||
t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP(), test.remoteaddr.Port)
|
t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP(), test.remoteaddr.Port)
|
||||||
}
|
}
|
||||||
if n.TCP() != int(testRemote.TCP) {
|
if n.TCP() != int(testRemote.TCP) {
|
||||||
|
|
@ -584,7 +586,7 @@ type dgramPipe struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type dgram struct {
|
type dgram struct {
|
||||||
to net.UDPAddr
|
to netip.AddrPort
|
||||||
data []byte
|
data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -606,7 +608,12 @@ func (c *dgramPipe) WriteToUDP(b []byte, to *net.UDPAddr) (n int, err error) {
|
||||||
if c.closed {
|
if c.closed {
|
||||||
return 0, errors.New("closed")
|
return 0, errors.New("closed")
|
||||||
}
|
}
|
||||||
c.queue = append(c.queue, dgram{*to, b})
|
addr, ok := netip.AddrFromSlice(to.IP)
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Errorf("invalid destination IP addr %v", to.IP))
|
||||||
|
}
|
||||||
|
addrPort := netip.AddrPortFrom(addr, uint16(to.Port))
|
||||||
|
c.queue = append(c.queue, dgram{addrPort, b})
|
||||||
c.cond.Signal()
|
c.cond.Signal()
|
||||||
return len(b), nil
|
return len(b), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -233,7 +233,7 @@ func (t *UDPv5) AllNodes() []*enode.Node {
|
||||||
|
|
||||||
for _, b := range &t.tab.buckets {
|
for _, b := range &t.tab.buckets {
|
||||||
for _, n := range b.entries {
|
for _, n := range b.entries {
|
||||||
nodes = append(nodes, unwrapNode(n))
|
nodes = append(nodes, n.Node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nodes
|
return nodes
|
||||||
|
|
@ -314,26 +314,26 @@ func (t *UDPv5) newRandomLookup(ctx context.Context) *lookup {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *UDPv5) newLookup(ctx context.Context, target enode.ID) *lookup {
|
func (t *UDPv5) newLookup(ctx context.Context, target enode.ID) *lookup {
|
||||||
return newLookup(ctx, t.tab, target, func(n *node) ([]*node, error) {
|
return newLookup(ctx, t.tab, target, func(n *enode.Node) ([]*enode.Node, error) {
|
||||||
return t.lookupWorker(n, target)
|
return t.lookupWorker(n, target)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookupWorker performs FINDNODE calls against a single node during lookup.
|
// lookupWorker performs FINDNODE calls against a single node during lookup.
|
||||||
func (t *UDPv5) lookupWorker(destNode *node, target enode.ID) ([]*node, error) {
|
func (t *UDPv5) lookupWorker(destNode *enode.Node, target enode.ID) ([]*enode.Node, error) {
|
||||||
var (
|
var (
|
||||||
dists = lookupDistances(target, destNode.ID())
|
dists = lookupDistances(target, destNode.ID())
|
||||||
nodes = nodesByDistance{target: target}
|
nodes = nodesByDistance{target: target}
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
var r []*enode.Node
|
var r []*enode.Node
|
||||||
r, err = t.findnode(unwrapNode(destNode), dists)
|
r, err = t.findnode(destNode, dists)
|
||||||
if errors.Is(err, errClosed) {
|
if errors.Is(err, errClosed) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, n := range r {
|
for _, n := range r {
|
||||||
if n.ID() != t.Self().ID() {
|
if n.ID() != t.Self().ID() {
|
||||||
nodes.push(wrapNode(n), findnodeResultLimit)
|
nodes.push(n, findnodeResultLimit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nodes.entries, err
|
return nodes.entries, err
|
||||||
|
|
@ -699,7 +699,7 @@ func (t *UDPv5) handlePacket(rawpacket []byte, fromAddr *net.UDPAddr) error {
|
||||||
}
|
}
|
||||||
if fromNode != nil {
|
if fromNode != nil {
|
||||||
// Handshake succeeded, add to table.
|
// Handshake succeeded, add to table.
|
||||||
t.tab.addInboundNode(wrapNode(fromNode))
|
t.tab.addInboundNode(fromNode)
|
||||||
}
|
}
|
||||||
if packet.Kind() != v5wire.WhoareyouPacket {
|
if packet.Kind() != v5wire.WhoareyouPacket {
|
||||||
// WHOAREYOU logged separately to report errors.
|
// WHOAREYOU logged separately to report errors.
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"reflect"
|
"reflect"
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -141,7 +142,7 @@ func TestUDPv5_unknownPacket(t *testing.T) {
|
||||||
|
|
||||||
// Make node known.
|
// Make node known.
|
||||||
n := test.getNode(test.remotekey, test.remoteaddr).Node()
|
n := test.getNode(test.remotekey, test.remoteaddr).Node()
|
||||||
test.table.addFoundNode(wrapNode(n))
|
test.table.addFoundNode(n, false)
|
||||||
|
|
||||||
test.packetIn(&v5wire.Unknown{Nonce: nonce})
|
test.packetIn(&v5wire.Unknown{Nonce: nonce})
|
||||||
test.waitPacketOut(func(p *v5wire.Whoareyou, addr *net.UDPAddr, _ v5wire.Nonce) {
|
test.waitPacketOut(func(p *v5wire.Whoareyou, addr *net.UDPAddr, _ v5wire.Nonce) {
|
||||||
|
|
@ -159,9 +160,9 @@ func TestUDPv5_findnodeHandling(t *testing.T) {
|
||||||
nodes253 := nodesAtDistance(test.table.self().ID(), 253, 16)
|
nodes253 := nodesAtDistance(test.table.self().ID(), 253, 16)
|
||||||
nodes249 := nodesAtDistance(test.table.self().ID(), 249, 4)
|
nodes249 := nodesAtDistance(test.table.self().ID(), 249, 4)
|
||||||
nodes248 := nodesAtDistance(test.table.self().ID(), 248, 10)
|
nodes248 := nodesAtDistance(test.table.self().ID(), 248, 10)
|
||||||
fillTable(test.table, wrapNodes(nodes253), true)
|
fillTable(test.table, nodes253, true)
|
||||||
fillTable(test.table, wrapNodes(nodes249), true)
|
fillTable(test.table, nodes249, true)
|
||||||
fillTable(test.table, wrapNodes(nodes248), true)
|
fillTable(test.table, nodes248, true)
|
||||||
|
|
||||||
// Requesting with distance zero should return the node's own record.
|
// Requesting with distance zero should return the node's own record.
|
||||||
test.packetIn(&v5wire.Findnode{ReqID: []byte{0}, Distances: []uint{0}})
|
test.packetIn(&v5wire.Findnode{ReqID: []byte{0}, Distances: []uint{0}})
|
||||||
|
|
@ -589,7 +590,7 @@ func TestUDPv5_lookup(t *testing.T) {
|
||||||
|
|
||||||
// Seed table with initial node.
|
// Seed table with initial node.
|
||||||
initialNode := lookupTestnet.node(256, 0)
|
initialNode := lookupTestnet.node(256, 0)
|
||||||
fillTable(test.table, []*node{wrapNode(initialNode)}, true)
|
fillTable(test.table, []*enode.Node{initialNode}, true)
|
||||||
|
|
||||||
// Start the lookup.
|
// Start the lookup.
|
||||||
resultC := make(chan []*enode.Node, 1)
|
resultC := make(chan []*enode.Node, 1)
|
||||||
|
|
@ -601,7 +602,7 @@ func TestUDPv5_lookup(t *testing.T) {
|
||||||
// Answer lookup packets.
|
// Answer lookup packets.
|
||||||
asked := make(map[enode.ID]bool)
|
asked := make(map[enode.ID]bool)
|
||||||
for done := false; !done; {
|
for done := false; !done; {
|
||||||
done = test.waitPacketOut(func(p v5wire.Packet, to *net.UDPAddr, _ v5wire.Nonce) {
|
done = test.waitPacketOut(func(p v5wire.Packet, to netip.AddrPort, _ v5wire.Nonce) {
|
||||||
recipient, key := lookupTestnet.nodeByAddr(to)
|
recipient, key := lookupTestnet.nodeByAddr(to)
|
||||||
switch p := p.(type) {
|
switch p := p.(type) {
|
||||||
case *v5wire.Ping:
|
case *v5wire.Ping:
|
||||||
|
|
@ -688,9 +689,9 @@ type udpV5Test struct {
|
||||||
db *enode.DB
|
db *enode.DB
|
||||||
udp *UDPv5
|
udp *UDPv5
|
||||||
localkey, remotekey *ecdsa.PrivateKey
|
localkey, remotekey *ecdsa.PrivateKey
|
||||||
remoteaddr *net.UDPAddr
|
remoteaddr netip.AddrPort
|
||||||
nodesByID map[enode.ID]*enode.LocalNode
|
nodesByID map[enode.ID]*enode.LocalNode
|
||||||
nodesByIP map[string]*enode.LocalNode
|
nodesByIP map[netip.Addr]*enode.LocalNode
|
||||||
}
|
}
|
||||||
|
|
||||||
// testCodec is the packet encoding used by protocol tests. This codec does not perform encryption.
|
// testCodec is the packet encoding used by protocol tests. This codec does not perform encryption.
|
||||||
|
|
@ -778,7 +779,7 @@ func (test *udpV5Test) packetIn(packet v5wire.Packet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// handles a packet as if it had been sent to the transport by the key/endpoint.
|
// handles a packet as if it had been sent to the transport by the key/endpoint.
|
||||||
func (test *udpV5Test) packetInFrom(key *ecdsa.PrivateKey, addr *net.UDPAddr, packet v5wire.Packet) {
|
func (test *udpV5Test) packetInFrom(key *ecdsa.PrivateKey, addr netip.AddrPort, packet v5wire.Packet) {
|
||||||
test.t.Helper()
|
test.t.Helper()
|
||||||
|
|
||||||
ln := test.getNode(key, addr)
|
ln := test.getNode(key, addr)
|
||||||
|
|
@ -793,17 +794,17 @@ func (test *udpV5Test) packetInFrom(key *ecdsa.PrivateKey, addr *net.UDPAddr, pa
|
||||||
}
|
}
|
||||||
|
|
||||||
// getNode ensures the test knows about a node at the given endpoint.
|
// getNode ensures the test knows about a node at the given endpoint.
|
||||||
func (test *udpV5Test) getNode(key *ecdsa.PrivateKey, addr *net.UDPAddr) *enode.LocalNode {
|
func (test *udpV5Test) getNode(key *ecdsa.PrivateKey, addr netip.AddrPort) *enode.LocalNode {
|
||||||
id := encodePubkey(&key.PublicKey).id()
|
id := encodePubkey(&key.PublicKey).id()
|
||||||
ln := test.nodesByID[id]
|
ln := test.nodesByID[id]
|
||||||
if ln == nil {
|
if ln == nil {
|
||||||
db, _ := enode.OpenDB("")
|
db, _ := enode.OpenDB("")
|
||||||
ln = enode.NewLocalNode(db, key)
|
ln = enode.NewLocalNode(db, key)
|
||||||
ln.SetStaticIP(addr.IP)
|
ln.SetStaticIP(addr.Addr().AsSlice())
|
||||||
ln.Set(enr.UDP(addr.Port))
|
ln.Set(enr.UDP(addr.Port()))
|
||||||
test.nodesByID[id] = ln
|
test.nodesByID[id] = ln
|
||||||
}
|
}
|
||||||
test.nodesByIP[string(addr.IP)] = ln
|
test.nodesByIP[string(addr.Addr().String())] = ln
|
||||||
return ln
|
return ln
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue