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