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 }