github.com/core-coin/go-core/v2@v2.1.9/p2p/discv5/table_test.go (about)

     1  // Copyright 2016 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 discv5
    18  
    19  import (
    20  	crand "crypto/rand"
    21  	"fmt"
    22  	"math/rand"
    23  	"net"
    24  	"reflect"
    25  	"testing"
    26  	"testing/quick"
    27  	"time"
    28  
    29  	"github.com/core-coin/go-core/v2/common"
    30  	"github.com/core-coin/go-core/v2/crypto"
    31  )
    32  
    33  func TestBucket_bumpNoDuplicates(t *testing.T) {
    34  	t.Parallel()
    35  	cfg := &quick.Config{
    36  		MaxCount: 1000,
    37  		Rand:     rand.New(rand.NewSource(time.Now().Unix())),
    38  		Values: func(args []reflect.Value, rand *rand.Rand) {
    39  			// generate a random list of nodes. this will be the content of the bucket.
    40  			n := rand.Intn(bucketSize-1) + 1
    41  			nodes := make([]*Node, n)
    42  			for i := range nodes {
    43  				nodes[i] = nodeAtDistance(common.Hash{}, 200)
    44  			}
    45  			args[0] = reflect.ValueOf(nodes)
    46  			// generate random bump positions.
    47  			bumps := make([]int, rand.Intn(100))
    48  			for i := range bumps {
    49  				bumps[i] = rand.Intn(len(nodes))
    50  			}
    51  			args[1] = reflect.ValueOf(bumps)
    52  		},
    53  	}
    54  
    55  	prop := func(nodes []*Node, bumps []int) (ok bool) {
    56  		b := &bucket{entries: make([]*Node, len(nodes))}
    57  		copy(b.entries, nodes)
    58  		for i, pos := range bumps {
    59  			b.bump(b.entries[pos])
    60  			if hasDuplicates(b.entries) {
    61  				t.Logf("bucket has duplicates after %d/%d bumps:", i+1, len(bumps))
    62  				for _, n := range b.entries {
    63  					t.Logf("  %p", n)
    64  				}
    65  				return false
    66  			}
    67  		}
    68  		return true
    69  	}
    70  	if err := quick.Check(prop, cfg); err != nil {
    71  		t.Error(err)
    72  	}
    73  }
    74  
    75  // nodeAtDistance creates a node for which logdist(base, n.sha) == ld.
    76  // The node's ID does not correspond to n.sha.
    77  func nodeAtDistance(base common.Hash, ld int) (n *Node) {
    78  	n = new(Node)
    79  	n.sha = hashAtDistance(base, ld)
    80  	copy(n.ID[:], n.sha[:]) // ensure the node still has a unique ID
    81  	return n
    82  }
    83  
    84  func TestTable_closest(t *testing.T) {
    85  	t.Parallel()
    86  
    87  	test := func(test *closeTest) bool {
    88  		// for any node table, Target and N
    89  		tab := newTable(test.Self, &net.UDPAddr{})
    90  		tab.stuff(test.All)
    91  
    92  		// check that doClosest(Target, N) returns nodes
    93  		result := tab.closest(test.Target, test.N).entries
    94  		if hasDuplicates(result) {
    95  			t.Errorf("result contains duplicates")
    96  			return false
    97  		}
    98  		if !sortedByDistanceTo(test.Target, result) {
    99  			t.Errorf("result is not sorted by distance to target")
   100  			return false
   101  		}
   102  
   103  		// check that the number of results is min(N, tablen)
   104  		wantN := test.N
   105  		if tab.count < test.N {
   106  			wantN = tab.count
   107  		}
   108  		if len(result) != wantN {
   109  			t.Errorf("wrong number of nodes: got %d, want %d", len(result), wantN)
   110  			return false
   111  		} else if len(result) == 0 {
   112  			return true // no need to check distance
   113  		}
   114  
   115  		// check that the result nodes have minimum distance to target.
   116  		for _, b := range tab.buckets {
   117  			for _, n := range b.entries {
   118  				if contains(result, n.ID) {
   119  					continue // don't run the check below for nodes in result
   120  				}
   121  				farthestResult := result[len(result)-1].sha
   122  				if distcmp(test.Target, n.sha, farthestResult) < 0 {
   123  					t.Errorf("table contains node that is closer to target but it's not in result")
   124  					t.Logf("  Target:          %v", test.Target)
   125  					t.Logf("  Farthest Result: %v", farthestResult)
   126  					t.Logf("  ID:              %v", n.ID)
   127  					return false
   128  				}
   129  			}
   130  		}
   131  		return true
   132  	}
   133  	if err := quick.Check(test, quickcfg()); err != nil {
   134  		t.Error(err)
   135  	}
   136  }
   137  
   138  func TestTable_ReadRandomNodesGetAll(t *testing.T) {
   139  	cfg := &quick.Config{
   140  		MaxCount: 200,
   141  		Rand:     rand.New(rand.NewSource(time.Now().Unix())),
   142  		Values: func(args []reflect.Value, rand *rand.Rand) {
   143  			args[0] = reflect.ValueOf(make([]*Node, rand.Intn(1000)))
   144  		},
   145  	}
   146  	test := func(buf []*Node) bool {
   147  		tab := newTable(NodeID{}, &net.UDPAddr{})
   148  		for i := 0; i < len(buf); i++ {
   149  			ld := cfg.Rand.Intn(len(tab.buckets))
   150  			tab.stuff([]*Node{nodeAtDistance(tab.self.sha, ld)})
   151  		}
   152  		gotN := tab.readRandomNodes(buf)
   153  		if gotN != tab.count {
   154  			t.Errorf("wrong number of nodes, got %d, want %d", gotN, tab.count)
   155  			return false
   156  		}
   157  		if hasDuplicates(buf[:gotN]) {
   158  			t.Errorf("result contains duplicates")
   159  			return false
   160  		}
   161  		return true
   162  	}
   163  	if err := quick.Check(test, cfg); err != nil {
   164  		t.Error(err)
   165  	}
   166  }
   167  
   168  type closeTest struct {
   169  	Self   NodeID
   170  	Target common.Hash
   171  	All    []*Node
   172  	N      int
   173  }
   174  
   175  func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value {
   176  	t := &closeTest{
   177  		Self:   gen(NodeID{}, rand).(NodeID),
   178  		Target: gen(common.Hash{}, rand).(common.Hash),
   179  		N:      rand.Intn(bucketSize),
   180  	}
   181  	for _, id := range gen([]NodeID{}, rand).([]NodeID) {
   182  		t.All = append(t.All, &Node{ID: id})
   183  	}
   184  	return reflect.ValueOf(t)
   185  }
   186  
   187  func hasDuplicates(slice []*Node) bool {
   188  	seen := make(map[NodeID]bool)
   189  	for i, e := range slice {
   190  		if e == nil {
   191  			panic(fmt.Sprintf("nil *Node at %d", i))
   192  		}
   193  		if seen[e.ID] {
   194  			return true
   195  		}
   196  		seen[e.ID] = true
   197  	}
   198  	return false
   199  }
   200  
   201  func sortedByDistanceTo(distbase common.Hash, slice []*Node) bool {
   202  	var last common.Hash
   203  	for i, e := range slice {
   204  		if i > 0 && distcmp(distbase, e.sha, last) < 0 {
   205  			return false
   206  		}
   207  		last = e.sha
   208  	}
   209  	return true
   210  }
   211  
   212  func contains(ns []*Node, id NodeID) bool {
   213  	for _, n := range ns {
   214  		if n.ID == id {
   215  			return true
   216  		}
   217  	}
   218  	return false
   219  }
   220  
   221  // gen wraps quick.Value so it's easier to use.
   222  // it generates a random value of the given value's type.
   223  func gen(typ interface{}, rand *rand.Rand) interface{} {
   224  	v, ok := quick.Value(reflect.TypeOf(typ), rand)
   225  	if !ok {
   226  		panic(fmt.Sprintf("couldn't generate random value of type %T", typ))
   227  	}
   228  	return v.Interface()
   229  }
   230  
   231  func newkey() *crypto.PrivateKey {
   232  	key, err := crypto.GenerateKey(crand.Reader)
   233  	if err != nil {
   234  		panic("couldn't generate key: " + err.Error())
   235  	}
   236  	return key
   237  }