github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/swarm/network/kademlia/kademlia_test.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 package kademlia 13 14 import ( 15 "fmt" 16 "math" 17 "math/rand" 18 "os" 19 "path/filepath" 20 "reflect" 21 "testing" 22 "testing/quick" 23 "time" 24 ) 25 26 var ( 27 quickrand = rand.New(rand.NewSource(time.Now().Unix())) 28 quickcfgFindClosest = &quick.Config{MaxCount: 50, Rand: quickrand} 29 quickcfgBootStrap = &quick.Config{MaxCount: 100, Rand: quickrand} 30 ) 31 32 type testNode struct { 33 addr Address 34 } 35 36 func (n *testNode) String() string { 37 return fmt.Sprintf("%x", n.addr[:]) 38 } 39 40 func (n *testNode) Addr() Address { 41 return n.addr 42 } 43 44 func (n *testNode) Drop() { 45 } 46 47 func (n *testNode) Url() string { 48 return "" 49 } 50 51 func (n *testNode) LastActive() time.Time { 52 return time.Now() 53 } 54 55 func TestOn(t *testing.T) { 56 addr, ok1 := gen(Address{}, quickrand).(Address) 57 other, ok2 := gen(Address{}, quickrand).(Address) 58 if !ok1 || !ok2 { 59 t.Errorf("oops") 60 } 61 kad := New(addr, NewDefaultKadParams()) 62 err := kad.On(&testNode{addr: other}, nil) 63 _ = err 64 } 65 66 func TestBootstrap(t *testing.T) { 67 68 test := func(test *bootstrapTest) bool { 69 // for any node kad.le, Target and N 70 params := NewDefaultKadParams() 71 params.MaxProx = test.MaxProx 72 params.BucketSize = test.BucketSize 73 params.ProxBinSize = test.BucketSize 74 kad := New(test.Self, params) 75 var err error 76 77 for p := 0; p < 9; p++ { 78 var nrs []*NodeRecord 79 n := math.Pow(float64(2), float64(7-p)) 80 for i := 0; i < int(n); i++ { 81 addr := RandomAddressAt(test.Self, p) 82 nrs = append(nrs, &NodeRecord{ 83 Addr: addr, 84 }) 85 } 86 kad.Add(nrs) 87 } 88 89 node := &testNode{test.Self} 90 91 n := 0 92 for n < 100 { 93 err = kad.On(node, nil) 94 if err != nil { 95 t.Fatalf("backend not accepting node: %v", err) 96 } 97 98 record, need, _ := kad.Suggest() 99 if !need { 100 break 101 } 102 n++ 103 if record == nil { 104 continue 105 } 106 node = &testNode{record.Addr} 107 } 108 exp := test.BucketSize * (test.MaxProx + 1) 109 if kad.Count() != exp { 110 t.Errorf("incorrect number of peers, expected %d, got %d\n%v", exp, kad.Count(), kad) 111 return false 112 } 113 return true 114 } 115 if err := quick.Check(test, quickcfgBootStrap); err != nil { 116 t.Error(err) 117 } 118 119 } 120 121 func TestFindClosest(t *testing.T) { 122 123 test := func(test *FindClosestTest) bool { 124 // for any node kad.le, Target and N 125 params := NewDefaultKadParams() 126 params.MaxProx = 7 127 kad := New(test.Self, params) 128 var err error 129 for _, node := range test.All { 130 err = kad.On(node, nil) 131 if err != nil && err.Error() != "bucket full" { 132 t.Fatalf("backend not accepting node: %v", err) 133 } 134 } 135 136 if len(test.All) == 0 || test.N == 0 { 137 return true 138 } 139 nodes := kad.FindClosest(test.Target, test.N) 140 141 // check that the number of results is min(N, kad.len) 142 wantN := test.N 143 if tlen := kad.Count(); tlen < test.N { 144 wantN = tlen 145 } 146 147 if len(nodes) != wantN { 148 t.Errorf("wrong number of nodes: got %d, want %d", len(nodes), wantN) 149 return false 150 } 151 152 if hasDuplicates(nodes) { 153 t.Errorf("result contains duplicates") 154 return false 155 } 156 157 if !sortedByDistanceTo(test.Target, nodes) { 158 t.Errorf("result is not sorted by distance to target") 159 return false 160 } 161 162 // check that the result nodes have minimum distance to target. 163 farthestResult := nodes[len(nodes)-1].Addr() 164 for i, b := range kad.buckets { 165 for j, n := range b { 166 if contains(nodes, n.Addr()) { 167 continue // don't run the check below for nodes in result 168 } 169 if test.Target.ProxCmp(n.Addr(), farthestResult) < 0 { 170 _ = i * j 171 t.Errorf("kad.le contains node that is closer to target but it's not in result") 172 return false 173 } 174 } 175 } 176 return true 177 } 178 if err := quick.Check(test, quickcfgFindClosest); err != nil { 179 t.Error(err) 180 } 181 } 182 183 type proxTest struct { 184 add bool 185 index int 186 addr Address 187 } 188 189 var ( 190 addresses []Address 191 ) 192 193 func TestProxAdjust(t *testing.T) { 194 r := rand.New(rand.NewSource(time.Now().UnixNano())) 195 self := gen(Address{}, r).(Address) 196 params := NewDefaultKadParams() 197 params.MaxProx = 7 198 kad := New(self, params) 199 200 var err error 201 for i := 0; i < 100; i++ { 202 a := gen(Address{}, r).(Address) 203 addresses = append(addresses, a) 204 err = kad.On(&testNode{addr: a}, nil) 205 if err != nil && err.Error() != "bucket full" { 206 t.Fatalf("backend not accepting node: %v", err) 207 } 208 if !kad.proxCheck(t) { 209 return 210 } 211 } 212 test := func(test *proxTest) bool { 213 node := &testNode{test.addr} 214 if test.add { 215 kad.On(node, nil) 216 } else { 217 kad.Off(node, nil) 218 } 219 return kad.proxCheck(t) 220 } 221 if err := quick.Check(test, quickcfgFindClosest); err != nil { 222 t.Error(err) 223 } 224 } 225 226 func TestSaveLoad(t *testing.T) { 227 r := rand.New(rand.NewSource(time.Now().UnixNano())) 228 addresses := gen([]Address{}, r).([]Address) 229 self := RandomAddress() 230 params := NewDefaultKadParams() 231 params.MaxProx = 7 232 kad := New(self, params) 233 234 var err error 235 236 for _, a := range addresses { 237 err = kad.On(&testNode{addr: a}, nil) 238 if err != nil && err.Error() != "bucket full" { 239 t.Fatalf("backend not accepting node: %v", err) 240 } 241 } 242 nodes := kad.FindClosest(self, 100) 243 244 path := filepath.Join(os.TempDir(), "bzz-kad-test-save-load.peers") 245 err = kad.Save(path, nil) 246 if err != nil && err.Error() != "bucket full" { 247 t.Fatalf("unepected error saving kaddb: %v", err) 248 } 249 kad = New(self, params) 250 err = kad.Load(path, nil) 251 if err != nil && err.Error() != "bucket full" { 252 t.Fatalf("unepected error loading kaddb: %v", err) 253 } 254 for _, b := range kad.db.Nodes { 255 for _, node := range b { 256 err = kad.On(&testNode{node.Addr}, nil) 257 if err != nil && err.Error() != "bucket full" { 258 t.Fatalf("backend not accepting node: %v", err) 259 } 260 } 261 } 262 loadednodes := kad.FindClosest(self, 100) 263 for i, node := range loadednodes { 264 if nodes[i].Addr() != node.Addr() { 265 t.Errorf("node mismatch at %d/%d: %v != %v", i, len(nodes), nodes[i].Addr(), node.Addr()) 266 } 267 } 268 } 269 270 func (self *Kademlia) proxCheck(t *testing.T) bool { 271 var sum int 272 for i, b := range self.buckets { 273 l := len(b) 274 // if we are in the high prox multibucket 275 if i >= self.proxLimit { 276 sum += l 277 } else if l == 0 { 278 t.Errorf("bucket %d empty, yet proxLimit is %d\n%v", len(b), self.proxLimit, self) 279 return false 280 } 281 } 282 // check if merged high prox bucket does not exceed size 283 if sum > 0 { 284 if sum != self.proxSize { 285 t.Errorf("proxSize incorrect, expected %v, got %v", sum, self.proxSize) 286 return false 287 } 288 last := len(self.buckets[self.proxLimit]) 289 if last > 0 && sum >= self.ProxBinSize+last { 290 t.Errorf("proxLimit %v incorrect, redundant non-empty bucket %d added to proxBin with %v (target %v)\n%v", self.proxLimit, last, sum-last, self.ProxBinSize, self) 291 return false 292 } 293 if self.proxLimit > 0 && sum < self.ProxBinSize { 294 t.Errorf("proxLimit %v incorrect. proxSize %v is less than target %v, yet there is more peers", self.proxLimit, sum, self.ProxBinSize) 295 return false 296 } 297 } 298 return true 299 } 300 301 type bootstrapTest struct { 302 MaxProx int 303 BucketSize int 304 Self Address 305 } 306 307 func (*bootstrapTest) Generate(rand *rand.Rand, size int) reflect.Value { 308 t := &bootstrapTest{ 309 Self: gen(Address{}, rand).(Address), 310 MaxProx: 5 + rand.Intn(2), 311 BucketSize: rand.Intn(3) + 1, 312 } 313 return reflect.ValueOf(t) 314 } 315 316 type FindClosestTest struct { 317 Self Address 318 Target Address 319 All []Node 320 N int 321 } 322 323 func (c FindClosestTest) String() string { 324 return fmt.Sprintf("A: %064x\nT: %064x\n(%d)\n", c.Self[:], c.Target[:], c.N) 325 } 326 327 func (*FindClosestTest) Generate(rand *rand.Rand, size int) reflect.Value { 328 t := &FindClosestTest{ 329 Self: gen(Address{}, rand).(Address), 330 Target: gen(Address{}, rand).(Address), 331 N: rand.Intn(bucketSize), 332 } 333 for _, a := range gen([]Address{}, rand).([]Address) { 334 t.All = append(t.All, &testNode{addr: a}) 335 } 336 return reflect.ValueOf(t) 337 } 338 339 func (*proxTest) Generate(rand *rand.Rand, size int) reflect.Value { 340 var add bool 341 if rand.Intn(1) == 0 { 342 add = true 343 } 344 var t *proxTest 345 if add { 346 t = &proxTest{ 347 addr: gen(Address{}, rand).(Address), 348 add: add, 349 } 350 } else { 351 t = &proxTest{ 352 index: rand.Intn(len(addresses)), 353 add: add, 354 } 355 } 356 return reflect.ValueOf(t) 357 } 358 359 func hasDuplicates(slice []Node) bool { 360 seen := make(map[Address]bool) 361 for _, node := range slice { 362 if seen[node.Addr()] { 363 return true 364 } 365 seen[node.Addr()] = true 366 } 367 return false 368 } 369 370 func contains(nodes []Node, addr Address) bool { 371 for _, n := range nodes { 372 if n.Addr() == addr { 373 return true 374 } 375 } 376 return false 377 } 378 379 // gen wraps quick.Value so it's easier to use. 380 // it generates a random value of the given value's type. 381 func gen(typ interface{}, rand *rand.Rand) interface{} { 382 v, ok := quick.Value(reflect.TypeOf(typ), rand) 383 if !ok { 384 panic(fmt.Sprintf("couldn't generate random value of type %T", typ)) 385 } 386 return v.Interface() 387 }