github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/p2p/discv5/table.go (about)

     1  
     2  //此源码被清华学神尹成大魔王专业翻译分析并修改
     3  //尹成QQ77025077
     4  //尹成微信18510341407
     5  //尹成所在QQ群721929980
     6  //尹成邮箱 yinc13@mails.tsinghua.edu.cn
     7  //尹成毕业于清华大学,微软区块链领域全球最有价值专家
     8  //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620
     9  //版权所有2016 Go Ethereum作者
    10  //此文件是Go以太坊库的一部分。
    11  //
    12  //Go-Ethereum库是免费软件:您可以重新分发它和/或修改
    13  //根据GNU发布的较低通用公共许可证的条款
    14  //自由软件基金会,或者许可证的第3版,或者
    15  //(由您选择)任何更高版本。
    16  //
    17  //Go以太坊图书馆的发行目的是希望它会有用,
    18  //但没有任何保证;甚至没有
    19  //适销性或特定用途的适用性。见
    20  //GNU较低的通用公共许可证,了解更多详细信息。
    21  //
    22  //你应该收到一份GNU较低级别的公共许可证副本
    23  //以及Go以太坊图书馆。如果没有,请参见<http://www.gnu.org/licenses/>。
    24  
    25  //包discv5实现了rlpx v5主题发现协议。
    26  //
    27  //主题发现协议提供了一种查找
    28  //可以连接到。它使用一个类似kademlia的协议来维护
    29  //所有监听的ID和端点的分布式数据库
    30  //节点。
    31  package discv5
    32  
    33  import (
    34  	"crypto/rand"
    35  	"encoding/binary"
    36  	"fmt"
    37  	"net"
    38  	"sort"
    39  
    40  	"github.com/ethereum/go-ethereum/common"
    41  )
    42  
    43  const (
    44  alpha      = 3  //Kademlia并发因子
    45  bucketSize = 16 //卡德米利亚水桶尺寸
    46  	hashBits   = len(common.Hash{}) * 8
    47  nBuckets   = hashBits + 1 //桶数
    48  
    49  	maxFindnodeFailures = 5
    50  )
    51  
    52  type Table struct {
    53  count         int               //节点数
    54  buckets       [nBuckets]*bucket //已知节点的距离索引
    55  nodeAddedHook func(*Node)       //用于测试
    56  self          *Node             //本地节点的元数据
    57  }
    58  
    59  //bucket包含按其上一个活动排序的节点。条目
    60  //最近激活的元素是条目中的第一个元素。
    61  type bucket struct {
    62  	entries      []*Node
    63  	replacements []*Node
    64  }
    65  
    66  func newTable(ourID NodeID, ourAddr *net.UDPAddr) *Table {
    67  	self := NewNode(ourID, ourAddr.IP, uint16(ourAddr.Port), uint16(ourAddr.Port))
    68  	tab := &Table{self: self}
    69  	for i := range tab.buckets {
    70  		tab.buckets[i] = new(bucket)
    71  	}
    72  	return tab
    73  }
    74  
    75  const printTable = false
    76  
    77  //选择bucketrefreshTarget选择随机刷新目标以保留所有kademlia
    78  //存储桶中充满了活动连接,并保持网络拓扑的健康。
    79  //这就需要选择更接近我们自己的地址,并且概率更高
    80  //为了刷新更近的存储桶。
    81  //
    82  //该算法近似于
    83  //从表中选择一个随机节点并选择一个目标地址
    84  //距离小于所选节点距离的两倍。
    85  //该算法稍后将得到改进,以专门针对最近
    86  //旧桶
    87  func (tab *Table) chooseBucketRefreshTarget() common.Hash {
    88  	entries := 0
    89  	if printTable {
    90  		fmt.Println()
    91  	}
    92  	for i, b := range &tab.buckets {
    93  		entries += len(b.entries)
    94  		if printTable {
    95  			for _, e := range b.entries {
    96  				fmt.Println(i, e.state, e.addr().String(), e.ID.String(), e.sha.Hex())
    97  			}
    98  		}
    99  	}
   100  
   101  	prefix := binary.BigEndian.Uint64(tab.self.sha[0:8])
   102  	dist := ^uint64(0)
   103  	entry := int(randUint(uint32(entries + 1)))
   104  	for _, b := range &tab.buckets {
   105  		if entry < len(b.entries) {
   106  			n := b.entries[entry]
   107  			dist = binary.BigEndian.Uint64(n.sha[0:8]) ^ prefix
   108  			break
   109  		}
   110  		entry -= len(b.entries)
   111  	}
   112  
   113  	ddist := ^uint64(0)
   114  	if dist+dist > dist {
   115  		ddist = dist
   116  	}
   117  	targetPrefix := prefix ^ randUint64n(ddist)
   118  
   119  	var target common.Hash
   120  	binary.BigEndian.PutUint64(target[0:8], targetPrefix)
   121  	rand.Read(target[8:])
   122  	return target
   123  }
   124  
   125  //readrandomnodes用来自
   126  //表。它不会多次写入同一节点。节点
   127  //切片是副本,可以由调用方修改。
   128  func (tab *Table) readRandomNodes(buf []*Node) (n int) {
   129  //TODO:基于树的桶在这里有帮助
   130  //找到所有非空桶,并从中获取新的部分。
   131  	var buckets [][]*Node
   132  	for _, b := range &tab.buckets {
   133  		if len(b.entries) > 0 {
   134  			buckets = append(buckets, b.entries[:])
   135  		}
   136  	}
   137  	if len(buckets) == 0 {
   138  		return 0
   139  	}
   140  //洗牌。
   141  	for i := uint32(len(buckets)) - 1; i > 0; i-- {
   142  		j := randUint(i)
   143  		buckets[i], buckets[j] = buckets[j], buckets[i]
   144  	}
   145  //将每个桶的头部移入buf,移除变空的桶。
   146  	var i, j int
   147  	for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) {
   148  		b := buckets[j]
   149  		buf[i] = &(*b[0])
   150  		buckets[j] = b[1:]
   151  		if len(b) == 1 {
   152  			buckets = append(buckets[:j], buckets[j+1:]...)
   153  		}
   154  		if len(buckets) == 0 {
   155  			break
   156  		}
   157  	}
   158  	return i + 1
   159  }
   160  
   161  func randUint(max uint32) uint32 {
   162  	if max < 2 {
   163  		return 0
   164  	}
   165  	var b [4]byte
   166  	rand.Read(b[:])
   167  	return binary.BigEndian.Uint32(b[:]) % max
   168  }
   169  
   170  func randUint64n(max uint64) uint64 {
   171  	if max < 2 {
   172  		return 0
   173  	}
   174  	var b [8]byte
   175  	rand.Read(b[:])
   176  	return binary.BigEndian.Uint64(b[:]) % max
   177  }
   178  
   179  //最近返回表中最接近
   180  //给定的ID。调用方必须保持tab.mutex。
   181  func (tab *Table) closest(target common.Hash, nresults int) *nodesByDistance {
   182  //这是一种非常浪费的查找最近节点的方法,但是
   183  //显然是正确的。我相信以树为基础的桶可以
   184  //这更容易有效地实施。
   185  	close := &nodesByDistance{target: target}
   186  	for _, b := range &tab.buckets {
   187  		for _, n := range b.entries {
   188  			close.push(n, nresults)
   189  		}
   190  	}
   191  	return close
   192  }
   193  
   194  //添加尝试添加给定节点的相应存储桶。如果
   195  //bucket有空间,添加节点立即成功。
   196  //否则,节点将添加到存储桶的替换缓存中。
   197  func (tab *Table) add(n *Node) (contested *Node) {
   198  //fmt.println(“添加”,n.addr().string(),n.id.string(),n.sha.hex())
   199  	if n.ID == tab.self.ID {
   200  		return
   201  	}
   202  	b := tab.buckets[logdist(tab.self.sha, n.sha)]
   203  	switch {
   204  	case b.bump(n):
   205  //N存在于B中。
   206  		return nil
   207  	case len(b.entries) < bucketSize:
   208  //B有空位。
   209  		b.addFront(n)
   210  		tab.count++
   211  		if tab.nodeAddedHook != nil {
   212  			tab.nodeAddedHook(n)
   213  		}
   214  		return nil
   215  	default:
   216  //B没有剩余空间,添加到替换缓存
   217  //并重新验证最后一个条目。
   218  //TODO:删除上一个节点
   219  		b.replacements = append(b.replacements, n)
   220  		if len(b.replacements) > bucketSize {
   221  			copy(b.replacements, b.replacements[1:])
   222  			b.replacements = b.replacements[:len(b.replacements)-1]
   223  		}
   224  		return b.entries[len(b.entries)-1]
   225  	}
   226  }
   227  
   228  //stufacture将表中的节点添加到相应bucket的末尾
   229  //如果桶没满。
   230  func (tab *Table) stuff(nodes []*Node) {
   231  outer:
   232  	for _, n := range nodes {
   233  		if n.ID == tab.self.ID {
   234  continue //不要增加自我
   235  		}
   236  		bucket := tab.buckets[logdist(tab.self.sha, n.sha)]
   237  		for i := range bucket.entries {
   238  			if bucket.entries[i].ID == n.ID {
   239  continue outer //已经在桶里了
   240  			}
   241  		}
   242  		if len(bucket.entries) < bucketSize {
   243  			bucket.entries = append(bucket.entries, n)
   244  			tab.count++
   245  			if tab.nodeAddedHook != nil {
   246  				tab.nodeAddedHook(n)
   247  			}
   248  		}
   249  	}
   250  }
   251  
   252  //删除从节点表中删除一个条目(用于清空
   253  //失败/未绑定的发现对等机)。
   254  func (tab *Table) delete(node *Node) {
   255  //fmt.println(“删除”,node.addr().string(),node.id.string(),node.sha.hex())
   256  	bucket := tab.buckets[logdist(tab.self.sha, node.sha)]
   257  	for i := range bucket.entries {
   258  		if bucket.entries[i].ID == node.ID {
   259  			bucket.entries = append(bucket.entries[:i], bucket.entries[i+1:]...)
   260  			tab.count--
   261  			return
   262  		}
   263  	}
   264  }
   265  
   266  func (tab *Table) deleteReplace(node *Node) {
   267  	b := tab.buckets[logdist(tab.self.sha, node.sha)]
   268  	i := 0
   269  	for i < len(b.entries) {
   270  		if b.entries[i].ID == node.ID {
   271  			b.entries = append(b.entries[:i], b.entries[i+1:]...)
   272  			tab.count--
   273  		} else {
   274  			i++
   275  		}
   276  	}
   277  //从替换缓存重新填充
   278  //TODO:可能使用随机索引
   279  	if len(b.entries) < bucketSize && len(b.replacements) > 0 {
   280  		ri := len(b.replacements) - 1
   281  		b.addFront(b.replacements[ri])
   282  		tab.count++
   283  		b.replacements[ri] = nil
   284  		b.replacements = b.replacements[:ri]
   285  	}
   286  }
   287  
   288  func (b *bucket) addFront(n *Node) {
   289  	b.entries = append(b.entries, nil)
   290  	copy(b.entries[1:], b.entries)
   291  	b.entries[0] = n
   292  }
   293  
   294  func (b *bucket) bump(n *Node) bool {
   295  	for i := range b.entries {
   296  		if b.entries[i].ID == n.ID {
   297  //把它移到前面
   298  			copy(b.entries[1:], b.entries[:i])
   299  			b.entries[0] = n
   300  			return true
   301  		}
   302  	}
   303  	return false
   304  }
   305  
   306  //nodesByDistance是节点列表,按
   307  //距离目标。
   308  type nodesByDistance struct {
   309  	entries []*Node
   310  	target  common.Hash
   311  }
   312  
   313  //push将给定节点添加到列表中,使总大小保持在maxelems以下。
   314  func (h *nodesByDistance) push(n *Node, maxElems int) {
   315  	ix := sort.Search(len(h.entries), func(i int) bool {
   316  		return distcmp(h.target, h.entries[i].sha, n.sha) > 0
   317  	})
   318  	if len(h.entries) < maxElems {
   319  		h.entries = append(h.entries, n)
   320  	}
   321  	if ix == len(h.entries) {
   322  //比我们现有的所有节点都要远。
   323  //如果有空间,那么节点现在是最后一个元素。
   324  	} else {
   325  //向下滑动现有条目以腾出空间
   326  //这将覆盖我们刚刚附加的条目。
   327  		copy(h.entries[ix+1:], h.entries[ix:])
   328  		h.entries[ix] = n
   329  	}
   330  }