github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/p2p/discover/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 19:16:41</date> 10 //</624450102999650304> 11 12 13 //包发现实现了节点发现协议。 14 // 15 //节点发现协议提供了一种查找 16 //可以连接到。它使用一个类似kademlia的协议来维护 17 //所有监听的ID和端点的分布式数据库 18 //节点。 19 package discover 20 21 import ( 22 "crypto/ecdsa" 23 crand "crypto/rand" 24 "encoding/binary" 25 "fmt" 26 mrand "math/rand" 27 "net" 28 "sort" 29 "sync" 30 "time" 31 32 "github.com/ethereum/go-ethereum/common" 33 "github.com/ethereum/go-ethereum/crypto" 34 "github.com/ethereum/go-ethereum/log" 35 "github.com/ethereum/go-ethereum/p2p/enode" 36 "github.com/ethereum/go-ethereum/p2p/netutil" 37 ) 38 39 const ( 40 alpha = 3 //Kademlia并发因子 41 bucketSize = 16 //卡德米利亚水桶尺寸 42 maxReplacements = 10 //每个桶更换清单的尺寸 43 44 //我们把桶放在距离的1/15以上,因为 45 //我们不太可能遇到更近的节点。 46 hashBits = len(common.Hash{}) * 8 47 nBuckets = hashBits / 15 //桶数 48 bucketMinDistance = hashBits - nBuckets //最近桶的对数距离 49 50 //IP地址限制。 51 bucketIPLimit, bucketSubnet = 2, 24 //最多2个地址来自同一个/24 52 tableIPLimit, tableSubnet = 10, 24 53 54 maxFindnodeFailures = 5 //将删除超过此限制的节点 55 refreshInterval = 30 * time.Minute 56 revalidateInterval = 10 * time.Second 57 copyNodesInterval = 30 * time.Second 58 seedMinTableTime = 5 * time.Minute 59 seedCount = 30 60 seedMaxAge = 5 * 24 * time.Hour 61 ) 62 63 type Table struct { 64 mutex sync.Mutex //保护存储桶、存储桶内容、托儿所、兰特 65 buckets [nBuckets]*bucket //已知节点的距离索引 66 nursery []*node //引导节点 67 rand *mrand.Rand //随机性来源,定期重新播种 68 ips netutil.DistinctNetSet 69 70 db *enode.DB //已知节点数据库 71 net transport 72 refreshReq chan chan struct{} 73 initDone chan struct{} 74 closeReq chan struct{} 75 closed chan struct{} 76 77 nodeAddedHook func(*node) //用于测试 78 } 79 80 //传输由UDP传输实现。 81 //它是一个接口,因此我们可以在不打开大量UDP的情况下进行测试 82 //不生成私钥的套接字。 83 type transport interface { 84 self() *enode.Node 85 ping(enode.ID, *net.UDPAddr) error 86 findnode(toid enode.ID, addr *net.UDPAddr, target encPubkey) ([]*node, error) 87 close() 88 } 89 90 //bucket包含按其上一个活动排序的节点。条目 91 //最近激活的元素是条目中的第一个元素。 92 type bucket struct { 93 entries []*node //实时条目,按上次联系时间排序 94 replacements []*node //如果重新验证失败,则使用最近看到的节点 95 ips netutil.DistinctNetSet 96 } 97 98 func newTable(t transport, db *enode.DB, bootnodes []*enode.Node) (*Table, error) { 99 tab := &Table{ 100 net: t, 101 db: db, 102 refreshReq: make(chan chan struct{}), 103 initDone: make(chan struct{}), 104 closeReq: make(chan struct{}), 105 closed: make(chan struct{}), 106 rand: mrand.New(mrand.NewSource(0)), 107 ips: netutil.DistinctNetSet{Subnet: tableSubnet, Limit: tableIPLimit}, 108 } 109 if err := tab.setFallbackNodes(bootnodes); err != nil { 110 return nil, err 111 } 112 for i := range tab.buckets { 113 tab.buckets[i] = &bucket{ 114 ips: netutil.DistinctNetSet{Subnet: bucketSubnet, Limit: bucketIPLimit}, 115 } 116 } 117 tab.seedRand() 118 tab.loadSeedNodes() 119 120 go tab.loop() 121 return tab, nil 122 } 123 124 func (tab *Table) self() *enode.Node { 125 return tab.net.self() 126 } 127 128 func (tab *Table) seedRand() { 129 var b [8]byte 130 crand.Read(b[:]) 131 132 tab.mutex.Lock() 133 tab.rand.Seed(int64(binary.BigEndian.Uint64(b[:]))) 134 tab.mutex.Unlock() 135 } 136 137 //readrandomnodes用表中的随机节点填充给定切片。结果 138 //保证在单个调用中是唯一的,任何节点都不会出现两次。 139 func (tab *Table) ReadRandomNodes(buf []*enode.Node) (n int) { 140 if !tab.isInitDone() { 141 return 0 142 } 143 tab.mutex.Lock() 144 defer tab.mutex.Unlock() 145 146 //找到所有非空桶,并从中获取新的部分。 147 var buckets [][]*node 148 for _, b := range &tab.buckets { 149 if len(b.entries) > 0 { 150 buckets = append(buckets, b.entries) 151 } 152 } 153 if len(buckets) == 0 { 154 return 0 155 } 156 //洗牌。 157 for i := len(buckets) - 1; i > 0; i-- { 158 j := tab.rand.Intn(len(buckets)) 159 buckets[i], buckets[j] = buckets[j], buckets[i] 160 } 161 //将每个桶的头部移入buf,移除变空的桶。 162 var i, j int 163 for ; i < len(buf); i, j = i+1, (j+1)%len(buckets) { 164 b := buckets[j] 165 buf[i] = unwrapNode(b[0]) 166 buckets[j] = b[1:] 167 if len(b) == 1 { 168 buckets = append(buckets[:j], buckets[j+1:]...) 169 } 170 if len(buckets) == 0 { 171 break 172 } 173 } 174 return i + 1 175 } 176 177 //close终止网络侦听器并刷新节点数据库。 178 func (tab *Table) Close() { 179 if tab.net != nil { 180 tab.net.close() 181 } 182 183 select { 184 case <-tab.closed: 185 //已经关闭。 186 case tab.closeReq <- struct{}{}: 187 <-tab.closed //等待RefreshLoop结束。 188 } 189 } 190 191 //setFallbackNodes设置初始接触点。这些节点 192 //如果表为空,则用于连接到网络 193 //数据库中没有已知节点。 194 func (tab *Table) setFallbackNodes(nodes []*enode.Node) error { 195 for _, n := range nodes { 196 if err := n.ValidateComplete(); err != nil { 197 return fmt.Errorf("bad bootstrap node %q: %v", n, err) 198 } 199 } 200 tab.nursery = wrapNodes(nodes) 201 return nil 202 } 203 204 //IsInitDone返回表的初始种子设定过程是否已完成。 205 func (tab *Table) isInitDone() bool { 206 select { 207 case <-tab.initDone: 208 return true 209 default: 210 return false 211 } 212 } 213 214 //解析搜索具有给定ID的特定节点。 215 //如果找不到节点,则返回nil。 216 func (tab *Table) Resolve(n *enode.Node) *enode.Node { 217 //如果节点存在于本地表中,则否 218 //需要网络交互。 219 hash := n.ID() 220 tab.mutex.Lock() 221 cl := tab.closest(hash, 1) 222 tab.mutex.Unlock() 223 if len(cl.entries) > 0 && cl.entries[0].ID() == hash { 224 return unwrapNode(cl.entries[0]) 225 } 226 //否则,请执行网络查找。 227 result := tab.lookup(encodePubkey(n.Pubkey()), true) 228 for _, n := range result { 229 if n.ID() == hash { 230 return unwrapNode(n) 231 } 232 } 233 return nil 234 } 235 236 //lookuprandom查找网络中的随机节点。 237 func (tab *Table) LookupRandom() []*enode.Node { 238 var target encPubkey 239 crand.Read(target[:]) 240 return unwrapNodes(tab.lookup(target, true)) 241 } 242 243 //查找对接近给定目标的节点执行网络搜索。它接近 244 //通过在每次迭代中查询更接近它的节点来确定目标。给定的目标是 245 //不需要是实际的节点标识符。 246 func (tab *Table) lookup(targetKey encPubkey, refreshIfEmpty bool) []*node { 247 var ( 248 target = enode.ID(crypto.Keccak256Hash(targetKey[:])) 249 asked = make(map[enode.ID]bool) 250 seen = make(map[enode.ID]bool) 251 reply = make(chan []*node, alpha) 252 pendingQueries = 0 253 result *nodesByDistance 254 ) 255 //如果我们撞到自己,不要再问了。 256 //在实践中不太可能经常发生。 257 asked[tab.self().ID()] = true 258 259 for { 260 tab.mutex.Lock() 261 //生成初始结果集 262 result = tab.closest(target, bucketSize) 263 tab.mutex.Unlock() 264 if len(result.entries) > 0 || !refreshIfEmpty { 265 break 266 } 267 //结果集为空,删除了所有节点,刷新。 268 //我们实际上在这里等待刷新完成。非常 269 //第一个查询将命中此情况并运行引导 270 //逻辑。 271 <-tab.refresh() 272 refreshIfEmpty = false 273 } 274 275 for { 276 //询问我们尚未询问的alpha最近的节点 277 for i := 0; i < len(result.entries) && pendingQueries < alpha; i++ { 278 n := result.entries[i] 279 if !asked[n.ID()] { 280 asked[n.ID()] = true 281 pendingQueries++ 282 go tab.findnode(n, targetKey, reply) 283 } 284 } 285 if pendingQueries == 0 { 286 //我们要求所有最近的节点停止搜索 287 break 288 } 289 //等待下一个答复 290 for _, n := range <-reply { 291 if n != nil && !seen[n.ID()] { 292 seen[n.ID()] = true 293 result.push(n, bucketSize) 294 } 295 } 296 pendingQueries-- 297 } 298 return result.entries 299 } 300 301 func (tab *Table) findnode(n *node, targetKey encPubkey, reply chan<- []*node) { 302 fails := tab.db.FindFails(n.ID()) 303 r, err := tab.net.findnode(n.ID(), n.addr(), targetKey) 304 if err != nil || len(r) == 0 { 305 fails++ 306 tab.db.UpdateFindFails(n.ID(), fails) 307 log.Trace("Findnode failed", "id", n.ID(), "failcount", fails, "err", err) 308 if fails >= maxFindnodeFailures { 309 log.Trace("Too many findnode failures, dropping", "id", n.ID(), "failcount", fails) 310 tab.delete(n) 311 } 312 } else if fails > 0 { 313 tab.db.UpdateFindFails(n.ID(), fails-1) 314 } 315 316 //抓取尽可能多的节点。他们中的一些人可能已经不在了,但我们会 317 //在重新验证期间,只需再次移除这些。 318 for _, n := range r { 319 tab.add(n) 320 } 321 reply <- r 322 } 323 324 func (tab *Table) refresh() <-chan struct{} { 325 done := make(chan struct{}) 326 select { 327 case tab.refreshReq <- done: 328 case <-tab.closed: 329 close(done) 330 } 331 return done 332 } 333 334 //循环计划刷新、重新验证运行并协调关闭。 335 func (tab *Table) loop() { 336 var ( 337 revalidate = time.NewTimer(tab.nextRevalidateTime()) 338 refresh = time.NewTicker(refreshInterval) 339 copyNodes = time.NewTicker(copyNodesInterval) 340 refreshDone = make(chan struct{}) //Dorefresh报告完成 341 revalidateDone chan struct{} //Dorevalidate报告完成的地方 342 waiting = []chan struct{}{tab.initDone} //在DoRefresh运行时保留等待的呼叫者 343 ) 344 defer refresh.Stop() 345 defer revalidate.Stop() 346 defer copyNodes.Stop() 347 348 //开始初始刷新。 349 go tab.doRefresh(refreshDone) 350 351 loop: 352 for { 353 select { 354 case <-refresh.C: 355 tab.seedRand() 356 if refreshDone == nil { 357 refreshDone = make(chan struct{}) 358 go tab.doRefresh(refreshDone) 359 } 360 case req := <-tab.refreshReq: 361 waiting = append(waiting, req) 362 if refreshDone == nil { 363 refreshDone = make(chan struct{}) 364 go tab.doRefresh(refreshDone) 365 } 366 case <-refreshDone: 367 for _, ch := range waiting { 368 close(ch) 369 } 370 waiting, refreshDone = nil, nil 371 case <-revalidate.C: 372 revalidateDone = make(chan struct{}) 373 go tab.doRevalidate(revalidateDone) 374 case <-revalidateDone: 375 revalidate.Reset(tab.nextRevalidateTime()) 376 revalidateDone = nil 377 case <-copyNodes.C: 378 go tab.copyLiveNodes() 379 case <-tab.closeReq: 380 break loop 381 } 382 } 383 384 if refreshDone != nil { 385 <-refreshDone 386 } 387 for _, ch := range waiting { 388 close(ch) 389 } 390 if revalidateDone != nil { 391 <-revalidateDone 392 } 393 close(tab.closed) 394 } 395 396 //DoRefresh执行查找随机目标以保留存储桶 397 //满的。如果表为空,则插入种子节点(初始 398 //引导或丢弃错误对等)。 399 func (tab *Table) doRefresh(done chan struct{}) { 400 defer close(done) 401 402 //从数据库加载节点并插入 403 //他们。这将产生一些以前看到的节点, 404 //(希望)还活着。 405 tab.loadSeedNodes() 406 407 //运行自我查找以发现新的邻居节点。 408 //只有当我们有一个secp256k1身份时,我们才能这样做。 409 var key ecdsa.PublicKey 410 if err := tab.self().Load((*enode.Secp256k1)(&key)); err == nil { 411 tab.lookup(encodePubkey(&key), false) 412 } 413 414 //kademlia文件指定bucket刷新应该 415 //在最近使用最少的存储桶中执行查找。我们不能 416 //坚持这一点,因为findnode目标是512位值 417 //(不是哈希大小)并且不容易生成 418 //属于选定的桶中的sha3 preimage。 419 //我们用随机目标执行一些查找。 420 for i := 0; i < 3; i++ { 421 var target encPubkey 422 crand.Read(target[:]) 423 tab.lookup(target, false) 424 } 425 } 426 427 func (tab *Table) loadSeedNodes() { 428 seeds := wrapNodes(tab.db.QuerySeeds(seedCount, seedMaxAge)) 429 seeds = append(seeds, tab.nursery...) 430 for i := range seeds { 431 seed := seeds[i] 432 age := log.Lazy{Fn: func() interface{} { return time.Since(tab.db.LastPongReceived(seed.ID())) }} 433 log.Trace("Found seed node in database", "id", seed.ID(), "addr", seed.addr(), "age", age) 434 tab.add(seed) 435 } 436 } 437 438 //Dorevalidate检查随机存储桶中的最后一个节点是否仍然活动 439 //如果没有,则替换或删除节点。 440 func (tab *Table) doRevalidate(done chan<- struct{}) { 441 defer func() { done <- struct{}{} }() 442 443 last, bi := tab.nodeToRevalidate() 444 if last == nil { 445 //找不到非空存储桶。 446 return 447 } 448 449 //ping所选节点并等待pong。 450 err := tab.net.ping(last.ID(), last.addr()) 451 452 tab.mutex.Lock() 453 defer tab.mutex.Unlock() 454 b := tab.buckets[bi] 455 if err == nil { 456 //节点响应,将其移到前面。 457 log.Debug("Revalidated node", "b", bi, "id", last.ID()) 458 b.bump(last) 459 return 460 } 461 //未收到回复,请选择替换项或删除节点(如果没有) 462 //任何替代品。 463 if r := tab.replace(b, last); r != nil { 464 log.Debug("Replaced dead node", "b", bi, "id", last.ID(), "ip", last.IP(), "r", r.ID(), "rip", r.IP()) 465 } else { 466 log.Debug("Removed dead node", "b", bi, "id", last.ID(), "ip", last.IP()) 467 } 468 } 469 470 //nodetorefalidate返回随机非空bucket中的最后一个节点。 471 func (tab *Table) nodeToRevalidate() (n *node, bi int) { 472 tab.mutex.Lock() 473 defer tab.mutex.Unlock() 474 475 for _, bi = range tab.rand.Perm(len(tab.buckets)) { 476 b := tab.buckets[bi] 477 if len(b.entries) > 0 { 478 last := b.entries[len(b.entries)-1] 479 return last, bi 480 } 481 } 482 return nil, 0 483 } 484 485 func (tab *Table) nextRevalidateTime() time.Duration { 486 tab.mutex.Lock() 487 defer tab.mutex.Unlock() 488 489 return time.Duration(tab.rand.Int63n(int64(revalidateInterval))) 490 } 491 492 //CopyLiveNodes将表中的节点添加到数据库中(如果它们在表中)。 493 //比Mintable时间长。 494 func (tab *Table) copyLiveNodes() { 495 tab.mutex.Lock() 496 defer tab.mutex.Unlock() 497 498 now := time.Now() 499 for _, b := range &tab.buckets { 500 for _, n := range b.entries { 501 if now.Sub(n.addedAt) >= seedMinTableTime { 502 tab.db.UpdateNode(unwrapNode(n)) 503 } 504 } 505 } 506 } 507 508 //最近返回表中最接近 509 //给定的ID。调用方必须保持tab.mutex。 510 func (tab *Table) closest(target enode.ID, nresults int) *nodesByDistance { 511 //这是一种非常浪费的查找最近节点的方法,但是 512 //显然是正确的。我相信以树为基础的桶可以 513 //这更容易有效地实施。 514 close := &nodesByDistance{target: target} 515 for _, b := range &tab.buckets { 516 for _, n := range b.entries { 517 close.push(n, nresults) 518 } 519 } 520 return close 521 } 522 523 func (tab *Table) len() (n int) { 524 for _, b := range &tab.buckets { 525 n += len(b.entries) 526 } 527 return n 528 } 529 530 //bucket返回给定节点id散列的bucket。 531 func (tab *Table) bucket(id enode.ID) *bucket { 532 d := enode.LogDist(tab.self().ID(), id) 533 if d <= bucketMinDistance { 534 return tab.buckets[0] 535 } 536 return tab.buckets[d-bucketMinDistance-1] 537 } 538 539 //添加将给定节点添加到其相应存储桶的尝试。如果桶有空间 540 //可用,添加节点立即成功。否则,如果 541 //bucket中最近活动的节点不响应ping数据包。 542 // 543 //调用方不能持有tab.mutex。 544 func (tab *Table) add(n *node) { 545 if n.ID() == tab.self().ID() { 546 return 547 } 548 549 tab.mutex.Lock() 550 defer tab.mutex.Unlock() 551 b := tab.bucket(n.ID()) 552 if !tab.bumpOrAdd(b, n) { 553 //节点不在表中。将其添加到替换列表中。 554 tab.addReplacement(b, n) 555 } 556 } 557 558 //addthroughping将给定节点添加到表中。与平原相比 559 //“添加”有一个附加的安全措施:如果表仍然存在 560 //未添加初始化节点。这可以防止攻击 561 //只需重复发送ping就可以填写表格。 562 // 563 //调用方不能持有tab.mutex。 564 func (tab *Table) addThroughPing(n *node) { 565 if !tab.isInitDone() { 566 return 567 } 568 tab.add(n) 569 } 570 571 //stufacture将表中的节点添加到相应bucket的末尾 572 //如果桶没满。调用方不能持有tab.mutex。 573 func (tab *Table) stuff(nodes []*node) { 574 tab.mutex.Lock() 575 defer tab.mutex.Unlock() 576 577 for _, n := range nodes { 578 if n.ID() == tab.self().ID() { 579 continue //不要增加自我 580 } 581 b := tab.bucket(n.ID()) 582 if len(b.entries) < bucketSize { 583 tab.bumpOrAdd(b, n) 584 } 585 } 586 } 587 588 //删除从节点表中删除一个条目。用于疏散死节点。 589 func (tab *Table) delete(node *node) { 590 tab.mutex.Lock() 591 defer tab.mutex.Unlock() 592 593 tab.deleteInBucket(tab.bucket(node.ID()), node) 594 } 595 596 func (tab *Table) addIP(b *bucket, ip net.IP) bool { 597 if netutil.IsLAN(ip) { 598 return true 599 } 600 if !tab.ips.Add(ip) { 601 log.Debug("IP exceeds table limit", "ip", ip) 602 return false 603 } 604 if !b.ips.Add(ip) { 605 log.Debug("IP exceeds bucket limit", "ip", ip) 606 tab.ips.Remove(ip) 607 return false 608 } 609 return true 610 } 611 612 func (tab *Table) removeIP(b *bucket, ip net.IP) { 613 if netutil.IsLAN(ip) { 614 return 615 } 616 tab.ips.Remove(ip) 617 b.ips.Remove(ip) 618 } 619 620 func (tab *Table) addReplacement(b *bucket, n *node) { 621 for _, e := range b.replacements { 622 if e.ID() == n.ID() { 623 return //已列入清单 624 } 625 } 626 if !tab.addIP(b, n.IP()) { 627 return 628 } 629 var removed *node 630 b.replacements, removed = pushNode(b.replacements, n, maxReplacements) 631 if removed != nil { 632 tab.removeIP(b, removed.IP()) 633 } 634 } 635 636 //replace从替换列表中删除n,如果“last”是 637 //桶中的最后一个条目。如果“last”不是最后一个条目,则它或已被替换 638 //和别人在一起或者变得活跃起来。 639 func (tab *Table) replace(b *bucket, last *node) *node { 640 if len(b.entries) == 0 || b.entries[len(b.entries)-1].ID() != last.ID() { 641 //条目已移动,不要替换它。 642 return nil 643 } 644 //还是最后一个条目。 645 if len(b.replacements) == 0 { 646 tab.deleteInBucket(b, last) 647 return nil 648 } 649 r := b.replacements[tab.rand.Intn(len(b.replacements))] 650 b.replacements = deleteNode(b.replacements, r) 651 b.entries[len(b.entries)-1] = r 652 tab.removeIP(b, last.IP()) 653 return r 654 } 655 656 //bump将给定节点移动到bucket条目列表的前面 657 //如果它包含在那个列表中。 658 func (b *bucket) bump(n *node) bool { 659 for i := range b.entries { 660 if b.entries[i].ID() == n.ID() { 661 //把它移到前面 662 copy(b.entries[1:], b.entries[:i]) 663 b.entries[0] = n 664 return true 665 } 666 } 667 return false 668 } 669 670 //bumporadd将n移动到bucket条目列表的前面,或者如果该列表不在,则将其添加。 671 //满的。如果n在桶中,返回值为真。 672 func (tab *Table) bumpOrAdd(b *bucket, n *node) bool { 673 if b.bump(n) { 674 return true 675 } 676 if len(b.entries) >= bucketSize || !tab.addIP(b, n.IP()) { 677 return false 678 } 679 b.entries, _ = pushNode(b.entries, n, bucketSize) 680 b.replacements = deleteNode(b.replacements, n) 681 n.addedAt = time.Now() 682 if tab.nodeAddedHook != nil { 683 tab.nodeAddedHook(n) 684 } 685 return true 686 } 687 688 func (tab *Table) deleteInBucket(b *bucket, n *node) { 689 b.entries = deleteNode(b.entries, n) 690 tab.removeIP(b, n.IP()) 691 } 692 693 //pushnode将n添加到列表的前面,最多保留max项。 694 func pushNode(list []*node, n *node, max int) ([]*node, *node) { 695 if len(list) < max { 696 list = append(list, nil) 697 } 698 removed := list[len(list)-1] 699 copy(list[1:], list) 700 list[0] = n 701 return list, removed 702 } 703 704 //删除节点从列表中删除n。 705 func deleteNode(list []*node, n *node) []*node { 706 for i := range list { 707 if list[i].ID() == n.ID() { 708 return append(list[:i], list[i+1:]...) 709 } 710 } 711 return list 712 } 713 714 //nodesByDistance是节点列表,按 715 //距离目标。 716 type nodesByDistance struct { 717 entries []*node 718 target enode.ID 719 } 720 721 //push将给定节点添加到列表中,使总大小保持在maxelems以下。 722 func (h *nodesByDistance) push(n *node, maxElems int) { 723 ix := sort.Search(len(h.entries), func(i int) bool { 724 return enode.DistCmp(h.target, h.entries[i].ID(), n.ID()) > 0 725 }) 726 if len(h.entries) < maxElems { 727 h.entries = append(h.entries, n) 728 } 729 if ix == len(h.entries) { 730 //比我们现有的所有节点都要远。 731 //如果有空间,那么节点现在是最后一个元素。 732 } else { 733 //向下滑动现有条目以腾出空间 734 //这将覆盖我们刚刚附加的条目。 735 copy(h.entries[ix+1:], h.entries[ix:]) 736 h.entries[ix] = n 737 } 738 } 739