github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/p2p/discover/table_util_test.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package discover
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/ecdsa"
    22  	"encoding/hex"
    23  	"errors"
    24  	"fmt"
    25  	"math/rand"
    26  	"net"
    27  	"slices"
    28  	"sync"
    29  
    30  	"github.com/ethereum/go-ethereum/crypto"
    31  	"github.com/ethereum/go-ethereum/p2p/enode"
    32  	"github.com/ethereum/go-ethereum/p2p/enr"
    33  )
    34  
    35  var nullNode *enode.Node
    36  
    37  func init() {
    38  	var r enr.Record
    39  	r.Set(enr.IP{0, 0, 0, 0})
    40  	nullNode = enode.SignNull(&r, enode.ID{})
    41  }
    42  
    43  func newTestTable(t transport) (*Table, *enode.DB) {
    44  	cfg := Config{}
    45  	db, _ := enode.OpenDB("")
    46  	tab, _ := newTable(t, db, cfg)
    47  	go tab.loop()
    48  	return tab, db
    49  }
    50  
    51  // nodeAtDistance creates a node for which enode.LogDist(base, n.id) == ld.
    52  func nodeAtDistance(base enode.ID, ld int, ip net.IP) *node {
    53  	var r enr.Record
    54  	r.Set(enr.IP(ip))
    55  	r.Set(enr.UDP(30303))
    56  	return wrapNode(enode.SignNull(&r, idAtDistance(base, ld)))
    57  }
    58  
    59  // nodesAtDistance creates n nodes for which enode.LogDist(base, node.ID()) == ld.
    60  func nodesAtDistance(base enode.ID, ld int, n int) []*enode.Node {
    61  	results := make([]*enode.Node, n)
    62  	for i := range results {
    63  		results[i] = unwrapNode(nodeAtDistance(base, ld, intIP(i)))
    64  	}
    65  	return results
    66  }
    67  
    68  func nodesToRecords(nodes []*enode.Node) []*enr.Record {
    69  	records := make([]*enr.Record, len(nodes))
    70  	for i := range nodes {
    71  		records[i] = nodes[i].Record()
    72  	}
    73  	return records
    74  }
    75  
    76  // idAtDistance returns a random hash such that enode.LogDist(a, b) == n
    77  func idAtDistance(a enode.ID, n int) (b enode.ID) {
    78  	if n == 0 {
    79  		return a
    80  	}
    81  	// flip bit at position n, fill the rest with random bits
    82  	b = a
    83  	pos := len(a) - n/8 - 1
    84  	bit := byte(0x01) << (byte(n%8) - 1)
    85  	if bit == 0 {
    86  		pos++
    87  		bit = 0x80
    88  	}
    89  	b[pos] = a[pos]&^bit | ^a[pos]&bit // TODO: randomize end bits
    90  	for i := pos + 1; i < len(a); i++ {
    91  		b[i] = byte(rand.Intn(255))
    92  	}
    93  	return b
    94  }
    95  
    96  func intIP(i int) net.IP {
    97  	return net.IP{byte(i), 0, 2, byte(i)}
    98  }
    99  
   100  // fillBucket inserts nodes into the given bucket until it is full.
   101  func fillBucket(tab *Table, n *node) (last *node) {
   102  	ld := enode.LogDist(tab.self().ID(), n.ID())
   103  	b := tab.bucket(n.ID())
   104  	for len(b.entries) < bucketSize {
   105  		b.entries = append(b.entries, nodeAtDistance(tab.self().ID(), ld, intIP(ld)))
   106  	}
   107  	return b.entries[bucketSize-1]
   108  }
   109  
   110  // fillTable adds nodes the table to the end of their corresponding bucket
   111  // if the bucket is not full. The caller must not hold tab.mutex.
   112  func fillTable(tab *Table, nodes []*node, setLive bool) {
   113  	for _, n := range nodes {
   114  		if setLive {
   115  			n.livenessChecks = 1
   116  		}
   117  		tab.addSeenNode(n)
   118  	}
   119  }
   120  
   121  type pingRecorder struct {
   122  	mu           sync.Mutex
   123  	dead, pinged map[enode.ID]bool
   124  	records      map[enode.ID]*enode.Node
   125  	n            *enode.Node
   126  }
   127  
   128  func newPingRecorder() *pingRecorder {
   129  	var r enr.Record
   130  	r.Set(enr.IP{0, 0, 0, 0})
   131  	n := enode.SignNull(&r, enode.ID{})
   132  
   133  	return &pingRecorder{
   134  		dead:    make(map[enode.ID]bool),
   135  		pinged:  make(map[enode.ID]bool),
   136  		records: make(map[enode.ID]*enode.Node),
   137  		n:       n,
   138  	}
   139  }
   140  
   141  // updateRecord updates a node record. Future calls to ping and
   142  // RequestENR will return this record.
   143  func (t *pingRecorder) updateRecord(n *enode.Node) {
   144  	t.mu.Lock()
   145  	defer t.mu.Unlock()
   146  	t.records[n.ID()] = n
   147  }
   148  
   149  // Stubs to satisfy the transport interface.
   150  func (t *pingRecorder) Self() *enode.Node           { return nullNode }
   151  func (t *pingRecorder) lookupSelf() []*enode.Node   { return nil }
   152  func (t *pingRecorder) lookupRandom() []*enode.Node { return nil }
   153  
   154  // ping simulates a ping request.
   155  func (t *pingRecorder) ping(n *enode.Node) (seq uint64, err error) {
   156  	t.mu.Lock()
   157  	defer t.mu.Unlock()
   158  
   159  	t.pinged[n.ID()] = true
   160  	if t.dead[n.ID()] {
   161  		return 0, errTimeout
   162  	}
   163  	if t.records[n.ID()] != nil {
   164  		seq = t.records[n.ID()].Seq()
   165  	}
   166  	return seq, nil
   167  }
   168  
   169  // RequestENR simulates an ENR request.
   170  func (t *pingRecorder) RequestENR(n *enode.Node) (*enode.Node, error) {
   171  	t.mu.Lock()
   172  	defer t.mu.Unlock()
   173  
   174  	if t.dead[n.ID()] || t.records[n.ID()] == nil {
   175  		return nil, errTimeout
   176  	}
   177  	return t.records[n.ID()], nil
   178  }
   179  
   180  func hasDuplicates(slice []*node) bool {
   181  	seen := make(map[enode.ID]bool, len(slice))
   182  	for i, e := range slice {
   183  		if e == nil {
   184  			panic(fmt.Sprintf("nil *Node at %d", i))
   185  		}
   186  		if seen[e.ID()] {
   187  			return true
   188  		}
   189  		seen[e.ID()] = true
   190  	}
   191  	return false
   192  }
   193  
   194  // checkNodesEqual checks whether the two given node lists contain the same nodes.
   195  func checkNodesEqual(got, want []*enode.Node) error {
   196  	if len(got) == len(want) {
   197  		for i := range got {
   198  			if !nodeEqual(got[i], want[i]) {
   199  				goto NotEqual
   200  			}
   201  		}
   202  	}
   203  	return nil
   204  
   205  NotEqual:
   206  	output := new(bytes.Buffer)
   207  	fmt.Fprintf(output, "got %d nodes:\n", len(got))
   208  	for _, n := range got {
   209  		fmt.Fprintf(output, "  %v %v\n", n.ID(), n)
   210  	}
   211  	fmt.Fprintf(output, "want %d:\n", len(want))
   212  	for _, n := range want {
   213  		fmt.Fprintf(output, "  %v %v\n", n.ID(), n)
   214  	}
   215  	return errors.New(output.String())
   216  }
   217  
   218  func nodeEqual(n1 *enode.Node, n2 *enode.Node) bool {
   219  	return n1.ID() == n2.ID() && n1.IP().Equal(n2.IP())
   220  }
   221  
   222  func sortByID(nodes []*enode.Node) {
   223  	slices.SortFunc(nodes, func(a, b *enode.Node) int {
   224  		return bytes.Compare(a.ID().Bytes(), b.ID().Bytes())
   225  	})
   226  }
   227  
   228  func sortedByDistanceTo(distbase enode.ID, slice []*node) bool {
   229  	return slices.IsSortedFunc(slice, func(a, b *node) int {
   230  		return enode.DistCmp(distbase, a.ID(), b.ID())
   231  	})
   232  }
   233  
   234  // hexEncPrivkey decodes h as a private key.
   235  func hexEncPrivkey(h string) *ecdsa.PrivateKey {
   236  	b, err := hex.DecodeString(h)
   237  	if err != nil {
   238  		panic(err)
   239  	}
   240  	key, err := crypto.ToECDSA(b)
   241  	if err != nil {
   242  		panic(err)
   243  	}
   244  	return key
   245  }
   246  
   247  // hexEncPubkey decodes h as a public key.
   248  func hexEncPubkey(h string) (ret encPubkey) {
   249  	b, err := hex.DecodeString(h)
   250  	if err != nil {
   251  		panic(err)
   252  	}
   253  	if len(b) != len(ret) {
   254  		panic("invalid length")
   255  	}
   256  	copy(ret[:], b)
   257  	return ret
   258  }