github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/p2p/pex/addrbook.go (about) 1 // Modified for Tendermint 2 // Originally Copyright (c) 2013-2014 Conformal Systems LLC. 3 // https://github.com/conformal/btcd/blob/master/LICENSE 4 5 package pex 6 7 import ( 8 crand "crypto/rand" 9 "encoding/binary" 10 "fmt" 11 "math" 12 mrand "math/rand" 13 "net" 14 "sync" 15 "time" 16 17 "github.com/minio/highwayhash" 18 19 "github.com/lazyledger/lazyledger-core/crypto" 20 tmmath "github.com/lazyledger/lazyledger-core/libs/math" 21 tmrand "github.com/lazyledger/lazyledger-core/libs/rand" 22 "github.com/lazyledger/lazyledger-core/libs/service" 23 tmsync "github.com/lazyledger/lazyledger-core/libs/sync" 24 "github.com/lazyledger/lazyledger-core/p2p" 25 ) 26 27 const ( 28 bucketTypeNew = 0x01 29 bucketTypeOld = 0x02 30 ) 31 32 // AddrBook is an address book used for tracking peers 33 // so we can gossip about them to others and select 34 // peers to dial. 35 // TODO: break this up? 36 type AddrBook interface { 37 service.Service 38 39 // Add our own addresses so we don't later add ourselves 40 AddOurAddress(*p2p.NetAddress) 41 // Check if it is our address 42 OurAddress(*p2p.NetAddress) bool 43 44 AddPrivateIDs([]string) 45 46 // Add and remove an address 47 AddAddress(addr *p2p.NetAddress, src *p2p.NetAddress) error 48 RemoveAddress(*p2p.NetAddress) 49 50 // Check if the address is in the book 51 HasAddress(*p2p.NetAddress) bool 52 53 // Do we need more peers? 54 NeedMoreAddrs() bool 55 // Is Address Book Empty? Answer should not depend on being in your own 56 // address book, or private peers 57 Empty() bool 58 59 // Pick an address to dial 60 PickAddress(biasTowardsNewAddrs int) *p2p.NetAddress 61 62 // Mark address 63 MarkGood(p2p.ID) 64 MarkAttempt(*p2p.NetAddress) 65 MarkBad(*p2p.NetAddress, time.Duration) // Move peer to bad peers list 66 // Add bad peers back to addrBook 67 ReinstateBadPeers() 68 69 IsGood(*p2p.NetAddress) bool 70 IsBanned(*p2p.NetAddress) bool 71 72 // Send a selection of addresses to peers 73 GetSelection() []*p2p.NetAddress 74 // Send a selection of addresses with bias 75 GetSelectionWithBias(biasTowardsNewAddrs int) []*p2p.NetAddress 76 77 Size() int 78 79 // Persist to disk 80 Save() 81 } 82 83 var _ AddrBook = (*addrBook)(nil) 84 85 // addrBook - concurrency safe peer address manager. 86 // Implements AddrBook. 87 type addrBook struct { 88 service.BaseService 89 90 // accessed concurrently 91 mtx tmsync.Mutex 92 ourAddrs map[string]struct{} 93 privateIDs map[p2p.ID]struct{} 94 addrLookup map[p2p.ID]*knownAddress // new & old 95 badPeers map[p2p.ID]*knownAddress // blacklisted peers 96 bucketsOld []map[string]*knownAddress 97 bucketsNew []map[string]*knownAddress 98 nOld int 99 nNew int 100 101 // immutable after creation 102 filePath string 103 key string // random prefix for bucket placement 104 routabilityStrict bool 105 hashKey []byte 106 107 wg sync.WaitGroup 108 } 109 110 func newHashKey() []byte { 111 result := make([]byte, highwayhash.Size) 112 crand.Read(result) //nolint:errcheck // ignore error 113 return result 114 } 115 116 // NewAddrBook creates a new address book. 117 // Use Start to begin processing asynchronous address updates. 118 func NewAddrBook(filePath string, routabilityStrict bool) AddrBook { 119 am := &addrBook{ 120 ourAddrs: make(map[string]struct{}), 121 privateIDs: make(map[p2p.ID]struct{}), 122 addrLookup: make(map[p2p.ID]*knownAddress), 123 badPeers: make(map[p2p.ID]*knownAddress), 124 filePath: filePath, 125 routabilityStrict: routabilityStrict, 126 hashKey: newHashKey(), 127 } 128 am.init() 129 am.BaseService = *service.NewBaseService(nil, "AddrBook", am) 130 return am 131 } 132 133 // Initialize the buckets. 134 // When modifying this, don't forget to update loadFromFile() 135 func (a *addrBook) init() { 136 a.key = crypto.CRandHex(24) // 24/2 * 8 = 96 bits 137 // New addr buckets 138 a.bucketsNew = make([]map[string]*knownAddress, newBucketCount) 139 for i := range a.bucketsNew { 140 a.bucketsNew[i] = make(map[string]*knownAddress) 141 } 142 // Old addr buckets 143 a.bucketsOld = make([]map[string]*knownAddress, oldBucketCount) 144 for i := range a.bucketsOld { 145 a.bucketsOld[i] = make(map[string]*knownAddress) 146 } 147 } 148 149 // OnStart implements Service. 150 func (a *addrBook) OnStart() error { 151 if err := a.BaseService.OnStart(); err != nil { 152 return err 153 } 154 a.loadFromFile(a.filePath) 155 156 // wg.Add to ensure that any invocation of .Wait() 157 // later on will wait for saveRoutine to terminate. 158 a.wg.Add(1) 159 go a.saveRoutine() 160 161 return nil 162 } 163 164 // OnStop implements Service. 165 func (a *addrBook) OnStop() { 166 a.BaseService.OnStop() 167 } 168 169 func (a *addrBook) Wait() { 170 a.wg.Wait() 171 } 172 173 func (a *addrBook) FilePath() string { 174 return a.filePath 175 } 176 177 //------------------------------------------------------- 178 179 // AddOurAddress one of our addresses. 180 func (a *addrBook) AddOurAddress(addr *p2p.NetAddress) { 181 a.mtx.Lock() 182 defer a.mtx.Unlock() 183 184 a.Logger.Info("Add our address to book", "addr", addr) 185 a.ourAddrs[addr.String()] = struct{}{} 186 } 187 188 // OurAddress returns true if it is our address. 189 func (a *addrBook) OurAddress(addr *p2p.NetAddress) bool { 190 a.mtx.Lock() 191 defer a.mtx.Unlock() 192 193 _, ok := a.ourAddrs[addr.String()] 194 return ok 195 } 196 197 func (a *addrBook) AddPrivateIDs(ids []string) { 198 a.mtx.Lock() 199 defer a.mtx.Unlock() 200 201 for _, id := range ids { 202 a.privateIDs[p2p.ID(id)] = struct{}{} 203 } 204 } 205 206 // AddAddress implements AddrBook 207 // Add address to a "new" bucket. If it's already in one, only add it probabilistically. 208 // Returns error if the addr is non-routable. Does not add self. 209 // NOTE: addr must not be nil 210 func (a *addrBook) AddAddress(addr *p2p.NetAddress, src *p2p.NetAddress) error { 211 a.mtx.Lock() 212 defer a.mtx.Unlock() 213 214 return a.addAddress(addr, src) 215 } 216 217 // RemoveAddress implements AddrBook - removes the address from the book. 218 func (a *addrBook) RemoveAddress(addr *p2p.NetAddress) { 219 a.mtx.Lock() 220 defer a.mtx.Unlock() 221 222 a.removeAddress(addr) 223 } 224 225 // IsGood returns true if peer was ever marked as good and haven't 226 // done anything wrong since then. 227 func (a *addrBook) IsGood(addr *p2p.NetAddress) bool { 228 a.mtx.Lock() 229 defer a.mtx.Unlock() 230 231 return a.addrLookup[addr.ID].isOld() 232 } 233 234 // IsBanned returns true if the peer is currently banned 235 func (a *addrBook) IsBanned(addr *p2p.NetAddress) bool { 236 a.mtx.Lock() 237 _, ok := a.badPeers[addr.ID] 238 a.mtx.Unlock() 239 240 return ok 241 } 242 243 // HasAddress returns true if the address is in the book. 244 func (a *addrBook) HasAddress(addr *p2p.NetAddress) bool { 245 a.mtx.Lock() 246 defer a.mtx.Unlock() 247 248 ka := a.addrLookup[addr.ID] 249 return ka != nil 250 } 251 252 // NeedMoreAddrs implements AddrBook - returns true if there are not have enough addresses in the book. 253 func (a *addrBook) NeedMoreAddrs() bool { 254 return a.Size() < needAddressThreshold 255 } 256 257 // Empty implements AddrBook - returns true if there are no addresses in the address book. 258 // Does not count the peer appearing in its own address book, or private peers. 259 func (a *addrBook) Empty() bool { 260 return a.Size() == 0 261 } 262 263 // PickAddress implements AddrBook. It picks an address to connect to. 264 // The address is picked randomly from an old or new bucket according 265 // to the biasTowardsNewAddrs argument, which must be between [0, 100] (or else is truncated to that range) 266 // and determines how biased we are to pick an address from a new bucket. 267 // PickAddress returns nil if the AddrBook is empty or if we try to pick 268 // from an empty bucket. 269 // nolint:gosec // G404: Use of weak random number generator 270 func (a *addrBook) PickAddress(biasTowardsNewAddrs int) *p2p.NetAddress { 271 a.mtx.Lock() 272 defer a.mtx.Unlock() 273 274 bookSize := a.size() 275 if bookSize <= 0 { 276 if bookSize < 0 { 277 panic(fmt.Sprintf("Addrbook size %d (new: %d + old: %d) is less than 0", a.nNew+a.nOld, a.nNew, a.nOld)) 278 } 279 return nil 280 } 281 if biasTowardsNewAddrs > 100 { 282 biasTowardsNewAddrs = 100 283 } 284 if biasTowardsNewAddrs < 0 { 285 biasTowardsNewAddrs = 0 286 } 287 288 // Bias between new and old addresses. 289 oldCorrelation := math.Sqrt(float64(a.nOld)) * (100.0 - float64(biasTowardsNewAddrs)) 290 newCorrelation := math.Sqrt(float64(a.nNew)) * float64(biasTowardsNewAddrs) 291 292 // pick a random peer from a random bucket 293 var bucket map[string]*knownAddress 294 pickFromOldBucket := (newCorrelation+oldCorrelation)*mrand.Float64() < oldCorrelation 295 if (pickFromOldBucket && a.nOld == 0) || 296 (!pickFromOldBucket && a.nNew == 0) { 297 return nil 298 } 299 // loop until we pick a random non-empty bucket 300 for len(bucket) == 0 { 301 if pickFromOldBucket { 302 bucket = a.bucketsOld[mrand.Intn(len(a.bucketsOld))] 303 } else { 304 bucket = a.bucketsNew[mrand.Intn(len(a.bucketsNew))] 305 } 306 } 307 // pick a random index and loop over the map to return that index 308 randIndex := mrand.Intn(len(bucket)) 309 for _, ka := range bucket { 310 if randIndex == 0 { 311 return ka.Addr 312 } 313 randIndex-- 314 } 315 return nil 316 } 317 318 // MarkGood implements AddrBook - it marks the peer as good and 319 // moves it into an "old" bucket. 320 func (a *addrBook) MarkGood(id p2p.ID) { 321 a.mtx.Lock() 322 defer a.mtx.Unlock() 323 324 ka := a.addrLookup[id] 325 if ka == nil { 326 return 327 } 328 ka.markGood() 329 if ka.isNew() { 330 if err := a.moveToOld(ka); err != nil { 331 a.Logger.Error("Error moving address to old", "err", err) 332 } 333 } 334 } 335 336 // MarkAttempt implements AddrBook - it marks that an attempt was made to connect to the address. 337 func (a *addrBook) MarkAttempt(addr *p2p.NetAddress) { 338 a.mtx.Lock() 339 defer a.mtx.Unlock() 340 341 ka := a.addrLookup[addr.ID] 342 if ka == nil { 343 return 344 } 345 ka.markAttempt() 346 } 347 348 // MarkBad implements AddrBook. Kicks address out from book, places 349 // the address in the badPeers pool. 350 func (a *addrBook) MarkBad(addr *p2p.NetAddress, banTime time.Duration) { 351 a.mtx.Lock() 352 defer a.mtx.Unlock() 353 354 if a.addBadPeer(addr, banTime) { 355 a.removeAddress(addr) 356 } 357 } 358 359 // ReinstateBadPeers removes bad peers from ban list and places them into a new 360 // bucket. 361 func (a *addrBook) ReinstateBadPeers() { 362 a.mtx.Lock() 363 defer a.mtx.Unlock() 364 365 for _, ka := range a.badPeers { 366 if ka.isBanned() { 367 continue 368 } 369 370 bucket, err := a.calcNewBucket(ka.Addr, ka.Src) 371 if err != nil { 372 a.Logger.Error("Failed to calculate new bucket (bad peer won't be reinstantiated)", 373 "addr", ka.Addr, "err", err) 374 continue 375 } 376 377 if err := a.addToNewBucket(ka, bucket); err != nil { 378 a.Logger.Error("Error adding peer to new bucket", "err", err) 379 } 380 delete(a.badPeers, ka.ID()) 381 382 a.Logger.Info("Reinstated address", "addr", ka.Addr) 383 } 384 } 385 386 // GetSelection implements AddrBook. 387 // It randomly selects some addresses (old & new). Suitable for peer-exchange protocols. 388 // Must never return a nil address. 389 func (a *addrBook) GetSelection() []*p2p.NetAddress { 390 a.mtx.Lock() 391 defer a.mtx.Unlock() 392 393 bookSize := a.size() 394 if bookSize <= 0 { 395 if bookSize < 0 { 396 panic(fmt.Sprintf("Addrbook size %d (new: %d + old: %d) is less than 0", a.nNew+a.nOld, a.nNew, a.nOld)) 397 } 398 return nil 399 } 400 401 numAddresses := tmmath.MaxInt( 402 tmmath.MinInt(minGetSelection, bookSize), 403 bookSize*getSelectionPercent/100) 404 numAddresses = tmmath.MinInt(maxGetSelection, numAddresses) 405 406 // XXX: instead of making a list of all addresses, shuffling, and slicing a random chunk, 407 // could we just select a random numAddresses of indexes? 408 allAddr := make([]*p2p.NetAddress, bookSize) 409 i := 0 410 for _, ka := range a.addrLookup { 411 allAddr[i] = ka.Addr 412 i++ 413 } 414 415 // Fisher-Yates shuffle the array. We only need to do the first 416 // `numAddresses' since we are throwing the rest. 417 for i := 0; i < numAddresses; i++ { 418 // pick a number between current index and the end 419 // nolint:gosec // G404: Use of weak random number generator 420 j := mrand.Intn(len(allAddr)-i) + i 421 allAddr[i], allAddr[j] = allAddr[j], allAddr[i] 422 } 423 424 // slice off the limit we are willing to share. 425 return allAddr[:numAddresses] 426 } 427 428 func percentageOfNum(p, n int) int { 429 return int(math.Round((float64(p) / float64(100)) * float64(n))) 430 } 431 432 // GetSelectionWithBias implements AddrBook. 433 // It randomly selects some addresses (old & new). Suitable for peer-exchange protocols. 434 // Must never return a nil address. 435 // 436 // Each address is picked randomly from an old or new bucket according to the 437 // biasTowardsNewAddrs argument, which must be between [0, 100] (or else is truncated to 438 // that range) and determines how biased we are to pick an address from a new 439 // bucket. 440 func (a *addrBook) GetSelectionWithBias(biasTowardsNewAddrs int) []*p2p.NetAddress { 441 a.mtx.Lock() 442 defer a.mtx.Unlock() 443 444 bookSize := a.size() 445 if bookSize <= 0 { 446 if bookSize < 0 { 447 panic(fmt.Sprintf("Addrbook size %d (new: %d + old: %d) is less than 0", a.nNew+a.nOld, a.nNew, a.nOld)) 448 } 449 return nil 450 } 451 452 if biasTowardsNewAddrs > 100 { 453 biasTowardsNewAddrs = 100 454 } 455 if biasTowardsNewAddrs < 0 { 456 biasTowardsNewAddrs = 0 457 } 458 459 numAddresses := tmmath.MaxInt( 460 tmmath.MinInt(minGetSelection, bookSize), 461 bookSize*getSelectionPercent/100) 462 numAddresses = tmmath.MinInt(maxGetSelection, numAddresses) 463 464 // number of new addresses that, if possible, should be in the beginning of the selection 465 // if there are no enough old addrs, will choose new addr instead. 466 numRequiredNewAdd := tmmath.MaxInt(percentageOfNum(biasTowardsNewAddrs, numAddresses), numAddresses-a.nOld) 467 selection := a.randomPickAddresses(bucketTypeNew, numRequiredNewAdd) 468 selection = append(selection, a.randomPickAddresses(bucketTypeOld, numAddresses-len(selection))...) 469 return selection 470 } 471 472 //------------------------------------------------ 473 474 // Size returns the number of addresses in the book. 475 func (a *addrBook) Size() int { 476 a.mtx.Lock() 477 defer a.mtx.Unlock() 478 479 return a.size() 480 } 481 482 func (a *addrBook) size() int { 483 return a.nNew + a.nOld 484 } 485 486 //---------------------------------------------------------- 487 488 // Save persists the address book to disk. 489 func (a *addrBook) Save() { 490 a.saveToFile(a.filePath) // thread safe 491 } 492 493 func (a *addrBook) saveRoutine() { 494 defer a.wg.Done() 495 496 saveFileTicker := time.NewTicker(dumpAddressInterval) 497 out: 498 for { 499 select { 500 case <-saveFileTicker.C: 501 a.saveToFile(a.filePath) 502 case <-a.Quit(): 503 break out 504 } 505 } 506 saveFileTicker.Stop() 507 a.saveToFile(a.filePath) 508 } 509 510 //---------------------------------------------------------- 511 512 func (a *addrBook) getBucket(bucketType byte, bucketIdx int) map[string]*knownAddress { 513 switch bucketType { 514 case bucketTypeNew: 515 return a.bucketsNew[bucketIdx] 516 case bucketTypeOld: 517 return a.bucketsOld[bucketIdx] 518 default: 519 panic("Invalid bucket type") 520 } 521 } 522 523 // Adds ka to new bucket. Returns false if it couldn't do it cuz buckets full. 524 // NOTE: currently it always returns true. 525 func (a *addrBook) addToNewBucket(ka *knownAddress, bucketIdx int) error { 526 // Consistency check to ensure we don't add an already known address 527 if ka.isOld() { 528 return errAddrBookOldAddressNewBucket{ka.Addr, bucketIdx} 529 } 530 531 addrStr := ka.Addr.String() 532 bucket := a.getBucket(bucketTypeNew, bucketIdx) 533 534 // Already exists? 535 if _, ok := bucket[addrStr]; ok { 536 return nil 537 } 538 539 // Enforce max addresses. 540 if len(bucket) > newBucketSize { 541 a.Logger.Info("new bucket is full, expiring new") 542 a.expireNew(bucketIdx) 543 } 544 545 // Add to bucket. 546 bucket[addrStr] = ka 547 // increment nNew if the peer doesnt already exist in a bucket 548 if ka.addBucketRef(bucketIdx) == 1 { 549 a.nNew++ 550 } 551 552 // Add it to addrLookup 553 a.addrLookup[ka.ID()] = ka 554 return nil 555 } 556 557 // Adds ka to old bucket. Returns false if it couldn't do it cuz buckets full. 558 func (a *addrBook) addToOldBucket(ka *knownAddress, bucketIdx int) bool { 559 // Sanity check 560 if ka.isNew() { 561 a.Logger.Error(fmt.Sprintf("Cannot add new address to old bucket: %v", ka)) 562 return false 563 } 564 if len(ka.Buckets) != 0 { 565 a.Logger.Error(fmt.Sprintf("Cannot add already old address to another old bucket: %v", ka)) 566 return false 567 } 568 569 addrStr := ka.Addr.String() 570 bucket := a.getBucket(bucketTypeOld, bucketIdx) 571 572 // Already exists? 573 if _, ok := bucket[addrStr]; ok { 574 return true 575 } 576 577 // Enforce max addresses. 578 if len(bucket) > oldBucketSize { 579 return false 580 } 581 582 // Add to bucket. 583 bucket[addrStr] = ka 584 if ka.addBucketRef(bucketIdx) == 1 { 585 a.nOld++ 586 } 587 588 // Ensure in addrLookup 589 a.addrLookup[ka.ID()] = ka 590 591 return true 592 } 593 594 func (a *addrBook) removeFromBucket(ka *knownAddress, bucketType byte, bucketIdx int) { 595 if ka.BucketType != bucketType { 596 a.Logger.Error(fmt.Sprintf("Bucket type mismatch: %v", ka)) 597 return 598 } 599 bucket := a.getBucket(bucketType, bucketIdx) 600 delete(bucket, ka.Addr.String()) 601 if ka.removeBucketRef(bucketIdx) == 0 { 602 if bucketType == bucketTypeNew { 603 a.nNew-- 604 } else { 605 a.nOld-- 606 } 607 delete(a.addrLookup, ka.ID()) 608 } 609 } 610 611 func (a *addrBook) removeFromAllBuckets(ka *knownAddress) { 612 for _, bucketIdx := range ka.Buckets { 613 bucket := a.getBucket(ka.BucketType, bucketIdx) 614 delete(bucket, ka.Addr.String()) 615 } 616 ka.Buckets = nil 617 if ka.BucketType == bucketTypeNew { 618 a.nNew-- 619 } else { 620 a.nOld-- 621 } 622 delete(a.addrLookup, ka.ID()) 623 } 624 625 //---------------------------------------------------------- 626 627 func (a *addrBook) pickOldest(bucketType byte, bucketIdx int) *knownAddress { 628 bucket := a.getBucket(bucketType, bucketIdx) 629 var oldest *knownAddress 630 for _, ka := range bucket { 631 if oldest == nil || ka.LastAttempt.Before(oldest.LastAttempt) { 632 oldest = ka 633 } 634 } 635 return oldest 636 } 637 638 // adds the address to a "new" bucket. if its already in one, 639 // it only adds it probabilistically 640 func (a *addrBook) addAddress(addr, src *p2p.NetAddress) error { 641 if addr == nil || src == nil { 642 return ErrAddrBookNilAddr{addr, src} 643 } 644 645 if err := addr.Valid(); err != nil { 646 return ErrAddrBookInvalidAddr{Addr: addr, AddrErr: err} 647 } 648 649 if _, ok := a.badPeers[addr.ID]; ok { 650 return ErrAddressBanned{addr} 651 } 652 653 if _, ok := a.privateIDs[addr.ID]; ok { 654 return ErrAddrBookPrivate{addr} 655 } 656 657 if _, ok := a.privateIDs[src.ID]; ok { 658 return ErrAddrBookPrivateSrc{src} 659 } 660 661 // TODO: we should track ourAddrs by ID and by IP:PORT and refuse both. 662 if _, ok := a.ourAddrs[addr.String()]; ok { 663 return ErrAddrBookSelf{addr} 664 } 665 666 if a.routabilityStrict && !addr.Routable() { 667 return ErrAddrBookNonRoutable{addr} 668 } 669 670 ka := a.addrLookup[addr.ID] 671 if ka != nil { 672 // If its already old and the address ID's are the same, ignore it. 673 // Thereby avoiding issues with a node on the network attempting to change 674 // the IP of a known node ID. (Which could yield an eclipse attack on the node) 675 if ka.isOld() && ka.Addr.ID == addr.ID { 676 return nil 677 } 678 // Already in max new buckets. 679 if len(ka.Buckets) == maxNewBucketsPerAddress { 680 return nil 681 } 682 // The more entries we have, the less likely we are to add more. 683 factor := int32(2 * len(ka.Buckets)) 684 // nolint:gosec // G404: Use of weak random number generator 685 if mrand.Int31n(factor) != 0 { 686 return nil 687 } 688 } else { 689 ka = newKnownAddress(addr, src) 690 } 691 692 bucket, err := a.calcNewBucket(addr, src) 693 if err != nil { 694 return err 695 } 696 return a.addToNewBucket(ka, bucket) 697 } 698 699 func (a *addrBook) randomPickAddresses(bucketType byte, num int) []*p2p.NetAddress { 700 var buckets []map[string]*knownAddress 701 switch bucketType { 702 case bucketTypeNew: 703 buckets = a.bucketsNew 704 case bucketTypeOld: 705 buckets = a.bucketsOld 706 default: 707 panic("unexpected bucketType") 708 } 709 total := 0 710 for _, bucket := range buckets { 711 total += len(bucket) 712 } 713 addresses := make([]*knownAddress, 0, total) 714 for _, bucket := range buckets { 715 for _, ka := range bucket { 716 addresses = append(addresses, ka) 717 } 718 } 719 selection := make([]*p2p.NetAddress, 0, num) 720 chosenSet := make(map[string]bool, num) 721 rand := tmrand.NewRand() 722 rand.Shuffle(total, func(i, j int) { 723 addresses[i], addresses[j] = addresses[j], addresses[i] 724 }) 725 for _, addr := range addresses { 726 if chosenSet[addr.Addr.String()] { 727 continue 728 } 729 chosenSet[addr.Addr.String()] = true 730 selection = append(selection, addr.Addr) 731 if len(selection) >= num { 732 return selection 733 } 734 } 735 return selection 736 } 737 738 // Make space in the new buckets by expiring the really bad entries. 739 // If no bad entries are available we remove the oldest. 740 func (a *addrBook) expireNew(bucketIdx int) { 741 for addrStr, ka := range a.bucketsNew[bucketIdx] { 742 // If an entry is bad, throw it away 743 if ka.isBad() { 744 a.Logger.Info(fmt.Sprintf("expiring bad address %v", addrStr)) 745 a.removeFromBucket(ka, bucketTypeNew, bucketIdx) 746 return 747 } 748 } 749 750 // If we haven't thrown out a bad entry, throw out the oldest entry 751 oldest := a.pickOldest(bucketTypeNew, bucketIdx) 752 a.removeFromBucket(oldest, bucketTypeNew, bucketIdx) 753 } 754 755 // Promotes an address from new to old. If the destination bucket is full, 756 // demote the oldest one to a "new" bucket. 757 // TODO: Demote more probabilistically? 758 func (a *addrBook) moveToOld(ka *knownAddress) error { 759 // Sanity check 760 if ka.isOld() { 761 a.Logger.Error(fmt.Sprintf("Cannot promote address that is already old %v", ka)) 762 return nil 763 } 764 if len(ka.Buckets) == 0 { 765 a.Logger.Error(fmt.Sprintf("Cannot promote address that isn't in any new buckets %v", ka)) 766 return nil 767 } 768 769 // Remove from all (new) buckets. 770 a.removeFromAllBuckets(ka) 771 // It's officially old now. 772 ka.BucketType = bucketTypeOld 773 774 // Try to add it to its oldBucket destination. 775 oldBucketIdx, err := a.calcOldBucket(ka.Addr) 776 if err != nil { 777 return err 778 } 779 added := a.addToOldBucket(ka, oldBucketIdx) 780 if !added { 781 // No room; move the oldest to a new bucket 782 oldest := a.pickOldest(bucketTypeOld, oldBucketIdx) 783 a.removeFromBucket(oldest, bucketTypeOld, oldBucketIdx) 784 newBucketIdx, err := a.calcNewBucket(oldest.Addr, oldest.Src) 785 if err != nil { 786 return err 787 } 788 if err := a.addToNewBucket(oldest, newBucketIdx); err != nil { 789 a.Logger.Error("Error adding peer to old bucket", "err", err) 790 } 791 792 // Finally, add our ka to old bucket again. 793 added = a.addToOldBucket(ka, oldBucketIdx) 794 if !added { 795 a.Logger.Error(fmt.Sprintf("Could not re-add ka %v to oldBucketIdx %v", ka, oldBucketIdx)) 796 } 797 } 798 return nil 799 } 800 801 func (a *addrBook) removeAddress(addr *p2p.NetAddress) { 802 ka := a.addrLookup[addr.ID] 803 if ka == nil { 804 return 805 } 806 a.Logger.Info("Remove address from book", "addr", addr) 807 a.removeFromAllBuckets(ka) 808 } 809 810 func (a *addrBook) addBadPeer(addr *p2p.NetAddress, banTime time.Duration) bool { 811 // check it exists in addrbook 812 ka := a.addrLookup[addr.ID] 813 // check address is not already there 814 if ka == nil { 815 return false 816 } 817 818 if _, alreadyBadPeer := a.badPeers[addr.ID]; !alreadyBadPeer { 819 // add to bad peer list 820 ka.ban(banTime) 821 a.badPeers[addr.ID] = ka 822 a.Logger.Info("Add address to blacklist", "addr", addr) 823 } 824 return true 825 } 826 827 //--------------------------------------------------------------------- 828 // calculate bucket placements 829 830 // hash(key + sourcegroup + int64(hash(key + group + sourcegroup)) % bucket_per_group) % num_new_buckets 831 func (a *addrBook) calcNewBucket(addr, src *p2p.NetAddress) (int, error) { 832 data1 := []byte{} 833 data1 = append(data1, []byte(a.key)...) 834 data1 = append(data1, []byte(a.groupKey(addr))...) 835 data1 = append(data1, []byte(a.groupKey(src))...) 836 hash1, err := a.hash(data1) 837 if err != nil { 838 return 0, err 839 } 840 hash64 := binary.BigEndian.Uint64(hash1) 841 hash64 %= newBucketsPerGroup 842 var hashbuf [8]byte 843 binary.BigEndian.PutUint64(hashbuf[:], hash64) 844 data2 := []byte{} 845 data2 = append(data2, []byte(a.key)...) 846 data2 = append(data2, a.groupKey(src)...) 847 data2 = append(data2, hashbuf[:]...) 848 849 hash2, err := a.hash(data2) 850 if err != nil { 851 return 0, err 852 } 853 result := int(binary.BigEndian.Uint64(hash2) % newBucketCount) 854 return result, nil 855 } 856 857 // hash(key + group + int64(hash(key + addr)) % buckets_per_group) % num_old_buckets 858 func (a *addrBook) calcOldBucket(addr *p2p.NetAddress) (int, error) { 859 data1 := []byte{} 860 data1 = append(data1, []byte(a.key)...) 861 data1 = append(data1, []byte(addr.String())...) 862 hash1, err := a.hash(data1) 863 if err != nil { 864 return 0, err 865 } 866 hash64 := binary.BigEndian.Uint64(hash1) 867 hash64 %= oldBucketsPerGroup 868 var hashbuf [8]byte 869 binary.BigEndian.PutUint64(hashbuf[:], hash64) 870 data2 := []byte{} 871 data2 = append(data2, []byte(a.key)...) 872 data2 = append(data2, a.groupKey(addr)...) 873 data2 = append(data2, hashbuf[:]...) 874 875 hash2, err := a.hash(data2) 876 if err != nil { 877 return 0, err 878 } 879 result := int(binary.BigEndian.Uint64(hash2) % oldBucketCount) 880 return result, nil 881 } 882 883 // Return a string representing the network group of this address. 884 // This is the /16 for IPv4 (e.g. 1.2.0.0), the /32 (/36 for he.net) for IPv6, the string 885 // "local" for a local address and the string "unroutable" for an unroutable 886 // address. 887 func (a *addrBook) groupKey(na *p2p.NetAddress) string { 888 return groupKeyFor(na, a.routabilityStrict) 889 } 890 891 func groupKeyFor(na *p2p.NetAddress, routabilityStrict bool) string { 892 if routabilityStrict && na.Local() { 893 return "local" 894 } 895 if routabilityStrict && !na.Routable() { 896 return "unroutable" 897 } 898 899 if ipv4 := na.IP.To4(); ipv4 != nil { 900 return na.IP.Mask(net.CIDRMask(16, 32)).String() 901 } 902 903 if na.RFC6145() || na.RFC6052() { 904 // last four bytes are the ip address 905 ip := na.IP[12:16] 906 return ip.Mask(net.CIDRMask(16, 32)).String() 907 } 908 909 if na.RFC3964() { 910 ip := na.IP[2:6] 911 return ip.Mask(net.CIDRMask(16, 32)).String() 912 } 913 914 if na.RFC4380() { 915 // teredo tunnels have the last 4 bytes as the v4 address XOR 916 // 0xff. 917 ip := net.IP(make([]byte, 4)) 918 for i, byte := range na.IP[12:16] { 919 ip[i] = byte ^ 0xff 920 } 921 return ip.Mask(net.CIDRMask(16, 32)).String() 922 } 923 924 if na.OnionCatTor() { 925 // group is keyed off the first 4 bits of the actual onion key. 926 return fmt.Sprintf("tor:%d", na.IP[6]&((1<<4)-1)) 927 } 928 929 // OK, so now we know ourselves to be a IPv6 address. 930 // bitcoind uses /32 for everything, except for Hurricane Electric's 931 // (he.net) IP range, which it uses /36 for. 932 bits := 32 933 heNet := &net.IPNet{IP: net.ParseIP("2001:470::"), Mask: net.CIDRMask(32, 128)} 934 if heNet.Contains(na.IP) { 935 bits = 36 936 } 937 ipv6Mask := net.CIDRMask(bits, 128) 938 return na.IP.Mask(ipv6Mask).String() 939 } 940 941 func (a *addrBook) hash(b []byte) ([]byte, error) { 942 hasher, err := highwayhash.New64(a.hashKey) 943 if err != nil { 944 return nil, err 945 } 946 hasher.Write(b) 947 return hasher.Sum(nil), nil 948 }