github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/p2p/discv5/table_test.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 package discv5 19 20 import ( 21 "crypto/ecdsa" 22 "fmt" 23 "math/rand" 24 25 "net" 26 "reflect" 27 "testing" 28 "testing/quick" 29 "time" 30 31 "github.com/AigarNetwork/aigar/common" 32 "github.com/AigarNetwork/aigar/crypto" 33 ) 34 35 type nullTransport struct{} 36 37 func (nullTransport) sendPing(remote *Node, remoteAddr *net.UDPAddr) []byte { return []byte{1} } 38 func (nullTransport) sendPong(remote *Node, pingHash []byte) {} 39 func (nullTransport) sendFindnode(remote *Node, target NodeID) {} 40 func (nullTransport) sendNeighbours(remote *Node, nodes []*Node) {} 41 func (nullTransport) localAddr() *net.UDPAddr { return new(net.UDPAddr) } 42 func (nullTransport) Close() {} 43 44 // func TestTable_pingReplace(t *testing.T) { 45 // doit := func(newNodeIsResponding, lastInBucketIsResponding bool) { 46 // transport := newPingRecorder() 47 // tab, _ := newTable(transport, NodeID{}, &net.UDPAddr{}) 48 // defer tab.Close() 49 // pingSender := NewNode(MustHexID("a502af0f59b2aab7746995408c79e9ca312d2793cc997e44fc55eda62f0150bbb8c59a6f9269ba3a081518b62699ee807c7c19c20125ddfccca872608af9e370"), net.IP{}, 99, 99) 50 // 51 // // fill up the sender's bucket. 52 // last := fillBucket(tab, 253) 53 // 54 // // this call to bond should replace the last node 55 // // in its bucket if the node is not responding. 56 // transport.responding[last.ID] = lastInBucketIsResponding 57 // transport.responding[pingSender.ID] = newNodeIsResponding 58 // tab.bond(true, pingSender.ID, &net.UDPAddr{}, 0) 59 // 60 // // first ping goes to sender (bonding pingback) 61 // if !transport.pinged[pingSender.ID] { 62 // t.Error("table did not ping back sender") 63 // } 64 // if newNodeIsResponding { 65 // // second ping goes to oldest node in bucket 66 // // to see whether it is still alive. 67 // if !transport.pinged[last.ID] { 68 // t.Error("table did not ping last node in bucket") 69 // } 70 // } 71 // 72 // tab.mutex.Lock() 73 // defer tab.mutex.Unlock() 74 // if l := len(tab.buckets[253].entries); l != bucketSize { 75 // t.Errorf("wrong bucket size after bond: got %d, want %d", l, bucketSize) 76 // } 77 // 78 // if lastInBucketIsResponding || !newNodeIsResponding { 79 // if !contains(tab.buckets[253].entries, last.ID) { 80 // t.Error("last entry was removed") 81 // } 82 // if contains(tab.buckets[253].entries, pingSender.ID) { 83 // t.Error("new entry was added") 84 // } 85 // } else { 86 // if contains(tab.buckets[253].entries, last.ID) { 87 // t.Error("last entry was not removed") 88 // } 89 // if !contains(tab.buckets[253].entries, pingSender.ID) { 90 // t.Error("new entry was not added") 91 // } 92 // } 93 // } 94 // 95 // doit(true, true) 96 // doit(false, true) 97 // doit(true, false) 98 // doit(false, false) 99 // } 100 101 func TestBucket_bumpNoDuplicates(t *testing.T) { 102 t.Parallel() 103 cfg := &quick.Config{ 104 MaxCount: 1000, 105 Rand: rand.New(rand.NewSource(time.Now().Unix())), 106 Values: func(args []reflect.Value, rand *rand.Rand) { 107 // generate a random list of nodes. this will be the content of the bucket. 108 n := rand.Intn(bucketSize-1) + 1 109 nodes := make([]*Node, n) 110 for i := range nodes { 111 nodes[i] = nodeAtDistance(common.Hash{}, 200) 112 } 113 args[0] = reflect.ValueOf(nodes) 114 // generate random bump positions. 115 bumps := make([]int, rand.Intn(100)) 116 for i := range bumps { 117 bumps[i] = rand.Intn(len(nodes)) 118 } 119 args[1] = reflect.ValueOf(bumps) 120 }, 121 } 122 123 prop := func(nodes []*Node, bumps []int) (ok bool) { 124 b := &bucket{entries: make([]*Node, len(nodes))} 125 copy(b.entries, nodes) 126 for i, pos := range bumps { 127 b.bump(b.entries[pos]) 128 if hasDuplicates(b.entries) { 129 t.Logf("bucket has duplicates after %d/%d bumps:", i+1, len(bumps)) 130 for _, n := range b.entries { 131 t.Logf(" %p", n) 132 } 133 return false 134 } 135 } 136 return true 137 } 138 if err := quick.Check(prop, cfg); err != nil { 139 t.Error(err) 140 } 141 } 142 143 // fillBucket inserts nodes into the given bucket until 144 // it is full. The node's IDs dont correspond to their 145 // hashes. 146 func fillBucket(tab *Table, ld int) (last *Node) { 147 b := tab.buckets[ld] 148 for len(b.entries) < bucketSize { 149 b.entries = append(b.entries, nodeAtDistance(tab.self.sha, ld)) 150 } 151 return b.entries[bucketSize-1] 152 } 153 154 // nodeAtDistance creates a node for which logdist(base, n.sha) == ld. 155 // The node's ID does not correspond to n.sha. 156 func nodeAtDistance(base common.Hash, ld int) (n *Node) { 157 n = new(Node) 158 n.sha = hashAtDistance(base, ld) 159 copy(n.ID[:], n.sha[:]) // ensure the node still has a unique ID 160 return n 161 } 162 163 type pingRecorder struct{ responding, pinged map[NodeID]bool } 164 165 func newPingRecorder() *pingRecorder { 166 return &pingRecorder{make(map[NodeID]bool), make(map[NodeID]bool)} 167 } 168 169 func (t *pingRecorder) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) { 170 panic("findnode called on pingRecorder") 171 } 172 func (t *pingRecorder) close() {} 173 func (t *pingRecorder) waitping(from NodeID) error { 174 return nil // remote always pings 175 } 176 func (t *pingRecorder) ping(toid NodeID, toaddr *net.UDPAddr) error { 177 t.pinged[toid] = true 178 if t.responding[toid] { 179 return nil 180 } else { 181 return errTimeout 182 } 183 } 184 185 func TestTable_closest(t *testing.T) { 186 t.Parallel() 187 188 test := func(test *closeTest) bool { 189 // for any node table, Target and N 190 tab := newTable(test.Self, &net.UDPAddr{}) 191 tab.stuff(test.All) 192 193 // check that doClosest(Target, N) returns nodes 194 result := tab.closest(test.Target, test.N).entries 195 if hasDuplicates(result) { 196 t.Errorf("result contains duplicates") 197 return false 198 } 199 if !sortedByDistanceTo(test.Target, result) { 200 t.Errorf("result is not sorted by distance to target") 201 return false 202 } 203 204 // check that the number of results is min(N, tablen) 205 wantN := test.N 206 if tab.count < test.N { 207 wantN = tab.count 208 } 209 if len(result) != wantN { 210 t.Errorf("wrong number of nodes: got %d, want %d", len(result), wantN) 211 return false 212 } else if len(result) == 0 { 213 return true // no need to check distance 214 } 215 216 // check that the result nodes have minimum distance to target. 217 for _, b := range tab.buckets { 218 for _, n := range b.entries { 219 if contains(result, n.ID) { 220 continue // don't run the check below for nodes in result 221 } 222 farthestResult := result[len(result)-1].sha 223 if distcmp(test.Target, n.sha, farthestResult) < 0 { 224 t.Errorf("table contains node that is closer to target but it's not in result") 225 t.Logf(" Target: %v", test.Target) 226 t.Logf(" Farthest Result: %v", farthestResult) 227 t.Logf(" ID: %v", n.ID) 228 return false 229 } 230 } 231 } 232 return true 233 } 234 if err := quick.Check(test, quickcfg()); err != nil { 235 t.Error(err) 236 } 237 } 238 239 func TestTable_ReadRandomNodesGetAll(t *testing.T) { 240 cfg := &quick.Config{ 241 MaxCount: 200, 242 Rand: rand.New(rand.NewSource(time.Now().Unix())), 243 Values: func(args []reflect.Value, rand *rand.Rand) { 244 args[0] = reflect.ValueOf(make([]*Node, rand.Intn(1000))) 245 }, 246 } 247 test := func(buf []*Node) bool { 248 tab := newTable(NodeID{}, &net.UDPAddr{}) 249 for i := 0; i < len(buf); i++ { 250 ld := cfg.Rand.Intn(len(tab.buckets)) 251 tab.stuff([]*Node{nodeAtDistance(tab.self.sha, ld)}) 252 } 253 gotN := tab.readRandomNodes(buf) 254 if gotN != tab.count { 255 t.Errorf("wrong number of nodes, got %d, want %d", gotN, tab.count) 256 return false 257 } 258 if hasDuplicates(buf[:gotN]) { 259 t.Errorf("result contains duplicates") 260 return false 261 } 262 return true 263 } 264 if err := quick.Check(test, cfg); err != nil { 265 t.Error(err) 266 } 267 } 268 269 type closeTest struct { 270 Self NodeID 271 Target common.Hash 272 All []*Node 273 N int 274 } 275 276 func (*closeTest) Generate(rand *rand.Rand, size int) reflect.Value { 277 t := &closeTest{ 278 Self: gen(NodeID{}, rand).(NodeID), 279 Target: gen(common.Hash{}, rand).(common.Hash), 280 N: rand.Intn(bucketSize), 281 } 282 for _, id := range gen([]NodeID{}, rand).([]NodeID) { 283 t.All = append(t.All, &Node{ID: id}) 284 } 285 return reflect.ValueOf(t) 286 } 287 288 func hasDuplicates(slice []*Node) bool { 289 seen := make(map[NodeID]bool) 290 for i, e := range slice { 291 if e == nil { 292 panic(fmt.Sprintf("nil *Node at %d", i)) 293 } 294 if seen[e.ID] { 295 return true 296 } 297 seen[e.ID] = true 298 } 299 return false 300 } 301 302 func sortedByDistanceTo(distbase common.Hash, slice []*Node) bool { 303 var last common.Hash 304 for i, e := range slice { 305 if i > 0 && distcmp(distbase, e.sha, last) < 0 { 306 return false 307 } 308 last = e.sha 309 } 310 return true 311 } 312 313 func contains(ns []*Node, id NodeID) bool { 314 for _, n := range ns { 315 if n.ID == id { 316 return true 317 } 318 } 319 return false 320 } 321 322 // gen wraps quick.Value so it's easier to use. 323 // it generates a random value of the given value's type. 324 func gen(typ interface{}, rand *rand.Rand) interface{} { 325 v, ok := quick.Value(reflect.TypeOf(typ), rand) 326 if !ok { 327 panic(fmt.Sprintf("couldn't generate random value of type %T", typ)) 328 } 329 return v.Interface() 330 } 331 332 func newkey() *ecdsa.PrivateKey { 333 key, err := crypto.GenerateKey() 334 if err != nil { 335 panic("couldn't generate key: " + err.Error()) 336 } 337 return key 338 }