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 }