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