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