github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/p2p/discv5/table.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 12:09:44</date>
    10  //</624342657539248128>
    11  
    12  
    13  //包discv5实现了rlpx v5主题发现协议。
    14  //
    15  //主题发现协议提供了一种查找
    16  //可以连接到。它使用一个类似kademlia的协议来维护
    17  //所有监听的ID和端点的分布式数据库
    18  //节点。
    19  package discv5
    20  
    21  import (
    22  	"crypto/rand"
    23  	"encoding/binary"
    24  	"fmt"
    25  	"net"
    26  	"sort"
    27  
    28  	"github.com/ethereum/go-ethereum/common"
    29  )
    30  
    31  const (
    32  alpha      = 3  //Kademlia并发因子
    33  bucketSize = 16 //卡德米利亚水桶尺寸
    34  	hashBits   = len(common.Hash{}) * 8
    35  nBuckets   = hashBits + 1 //桶数
    36  
    37  	maxFindnodeFailures = 5
    38  )
    39  
    40  type Table struct {
    41  count         int               //节点数
    42  buckets       [nBuckets]*bucket //已知节点的距离索引
    43  nodeAddedHook func(*Node)       //用于测试
    44  self          *Node             //本地节点的元数据
    45  }
    46  
    47  //bucket包含按其上一个活动排序的节点。条目
    48  //最近激活的元素是条目中的第一个元素。
    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  //选择bucketrefreshTarget选择随机刷新目标以保留所有kademlia
    66  //存储桶中充满了活动连接,并保持网络拓扑的健康。
    67  //这就需要选择更接近我们自己的地址,并且概率更高
    68  //为了刷新更近的存储桶。
    69  //
    70  //该算法近似于
    71  //从表中选择一个随机节点并选择一个目标地址
    72  //距离小于所选节点距离的两倍。
    73  //该算法稍后将得到改进,以专门针对最近
    74  //旧桶
    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用来自
   114  //表。它不会多次写入同一节点。节点
   115  //切片是副本,可以由调用方修改。
   116  func (tab *Table) readRandomNodes(buf []*Node) (n int) {
   117  //TODO:基于树的桶在这里有帮助
   118  //找到所有非空桶,并从中获取新的部分。
   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  //洗牌。
   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  //将每个桶的头部移入buf,移除变空的桶。
   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  //最近返回表中最接近
   168  //给定的ID。调用方必须保持tab.mutex。
   169  func (tab *Table) closest(target common.Hash, nresults int) *nodesByDistance {
   170  //这是一种非常浪费的查找最近节点的方法,但是
   171  //显然是正确的。我相信以树为基础的桶可以
   172  //这更容易有效地实施。
   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  //添加尝试添加给定节点的相应存储桶。如果
   183  //bucket有空间,添加节点立即成功。
   184  //否则,节点将添加到存储桶的替换缓存中。
   185  func (tab *Table) add(n *Node) (contested *Node) {
   186  //fmt.println(“添加”,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存在于B中。
   194  		return nil
   195  	case len(b.entries) < bucketSize:
   196  //B有空位。
   197  		b.addFront(n)
   198  		tab.count++
   199  		if tab.nodeAddedHook != nil {
   200  			tab.nodeAddedHook(n)
   201  		}
   202  		return nil
   203  	default:
   204  //B没有剩余空间,添加到替换缓存
   205  //并重新验证最后一个条目。
   206  //TODO:删除上一个节点
   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  //stufacture将表中的节点添加到相应bucket的末尾
   217  //如果桶没满。
   218  func (tab *Table) stuff(nodes []*Node) {
   219  outer:
   220  	for _, n := range nodes {
   221  		if n.ID == tab.self.ID {
   222  continue //不要增加自我
   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 //已经在桶里了
   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  //删除从节点表中删除一个条目(用于清空
   241  //失败/未绑定的发现对等机)。
   242  func (tab *Table) delete(node *Node) {
   243  //fmt.println(“删除”,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  //从替换缓存重新填充
   266  //TODO:可能使用随机索引
   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  //把它移到前面
   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是节点列表,按
   295  //距离目标。
   296  type nodesByDistance struct {
   297  	entries []*Node
   298  	target  common.Hash
   299  }
   300  
   301  //push将给定节点添加到列表中,使总大小保持在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  //比我们现有的所有节点都要远。
   311  //如果有空间,那么节点现在是最后一个元素。
   312  	} else {
   313  //向下滑动现有条目以腾出空间
   314  //这将覆盖我们刚刚附加的条目。
   315  		copy(h.entries[ix+1:], h.entries[ix:])
   316  		h.entries[ix] = n
   317  	}
   318  }
   319