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