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