github.com/klaytn/klaytn@v1.12.1/networks/p2p/discover/table.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2015 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from p2p/discover/table.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package discover
    22  
    23  import (
    24  	crand "crypto/rand"
    25  	"encoding/binary"
    26  	"errors"
    27  	"fmt"
    28  	mrand "math/rand"
    29  	"net"
    30  	"sort"
    31  	"sync"
    32  	"time"
    33  
    34  	"github.com/klaytn/klaytn/common"
    35  	"github.com/klaytn/klaytn/crypto"
    36  	"github.com/klaytn/klaytn/log"
    37  	"github.com/klaytn/klaytn/networks/p2p/netutil"
    38  )
    39  
    40  const (
    41  	alpha           = 3  // Kademlia concurrency factor
    42  	bucketSize      = 16 // Kademlia bucket size
    43  	maxReplacements = 10 // Size of per-bucket replacement list
    44  
    45  	maxBondingPingPongs = 16 // Limit on the number of concurrent ping/pong interactions
    46  	maxFindnodeFailures = 5  // Nodes exceeding this limit are dropped
    47  
    48  	refreshInterval    = 30 * time.Minute
    49  	revalidateInterval = 10 * time.Second
    50  	copyNodesInterval  = 30 * time.Second
    51  
    52  	seedCount  = 30
    53  	seedMaxAge = 5 * 24 * time.Hour
    54  )
    55  
    56  type DiscoveryType uint8
    57  
    58  type Discovery interface {
    59  	Self() *Node
    60  	Close()
    61  	Resolve(target NodeID, targetType NodeType) *Node
    62  	Lookup(target NodeID, targetType NodeType) []*Node
    63  	GetNodes(targetType NodeType, max int) []*Node
    64  	ReadRandomNodes([]*Node, NodeType) int
    65  	RetrieveNodes(target common.Hash, nType NodeType, nresults int) []*Node // replace of closest():Table
    66  
    67  	HasBond(id NodeID) bool
    68  	Bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16, nType NodeType) (*Node, error)
    69  	IsAuthorized(fromID NodeID, nType NodeType) bool
    70  
    71  	// interfaces for API
    72  	Name() string
    73  	CreateUpdateNodeOnDB(n *Node) error
    74  	CreateUpdateNodeOnTable(n *Node) error
    75  	GetNodeFromDB(id NodeID) (*Node, error)
    76  	DeleteNodeFromDB(n *Node) error
    77  	DeleteNodeFromTable(n *Node) error
    78  	GetBucketEntries() []*Node
    79  	GetReplacements() []*Node
    80  
    81  	GetAuthorizedNodes() []*Node
    82  	PutAuthorizedNodes(nodes []*Node)
    83  	DeleteAuthorizedNodes(nodes []*Node)
    84  }
    85  
    86  type Table struct {
    87  	nursery []*Node     // bootstrap nodes
    88  	rand    *mrand.Rand // source of randomness, periodically reseeded
    89  	randMu  sync.Mutex
    90  	ips     netutil.DistinctNetSet
    91  
    92  	db         *nodeDB // database of known nodes
    93  	refreshReq chan chan struct{}
    94  	initDone   chan struct{}
    95  	closeReq   chan struct{}
    96  	closed     chan struct{}
    97  
    98  	bondmu    sync.Mutex
    99  	bonding   map[NodeID]*bondproc
   100  	bondslots chan struct{} // limits total number of active bonding processes
   101  
   102  	nodeAddedHook func(*Node) // for testing
   103  
   104  	net  transport
   105  	self *Node // metadata of the local node
   106  
   107  	storages   map[NodeType]discoverStorage
   108  	storagesMu sync.RWMutex
   109  
   110  	localLogger log.Logger
   111  }
   112  
   113  type bondproc struct {
   114  	err  error
   115  	n    *Node
   116  	done chan struct{}
   117  }
   118  
   119  // transport is implemented by the UDP transport.
   120  // it is an interface so we can test without opening lots of UDP
   121  // sockets and without generating a private key.
   122  type transport interface {
   123  	ping(toid NodeID, toaddr *net.UDPAddr) error
   124  	waitping(NodeID) error
   125  	findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID, targetNT NodeType, max int) ([]*Node, error)
   126  	close()
   127  }
   128  
   129  func newTable(cfg *Config) (Discovery, error) {
   130  	// If no node database was given, use an in-memory one
   131  	db, err := newNodeDB(cfg.NodeDBPath, Version, cfg.Id)
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  
   136  	tab := &Table{
   137  		net:         cfg.udp,
   138  		db:          db,
   139  		self:        NewNode(cfg.Id, cfg.Addr.IP, uint16(cfg.Addr.Port), uint16(cfg.Addr.Port), nil, cfg.NodeType),
   140  		bonding:     make(map[NodeID]*bondproc),
   141  		bondslots:   make(chan struct{}, maxBondingPingPongs),
   142  		refreshReq:  make(chan chan struct{}),
   143  		initDone:    make(chan struct{}),
   144  		closeReq:    make(chan struct{}),
   145  		closed:      make(chan struct{}),
   146  		rand:        mrand.New(mrand.NewSource(0)),
   147  		storages:    make(map[NodeType]discoverStorage),
   148  		localLogger: logger.NewWith("Discover", "Table"),
   149  	}
   150  
   151  	switch cfg.NodeType {
   152  	case NodeTypeCN:
   153  		tab.addStorage(NodeTypeCN, &simpleStorage{targetType: NodeTypeCN, noDiscover: true, max: 100})
   154  		tab.addStorage(NodeTypeBN, &simpleStorage{targetType: NodeTypeBN, noDiscover: true, max: 3})
   155  	case NodeTypePN:
   156  		tab.addStorage(NodeTypePN, &simpleStorage{targetType: NodeTypePN, noDiscover: true, max: 1})
   157  		tab.addStorage(NodeTypeEN, &KademliaStorage{targetType: NodeTypeEN, noDiscover: true})
   158  		tab.addStorage(NodeTypeBN, &simpleStorage{targetType: NodeTypeBN, noDiscover: true, max: 3})
   159  	case NodeTypeEN:
   160  		tab.addStorage(NodeTypePN, &simpleStorage{targetType: NodeTypePN, noDiscover: true, max: 2})
   161  		tab.addStorage(NodeTypeEN, &KademliaStorage{targetType: NodeTypeEN})
   162  		tab.addStorage(NodeTypeBN, &simpleStorage{targetType: NodeTypeBN, noDiscover: true, max: 3})
   163  	case NodeTypeBN:
   164  		tab.addStorage(NodeTypeCN, NewSimpleStorage(NodeTypeCN, true, 100, cfg.AuthorizedNodes))
   165  		tab.addStorage(NodeTypePN, NewSimpleStorage(NodeTypePN, true, 100, cfg.AuthorizedNodes))
   166  		tab.addStorage(NodeTypeEN, &KademliaStorage{targetType: NodeTypeEN, noDiscover: true})
   167  		tab.addStorage(NodeTypeBN, &simpleStorage{targetType: NodeTypeBN, max: 3})
   168  	}
   169  
   170  	if err := tab.setFallbackNodes(cfg.Bootnodes); err != nil {
   171  		return nil, err
   172  	}
   173  	for i := 0; i < cap(tab.bondslots); i++ {
   174  		tab.bondslots <- struct{}{}
   175  	}
   176  
   177  	tab.seedRand()
   178  	tab.loadSeedNodes(false)
   179  	// Start the background expiration goroutine after loading seeds so that the search for
   180  	// seed nodes also considers older nodes that would otherwise be removed by the
   181  	// expiration.
   182  	tab.db.ensureExpirer()
   183  	tab.localLogger.Debug("new "+tab.Name()+" created", "err", nil)
   184  	return tab, nil
   185  }
   186  
   187  func (tab *Table) IsAuthorized(fromID NodeID, nType NodeType) bool {
   188  	tab.storagesMu.RLock()
   189  	defer tab.storagesMu.RUnlock()
   190  	if tab.storages[nType] != nil {
   191  		return tab.storages[nType].isAuthorized(fromID)
   192  	}
   193  	return true
   194  }
   195  
   196  // setFallbackNodes sets the initial points of contact. These nodes
   197  // are used to connect to the network if the table is empty and there
   198  // are no known nodes in the database.
   199  func (tab *Table) setFallbackNodes(nodes []*Node) error {
   200  	for _, n := range nodes {
   201  		if err := n.validateComplete(); err != nil {
   202  			return fmt.Errorf("bad bootstrap/fallback node %q (%v)", n, err)
   203  		}
   204  	}
   205  	tab.nursery = make([]*Node, 0, len(nodes))
   206  	for _, n := range nodes {
   207  		cpy := *n
   208  		// Recompute cpy.sha because the node might not have been
   209  		// created by NewNode or ParseNode.
   210  		cpy.sha = crypto.Keccak256Hash(n.ID[:])
   211  		tab.nursery = append(tab.nursery, &cpy)
   212  	}
   213  	return nil
   214  }
   215  
   216  func (tab *Table) findNewNode(seeds *nodesByDistance, targetID NodeID, targetNT NodeType, recursiveFind bool, max int) []*Node {
   217  	var (
   218  		asked          = make(map[NodeID]bool)
   219  		seen           = make(map[NodeID]bool)
   220  		reply          = make(chan []*Node, alpha)
   221  		pendingQueries = 0
   222  	)
   223  
   224  	// don't query further if we hit ourself.
   225  	// unlikely to happen often in practice.
   226  	asked[tab.self.ID] = true
   227  	for _, e := range seeds.entries {
   228  		seen[e.ID] = true
   229  	}
   230  
   231  	for {
   232  		// ask the alpha closest nodes that we haven't asked yet
   233  		for i := 0; i < len(seeds.entries) && pendingQueries < alpha; i++ {
   234  			n := seeds.entries[i]
   235  			if !asked[n.ID] {
   236  				asked[n.ID] = true
   237  				pendingQueries++
   238  				go func() {
   239  					// Find potential neighbors to bond with
   240  					r, err := tab.net.findnode(n.ID, n.addr(), targetID, targetNT, max)
   241  					if err != nil {
   242  						// Bump the failure counter to detect and evacuate non-bonded entries
   243  						fails := tab.db.findFails(n.ID) + 1
   244  						tab.db.updateFindFails(n.ID, fails)
   245  						tab.localLogger.Trace("Bumping findnode failure counter", "id", n.ID, "failcount", fails)
   246  
   247  						if fails >= maxFindnodeFailures {
   248  							tab.localLogger.Trace("Too many findnode failures, dropping", "id", n.ID, "failcount", fails)
   249  							tab.delete(n)
   250  						}
   251  					}
   252  					if targetNT != NodeTypeBN {
   253  						r = removeBn(r)
   254  					}
   255  					reply <- tab.bondall(r)
   256  				}()
   257  			}
   258  		}
   259  		if pendingQueries == 0 {
   260  			// we have asked all closest nodes, stop the search
   261  			break
   262  		}
   263  
   264  		if recursiveFind {
   265  			// wait for the next reply
   266  			for _, n := range <-reply {
   267  				if n != nil && !seen[n.ID] {
   268  					seen[n.ID] = true
   269  					seeds.push(n, max)
   270  				}
   271  			}
   272  			pendingQueries--
   273  		} else {
   274  			for i := 0; i < pendingQueries; i++ {
   275  				for _, n := range <-reply {
   276  					if n != nil && !seen[n.ID] {
   277  						seen[n.ID] = true
   278  						if len(seeds.entries) < max {
   279  							seeds.entries = append(seeds.entries, n)
   280  						}
   281  					}
   282  				}
   283  			}
   284  			break
   285  		}
   286  	}
   287  	if targetNT != NodeTypeBN {
   288  		seeds.entries = removeBn(seeds.entries)
   289  	}
   290  	tab.localLogger.Debug("findNewNode: found nodes", "length", len(seeds.entries), "nodeType", targetNT)
   291  	return seeds.entries
   292  }
   293  
   294  func (tab *Table) addStorage(nType NodeType, s discoverStorage) {
   295  	tab.storagesMu.Lock()
   296  	defer tab.storagesMu.Unlock()
   297  	s.setTable(tab)
   298  	tab.storages[nType] = s
   299  	s.init()
   300  }
   301  
   302  func (tab *Table) seedRand() {
   303  	var b [8]byte
   304  	crand.Read(b[:])
   305  
   306  	// tab.mutex.Lock()
   307  	tab.randMu.Lock()
   308  	tab.rand.Seed(int64(binary.BigEndian.Uint64(b[:])))
   309  	tab.randMu.Unlock()
   310  	// tab.mutex.Unlock()
   311  }
   312  
   313  // Self returns the local node.
   314  // The returned node should not be modified by the caller.
   315  func (tab *Table) Self() *Node {
   316  	return tab.self
   317  }
   318  
   319  // ReadRandomNodes fills the given slice with random nodes from the
   320  // table. It will not write the same node more than once. The nodes in
   321  // the slice are copies and can be modified by the caller.
   322  func (tab *Table) ReadRandomNodes(buf []*Node, nType NodeType) (n int) {
   323  	if !tab.isInitDone() {
   324  		return 0
   325  	}
   326  
   327  	tab.storagesMu.RLock()
   328  	defer tab.storagesMu.RUnlock()
   329  	if tab.storages[nType] == nil {
   330  		tab.localLogger.Warn("ReadRandomNodes: Not Supported NodeType", "NodeType", nType)
   331  		return 0
   332  	}
   333  
   334  	return tab.storages[nType].readRandomNodes(buf)
   335  }
   336  
   337  // Close terminates the network listener and flushes the node database.
   338  func (tab *Table) Close() {
   339  	select {
   340  	case <-tab.closed:
   341  		// already closed.
   342  	case tab.closeReq <- struct{}{}:
   343  		<-tab.closed // wait for refreshLoop to end.
   344  	}
   345  }
   346  
   347  // isInitDone returns whether the table's initial seeding procedure has completed.
   348  func (tab *Table) isInitDone() bool {
   349  	select {
   350  	case <-tab.initDone:
   351  		return true
   352  	default:
   353  		return false
   354  	}
   355  }
   356  
   357  // Resolve searches for a specific node with the given ID.
   358  // It returns nil if the node could not be found.
   359  func (tab *Table) Resolve(targetID NodeID, targetType NodeType) *Node {
   360  	// If the node is present in the local table, no
   361  	// network interaction is required.
   362  	hash := crypto.Keccak256Hash(targetID[:])
   363  	cl := tab.closest(hash, targetType, 1)
   364  	if len(cl.entries) > 0 && cl.entries[0].ID == targetID {
   365  		return cl.entries[0]
   366  	}
   367  	// Otherwise, do a network lookup.
   368  	result := tab.Lookup(targetID, targetType)
   369  	for _, n := range result {
   370  		if n.ID == targetID {
   371  			return n
   372  		}
   373  	}
   374  	return nil
   375  }
   376  
   377  // Lookup performs a network search for nodes close
   378  // to the given target. It approaches the target by querying
   379  // nodes that are closer to it on each iteration.
   380  // The given target does not need to be an actual node
   381  // identifier.
   382  func (tab *Table) Lookup(targetID NodeID, targetType NodeType) []*Node {
   383  	return tab.lookup(targetID, true, targetType)
   384  }
   385  
   386  func (tab *Table) lookup(targetID NodeID, refreshIfEmpty bool, targetNT NodeType) []*Node {
   387  	tab.storagesMu.RLock()
   388  	defer tab.storagesMu.RUnlock()
   389  
   390  	if tab.storages[targetNT] == nil {
   391  		tab.localLogger.Warn("lookup: Not Supported NodeType", "NodeType", targetNT)
   392  		return []*Node{}
   393  	}
   394  	return tab.storages[targetNT].lookup(targetID, refreshIfEmpty, targetNT)
   395  }
   396  
   397  func (tab *Table) GetNodes(targetNT NodeType, max int) []*Node {
   398  	tab.storagesMu.RLock()
   399  	defer tab.storagesMu.RUnlock()
   400  
   401  	if tab.storages[targetNT] == nil {
   402  		tab.localLogger.Warn("getNodes: Not Supported NodeType", "NodeType", targetNT)
   403  		return []*Node{}
   404  	}
   405  	return tab.storages[targetNT].getNodes(max)
   406  }
   407  
   408  func removeBn(nodes []*Node) []*Node {
   409  	tmp := nodes[:0]
   410  	for _, n := range nodes {
   411  		if n.NType != NodeTypeBN {
   412  			tmp = append(tmp, n)
   413  		}
   414  	}
   415  	return tmp
   416  }
   417  
   418  func (tab *Table) refresh() <-chan struct{} {
   419  	done := make(chan struct{})
   420  	select {
   421  	case tab.refreshReq <- done:
   422  	case <-tab.closed:
   423  		close(done)
   424  	}
   425  	return done
   426  }
   427  
   428  // loop schedules refresh, revalidate runs and coordinates shutdown.
   429  func (tab *Table) loop() {
   430  	var (
   431  		revalidate     = time.NewTimer(tab.nextRevalidateTime())
   432  		refresh        = time.NewTicker(refreshInterval)
   433  		copyNodes      = time.NewTicker(copyNodesInterval)
   434  		revalidateDone = make(chan struct{})
   435  		refreshDone    = make(chan struct{})           // where doRefresh reports completion
   436  		waiting        = []chan struct{}{tab.initDone} // holds waiting callers while doRefresh runs
   437  	)
   438  	defer refresh.Stop()
   439  	defer revalidate.Stop()
   440  	defer copyNodes.Stop()
   441  
   442  	// Start initial refresh.
   443  	go tab.doRefresh(refreshDone)
   444  
   445  loop:
   446  	for {
   447  		select {
   448  		case <-refresh.C:
   449  			tab.seedRand()
   450  			if refreshDone == nil {
   451  				refreshDone = make(chan struct{})
   452  				go tab.doRefresh(refreshDone)
   453  			}
   454  		case req := <-tab.refreshReq:
   455  			waiting = append(waiting, req)
   456  			if refreshDone == nil {
   457  				refreshDone = make(chan struct{})
   458  				go tab.doRefresh(refreshDone)
   459  			}
   460  		case <-refreshDone:
   461  			for _, ch := range waiting {
   462  				close(ch)
   463  			}
   464  			waiting, refreshDone = nil, nil
   465  		case <-revalidate.C:
   466  			go tab.doRevalidate(revalidateDone)
   467  		case <-revalidateDone:
   468  			tt := tab.nextRevalidateTime()
   469  			revalidate.Reset(tt)
   470  		case <-copyNodes.C:
   471  			go tab.copyBondedNodes()
   472  		case <-tab.closeReq:
   473  			break loop
   474  		}
   475  	}
   476  
   477  	if tab.net != nil {
   478  		tab.net.close()
   479  	}
   480  	if refreshDone != nil {
   481  		<-refreshDone
   482  	}
   483  	for _, ch := range waiting {
   484  		close(ch)
   485  	}
   486  	tab.db.close()
   487  	close(tab.closed)
   488  }
   489  
   490  // doRefresh performs a lookup for a random target to keep buckets
   491  // full. seed nodes are inserted if the table is empty (initial
   492  // bootstrap or discarded faulty peers).
   493  func (tab *Table) doRefresh(done chan struct{}) {
   494  	tab.localLogger.Trace("doRefresh()")
   495  	defer close(done)
   496  
   497  	// Load nodes from the database and insert
   498  	// them. This should yield a few previously seen nodes that are
   499  	// (hopefully) still alive.
   500  	tab.loadSeedNodes(true)
   501  
   502  	tab.storagesMu.RLock()
   503  	defer tab.storagesMu.RUnlock()
   504  	for _, ds := range tab.storages {
   505  		ds.doRefresh()
   506  	}
   507  }
   508  
   509  func (tab *Table) loadSeedNodes(bond bool) {
   510  	// TODO-Klaytn-Node Separate logic to storages.
   511  	seeds := tab.db.querySeeds(seedCount, seedMaxAge)
   512  	seeds = removeBn(seeds)
   513  	seeds = append(seeds, tab.nursery...)
   514  	if bond {
   515  		seeds = tab.bondall(seeds)
   516  	}
   517  	for i := range seeds {
   518  		seed := seeds[i]
   519  		age := log.Lazy{Fn: func() interface{} { return time.Since(tab.db.bondTime(seed.ID)) }}
   520  		tab.localLogger.Debug("Found seed node in database", "id", seed.ID, "addr", seed.addr(), "age", age)
   521  		tab.add(seed)
   522  	}
   523  }
   524  
   525  // doRevalidate checks that the last node in a random bucket is still live
   526  // and replaces or deletes the node if it isn't.
   527  func (tab *Table) doRevalidate(done chan<- struct{}) {
   528  	defer func() { done <- struct{}{} }()
   529  
   530  	tab.storagesMu.RLock()
   531  	defer tab.storagesMu.RUnlock()
   532  	for _, ds := range tab.storages {
   533  		ds.doRevalidate()
   534  	}
   535  }
   536  
   537  func (tab *Table) nextRevalidateTime() time.Duration {
   538  	tab.randMu.Lock()
   539  	defer tab.randMu.Unlock()
   540  
   541  	return time.Duration(tab.rand.Int63n(int64(revalidateInterval)))
   542  }
   543  
   544  // copyBondedNodes adds nodes from the table to the database if they have been in the table
   545  // longer then minTableTime.
   546  func (tab *Table) copyBondedNodes() {
   547  	tab.storagesMu.RLock()
   548  	defer tab.storagesMu.RUnlock()
   549  	for _, ds := range tab.storages {
   550  		ds.copyBondedNodes()
   551  	}
   552  }
   553  
   554  // closest returns the n nodes in the table that are closest to the
   555  // given id. The caller must hold tab.mutex.
   556  func (tab *Table) closest(target common.Hash, nType NodeType, nresults int) *nodesByDistance {
   557  	tab.storagesMu.RLock()
   558  	defer tab.storagesMu.RUnlock()
   559  
   560  	if tab.storages[nType] == nil {
   561  		tab.localLogger.Warn("closest(): Not Supported NodeType", "NodeType", nType)
   562  		return &nodesByDistance{}
   563  	}
   564  	return tab.storages[nType].closest(target, nresults)
   565  }
   566  
   567  // RetrieveNodes returns node list except bootnode. This method is used to make a result of FINDNODE request.
   568  func (tab *Table) RetrieveNodes(target common.Hash, nType NodeType, nresults int) []*Node {
   569  	tab.storagesMu.RLock()
   570  	defer tab.storagesMu.RUnlock()
   571  
   572  	if tab.storages[nType] == nil {
   573  		tab.localLogger.Warn("RetrieveNodes: Not Supported NodeType", "NodeType", nType)
   574  		return []*Node{}
   575  	}
   576  	nodes := tab.storages[nType].closest(target, nresults).entries
   577  	if nType != NodeTypeBN {
   578  		nodes = removeBn(nodes)
   579  	}
   580  	return nodes
   581  }
   582  
   583  func (tab *Table) len() (n int) {
   584  	tab.storagesMu.RLock()
   585  	defer tab.storagesMu.RUnlock()
   586  
   587  	for _, ds := range tab.storages {
   588  		n += ds.len()
   589  	}
   590  	return n
   591  }
   592  
   593  func (tab *Table) nodes() (n []*Node) {
   594  	tab.storagesMu.RLock()
   595  	defer tab.storagesMu.RUnlock()
   596  
   597  	for _, ds := range tab.storages {
   598  		n = append(n, ds.nodeAll()...)
   599  	}
   600  	return n
   601  }
   602  
   603  // bondall bonds with all given nodes concurrently and returns
   604  // those nodes for which bonding has probably succeeded.
   605  func (tab *Table) bondall(nodes []*Node) (result []*Node) {
   606  	rc := make(chan *Node, len(nodes))
   607  	for i := range nodes {
   608  		go func(n *Node) {
   609  			nn, _ := tab.Bond(false, n.ID, n.addr(), n.TCP, n.NType)
   610  			rc <- nn
   611  		}(nodes[i])
   612  	}
   613  	for range nodes {
   614  		if n := <-rc; n != nil {
   615  			result = append(result, n)
   616  		}
   617  	}
   618  	return result
   619  }
   620  
   621  // Bond ensures the local node has a bond with the given remote node.
   622  // It also attempts to insert the node into the table if bonding succeeds.
   623  // The caller must not hold tab.mutex.
   624  //
   625  // A bond is must be established before sending findnode requests.
   626  // Both sides must have completed a ping/pong exchange for a bond to
   627  // exist. The total number of active bonding processes is limited in
   628  // order to restrain network use.
   629  //
   630  // bond is meant to operate idempotently in that bonding with a remote
   631  // node which still remembers a previously established bond will work.
   632  // The remote node will simply not send a ping back, causing waitping
   633  // to time out.
   634  //
   635  // If pinged is true, the remote node has just pinged us and one half
   636  // of the process can be skipped.
   637  func (tab *Table) Bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16, nType NodeType) (*Node, error) {
   638  	if id == tab.self.ID {
   639  		return nil, errors.New("is self")
   640  	}
   641  	if pinged && !tab.isInitDone() {
   642  		return nil, errors.New("still initializing")
   643  	}
   644  	// Start bonding if we haven't seen this node for a while or if it failed findnode too often.
   645  	node, fails := tab.db.node(id), tab.db.findFails(id)
   646  	age := time.Since(tab.db.bondTime(id))
   647  	var result error
   648  	// A Bootnode always add node(cn, pn, en) to table.
   649  	if fails > 0 || age > nodeDBNodeExpiration || (node == nil && tab.self.NType == NodeTypeBN) {
   650  		tab.localLogger.Trace("Bond - Starting bonding ping/pong", "id", id, "known", node != nil, "failcount", fails, "age", age)
   651  
   652  		tab.bondmu.Lock()
   653  		w := tab.bonding[id]
   654  		if w != nil {
   655  			// Wait for an existing bonding process to complete.
   656  			tab.bondmu.Unlock()
   657  			<-w.done
   658  		} else {
   659  			// Register a new bonding process.
   660  			w = &bondproc{done: make(chan struct{})}
   661  			tab.bonding[id] = w
   662  			tab.bondmu.Unlock()
   663  			// Do the ping/pong. The result goes into w.
   664  			tab.pingpong(w, pinged, id, addr, tcpPort, nType)
   665  			// Unregister the process after it's done.
   666  			tab.bondmu.Lock()
   667  			delete(tab.bonding, id)
   668  			tab.bondmu.Unlock()
   669  		}
   670  		// Retrieve the bonding results
   671  		result = w.err
   672  		tab.localLogger.Trace("Bond", "error", result)
   673  		if result == nil {
   674  			node = w.n
   675  		}
   676  	}
   677  	// Add the node to the table even if the bonding ping/pong
   678  	// fails. It will be replaced quickly if it continues to be
   679  	// unresponsive.
   680  	if node != nil {
   681  		tab.localLogger.Trace("Bond - Add", "id", node.ID, "type", node.NType, "sha", node.sha)
   682  		tab.add(node)
   683  		tab.db.updateFindFails(id, 0)
   684  		lenEntries := len(tab.GetBucketEntries())
   685  		lenReplacements := len(tab.GetReplacements())
   686  		bucketEntriesGauge.Update(int64(lenEntries))
   687  		bucketReplacementsGauge.Update(int64(lenReplacements))
   688  	}
   689  	return node, result
   690  }
   691  
   692  func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16, nType NodeType) {
   693  	// Request a bonding slot to limit network usage
   694  	<-tab.bondslots
   695  	defer func() { tab.bondslots <- struct{}{} }()
   696  
   697  	// Ping the remote side and wait for a pong.
   698  	if w.err = tab.ping(id, addr); w.err != nil {
   699  		close(w.done)
   700  		return
   701  	}
   702  	if !pinged {
   703  		// Give the remote node a chance to ping us before we start
   704  		// sending findnode requests. If they still remember us,
   705  		// waitping will simply time out.
   706  		tab.localLogger.Trace("pingpong-waitping", "to", id)
   707  		tab.net.waitping(id)
   708  	}
   709  	// Bonding succeeded, update the node database.
   710  	w.n = NewNode(id, addr.IP, uint16(addr.Port), tcpPort, nil, nType)
   711  	tab.localLogger.Trace("pingpong-success, make new node", "node", w.n)
   712  	close(w.done)
   713  }
   714  
   715  // ping a remote endpoint and wait for a reply, also updating the node
   716  // database accordingly.
   717  func (tab *Table) ping(id NodeID, addr *net.UDPAddr) error {
   718  	tab.localLogger.Trace("ping", "to", id)
   719  	tab.db.updateLastPing(id, time.Now())
   720  	if err := tab.net.ping(id, addr); err != nil {
   721  		return err
   722  	}
   723  	tab.db.updateBondTime(id, time.Now())
   724  	return nil
   725  }
   726  
   727  // bucket returns the bucket for the given node ID hash.
   728  // This method is for only unit tests.
   729  func (tab *Table) bucket(sha common.Hash, nType NodeType) *bucket {
   730  	tab.storagesMu.RLock()
   731  	defer tab.storagesMu.RUnlock()
   732  
   733  	if tab.storages[nType] == nil {
   734  		tab.localLogger.Warn("bucket(): Not Supported NodeType", "NodeType", nType)
   735  		return &bucket{}
   736  	}
   737  	if _, ok := tab.storages[nType].(*KademliaStorage); !ok {
   738  		tab.localLogger.Warn("bucket(): bucket() only allowed to use at KademliaStorage", "NodeType", nType)
   739  		return &bucket{}
   740  	}
   741  	ks := tab.storages[nType].(*KademliaStorage)
   742  
   743  	ks.bucketsMu.Lock()
   744  	defer ks.bucketsMu.Unlock()
   745  	return ks.bucket(sha)
   746  }
   747  
   748  // add attempts to add the given node its corresponding bucket. If the
   749  // bucket has space available, adding the node succeeds immediately.
   750  // Otherwise, the node is added if the least recently active node in
   751  // the bucket does not respond to a ping packet.
   752  //
   753  // The caller must not hold tab.mutex.
   754  func (tab *Table) add(new *Node) {
   755  	tab.localLogger.Trace("add(node)", "NodeType", new.NType, "node", new, "sha", new.sha)
   756  	tab.storagesMu.RLock()
   757  	defer tab.storagesMu.RUnlock()
   758  	if new.NType == NodeTypeBN {
   759  		for _, ds := range tab.storages {
   760  			ds.add(new)
   761  		}
   762  	} else {
   763  		if tab.storages[new.NType] == nil {
   764  			tab.localLogger.Warn("add(): Not Supported NodeType", "NodeType", new.NType)
   765  			return
   766  		}
   767  		tab.storages[new.NType].add(new)
   768  	}
   769  }
   770  
   771  // stuff adds nodes the table to the end of their corresponding bucket
   772  // if the bucket is not full.
   773  func (tab *Table) stuff(nodes []*Node, nType NodeType) {
   774  	tab.storagesMu.RLock()
   775  	defer tab.storagesMu.RUnlock()
   776  	if tab.storages[nType] == nil {
   777  		tab.localLogger.Warn("stuff(): Not Supported NodeType", "NodeType", nType)
   778  		return
   779  	}
   780  	tab.storages[nType].stuff(nodes)
   781  }
   782  
   783  // delete removes an entry from the node table (used to evacuate
   784  // failed/non-bonded discovery peers).
   785  func (tab *Table) delete(node *Node) {
   786  	tab.storagesMu.RLock()
   787  	defer tab.storagesMu.RUnlock()
   788  	for _, ds := range tab.storages {
   789  		ds.delete(node)
   790  	}
   791  }
   792  
   793  func (tab *Table) HasBond(id NodeID) bool {
   794  	return tab.db.hasBond(id)
   795  }
   796  
   797  // nodesByDistance is a list of nodes, ordered by
   798  // distance to target.
   799  type nodesByDistance struct {
   800  	entries []*Node
   801  	target  common.Hash
   802  }
   803  
   804  // push adds the given node to the list, keeping the total size below maxElems.
   805  func (h *nodesByDistance) push(n *Node, maxElems int) {
   806  	ix := sort.Search(len(h.entries), func(i int) bool {
   807  		return distcmp(h.target, h.entries[i].sha, n.sha) > 0
   808  	})
   809  	if len(h.entries) < maxElems {
   810  		h.entries = append(h.entries, n)
   811  	}
   812  	if ix == len(h.entries) {
   813  		// farther away than all nodes we already have.
   814  		// if there was room for it, the node is now the last element.
   815  	} else {
   816  		// slide existing entries down to make room
   817  		// this will overwrite the entry we just appended.
   818  		copy(h.entries[ix+1:], h.entries[ix:])
   819  		h.entries[ix] = n
   820  	}
   821  }
   822  
   823  func (h *nodesByDistance) String() string {
   824  	return fmt.Sprintf("nodeByDistance target: %s, entries: %s", h.target, h.entries)
   825  }