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