github.com/core-coin/go-core/v2@v2.1.9/p2p/discover/table_util_test.go (about)

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