github.com/jonasnick/go-ethereum@v0.7.12-0.20150216215225-22176f05d387/p2p/discover/table.go (about)

     1  // Package discover implements the Node Discovery Protocol.
     2  //
     3  // The Node Discovery protocol provides a way to find RLPx nodes that
     4  // can be connected to. It uses a Kademlia-like protocol to maintain a
     5  // distributed database of the IDs and endpoints of all listening
     6  // nodes.
     7  package discover
     8  
     9  import (
    10  	"net"
    11  	"sort"
    12  	"sync"
    13  	"time"
    14  )
    15  
    16  const (
    17  	alpha      = 3              // Kademlia concurrency factor
    18  	bucketSize = 16             // Kademlia bucket size
    19  	nBuckets   = nodeIDBits + 1 // Number of buckets
    20  )
    21  
    22  type Table struct {
    23  	mutex   sync.Mutex        // protects buckets, their content, and nursery
    24  	buckets [nBuckets]*bucket // index of known nodes by distance
    25  	nursery []*Node           // bootstrap nodes
    26  
    27  	net  transport
    28  	self *Node // metadata of the local node
    29  }
    30  
    31  // transport is implemented by the UDP transport.
    32  // it is an interface so we can test without opening lots of UDP
    33  // sockets and without generating a private key.
    34  type transport interface {
    35  	ping(*Node) error
    36  	findnode(e *Node, target NodeID) ([]*Node, error)
    37  	close()
    38  }
    39  
    40  // bucket contains nodes, ordered by their last activity.
    41  type bucket struct {
    42  	lastLookup time.Time
    43  	entries    []*Node
    44  }
    45  
    46  func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr) *Table {
    47  	tab := &Table{net: t, self: newNode(ourID, ourAddr)}
    48  	for i := range tab.buckets {
    49  		tab.buckets[i] = new(bucket)
    50  	}
    51  	return tab
    52  }
    53  
    54  // Self returns the local node ID.
    55  func (tab *Table) Self() NodeID {
    56  	return tab.self.ID
    57  }
    58  
    59  // Close terminates the network listener.
    60  func (tab *Table) Close() {
    61  	tab.net.close()
    62  }
    63  
    64  // Bootstrap sets the bootstrap nodes. These nodes are used to connect
    65  // to the network if the table is empty. Bootstrap will also attempt to
    66  // fill the table by performing random lookup operations on the
    67  // network.
    68  func (tab *Table) Bootstrap(nodes []*Node) {
    69  	tab.mutex.Lock()
    70  	// TODO: maybe filter nodes with bad fields (nil, etc.) to avoid strange crashes
    71  	tab.nursery = make([]*Node, 0, len(nodes))
    72  	for _, n := range nodes {
    73  		cpy := *n
    74  		tab.nursery = append(tab.nursery, &cpy)
    75  	}
    76  	tab.mutex.Unlock()
    77  	tab.refresh()
    78  }
    79  
    80  // Lookup performs a network search for nodes close
    81  // to the given target. It approaches the target by querying
    82  // nodes that are closer to it on each iteration.
    83  func (tab *Table) Lookup(target NodeID) []*Node {
    84  	var (
    85  		asked          = make(map[NodeID]bool)
    86  		seen           = make(map[NodeID]bool)
    87  		reply          = make(chan []*Node, alpha)
    88  		pendingQueries = 0
    89  	)
    90  	// don't query further if we hit the target or ourself.
    91  	// unlikely to happen often in practice.
    92  	asked[target] = true
    93  	asked[tab.self.ID] = true
    94  
    95  	tab.mutex.Lock()
    96  	// update last lookup stamp (for refresh logic)
    97  	tab.buckets[logdist(tab.self.ID, target)].lastLookup = time.Now()
    98  	// generate initial result set
    99  	result := tab.closest(target, bucketSize)
   100  	tab.mutex.Unlock()
   101  
   102  	for {
   103  		// ask the alpha closest nodes that we haven't asked yet
   104  		for i := 0; i < len(result.entries) && pendingQueries < alpha; i++ {
   105  			n := result.entries[i]
   106  			if !asked[n.ID] {
   107  				asked[n.ID] = true
   108  				pendingQueries++
   109  				go func() {
   110  					result, _ := tab.net.findnode(n, target)
   111  					reply <- result
   112  				}()
   113  			}
   114  		}
   115  		if pendingQueries == 0 {
   116  			// we have asked all closest nodes, stop the search
   117  			break
   118  		}
   119  
   120  		// wait for the next reply
   121  		for _, n := range <-reply {
   122  			cn := n
   123  			if !seen[n.ID] {
   124  				seen[n.ID] = true
   125  				result.push(cn, bucketSize)
   126  			}
   127  		}
   128  		pendingQueries--
   129  	}
   130  	return result.entries
   131  }
   132  
   133  // refresh performs a lookup for a random target to keep buckets full.
   134  func (tab *Table) refresh() {
   135  	ld := -1 // logdist of chosen bucket
   136  	tab.mutex.Lock()
   137  	for i, b := range tab.buckets {
   138  		if i > 0 && b.lastLookup.Before(time.Now().Add(-1*time.Hour)) {
   139  			ld = i
   140  			break
   141  		}
   142  	}
   143  	tab.mutex.Unlock()
   144  
   145  	result := tab.Lookup(randomID(tab.self.ID, ld))
   146  	if len(result) == 0 {
   147  		// bootstrap the table with a self lookup
   148  		tab.mutex.Lock()
   149  		tab.add(tab.nursery)
   150  		tab.mutex.Unlock()
   151  		tab.Lookup(tab.self.ID)
   152  		// TODO: the Kademlia paper says that we're supposed to perform
   153  		// random lookups in all buckets further away than our closest neighbor.
   154  	}
   155  }
   156  
   157  // closest returns the n nodes in the table that are closest to the
   158  // given id. The caller must hold tab.mutex.
   159  func (tab *Table) closest(target NodeID, nresults int) *nodesByDistance {
   160  	// This is a very wasteful way to find the closest nodes but
   161  	// obviously correct. I believe that tree-based buckets would make
   162  	// this easier to implement efficiently.
   163  	close := &nodesByDistance{target: target}
   164  	for _, b := range tab.buckets {
   165  		for _, n := range b.entries {
   166  			close.push(n, nresults)
   167  		}
   168  	}
   169  	return close
   170  }
   171  
   172  func (tab *Table) len() (n int) {
   173  	for _, b := range tab.buckets {
   174  		n += len(b.entries)
   175  	}
   176  	return n
   177  }
   178  
   179  // bumpOrAdd updates the activity timestamp for the given node and
   180  // attempts to insert the node into a bucket. The returned Node might
   181  // not be part of the table. The caller must hold tab.mutex.
   182  func (tab *Table) bumpOrAdd(node NodeID, from *net.UDPAddr) (n *Node) {
   183  	b := tab.buckets[logdist(tab.self.ID, node)]
   184  	if n = b.bump(node); n == nil {
   185  		n = newNode(node, from)
   186  		if len(b.entries) == bucketSize {
   187  			tab.pingReplace(n, b)
   188  		} else {
   189  			b.entries = append(b.entries, n)
   190  		}
   191  	}
   192  	return n
   193  }
   194  
   195  func (tab *Table) pingReplace(n *Node, b *bucket) {
   196  	old := b.entries[bucketSize-1]
   197  	go func() {
   198  		if err := tab.net.ping(old); err == nil {
   199  			// it responded, we don't need to replace it.
   200  			return
   201  		}
   202  		// it didn't respond, replace the node if it is still the oldest node.
   203  		tab.mutex.Lock()
   204  		if len(b.entries) > 0 && b.entries[len(b.entries)-1] == old {
   205  			// slide down other entries and put the new one in front.
   206  			// TODO: insert in correct position to keep the order
   207  			copy(b.entries[1:], b.entries)
   208  			b.entries[0] = n
   209  		}
   210  		tab.mutex.Unlock()
   211  	}()
   212  }
   213  
   214  // bump updates the activity timestamp for the given node.
   215  // The caller must hold tab.mutex.
   216  func (tab *Table) bump(node NodeID) {
   217  	tab.buckets[logdist(tab.self.ID, node)].bump(node)
   218  }
   219  
   220  // add puts the entries into the table if their corresponding
   221  // bucket is not full. The caller must hold tab.mutex.
   222  func (tab *Table) add(entries []*Node) {
   223  outer:
   224  	for _, n := range entries {
   225  		if n == nil || n.ID == tab.self.ID {
   226  			// skip bad entries. The RLP decoder returns nil for empty
   227  			// input lists.
   228  			continue
   229  		}
   230  		bucket := tab.buckets[logdist(tab.self.ID, n.ID)]
   231  		for i := range bucket.entries {
   232  			if bucket.entries[i].ID == n.ID {
   233  				// already in bucket
   234  				continue outer
   235  			}
   236  		}
   237  		if len(bucket.entries) < bucketSize {
   238  			bucket.entries = append(bucket.entries, n)
   239  		}
   240  	}
   241  }
   242  
   243  func (b *bucket) bump(id NodeID) *Node {
   244  	for i, n := range b.entries {
   245  		if n.ID == id {
   246  			n.active = time.Now()
   247  			// move it to the front
   248  			copy(b.entries[1:], b.entries[:i+1])
   249  			b.entries[0] = n
   250  			return n
   251  		}
   252  	}
   253  	return nil
   254  }
   255  
   256  // nodesByDistance is a list of nodes, ordered by
   257  // distance to target.
   258  type nodesByDistance struct {
   259  	entries []*Node
   260  	target  NodeID
   261  }
   262  
   263  // push adds the given node to the list, keeping the total size below maxElems.
   264  func (h *nodesByDistance) push(n *Node, maxElems int) {
   265  	ix := sort.Search(len(h.entries), func(i int) bool {
   266  		return distcmp(h.target, h.entries[i].ID, n.ID) > 0
   267  	})
   268  	if len(h.entries) < maxElems {
   269  		h.entries = append(h.entries, n)
   270  	}
   271  	if ix == len(h.entries) {
   272  		// farther away than all nodes we already have.
   273  		// if there was room for it, the node is now the last element.
   274  	} else {
   275  		// slide existing entries down to make room
   276  		// this will overwrite the entry we just appended.
   277  		copy(h.entries[ix+1:], h.entries[ix:])
   278  		h.entries[ix] = n
   279  	}
   280  }