github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/network/kademlia.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 19:16:43</date> 10 //</624450113812566016> 11 12 13 package network 14 15 import ( 16 "bytes" 17 "fmt" 18 "math/rand" 19 "strings" 20 "sync" 21 "time" 22 23 "github.com/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/swarm/log" 25 "github.com/ethereum/go-ethereum/swarm/pot" 26 ) 27 28 /* 29 30 根据相对于固定点X的接近顺序,将点分类为 31 将空间(n字节长的字节序列)放入容器中。每个项目位于 32 最远距离X的一半是前一个箱中的项目。给出了一个 33 均匀分布项(任意序列上的哈希函数) 34 接近比例映射到一系列子集上,基数为负 35 指数尺度。 36 37 它还具有属于同一存储箱的任何两个项目所在的属性。 38 最远的距离是彼此距离x的一半。 39 40 如果我们把垃圾箱中的随机样本看作是网络中的连接, 41 互联节点的相对邻近性可以作为局部 42 当任务要在两个路径之间查找路径时,用于图形遍历的决策 43 点。因为在每个跳跃中,有限距离的一半 44 达到一个跳数所需的保证不变的最大跳数限制。 45 节点。 46 **/ 47 48 49 var Pof = pot.DefaultPof(256) 50 51 //kadparams保存kademlia的配置参数 52 type KadParams struct { 53 //可调参数 54 MaxProxDisplay int //表显示的行数 55 NeighbourhoodSize int //最近邻核最小基数 56 MinBinSize int //一行中的最小对等数 57 MaxBinSize int //修剪前一行中的最大对等数 58 RetryInterval int64 //对等机首次重新拨号前的初始间隔 59 RetryExponent int //用指数乘以重试间隔 60 MaxRetries int //重拨尝试的最大次数 61 //制裁或阻止建议同伴的职能 62 Reachable func(*BzzAddr) bool `json:"-"` 63 } 64 65 //newkadparams返回带有默认值的params结构 66 func NewKadParams() *KadParams { 67 return &KadParams{ 68 MaxProxDisplay: 16, 69 NeighbourhoodSize: 2, 70 MinBinSize: 2, 71 MaxBinSize: 4, 72 RetryInterval: 4200000000, //4.2秒 73 MaxRetries: 42, 74 RetryExponent: 2, 75 } 76 } 77 78 //Kademlia是一个活动对等端表和一个已知对等端数据库(节点记录) 79 type Kademlia struct { 80 lock sync.RWMutex 81 *KadParams //Kademlia配置参数 82 base []byte //表的不可变基址 83 addrs *pot.Pot //用于已知对等地址的POTS容器 84 conns *pot.Pot //用于实时对等连接的POTS容器 85 depth uint8 //存储上一个当前饱和深度 86 nDepth int //存储上一个邻居深度 87 nDepthC chan int //由depthc函数返回,用于信号邻域深度变化 88 addrCountC chan int //由addrcountc函数返回以指示对等计数更改 89 } 90 91 //newkademlia为基地址addr创建一个kademlia表 92 //参数与参数相同 93 //如果params为nil,则使用默认值 94 func NewKademlia(addr []byte, params *KadParams) *Kademlia { 95 if params == nil { 96 params = NewKadParams() 97 } 98 return &Kademlia{ 99 base: addr, 100 KadParams: params, 101 addrs: pot.NewPot(nil, 0), 102 conns: pot.NewPot(nil, 0), 103 } 104 } 105 106 //条目表示一个kademlia表条目(bzzaddr的扩展) 107 type entry struct { 108 *BzzAddr 109 conn *Peer 110 seenAt time.Time 111 retries int 112 } 113 114 //NewEntry从*对等创建一个Kademlia对等 115 func newEntry(p *BzzAddr) *entry { 116 return &entry{ 117 BzzAddr: p, 118 seenAt: time.Now(), 119 } 120 } 121 122 //label是调试条目的短标记 123 func Label(e *entry) string { 124 return fmt.Sprintf("%s (%d)", e.Hex()[:4], e.retries) 125 } 126 127 //十六进制是入口地址的十六进制序列化 128 func (e *entry) Hex() string { 129 return fmt.Sprintf("%x", e.Address()) 130 } 131 132 //寄存器将每个地址作为kademlia对等记录输入 133 //已知对等地址数据库 134 func (k *Kademlia) Register(peers ...*BzzAddr) error { 135 k.lock.Lock() 136 defer k.lock.Unlock() 137 var known, size int 138 for _, p := range peers { 139 //如果自我接收错误,对等方应该知道得更好。 140 //应该为此受到惩罚 141 if bytes.Equal(p.Address(), k.base) { 142 return fmt.Errorf("add peers: %x is self", k.base) 143 } 144 var found bool 145 k.addrs, _, found, _ = pot.Swap(k.addrs, p, Pof, func(v pot.Val) pot.Val { 146 //如果没有找到 147 if v == nil { 148 //在conn中插入新的脱机对等 149 return newEntry(p) 150 } 151 //在已知的同龄人中发现,什么都不做 152 return v 153 }) 154 if found { 155 known++ 156 } 157 size++ 158 } 159 //仅当有新地址时才发送新地址计数值 160 if k.addrCountC != nil && size-known > 0 { 161 k.addrCountC <- k.addrs.Size() 162 } 163 164 k.sendNeighbourhoodDepthChange() 165 return nil 166 } 167 168 //SuggestPeer返回未连接的对等地址作为连接的对等建议 169 func (k *Kademlia) SuggestPeer() (suggestedPeer *BzzAddr, saturationDepth int, changed bool) { 170 k.lock.Lock() 171 defer k.lock.Unlock() 172 radius := neighbourhoodRadiusForPot(k.conns, k.NeighbourhoodSize, k.base) 173 //按连接对等数的升序收集未饱和的垃圾箱 174 //从浅到深(po的升序) 175 //将它们插入bin数组的映射中,并用连接的对等方的数量进行键控 176 saturation := make(map[int][]int) 177 var lastPO int //迭代中最后一个非空的po bin 178 saturationDepth = -1 //最深的Po,所有较浅的箱子都有大于等于k.minbinsizepeer的箱子。 179 var pastDepth bool //迭代的po是否大于等于深度 180 k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val) bool) bool) bool { 181 //处理跳过的空容器 182 for ; lastPO < po; lastPO++ { 183 //找到最低的不饱和料仓 184 if saturationDepth == -1 { 185 saturationDepth = lastPO 186 } 187 //如果有空的垃圾桶,深度肯定会过去。 188 pastDepth = true 189 saturation[0] = append(saturation[0], lastPO) 190 } 191 lastPO = po + 1 192 //通过半径,深度肯定通过 193 if po >= radius { 194 pastDepth = true 195 } 196 //超出深度后,即使尺寸大于等于K.MinBinSize,料仓也被视为不饱和。 197 //为了实现与所有邻居的完全连接 198 if pastDepth && size >= k.MinBinSize { 199 size = k.MinBinSize - 1 200 } 201 //处理非空不饱和料仓 202 if size < k.MinBinSize { 203 //找到最低的不饱和料仓 204 if saturationDepth == -1 { 205 saturationDepth = po 206 } 207 saturation[size] = append(saturation[size], po) 208 } 209 return true 210 }) 211 //触发比最近连接更近的对等请求,包括 212 //从最近连接到最近地址的所有箱子都是不饱和的。 213 var nearestAddrAt int 214 k.addrs.EachNeighbour(k.base, Pof, func(_ pot.Val, po int) bool { 215 nearestAddrAt = po 216 return false 217 }) 218 //包含大小为0的容器会影响请求连接 219 //优先于非空的浅仓 220 for ; lastPO <= nearestAddrAt; lastPO++ { 221 saturation[0] = append(saturation[0], lastPO) 222 } 223 //所有的po箱都是饱和的,即minsize>=k.minbinSize,不建议同行使用。 224 if len(saturation) == 0 { 225 return nil, 0, false 226 } 227 //在通讯簿中查找第一个可调用的对等点 228 //从最小尺寸的箱子开始,从浅到深 229 //对于每个垃圾箱(直到邻里半径),我们可以找到可调用的候选对等物。 230 for size := 0; size < k.MinBinSize && suggestedPeer == nil; size++ { 231 bins, ok := saturation[size] 232 if !ok { 233 //没有这种尺寸的箱子 234 continue 235 } 236 cur := 0 237 curPO := bins[0] 238 k.addrs.EachBin(k.base, Pof, curPO, func(po, _ int, f func(func(pot.Val) bool) bool) bool { 239 curPO = bins[cur] 240 //查找下一个大小为的纸盒 241 if curPO == po { 242 cur++ 243 } else { 244 //跳过没有地址的存储箱 245 for ; cur < len(bins) && curPO < po; cur++ { 246 curPO = bins[cur] 247 } 248 if po < curPO { 249 cur-- 250 return true 251 } 252 //没有地址时停止 253 if curPO < po { 254 return false 255 } 256 } 257 //科普发现 258 //从未饱和的bin中的地址中找到一个可调用的对等方 259 //如果发现停止 260 f(func(val pot.Val) bool { 261 e := val.(*entry) 262 if k.callable(e) { 263 suggestedPeer = e.BzzAddr 264 return false 265 } 266 return true 267 }) 268 return cur < len(bins) && suggestedPeer == nil 269 }) 270 } 271 272 if uint8(saturationDepth) < k.depth { 273 k.depth = uint8(saturationDepth) 274 return suggestedPeer, saturationDepth, true 275 } 276 return suggestedPeer, 0, false 277 } 278 279 //在上,将对等机作为Kademlia对等机插入活动对等机 280 func (k *Kademlia) On(p *Peer) (uint8, bool) { 281 k.lock.Lock() 282 defer k.lock.Unlock() 283 var ins bool 284 k.conns, _, _, _ = pot.Swap(k.conns, p, Pof, func(v pot.Val) pot.Val { 285 //如果找不到现场 286 if v == nil { 287 ins = true 288 //在conns中插入新的在线对等点 289 return p 290 } 291 //在同龄人中找到,什么都不做 292 return v 293 }) 294 if ins && !p.BzzPeer.LightNode { 295 a := newEntry(p.BzzAddr) 296 a.conn = p 297 //在加法器中插入新的在线对等点 298 k.addrs, _, _, _ = pot.Swap(k.addrs, p, Pof, func(v pot.Val) pot.Val { 299 return a 300 }) 301 //仅当插入对等端时才发送新的地址计数值 302 if k.addrCountC != nil { 303 k.addrCountC <- k.addrs.Size() 304 } 305 } 306 log.Trace(k.string()) 307 //计算饱和深度是否改变 308 depth := uint8(k.saturation()) 309 var changed bool 310 if depth != k.depth { 311 changed = true 312 k.depth = depth 313 } 314 k.sendNeighbourhoodDepthChange() 315 return k.depth, changed 316 } 317 318 //Neighbourhooddepthc返回发送新Kademlia的频道 319 //每一次变化的邻里深度。 320 //不从返回通道接收将阻塞功能 321 //当邻近深度改变时。 322 //托多:为什么要导出它,如果应该的话;为什么我们不能有多个订户? 323 func (k *Kademlia) NeighbourhoodDepthC() <-chan int { 324 k.lock.Lock() 325 defer k.lock.Unlock() 326 if k.nDepthC == nil { 327 k.nDepthC = make(chan int) 328 } 329 return k.nDepthC 330 } 331 332 //sendnieghbourhooddepthchange向k.ndepth通道发送新的邻近深度 333 //如果已初始化。 334 func (k *Kademlia) sendNeighbourhoodDepthChange() { 335 //当调用neighbourhooddepthc并由其返回时,将初始化ndepthc。 336 //它提供了邻域深度变化的信号。 337 //如果满足这个条件,代码的这一部分将向ndepthc发送新的邻域深度。 338 if k.nDepthC != nil { 339 nDepth := depthForPot(k.conns, k.NeighbourhoodSize, k.base) 340 if nDepth != k.nDepth { 341 k.nDepth = nDepth 342 k.nDepthC <- nDepth 343 } 344 } 345 } 346 347 //addrCountc返回发送新的 348 //每次更改的地址计数值。 349 //不从返回通道接收将阻止寄存器功能 350 //地址计数值更改时。 351 func (k *Kademlia) AddrCountC() <-chan int { 352 if k.addrCountC == nil { 353 k.addrCountC = make(chan int) 354 } 355 return k.addrCountC 356 } 357 358 //关闭从活动对等中删除对等 359 func (k *Kademlia) Off(p *Peer) { 360 k.lock.Lock() 361 defer k.lock.Unlock() 362 var del bool 363 if !p.BzzPeer.LightNode { 364 k.addrs, _, _, _ = pot.Swap(k.addrs, p, Pof, func(v pot.Val) pot.Val { 365 //v不能为零,必须选中,否则我们将覆盖条目 366 if v == nil { 367 panic(fmt.Sprintf("connected peer not found %v", p)) 368 } 369 del = true 370 return newEntry(p.BzzAddr) 371 }) 372 } else { 373 del = true 374 } 375 376 if del { 377 k.conns, _, _, _ = pot.Swap(k.conns, p, Pof, func(_ pot.Val) pot.Val { 378 //V不能为零,但不需要检查 379 return nil 380 }) 381 //仅当对等端被删除时才发送新的地址计数值 382 if k.addrCountC != nil { 383 k.addrCountC <- k.addrs.Size() 384 } 385 k.sendNeighbourhoodDepthChange() 386 } 387 } 388 389 //eachconn是一个带有args(base、po、f)的迭代器,将f应用于每个活动对等端 390 //从基地测量,接近订单为po或更低 391 //如果基为零,则使用Kademlia基地址 392 func (k *Kademlia) EachConn(base []byte, o int, f func(*Peer, int) bool) { 393 k.lock.RLock() 394 defer k.lock.RUnlock() 395 k.eachConn(base, o, f) 396 } 397 398 func (k *Kademlia) eachConn(base []byte, o int, f func(*Peer, int) bool) { 399 if len(base) == 0 { 400 base = k.base 401 } 402 k.conns.EachNeighbour(base, Pof, func(val pot.Val, po int) bool { 403 if po > o { 404 return true 405 } 406 return f(val.(*Peer), po) 407 }) 408 } 409 410 //用(base,po,f)调用的eachaddr是一个迭代器,将f应用于每个已知的对等端 411 //从底部测量,接近顺序为O或更低 412 //如果基为零,则使用Kademlia基地址 413 func (k *Kademlia) EachAddr(base []byte, o int, f func(*BzzAddr, int) bool) { 414 k.lock.RLock() 415 defer k.lock.RUnlock() 416 k.eachAddr(base, o, f) 417 } 418 419 func (k *Kademlia) eachAddr(base []byte, o int, f func(*BzzAddr, int) bool) { 420 if len(base) == 0 { 421 base = k.base 422 } 423 k.addrs.EachNeighbour(base, Pof, func(val pot.Val, po int) bool { 424 if po > o { 425 return true 426 } 427 return f(val.(*entry).BzzAddr, po) 428 }) 429 } 430 431 //Neighbourhooddepth返回壶的深度,请参见壶的深度 432 func (k *Kademlia) NeighbourhoodDepth() (depth int) { 433 k.lock.RLock() 434 defer k.lock.RUnlock() 435 return depthForPot(k.conns, k.NeighbourhoodSize, k.base) 436 } 437 438 //邻里公路返回卡德米利亚的邻里半径。 439 //邻域半径包含大小大于等于neighbourhood size的最近邻域集 440 //也就是说,邻里半径是最深的,这样所有的垃圾箱就不会全部变浅。 441 //至少包含邻居大小连接的对等点 442 //如果连接的邻居大小的对等点总数少于,则返回0 443 //呼叫方必须持有锁 444 func neighbourhoodRadiusForPot(p *pot.Pot, neighbourhoodSize int, pivotAddr []byte) (depth int) { 445 if p.Size() <= neighbourhoodSize { 446 return 0 447 } 448 //迭代中的对等方总数 449 var size int 450 f := func(v pot.Val, i int) bool { 451 //po==256表示addr是透视地址(self) 452 if i == 256 { 453 return true 454 } 455 size++ 456 457 //这意味着我们都有NN同龄人。 458 //默认情况下,深度设置为最远nn对等端的bin。 459 if size == neighbourhoodSize { 460 depth = i 461 return false 462 } 463 464 return true 465 } 466 p.EachNeighbour(pivotAddr, Pof, f) 467 return depth 468 } 469 470 //depth for pot返回pot的深度 471 //深度是最近邻区最小延伸半径 472 //包括所有空的采购订单仓。也就是说,深度是最深的Po,因此 473 //-深度不超过邻里半径 474 //-所有比深度浅的箱子都不是空的。 475 //呼叫方必须持有锁 476 func depthForPot(p *pot.Pot, neighbourhoodSize int, pivotAddr []byte) (depth int) { 477 if p.Size() <= neighbourhoodSize { 478 return 0 479 } 480 //确定深度是一个两步过程 481 //首先,我们找到邻近地区最浅的同类的近距离垃圾箱。 482 //深度的数值不能高于此值 483 maxDepth := neighbourhoodRadiusForPot(p, neighbourhoodSize, pivotAddr) 484 485 //第二步是从最浅到最深依次测试空仓。 486 //如果发现空的垃圾箱,这将是实际深度 487 //如果达到第一步确定的最大深度,则停止迭代 488 p.EachBin(pivotAddr, Pof, 0, func(po int, _ int, f func(func(pot.Val) bool) bool) bool { 489 if po == depth { 490 if maxDepth == depth { 491 return false 492 } 493 depth++ 494 return true 495 } 496 return false 497 }) 498 499 return depth 500 } 501 502 //Callable决定地址条目是否表示可调用对等 503 func (k *Kademlia) callable(e *entry) bool { 504 //如果对等方处于活动状态或超过了maxretries,则不可调用 505 if e.conn != nil || e.retries > k.MaxRetries { 506 return false 507 } 508 //根据上次看到后经过的时间计算允许的重试次数 509 timeAgo := int64(time.Since(e.seenAt)) 510 div := int64(k.RetryExponent) 511 div += (150000 - rand.Int63n(300000)) * div / 1000000 512 var retries int 513 for delta := timeAgo; delta > k.RetryInterval; delta /= div { 514 retries++ 515 } 516 //它从不并发调用,因此可以安全地递增 517 //可以再次重试对等机 518 if retries < e.retries { 519 log.Trace(fmt.Sprintf("%08x: %v long time since last try (at %v) needed before retry %v, wait only warrants %v", k.BaseAddr()[:4], e, timeAgo, e.retries, retries)) 520 return false 521 } 522 //制裁或阻止建议同伴的职能 523 if k.Reachable != nil && !k.Reachable(e.BzzAddr) { 524 log.Trace(fmt.Sprintf("%08x: peer %v is temporarily not callable", k.BaseAddr()[:4], e)) 525 return false 526 } 527 e.retries++ 528 log.Trace(fmt.Sprintf("%08x: peer %v is callable", k.BaseAddr()[:4], e)) 529 530 return true 531 } 532 533 //baseaddr返回kademlia基地址 534 func (k *Kademlia) BaseAddr() []byte { 535 return k.base 536 } 537 538 //字符串返回用ASCII显示的kademlia表+kaddb表 539 func (k *Kademlia) String() string { 540 k.lock.RLock() 541 defer k.lock.RUnlock() 542 return k.string() 543 } 544 545 //字符串返回用ASCII显示的kademlia表+kaddb表 546 //呼叫方必须持有锁 547 func (k *Kademlia) string() string { 548 wsrow := " " 549 var rows []string 550 551 rows = append(rows, "=========================================================================") 552 rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %x", time.Now().UTC().Format(time.UnixDate), k.BaseAddr()[:3])) 553 rows = append(rows, fmt.Sprintf("population: %d (%d), NeighbourhoodSize: %d, MinBinSize: %d, MaxBinSize: %d", k.conns.Size(), k.addrs.Size(), k.NeighbourhoodSize, k.MinBinSize, k.MaxBinSize)) 554 555 liverows := make([]string, k.MaxProxDisplay) 556 peersrows := make([]string, k.MaxProxDisplay) 557 558 depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base) 559 rest := k.conns.Size() 560 k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val) bool) bool) bool { 561 var rowlen int 562 if po >= k.MaxProxDisplay { 563 po = k.MaxProxDisplay - 1 564 } 565 row := []string{fmt.Sprintf("%2d", size)} 566 rest -= size 567 f(func(val pot.Val) bool { 568 e := val.(*Peer) 569 row = append(row, fmt.Sprintf("%x", e.Address()[:2])) 570 rowlen++ 571 return rowlen < 4 572 }) 573 r := strings.Join(row, " ") 574 r = r + wsrow 575 liverows[po] = r[:31] 576 return true 577 }) 578 579 k.addrs.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val) bool) bool) bool { 580 var rowlen int 581 if po >= k.MaxProxDisplay { 582 po = k.MaxProxDisplay - 1 583 } 584 if size < 0 { 585 panic("wtf") 586 } 587 row := []string{fmt.Sprintf("%2d", size)} 588 //我们也在现场展示同龄人 589 f(func(val pot.Val) bool { 590 e := val.(*entry) 591 row = append(row, Label(e)) 592 rowlen++ 593 return rowlen < 4 594 }) 595 peersrows[po] = strings.Join(row, " ") 596 return true 597 }) 598 599 for i := 0; i < k.MaxProxDisplay; i++ { 600 if i == depth { 601 rows = append(rows, fmt.Sprintf("============ DEPTH: %d ==========================================", i)) 602 } 603 left := liverows[i] 604 right := peersrows[i] 605 if len(left) == 0 { 606 left = " 0 " 607 } 608 if len(right) == 0 { 609 right = " 0" 610 } 611 rows = append(rows, fmt.Sprintf("%03d %v | %v", i, left, right)) 612 } 613 rows = append(rows, "=========================================================================") 614 return "\n" + strings.Join(rows, "\n") 615 } 616 617 //Peerpot保存有关预期最近邻居的信息 618 //仅用于测试 619 //TODO移动到单独的测试工具文件 620 type PeerPot struct { 621 NNSet [][]byte 622 } 623 624 //newpeerpotmap用键创建一个pot记录的映射*bzzaddr 625 //作为地址的十六进制表示。 626 //使用通过的卡德米利亚的邻里大小 627 //仅用于测试 628 //TODO移动到单独的测试工具文件 629 func NewPeerPotMap(neighbourhoodSize int, addrs [][]byte) map[string]*PeerPot { 630 631 //为运行状况检查创建所有节点的表 632 np := pot.NewPot(nil, 0) 633 for _, addr := range addrs { 634 np, _, _ = pot.Add(np, addr, Pof) 635 } 636 ppmap := make(map[string]*PeerPot) 637 638 //为连接生成一个通晓真相的来源 639 //每经过一个卡德米利亚 640 for i, a := range addrs { 641 642 //实际Kademlia深度 643 depth := depthForPot(np, neighbourhoodSize, a) 644 645 //全神经网络节点 646 var nns [][]byte 647 648 //从最深的地方到最浅的地方 649 np.EachNeighbour(a, Pof, func(val pot.Val, po int) bool { 650 addr := val.([]byte) 651 //po==256表示addr是透视地址(self) 652 //我们在地图上不包括自己 653 if po == 256 { 654 return true 655 } 656 //附加找到的任何邻居 657 //邻居是指深度内或深度之外的任何对等体。 658 if po >= depth { 659 nns = append(nns, addr) 660 return true 661 } 662 return false 663 }) 664 665 log.Trace(fmt.Sprintf("%x PeerPotMap NNS: %s", addrs[i][:4], LogAddrs(nns))) 666 ppmap[common.Bytes2Hex(a)] = &PeerPot{ 667 NNSet: nns, 668 } 669 } 670 return ppmap 671 } 672 673 //饱和返回节点具有小于minbinsize对等点的最小采购订单值。 674 //如果迭代器达到邻域半径,则返回最后一个bin+1 675 func (k *Kademlia) saturation() int { 676 prev := -1 677 radius := neighbourhoodRadiusForPot(k.conns, k.NeighbourhoodSize, k.base) 678 k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val) bool) bool) bool { 679 prev++ 680 if po >= radius { 681 return false 682 } 683 return prev == po && size >= k.MinBinSize 684 }) 685 if prev < 0 { 686 return 0 687 } 688 return prev 689 } 690 691 //知道的邻居测试是否所有邻居都在同一个地方 692 //在卡德米利亚已知的同龄人中发现 693 //它仅用于健康功能的测试 694 //TODO移动到单独的测试工具文件 695 func (k *Kademlia) knowNeighbours(addrs [][]byte) (got bool, n int, missing [][]byte) { 696 pm := make(map[string]bool) 697 depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base) 698 //创建一张地图,让所有同行深入了解卡德米利亚。 699 k.eachAddr(nil, 255, func(p *BzzAddr, po int) bool { 700 //与卡德米利亚基地地址相比,从最深到最浅 701 //包括所有料仓(自身除外)(0<=料仓<=255) 702 if po < depth { 703 return false 704 } 705 pk := common.Bytes2Hex(p.Address()) 706 pm[pk] = true 707 return true 708 }) 709 710 //遍历Peerpot地图中最近的邻居 711 //如果我们在上面创建的地图中找不到邻居 712 //那我们就不了解所有的邻居了 713 //(可悲的是,这在现代社会太普遍了) 714 var gots int 715 var culprits [][]byte 716 for _, p := range addrs { 717 pk := common.Bytes2Hex(p) 718 if pm[pk] { 719 gots++ 720 } else { 721 log.Trace(fmt.Sprintf("%08x: known nearest neighbour %s not found", k.base, pk)) 722 culprits = append(culprits, p) 723 } 724 } 725 return gots == len(addrs), gots, culprits 726 } 727 728 //连接的邻居测试Peerpot中的所有邻居 729 //目前连接在卡德米利亚 730 //它仅用于健康功能的测试 731 func (k *Kademlia) connectedNeighbours(peers [][]byte) (got bool, n int, missing [][]byte) { 732 pm := make(map[string]bool) 733 734 //创建一个地图,所有深度和深度的对等点都连接在Kademlia中。 735 //与卡德米利亚基地地址相比,从最深到最浅 736 //包括所有料仓(自身除外)(0<=料仓<=255) 737 depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base) 738 k.eachConn(nil, 255, func(p *Peer, po int) bool { 739 if po < depth { 740 return false 741 } 742 pk := common.Bytes2Hex(p.Address()) 743 pm[pk] = true 744 return true 745 }) 746 747 //遍历Peerpot地图中最近的邻居 748 //如果我们在上面创建的地图中找不到邻居 749 //那我们就不了解所有的邻居了 750 var gots int 751 var culprits [][]byte 752 for _, p := range peers { 753 pk := common.Bytes2Hex(p) 754 if pm[pk] { 755 gots++ 756 } else { 757 log.Trace(fmt.Sprintf("%08x: ExpNN: %s not found", k.base, pk)) 758 culprits = append(culprits, p) 759 } 760 } 761 return gots == len(peers), gots, culprits 762 } 763 764 //卡德米利亚的健康状况 765 //仅用于测试 766 type Health struct { 767 KnowNN bool //节点是否知道其所有邻居 768 CountKnowNN int //已知邻居数量 769 MissingKnowNN [][]byte //我们应该知道哪些邻居,但我们不知道 770 ConnectNN bool //节点是否连接到其所有邻居 771 CountConnectNN int //连接到的邻居数量 772 MissingConnectNN [][]byte //我们应该和哪个邻居有联系,但我们没有 773 Saturated bool //我们是否与所有我们想联系的同龄人建立了联系 774 Hive string 775 } 776 777 //健康报告Kademlia连接性的健康状态 778 // 779 //peerpot参数提供了网络的全知视图 780 //结果健康对象是 781 //问题中的卡德米利亚(接受者)的实际组成是什么,以及 782 //当我们考虑到我们对网络的所有了解时,应该是什么情况呢? 783 // 784 //仅用于测试 785 func (k *Kademlia) Healthy(pp *PeerPot) *Health { 786 k.lock.RLock() 787 defer k.lock.RUnlock() 788 if len(pp.NNSet) < k.NeighbourhoodSize { 789 log.Warn("peerpot NNSet < NeighbourhoodSize") 790 } 791 gotnn, countgotnn, culpritsgotnn := k.connectedNeighbours(pp.NNSet) 792 knownn, countknownn, culpritsknownn := k.knowNeighbours(pp.NNSet) 793 depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base) 794 saturated := k.saturation() < depth 795 log.Trace(fmt.Sprintf("%08x: healthy: knowNNs: %v, gotNNs: %v, saturated: %v\n", k.base, knownn, gotnn, saturated)) 796 return &Health{ 797 KnowNN: knownn, 798 CountKnowNN: countknownn, 799 MissingKnowNN: culpritsknownn, 800 ConnectNN: gotnn, 801 CountConnectNN: countgotnn, 802 MissingConnectNN: culpritsgotnn, 803 Saturated: saturated, 804 Hive: k.string(), 805 } 806 } 807