1 // Modified for Tendermint
2 // Originally Copyright (c) 2013-2014 Conformal Systems LLC.
3 // https://github.com/conformal/btcd/blob/master/LICENSE
17 log "github.com/sirupsen/logrus"
18 "github.com/tendermint/go-crypto"
19 cmn "github.com/tendermint/tmlibs/common"
23 // addresses under which the address manager will claim to need more addresses.
24 needAddressThreshold = 1000
26 // interval used to dump the address cache to disk for future use.
27 dumpAddressInterval = time.Minute * 2
29 // max addresses in each old address bucket.
32 // buckets we split old addresses over.
35 // max addresses in each new address bucket.
38 // buckets that we spread new addresses over.
41 // old buckets over which an address group will be spread.
42 oldBucketsPerGroup = 4
44 // new buckets over which an source address group will be spread.
45 newBucketsPerGroup = 32
47 // buckets a frequently seen new address may end up in.
48 maxNewBucketsPerAddress = 4
50 // days before which we assume an address has vanished
51 // if we have not seen it announced in that long.
54 // tries without a single success before we assume an address is bad.
57 // max failures we will accept without a success before considering an address bad.
60 // days since the last success before we will consider evicting an address.
63 // % of total addresses known returned by GetSelection.
64 getSelectionPercent = 23
66 // min addresses that must be returned by GetSelection. Useful for bootstrapping.
69 // max addresses returned by GetSelection
70 // NOTE: this must match "maxPexMessageSize"
79 // AddrBook - concurrency safe peer address manager.
80 type AddrBook struct {
83 // immutable after creation
85 routabilityStrict bool
90 ourAddrs map[string]*NetAddress
91 addrLookup map[string]*knownAddress // new & old
92 bucketsNew []map[string]*knownAddress
93 bucketsOld []map[string]*knownAddress
98 // NewAddrBook creates a new address book. Use Start to begin processing asynchronous address updates.
99 func NewAddrBook(filePath string, routabilityStrict bool) *AddrBook {
102 routabilityStrict: routabilityStrict,
103 key: crypto.CRandHex(24),
104 rand: rand.New(rand.NewSource(time.Now().UnixNano())),
105 ourAddrs: make(map[string]*NetAddress),
106 addrLookup: make(map[string]*knownAddress),
107 bucketsNew: make([]map[string]*knownAddress, newBucketCount),
108 bucketsOld: make([]map[string]*knownAddress, oldBucketCount),
110 for i := range a.bucketsNew {
111 a.bucketsNew[i] = make(map[string]*knownAddress)
113 for i := range a.bucketsOld {
114 a.bucketsOld[i] = make(map[string]*knownAddress)
116 a.BaseService = *cmn.NewBaseService(nil, "AddrBook", a)
120 // OnStart implements Service.
121 func (a *AddrBook) OnStart() error {
122 if err := a.BaseService.OnStart(); err != nil {
131 // OnStop implements Service.
132 func (a *AddrBook) OnStop() {
133 a.BaseService.OnStop()
136 // AddAddress add address to address book
137 func (a *AddrBook) AddAddress(addr, src *NetAddress) {
140 log.WithFields(log.Fields{"addr": addr, "src": src}).Debug("add address to address book")
141 a.addAddress(addr, src)
144 func (a *AddrBook) NeedMoreAddrs() bool {
145 return a.Size() < needAddressThreshold
148 func (a *AddrBook) Size() int {
150 defer a.mtx.RUnlock()
154 func (a *AddrBook) size() int {
155 return a.nNew + a.nOld
158 // PickAddress picks a random address from random bucket
159 func (a *AddrBook) PickAddress(bias int) *NetAddress {
161 defer a.mtx.RUnlock()
167 // make sure bias is in the range [0, 100]
174 oldCorrelation := math.Sqrt(float64(a.nOld)) * (100.0 - float64(bias))
175 newCorrelation := math.Sqrt(float64(a.nNew)) * float64(bias)
176 pickFromOldBucket := (newCorrelation+oldCorrelation)*a.rand.Float64() < oldCorrelation
177 if (pickFromOldBucket && a.nOld == 0) || (!pickFromOldBucket && a.nNew == 0) {
181 var bucket map[string]*knownAddress
182 for len(bucket) == 0 {
183 if pickFromOldBucket {
184 bucket = a.bucketsOld[a.rand.Intn(len(a.bucketsOld))]
186 bucket = a.bucketsNew[a.rand.Intn(len(a.bucketsNew))]
190 randIndex := a.rand.Intn(len(bucket))
191 for _, ka := range bucket {
200 // MarkGood marks the peer as good and moves it into an "old" bucket.
201 func (a *AddrBook) MarkGood(addr *NetAddress) {
205 if ka := a.addrLookup[addr.String()]; ka != nil {
213 // MarkAttempt marks that an attempt was made to connect to the address.
214 func (a *AddrBook) MarkAttempt(addr *NetAddress) {
218 if ka := a.addrLookup[addr.String()]; ka != nil {
223 // RemoveAddress removes the address from the book.
224 func (a *AddrBook) RemoveAddress(addr *NetAddress) {
228 if ka := a.addrLookup[addr.String()]; ka != nil {
229 log.WithField("addr", addr).Debug("remove address from address book")
230 a.removeFromAllBuckets(ka)
234 // GetSelection randomly selects some addresses (old & new). Suitable for peer-exchange protocols.
235 func (a *AddrBook) GetSelection() []*NetAddress {
237 defer a.mtx.RUnlock()
244 numAddresses := cmn.MaxInt(cmn.MinInt(minGetSelection, bookSize), bookSize*getSelectionPercent/100)
245 numAddresses = cmn.MinInt(maxGetSelection, numAddresses)
247 allAddr := make([]*NetAddress, bookSize)
249 for _, ka := range a.addrLookup {
254 // Fisher-Yates shuffle the array. We only need to do the first
255 // `numAddresses' since we are throwing the rest.
256 for i := 0; i < numAddresses; i++ {
257 // pick a number between current index and the end
258 j := rand.Intn(len(allAddr)-i) + i
259 allAddr[i], allAddr[j] = allAddr[j], allAddr[i]
262 // slice off the limit we are willing to share.
263 return allAddr[:numAddresses]
266 type addrBookJSON struct {
268 Addrs []*knownAddress
271 func (a *AddrBook) Save() {
273 defer a.mtx.RUnlock()
275 log.WithField("size", a.Size()).Debug("saving address book to file")
276 aJSON := &addrBookJSON{
278 Addrs: []*knownAddress{},
280 for _, ka := range a.addrLookup {
281 aJSON.Addrs = append(aJSON.Addrs, ka)
284 jsonBytes, err := json.MarshalIndent(aJSON, "", "\t")
286 log.WithField("err", err).Error("failed to save AddrBook to file")
290 if err = cmn.WriteFileAtomic(a.filePath, jsonBytes, 0644); err != nil {
291 log.WithFields(log.Fields{"file": a.filePath, "err": err}).Error("Failed to save AddrBook to file")
295 func (a *AddrBook) loadFromFile() bool {
296 if _, err := os.Stat(a.filePath); os.IsNotExist(err) {
300 r, err := os.Open(a.filePath)
302 cmn.PanicCrisis(cmn.Fmt("Error opening file %s: %v", a.filePath, err))
306 aJSON := &addrBookJSON{}
307 if err = json.NewDecoder(r).Decode(aJSON); err != nil {
308 cmn.PanicCrisis(cmn.Fmt("Error reading file %s: %v", a.filePath, err))
312 for _, ka := range aJSON.Addrs {
313 for _, bucketIndex := range ka.Buckets {
314 bucket := a.getBucket(ka.BucketType, bucketIndex)
315 bucket[ka.Addr.String()] = ka
317 a.addrLookup[ka.Addr.String()] = ka
318 if ka.BucketType == bucketTypeNew {
327 func (a *AddrBook) saveRoutine() {
328 ticker := time.NewTicker(dumpAddressInterval)
340 func (a *AddrBook) getBucket(bucketType byte, bucketIdx int) map[string]*knownAddress {
343 return a.bucketsNew[bucketIdx]
345 return a.bucketsOld[bucketIdx]
347 cmn.PanicSanity("Should not happen")
352 // Adds ka to new bucket. Returns false if it couldn't do it cuz buckets full.
353 // NOTE: currently it always returns true.
354 func (a *AddrBook) addToNewBucket(ka *knownAddress, bucketIdx int) bool {
356 log.WithField("know address", ka).Error("cant add old address to new bucket")
360 addrStr := ka.Addr.String()
361 bucket := a.getBucket(bucketTypeNew, bucketIdx)
362 if _, ok := bucket[addrStr]; ok {
366 if len(bucket) > newBucketSize {
367 log.Debug("addToNewBucket: new bucket is full, expiring new")
368 a.expireNew(bucketIdx)
372 a.addrLookup[addrStr] = ka
373 if ka.addBucketRef(bucketIdx) == 1 {
379 // Adds ka to old bucket. Returns false if it couldn't do it cuz buckets full.
380 func (a *AddrBook) addToOldBucket(ka *knownAddress, bucketIdx int) bool {
382 log.WithField("know address", ka).Error("cannot add old address to new bucket")
385 if len(ka.Buckets) != 0 {
386 log.WithField("know address", ka).Error("cannot add already old address to another old bucket")
390 addrStr := ka.Addr.String()
391 bucket := a.getBucket(bucketTypeOld, bucketIdx)
392 if _, ok := bucket[addrStr]; ok {
396 if len(bucket) > oldBucketSize {
402 a.addrLookup[addrStr] = ka
403 if ka.addBucketRef(bucketIdx) == 1 {
409 func (a *AddrBook) removeFromBucket(ka *knownAddress, bucketType byte, bucketIdx int) {
410 if ka.BucketType != bucketType {
411 log.WithField("know address", ka).Error("Bucket type mismatch")
415 bucket := a.getBucket(bucketType, bucketIdx)
416 delete(bucket, ka.Addr.String())
417 if ka.removeBucketRef(bucketIdx) == 0 {
418 if bucketType == bucketTypeNew {
423 delete(a.addrLookup, ka.Addr.String())
427 func (a *AddrBook) removeFromAllBuckets(ka *knownAddress) {
428 for _, bucketIdx := range ka.Buckets {
429 bucket := a.getBucket(ka.BucketType, bucketIdx)
430 delete(bucket, ka.Addr.String())
433 if ka.BucketType == bucketTypeNew {
438 delete(a.addrLookup, ka.Addr.String())
441 func (a *AddrBook) pickOldest(bucketType byte, bucketIdx int) *knownAddress {
442 bucket := a.getBucket(bucketType, bucketIdx)
443 var oldest *knownAddress
444 for _, ka := range bucket {
445 if oldest == nil || ka.LastAttempt.Before(oldest.LastAttempt) {
452 func (a *AddrBook) addAddress(addr, src *NetAddress) {
453 if _, ok := a.ourAddrs[addr.String()]; ok {
456 if a.routabilityStrict && !addr.Routable() {
457 log.WithField("address", addr).Error("cannot add non-routable address")
461 ka := a.addrLookup[addr.String()]
466 if len(ka.Buckets) == maxNewBucketsPerAddress {
469 if factor := int32(2 * len(ka.Buckets)); a.rand.Int31n(factor) != 0 {
473 ka = newKnownAddress(addr, src)
476 bucket := a.calcNewBucket(addr, src)
477 a.addToNewBucket(ka, bucket)
480 // Make space in the new buckets by expiring the really bad entries.
481 // If no bad entries are available we remove the oldest.
482 func (a *AddrBook) expireNew(bucketIdx int) {
483 for addrStr, ka := range a.bucketsNew[bucketIdx] {
485 log.WithField("addr", addrStr).Info("expiring bad address")
486 a.removeFromBucket(ka, bucketTypeNew, bucketIdx)
491 oldest := a.pickOldest(bucketTypeNew, bucketIdx)
492 a.removeFromBucket(oldest, bucketTypeNew, bucketIdx)
495 // Promotes an address from new to old.
496 // TODO: Move to old probabilistically.
497 // The better a node is, the less likely it should be evicted from an old bucket.
498 func (a *AddrBook) moveToOld(ka *knownAddress) {
500 log.WithField("know address", ka).Error("cannot promote address that is already old")
503 if len(ka.Buckets) == 0 {
504 log.WithField("know address", ka).Error("cannot promote address that isn't in any new buckets")
508 a.removeFromAllBuckets(ka)
509 ka.BucketType = bucketTypeOld
511 // Try to add it to its oldBucket destination.
512 oldBucketIdx := a.calcOldBucket(ka.Addr)
513 if ok := a.addToOldBucket(ka, oldBucketIdx); ok {
517 // No room, must evict something
518 oldest := a.pickOldest(bucketTypeOld, oldBucketIdx)
519 a.removeFromBucket(oldest, bucketTypeOld, oldBucketIdx)
520 // Find new bucket to put oldest in
521 newBucketIdx := a.calcNewBucket(oldest.Addr, oldest.Src)
522 a.addToNewBucket(oldest, newBucketIdx)
524 if ok := a.addToOldBucket(ka, oldBucketIdx); !ok {
525 log.WithField("know address", ka).Error("could not re-add to oldBucketIdx")
529 // doublesha256( key + sourcegroup +
530 // int64(doublesha256(key + group + sourcegroup))%bucket_per_group ) % num_new_buckets
531 func (a *AddrBook) calcNewBucket(addr, src *NetAddress) int {
533 data1 = append(data1, []byte(a.key)...)
534 data1 = append(data1, []byte(a.groupKey(addr))...)
535 data1 = append(data1, []byte(a.groupKey(src))...)
536 hash1 := doubleSha256(data1)
537 hash64 := binary.BigEndian.Uint64(hash1)
538 hash64 %= newBucketsPerGroup
540 binary.BigEndian.PutUint64(hashbuf[:], hash64)
542 data2 = append(data2, []byte(a.key)...)
543 data2 = append(data2, a.groupKey(src)...)
544 data2 = append(data2, hashbuf[:]...)
546 hash2 := doubleSha256(data2)
547 return int(binary.BigEndian.Uint64(hash2) % newBucketCount)
550 // doublesha256( key + group +
551 // int64(doublesha256(key + addr))%buckets_per_group ) % num_old_buckets
552 func (a *AddrBook) calcOldBucket(addr *NetAddress) int {
554 data1 = append(data1, []byte(a.key)...)
555 data1 = append(data1, []byte(addr.String())...)
556 hash1 := doubleSha256(data1)
557 hash64 := binary.BigEndian.Uint64(hash1)
558 hash64 %= oldBucketsPerGroup
560 binary.BigEndian.PutUint64(hashbuf[:], hash64)
562 data2 = append(data2, []byte(a.key)...)
563 data2 = append(data2, a.groupKey(addr)...)
564 data2 = append(data2, hashbuf[:]...)
566 hash2 := doubleSha256(data2)
567 return int(binary.BigEndian.Uint64(hash2) % oldBucketCount)
570 // Return a string representing the network group of this address.
571 // This is the /16 for IPv6, the /32 (/36 for he.net) for IPv6, the string
572 // "local" for a local address and the string "unroutable for an unroutable
574 func (a *AddrBook) groupKey(na *NetAddress) string {
575 if a.routabilityStrict && na.Local() {
578 if a.routabilityStrict && !na.Routable() {
582 if ipv4 := na.IP.To4(); ipv4 != nil {
583 return (&net.IPNet{IP: na.IP, Mask: net.CIDRMask(16, 32)}).String()
585 if na.RFC6145() || na.RFC6052() {
586 // last four bytes are the ip address
587 ip := net.IP(na.IP[12:16])
588 return (&net.IPNet{IP: ip, Mask: net.CIDRMask(16, 32)}).String()
592 ip := net.IP(na.IP[2:7])
593 return (&net.IPNet{IP: ip, Mask: net.CIDRMask(16, 32)}).String()
597 // teredo tunnels have the last 4 bytes as the v4 address XOR
599 ip := net.IP(make([]byte, 4))
600 for i, byte := range na.IP[12:16] {
603 return (&net.IPNet{IP: ip, Mask: net.CIDRMask(16, 32)}).String()
606 // OK, so now we know ourselves to be a IPv6 address.
607 // bitcoind uses /32 for everything, except for Hurricane Electric's
608 // (he.net) IP range, which it uses /36 for.
610 heNet := &net.IPNet{IP: net.ParseIP("2001:470::"),
611 Mask: net.CIDRMask(32, 128)}
612 if heNet.Contains(na.IP) {
616 return (&net.IPNet{IP: na.IP, Mask: net.CIDRMask(bits, 128)}).String()
619 //-----------------------------------------------------------------------------
624 tracks information about a known network address that is used
625 to determine how viable an address is.
627 type knownAddress struct {
631 LastAttempt time.Time
632 LastSuccess time.Time
637 func newKnownAddress(addr, src *NetAddress) *knownAddress {
638 return &knownAddress{
642 LastAttempt: time.Now(),
643 BucketType: bucketTypeNew,
648 func (ka *knownAddress) isOld() bool {
649 return ka.BucketType == bucketTypeOld
652 func (ka *knownAddress) isNew() bool {
653 return ka.BucketType == bucketTypeNew
656 func (ka *knownAddress) markAttempt() {
657 ka.LastAttempt = time.Now()
661 func (ka *knownAddress) markGood() {
668 func (ka *knownAddress) addBucketRef(bucketIdx int) int {
669 for _, bucket := range ka.Buckets {
670 if bucket == bucketIdx {
674 ka.Buckets = append(ka.Buckets, bucketIdx)
675 return len(ka.Buckets)
678 func (ka *knownAddress) removeBucketRef(bucketIdx int) int {
680 for _, bucket := range ka.Buckets {
681 if bucket != bucketIdx {
682 buckets = append(buckets, bucket)
685 if len(buckets) != len(ka.Buckets)-1 {
689 return len(ka.Buckets)
693 An address is bad if the address in question has not been tried in the last
694 minute and meets one of the following criteria:
696 1) It claims to be from the future
697 2) It hasn't been seen in over a month
698 3) It has failed at least three times and never succeeded
699 4) It has failed ten times in the last week
701 All addresses that meet these criteria are assumed to be worthless and not
702 worth keeping hold of.
704 func (ka *knownAddress) isBad() bool {
705 if ka.BucketType == bucketTypeOld {
708 // Has been attempted in the last minute --> bad
709 if ka.LastAttempt.After(time.Now().Add(-1*time.Minute)) && ka.Attempts != 0 {
714 if ka.LastAttempt.Before(time.Now().Add(-1 * numMissingDays * time.Hour * 24)) {
719 if ka.LastSuccess.IsZero() && ka.Attempts >= numRetries {
723 // Hasn't succeeded in too long?
724 if ka.LastSuccess.Before(time.Now().Add(-1*minBadDays*time.Hour*24)) && ka.Attempts >= maxFailures {