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