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