github.com/core-coin/go-core/v2@v2.1.9/p2p/discv5/table.go (about)

     1  // Copyright 2016 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-core library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package discv5 is a prototype implementation of Discvery v5.
    18  // Deprecated: do not use this package.
    19  package discv5
    20  
    21  import (
    22  	"crypto/rand"
    23  	"encoding/binary"
    24  	"fmt"
    25  	"net"
    26  	"sort"
    27  
    28  	"github.com/core-coin/go-core/v2/common"
    29  )
    30  
    31  const (
    32  	alpha      = 3  // Kademlia concurrency factor
    33  	bucketSize = 16 // Kademlia bucket size
    34  	hashBits   = len(common.Hash{}) * 8
    35  	nBuckets   = hashBits + 1 // Number of buckets
    36  
    37  	maxFindnodeFailures = 5
    38  )
    39  
    40  type Table struct {
    41  	count         int               // number of nodes
    42  	buckets       [nBuckets]*bucket // index of known nodes by distance
    43  	nodeAddedHook func(*Node)       // for testing
    44  	self          *Node             // metadata of the local node
    45  }
    46  
    47  // bucket contains nodes, ordered by their last activity. the entry
    48  // that was most recently active is the first element in entries.
    49  type bucket struct {
    50  	entries      []*Node
    51  	replacements []*Node
    52  }
    53  
    54  func newTable(ourID NodeID, ourAddr *net.UDPAddr) *Table {
    55  	self := NewNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port))
    56  	tab := &Table{self: self}
    57  	for i := range tab.buckets {
    58  		tab.buckets[i] = new(bucket)
    59  	}
    60  	return tab
    61  }
    62  
    63  const printTable = false
    64  
    65  // chooseBucketRefreshTarget selects random refresh targets to keep all Kademlia
    66  // buckets filled with live connections and keep the network topology healthy.
    67  // This requires selecting addresses closer to our own with a higher probability
    68  // in order to refresh closer buckets too.
    69  //
    70  // This algorithm approximates the distance distribution of existing nodes in the
    71  // table by selecting a random node from the table and selecting a target address
    72  // with a distance less than twice of that of the selected node.
    73  // This algorithm will be improved later to specifically target the least recently
    74  // used buckets.
    75  func (tab *Table) chooseBucketRefreshTarget() common.Hash {
    76  	entries := 0
    77  	if printTable {
    78  		fmt.Println()
    79  	}
    80  	for i, b := range &tab.buckets {
    81  		entries += len(b.entries)
    82  		if printTable {
    83  			for _, e := range b.entries {
    84  				fmt.Println(i, e.state, e.addr().String(), e.ID.String(), e.sha.Hex())
    85  			}
    86  		}
    87  	}
    88  
    89  	prefix := binary.BigEndian.Uint64(tab.self.sha[0:8])
    90  	dist := ^uint64(0)
    91  	entry := int(randUint(uint32(entries + 1)))
    92  	for _, b := range &tab.buckets {
    93  		if entry < len(b.entries) {
    94  			n := b.entries[entry]
    95  			dist = binary.BigEndian.Uint64(n.sha[0:8]) ^ prefix
    96  			break
    97  		}
    98  		entry -= len(b.entries)
    99  	}
   100  
   101  	ddist := ^uint64(0)
   102  	if dist+dist > dist {
   103  		ddist = dist
   104  	}
   105  	targetPrefix := prefix ^ randUint64n(ddist)
   106  
   107  	var target common.Hash
   108  	binary.BigEndian.PutUint64(target[0:8], targetPrefix)
   109  	rand.Read(target[8:])
   110  	return target
   111  }
   112  
   113  // readRandomNodes fills the given slice with random nodes from the
   114  // table. It will not write the same node more than once. The nodes in
   115  // the slice are copies and can be modified by the caller.
   116  func (tab *Table) readRandomNodes(buf []*Node) (n int) {
   117  	// TODO: tree-based buckets would help here
   118  	// Find all non-empty buckets and get a fresh slice of their entries.
   119  	var buckets [][]*Node
   120  	for _, b := range &tab.buckets {
   121  		if len(b.entries) > 0 {
   122  			buckets = append(buckets, b.entries)
   123  		}
   124  	}
   125  	if len(buckets) == 0 {
   126  		return 0
   127  	}
   128  	// Shuffle the buckets.
   129  	for i := uint32(len(buckets)) - 1; i > 0; i-- {
   130  		j := randUint(i)
   131  		buckets[i], buckets[j] = buckets[j], buckets[i]
   132  	}
   133  	// Move head of each bucket into buf, removing buckets that become empty.
   134  	var i, j int
   135  	for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) {
   136  		b := buckets[j]
   137  		buf[i] = &(*b[0])
   138  		buckets[j] = b[1:]
   139  		if len(b) == 1 {
   140  			buckets = append(buckets[:j], buckets[j+1:]...)
   141  		}
   142  		if len(buckets) == 0 {
   143  			break
   144  		}
   145  	}
   146  	return i + 1
   147  }
   148  
   149  func randUint(max uint32) uint32 {
   150  	if max < 2 {
   151  		return 0
   152  	}
   153  	var b [4]byte
   154  	rand.Read(b[:])
   155  	return binary.BigEndian.Uint32(b[:]) % max
   156  }
   157  
   158  func randUint64n(max uint64) uint64 {
   159  	if max < 2 {
   160  		return 0
   161  	}
   162  	var b [8]byte
   163  	rand.Read(b[:])
   164  	return binary.BigEndian.Uint64(b[:]) % max
   165  }
   166  
   167  // closest returns the n nodes in the table that are closest to the
   168  // given id. The caller must hold tab.mutex.
   169  func (tab *Table) closest(target common.Hash, nresults int) *nodesByDistance {
   170  	// This is a very wasteful way to find the closest nodes but
   171  	// obviously correct. I believe that tree-based buckets would make
   172  	// this easier to implement efficiently.
   173  	close := &nodesByDistance{target: target}
   174  	for _, b := range &tab.buckets {
   175  		for _, n := range b.entries {
   176  			close.push(n, nresults)
   177  		}
   178  	}
   179  	return close
   180  }
   181  
   182  // add attempts to add the given node its corresponding bucket. If the
   183  // bucket has space available, adding the node succeeds immediately.
   184  // Otherwise, the node is added to the replacement cache for the bucket.
   185  func (tab *Table) add(n *Node) (contested *Node) {
   186  	//fmt.Println("add", n.addr().String(), n.ID.String(), n.sha.Hex())
   187  	if n.ID == tab.self.ID {
   188  		return
   189  	}
   190  	b := tab.buckets[logdist(tab.self.sha, n.sha)]
   191  	switch {
   192  	case b.bump(n):
   193  		// n exists in b.
   194  		return nil
   195  	case len(b.entries) < bucketSize:
   196  		// b has space available.
   197  		b.addFront(n)
   198  		tab.count++
   199  		if tab.nodeAddedHook != nil {
   200  			tab.nodeAddedHook(n)
   201  		}
   202  		return nil
   203  	default:
   204  		// b has no space left, add to replacement cache
   205  		// and revalidate the last entry.
   206  		// TODO: drop previous node
   207  		b.replacements = append(b.replacements, n)
   208  		if len(b.replacements) > bucketSize {
   209  			copy(b.replacements, b.replacements[1:])
   210  			b.replacements = b.replacements[:len(b.replacements)-1]
   211  		}
   212  		return b.entries[len(b.entries)-1]
   213  	}
   214  }
   215  
   216  // stuff adds nodes the table to the end of their corresponding bucket
   217  // if the bucket is not full.
   218  func (tab *Table) stuff(nodes []*Node) {
   219  outer:
   220  	for _, n := range nodes {
   221  		if n.ID == tab.self.ID {
   222  			continue // don't add self
   223  		}
   224  		bucket := tab.buckets[logdist(tab.self.sha, n.sha)]
   225  		for i := range bucket.entries {
   226  			if bucket.entries[i].ID == n.ID {
   227  				continue outer // already in bucket
   228  			}
   229  		}
   230  		if len(bucket.entries) < bucketSize {
   231  			bucket.entries = append(bucket.entries, n)
   232  			tab.count++
   233  			if tab.nodeAddedHook != nil {
   234  				tab.nodeAddedHook(n)
   235  			}
   236  		}
   237  	}
   238  }
   239  
   240  // delete removes an entry from the node table (used to evacuate
   241  // failed/non-bonded discovery peers).
   242  func (tab *Table) delete(node *Node) {
   243  	//fmt.Println("delete", node.addr().String(), node.ID.String(), node.sha.Hex())
   244  	bucket := tab.buckets[logdist(tab.self.sha, node.sha)]
   245  	for i := range bucket.entries {
   246  		if bucket.entries[i].ID == node.ID {
   247  			bucket.entries = append(bucket.entries[:i], bucket.entries[i+1:]...)
   248  			tab.count--
   249  			return
   250  		}
   251  	}
   252  }
   253  
   254  func (tab *Table) deleteReplace(node *Node) {
   255  	b := tab.buckets[logdist(tab.self.sha, node.sha)]
   256  	i := 0
   257  	for i < len(b.entries) {
   258  		if b.entries[i].ID == node.ID {
   259  			b.entries = append(b.entries[:i], b.entries[i+1:]...)
   260  			tab.count--
   261  		} else {
   262  			i++
   263  		}
   264  	}
   265  	// refill from replacement cache
   266  	// TODO: maybe use random index
   267  	if len(b.entries) < bucketSize && len(b.replacements) > 0 {
   268  		ri := len(b.replacements) - 1
   269  		b.addFront(b.replacements[ri])
   270  		tab.count++
   271  		b.replacements[ri] = nil
   272  		b.replacements = b.replacements[:ri]
   273  	}
   274  }
   275  
   276  func (b *bucket) addFront(n *Node) {
   277  	b.entries = append(b.entries, nil)
   278  	copy(b.entries[1:], b.entries)
   279  	b.entries[0] = n
   280  }
   281  
   282  func (b *bucket) bump(n *Node) bool {
   283  	for i := range b.entries {
   284  		if b.entries[i].ID == n.ID {
   285  			// move it to the front
   286  			copy(b.entries[1:], b.entries[:i])
   287  			b.entries[0] = n
   288  			return true
   289  		}
   290  	}
   291  	return false
   292  }
   293  
   294  // nodesByDistance is a list of nodes, ordered by
   295  // distance to target.
   296  type nodesByDistance struct {
   297  	entries []*Node
   298  	target  common.Hash
   299  }
   300  
   301  // push adds the given node to the list, keeping the total size below maxElems.
   302  func (h *nodesByDistance) push(n *Node, maxElems int) {
   303  	ix := sort.Search(len(h.entries), func(i int) bool {
   304  		return distcmp(h.target, h.entries[i].sha, n.sha) > 0
   305  	})
   306  	if len(h.entries) < maxElems {
   307  		h.entries = append(h.entries, n)
   308  	}
   309  	if ix == len(h.entries) {
   310  		// farther away than all nodes we already have.
   311  		// if there was room for it, the node is now the last element.
   312  	} else {
   313  		// slide existing entries down to make room
   314  		// this will overwrite the entry we just appended.
   315  		copy(h.entries[ix+1:], h.entries[ix:])
   316  		h.entries[ix] = n
   317  	}
   318  }