github.com/KYVENetwork/cometbft/v38@v38.0.3/p2p/pex/addrbook.go (about)

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