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