github.com/klaytn/klaytn@v1.12.1/networks/p2p/discover/table.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2015 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from p2p/discover/table.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package discover 22 23 import ( 24 crand "crypto/rand" 25 "encoding/binary" 26 "errors" 27 "fmt" 28 mrand "math/rand" 29 "net" 30 "sort" 31 "sync" 32 "time" 33 34 "github.com/klaytn/klaytn/common" 35 "github.com/klaytn/klaytn/crypto" 36 "github.com/klaytn/klaytn/log" 37 "github.com/klaytn/klaytn/networks/p2p/netutil" 38 ) 39 40 const ( 41 alpha = 3 // Kademlia concurrency factor 42 bucketSize = 16 // Kademlia bucket size 43 maxReplacements = 10 // Size of per-bucket replacement list 44 45 maxBondingPingPongs = 16 // Limit on the number of concurrent ping/pong interactions 46 maxFindnodeFailures = 5 // Nodes exceeding this limit are dropped 47 48 refreshInterval = 30 * time.Minute 49 revalidateInterval = 10 * time.Second 50 copyNodesInterval = 30 * time.Second 51 52 seedCount = 30 53 seedMaxAge = 5 * 24 * time.Hour 54 ) 55 56 type DiscoveryType uint8 57 58 type Discovery interface { 59 Self() *Node 60 Close() 61 Resolve(target NodeID, targetType NodeType) *Node 62 Lookup(target NodeID, targetType NodeType) []*Node 63 GetNodes(targetType NodeType, max int) []*Node 64 ReadRandomNodes([]*Node, NodeType) int 65 RetrieveNodes(target common.Hash, nType NodeType, nresults int) []*Node // replace of closest():Table 66 67 HasBond(id NodeID) bool 68 Bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16, nType NodeType) (*Node, error) 69 IsAuthorized(fromID NodeID, nType NodeType) bool 70 71 // interfaces for API 72 Name() string 73 CreateUpdateNodeOnDB(n *Node) error 74 CreateUpdateNodeOnTable(n *Node) error 75 GetNodeFromDB(id NodeID) (*Node, error) 76 DeleteNodeFromDB(n *Node) error 77 DeleteNodeFromTable(n *Node) error 78 GetBucketEntries() []*Node 79 GetReplacements() []*Node 80 81 GetAuthorizedNodes() []*Node 82 PutAuthorizedNodes(nodes []*Node) 83 DeleteAuthorizedNodes(nodes []*Node) 84 } 85 86 type Table struct { 87 nursery []*Node // bootstrap nodes 88 rand *mrand.Rand // source of randomness, periodically reseeded 89 randMu sync.Mutex 90 ips netutil.DistinctNetSet 91 92 db *nodeDB // database of known nodes 93 refreshReq chan chan struct{} 94 initDone chan struct{} 95 closeReq chan struct{} 96 closed chan struct{} 97 98 bondmu sync.Mutex 99 bonding map[NodeID]*bondproc 100 bondslots chan struct{} // limits total number of active bonding processes 101 102 nodeAddedHook func(*Node) // for testing 103 104 net transport 105 self *Node // metadata of the local node 106 107 storages map[NodeType]discoverStorage 108 storagesMu sync.RWMutex 109 110 localLogger log.Logger 111 } 112 113 type bondproc struct { 114 err error 115 n *Node 116 done chan struct{} 117 } 118 119 // transport is implemented by the UDP transport. 120 // it is an interface so we can test without opening lots of UDP 121 // sockets and without generating a private key. 122 type transport interface { 123 ping(toid NodeID, toaddr *net.UDPAddr) error 124 waitping(NodeID) error 125 findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID, targetNT NodeType, max int) ([]*Node, error) 126 close() 127 } 128 129 func newTable(cfg *Config) (Discovery, error) { 130 // If no node database was given, use an in-memory one 131 db, err := newNodeDB(cfg.NodeDBPath, Version, cfg.Id) 132 if err != nil { 133 return nil, err 134 } 135 136 tab := &Table{ 137 net: cfg.udp, 138 db: db, 139 self: NewNode(cfg.Id, cfg.Addr.IP, uint16(cfg.Addr.Port), uint16(cfg.Addr.Port), nil, cfg.NodeType), 140 bonding: make(map[NodeID]*bondproc), 141 bondslots: make(chan struct{}, maxBondingPingPongs), 142 refreshReq: make(chan chan struct{}), 143 initDone: make(chan struct{}), 144 closeReq: make(chan struct{}), 145 closed: make(chan struct{}), 146 rand: mrand.New(mrand.NewSource(0)), 147 storages: make(map[NodeType]discoverStorage), 148 localLogger: logger.NewWith("Discover", "Table"), 149 } 150 151 switch cfg.NodeType { 152 case NodeTypeCN: 153 tab.addStorage(NodeTypeCN, &simpleStorage{targetType: NodeTypeCN, noDiscover: true, max: 100}) 154 tab.addStorage(NodeTypeBN, &simpleStorage{targetType: NodeTypeBN, noDiscover: true, max: 3}) 155 case NodeTypePN: 156 tab.addStorage(NodeTypePN, &simpleStorage{targetType: NodeTypePN, noDiscover: true, max: 1}) 157 tab.addStorage(NodeTypeEN, &KademliaStorage{targetType: NodeTypeEN, noDiscover: true}) 158 tab.addStorage(NodeTypeBN, &simpleStorage{targetType: NodeTypeBN, noDiscover: true, max: 3}) 159 case NodeTypeEN: 160 tab.addStorage(NodeTypePN, &simpleStorage{targetType: NodeTypePN, noDiscover: true, max: 2}) 161 tab.addStorage(NodeTypeEN, &KademliaStorage{targetType: NodeTypeEN}) 162 tab.addStorage(NodeTypeBN, &simpleStorage{targetType: NodeTypeBN, noDiscover: true, max: 3}) 163 case NodeTypeBN: 164 tab.addStorage(NodeTypeCN, NewSimpleStorage(NodeTypeCN, true, 100, cfg.AuthorizedNodes)) 165 tab.addStorage(NodeTypePN, NewSimpleStorage(NodeTypePN, true, 100, cfg.AuthorizedNodes)) 166 tab.addStorage(NodeTypeEN, &KademliaStorage{targetType: NodeTypeEN, noDiscover: true}) 167 tab.addStorage(NodeTypeBN, &simpleStorage{targetType: NodeTypeBN, max: 3}) 168 } 169 170 if err := tab.setFallbackNodes(cfg.Bootnodes); err != nil { 171 return nil, err 172 } 173 for i := 0; i < cap(tab.bondslots); i++ { 174 tab.bondslots <- struct{}{} 175 } 176 177 tab.seedRand() 178 tab.loadSeedNodes(false) 179 // Start the background expiration goroutine after loading seeds so that the search for 180 // seed nodes also considers older nodes that would otherwise be removed by the 181 // expiration. 182 tab.db.ensureExpirer() 183 tab.localLogger.Debug("new "+tab.Name()+" created", "err", nil) 184 return tab, nil 185 } 186 187 func (tab *Table) IsAuthorized(fromID NodeID, nType NodeType) bool { 188 tab.storagesMu.RLock() 189 defer tab.storagesMu.RUnlock() 190 if tab.storages[nType] != nil { 191 return tab.storages[nType].isAuthorized(fromID) 192 } 193 return true 194 } 195 196 // setFallbackNodes sets the initial points of contact. These nodes 197 // are used to connect to the network if the table is empty and there 198 // are no known nodes in the database. 199 func (tab *Table) setFallbackNodes(nodes []*Node) error { 200 for _, n := range nodes { 201 if err := n.validateComplete(); err != nil { 202 return fmt.Errorf("bad bootstrap/fallback node %q (%v)", n, err) 203 } 204 } 205 tab.nursery = make([]*Node, 0, len(nodes)) 206 for _, n := range nodes { 207 cpy := *n 208 // Recompute cpy.sha because the node might not have been 209 // created by NewNode or ParseNode. 210 cpy.sha = crypto.Keccak256Hash(n.ID[:]) 211 tab.nursery = append(tab.nursery, &cpy) 212 } 213 return nil 214 } 215 216 func (tab *Table) findNewNode(seeds *nodesByDistance, targetID NodeID, targetNT NodeType, recursiveFind bool, max int) []*Node { 217 var ( 218 asked = make(map[NodeID]bool) 219 seen = make(map[NodeID]bool) 220 reply = make(chan []*Node, alpha) 221 pendingQueries = 0 222 ) 223 224 // don't query further if we hit ourself. 225 // unlikely to happen often in practice. 226 asked[tab.self.ID] = true 227 for _, e := range seeds.entries { 228 seen[e.ID] = true 229 } 230 231 for { 232 // ask the alpha closest nodes that we haven't asked yet 233 for i := 0; i < len(seeds.entries) && pendingQueries < alpha; i++ { 234 n := seeds.entries[i] 235 if !asked[n.ID] { 236 asked[n.ID] = true 237 pendingQueries++ 238 go func() { 239 // Find potential neighbors to bond with 240 r, err := tab.net.findnode(n.ID, n.addr(), targetID, targetNT, max) 241 if err != nil { 242 // Bump the failure counter to detect and evacuate non-bonded entries 243 fails := tab.db.findFails(n.ID) + 1 244 tab.db.updateFindFails(n.ID, fails) 245 tab.localLogger.Trace("Bumping findnode failure counter", "id", n.ID, "failcount", fails) 246 247 if fails >= maxFindnodeFailures { 248 tab.localLogger.Trace("Too many findnode failures, dropping", "id", n.ID, "failcount", fails) 249 tab.delete(n) 250 } 251 } 252 if targetNT != NodeTypeBN { 253 r = removeBn(r) 254 } 255 reply <- tab.bondall(r) 256 }() 257 } 258 } 259 if pendingQueries == 0 { 260 // we have asked all closest nodes, stop the search 261 break 262 } 263 264 if recursiveFind { 265 // wait for the next reply 266 for _, n := range <-reply { 267 if n != nil && !seen[n.ID] { 268 seen[n.ID] = true 269 seeds.push(n, max) 270 } 271 } 272 pendingQueries-- 273 } else { 274 for i := 0; i < pendingQueries; i++ { 275 for _, n := range <-reply { 276 if n != nil && !seen[n.ID] { 277 seen[n.ID] = true 278 if len(seeds.entries) < max { 279 seeds.entries = append(seeds.entries, n) 280 } 281 } 282 } 283 } 284 break 285 } 286 } 287 if targetNT != NodeTypeBN { 288 seeds.entries = removeBn(seeds.entries) 289 } 290 tab.localLogger.Debug("findNewNode: found nodes", "length", len(seeds.entries), "nodeType", targetNT) 291 return seeds.entries 292 } 293 294 func (tab *Table) addStorage(nType NodeType, s discoverStorage) { 295 tab.storagesMu.Lock() 296 defer tab.storagesMu.Unlock() 297 s.setTable(tab) 298 tab.storages[nType] = s 299 s.init() 300 } 301 302 func (tab *Table) seedRand() { 303 var b [8]byte 304 crand.Read(b[:]) 305 306 // tab.mutex.Lock() 307 tab.randMu.Lock() 308 tab.rand.Seed(int64(binary.BigEndian.Uint64(b[:]))) 309 tab.randMu.Unlock() 310 // tab.mutex.Unlock() 311 } 312 313 // Self returns the local node. 314 // The returned node should not be modified by the caller. 315 func (tab *Table) Self() *Node { 316 return tab.self 317 } 318 319 // ReadRandomNodes fills the given slice with random nodes from the 320 // table. It will not write the same node more than once. The nodes in 321 // the slice are copies and can be modified by the caller. 322 func (tab *Table) ReadRandomNodes(buf []*Node, nType NodeType) (n int) { 323 if !tab.isInitDone() { 324 return 0 325 } 326 327 tab.storagesMu.RLock() 328 defer tab.storagesMu.RUnlock() 329 if tab.storages[nType] == nil { 330 tab.localLogger.Warn("ReadRandomNodes: Not Supported NodeType", "NodeType", nType) 331 return 0 332 } 333 334 return tab.storages[nType].readRandomNodes(buf) 335 } 336 337 // Close terminates the network listener and flushes the node database. 338 func (tab *Table) Close() { 339 select { 340 case <-tab.closed: 341 // already closed. 342 case tab.closeReq <- struct{}{}: 343 <-tab.closed // wait for refreshLoop to end. 344 } 345 } 346 347 // isInitDone returns whether the table's initial seeding procedure has completed. 348 func (tab *Table) isInitDone() bool { 349 select { 350 case <-tab.initDone: 351 return true 352 default: 353 return false 354 } 355 } 356 357 // Resolve searches for a specific node with the given ID. 358 // It returns nil if the node could not be found. 359 func (tab *Table) Resolve(targetID NodeID, targetType NodeType) *Node { 360 // If the node is present in the local table, no 361 // network interaction is required. 362 hash := crypto.Keccak256Hash(targetID[:]) 363 cl := tab.closest(hash, targetType, 1) 364 if len(cl.entries) > 0 && cl.entries[0].ID == targetID { 365 return cl.entries[0] 366 } 367 // Otherwise, do a network lookup. 368 result := tab.Lookup(targetID, targetType) 369 for _, n := range result { 370 if n.ID == targetID { 371 return n 372 } 373 } 374 return nil 375 } 376 377 // Lookup performs a network search for nodes close 378 // to the given target. It approaches the target by querying 379 // nodes that are closer to it on each iteration. 380 // The given target does not need to be an actual node 381 // identifier. 382 func (tab *Table) Lookup(targetID NodeID, targetType NodeType) []*Node { 383 return tab.lookup(targetID, true, targetType) 384 } 385 386 func (tab *Table) lookup(targetID NodeID, refreshIfEmpty bool, targetNT NodeType) []*Node { 387 tab.storagesMu.RLock() 388 defer tab.storagesMu.RUnlock() 389 390 if tab.storages[targetNT] == nil { 391 tab.localLogger.Warn("lookup: Not Supported NodeType", "NodeType", targetNT) 392 return []*Node{} 393 } 394 return tab.storages[targetNT].lookup(targetID, refreshIfEmpty, targetNT) 395 } 396 397 func (tab *Table) GetNodes(targetNT NodeType, max int) []*Node { 398 tab.storagesMu.RLock() 399 defer tab.storagesMu.RUnlock() 400 401 if tab.storages[targetNT] == nil { 402 tab.localLogger.Warn("getNodes: Not Supported NodeType", "NodeType", targetNT) 403 return []*Node{} 404 } 405 return tab.storages[targetNT].getNodes(max) 406 } 407 408 func removeBn(nodes []*Node) []*Node { 409 tmp := nodes[:0] 410 for _, n := range nodes { 411 if n.NType != NodeTypeBN { 412 tmp = append(tmp, n) 413 } 414 } 415 return tmp 416 } 417 418 func (tab *Table) refresh() <-chan struct{} { 419 done := make(chan struct{}) 420 select { 421 case tab.refreshReq <- done: 422 case <-tab.closed: 423 close(done) 424 } 425 return done 426 } 427 428 // loop schedules refresh, revalidate runs and coordinates shutdown. 429 func (tab *Table) loop() { 430 var ( 431 revalidate = time.NewTimer(tab.nextRevalidateTime()) 432 refresh = time.NewTicker(refreshInterval) 433 copyNodes = time.NewTicker(copyNodesInterval) 434 revalidateDone = make(chan struct{}) 435 refreshDone = make(chan struct{}) // where doRefresh reports completion 436 waiting = []chan struct{}{tab.initDone} // holds waiting callers while doRefresh runs 437 ) 438 defer refresh.Stop() 439 defer revalidate.Stop() 440 defer copyNodes.Stop() 441 442 // Start initial refresh. 443 go tab.doRefresh(refreshDone) 444 445 loop: 446 for { 447 select { 448 case <-refresh.C: 449 tab.seedRand() 450 if refreshDone == nil { 451 refreshDone = make(chan struct{}) 452 go tab.doRefresh(refreshDone) 453 } 454 case req := <-tab.refreshReq: 455 waiting = append(waiting, req) 456 if refreshDone == nil { 457 refreshDone = make(chan struct{}) 458 go tab.doRefresh(refreshDone) 459 } 460 case <-refreshDone: 461 for _, ch := range waiting { 462 close(ch) 463 } 464 waiting, refreshDone = nil, nil 465 case <-revalidate.C: 466 go tab.doRevalidate(revalidateDone) 467 case <-revalidateDone: 468 tt := tab.nextRevalidateTime() 469 revalidate.Reset(tt) 470 case <-copyNodes.C: 471 go tab.copyBondedNodes() 472 case <-tab.closeReq: 473 break loop 474 } 475 } 476 477 if tab.net != nil { 478 tab.net.close() 479 } 480 if refreshDone != nil { 481 <-refreshDone 482 } 483 for _, ch := range waiting { 484 close(ch) 485 } 486 tab.db.close() 487 close(tab.closed) 488 } 489 490 // doRefresh performs a lookup for a random target to keep buckets 491 // full. seed nodes are inserted if the table is empty (initial 492 // bootstrap or discarded faulty peers). 493 func (tab *Table) doRefresh(done chan struct{}) { 494 tab.localLogger.Trace("doRefresh()") 495 defer close(done) 496 497 // Load nodes from the database and insert 498 // them. This should yield a few previously seen nodes that are 499 // (hopefully) still alive. 500 tab.loadSeedNodes(true) 501 502 tab.storagesMu.RLock() 503 defer tab.storagesMu.RUnlock() 504 for _, ds := range tab.storages { 505 ds.doRefresh() 506 } 507 } 508 509 func (tab *Table) loadSeedNodes(bond bool) { 510 // TODO-Klaytn-Node Separate logic to storages. 511 seeds := tab.db.querySeeds(seedCount, seedMaxAge) 512 seeds = removeBn(seeds) 513 seeds = append(seeds, tab.nursery...) 514 if bond { 515 seeds = tab.bondall(seeds) 516 } 517 for i := range seeds { 518 seed := seeds[i] 519 age := log.Lazy{Fn: func() interface{} { return time.Since(tab.db.bondTime(seed.ID)) }} 520 tab.localLogger.Debug("Found seed node in database", "id", seed.ID, "addr", seed.addr(), "age", age) 521 tab.add(seed) 522 } 523 } 524 525 // doRevalidate checks that the last node in a random bucket is still live 526 // and replaces or deletes the node if it isn't. 527 func (tab *Table) doRevalidate(done chan<- struct{}) { 528 defer func() { done <- struct{}{} }() 529 530 tab.storagesMu.RLock() 531 defer tab.storagesMu.RUnlock() 532 for _, ds := range tab.storages { 533 ds.doRevalidate() 534 } 535 } 536 537 func (tab *Table) nextRevalidateTime() time.Duration { 538 tab.randMu.Lock() 539 defer tab.randMu.Unlock() 540 541 return time.Duration(tab.rand.Int63n(int64(revalidateInterval))) 542 } 543 544 // copyBondedNodes adds nodes from the table to the database if they have been in the table 545 // longer then minTableTime. 546 func (tab *Table) copyBondedNodes() { 547 tab.storagesMu.RLock() 548 defer tab.storagesMu.RUnlock() 549 for _, ds := range tab.storages { 550 ds.copyBondedNodes() 551 } 552 } 553 554 // closest returns the n nodes in the table that are closest to the 555 // given id. The caller must hold tab.mutex. 556 func (tab *Table) closest(target common.Hash, nType NodeType, nresults int) *nodesByDistance { 557 tab.storagesMu.RLock() 558 defer tab.storagesMu.RUnlock() 559 560 if tab.storages[nType] == nil { 561 tab.localLogger.Warn("closest(): Not Supported NodeType", "NodeType", nType) 562 return &nodesByDistance{} 563 } 564 return tab.storages[nType].closest(target, nresults) 565 } 566 567 // RetrieveNodes returns node list except bootnode. This method is used to make a result of FINDNODE request. 568 func (tab *Table) RetrieveNodes(target common.Hash, nType NodeType, nresults int) []*Node { 569 tab.storagesMu.RLock() 570 defer tab.storagesMu.RUnlock() 571 572 if tab.storages[nType] == nil { 573 tab.localLogger.Warn("RetrieveNodes: Not Supported NodeType", "NodeType", nType) 574 return []*Node{} 575 } 576 nodes := tab.storages[nType].closest(target, nresults).entries 577 if nType != NodeTypeBN { 578 nodes = removeBn(nodes) 579 } 580 return nodes 581 } 582 583 func (tab *Table) len() (n int) { 584 tab.storagesMu.RLock() 585 defer tab.storagesMu.RUnlock() 586 587 for _, ds := range tab.storages { 588 n += ds.len() 589 } 590 return n 591 } 592 593 func (tab *Table) nodes() (n []*Node) { 594 tab.storagesMu.RLock() 595 defer tab.storagesMu.RUnlock() 596 597 for _, ds := range tab.storages { 598 n = append(n, ds.nodeAll()...) 599 } 600 return n 601 } 602 603 // bondall bonds with all given nodes concurrently and returns 604 // those nodes for which bonding has probably succeeded. 605 func (tab *Table) bondall(nodes []*Node) (result []*Node) { 606 rc := make(chan *Node, len(nodes)) 607 for i := range nodes { 608 go func(n *Node) { 609 nn, _ := tab.Bond(false, n.ID, n.addr(), n.TCP, n.NType) 610 rc <- nn 611 }(nodes[i]) 612 } 613 for range nodes { 614 if n := <-rc; n != nil { 615 result = append(result, n) 616 } 617 } 618 return result 619 } 620 621 // Bond ensures the local node has a bond with the given remote node. 622 // It also attempts to insert the node into the table if bonding succeeds. 623 // The caller must not hold tab.mutex. 624 // 625 // A bond is must be established before sending findnode requests. 626 // Both sides must have completed a ping/pong exchange for a bond to 627 // exist. The total number of active bonding processes is limited in 628 // order to restrain network use. 629 // 630 // bond is meant to operate idempotently in that bonding with a remote 631 // node which still remembers a previously established bond will work. 632 // The remote node will simply not send a ping back, causing waitping 633 // to time out. 634 // 635 // If pinged is true, the remote node has just pinged us and one half 636 // of the process can be skipped. 637 func (tab *Table) Bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16, nType NodeType) (*Node, error) { 638 if id == tab.self.ID { 639 return nil, errors.New("is self") 640 } 641 if pinged && !tab.isInitDone() { 642 return nil, errors.New("still initializing") 643 } 644 // Start bonding if we haven't seen this node for a while or if it failed findnode too often. 645 node, fails := tab.db.node(id), tab.db.findFails(id) 646 age := time.Since(tab.db.bondTime(id)) 647 var result error 648 // A Bootnode always add node(cn, pn, en) to table. 649 if fails > 0 || age > nodeDBNodeExpiration || (node == nil && tab.self.NType == NodeTypeBN) { 650 tab.localLogger.Trace("Bond - Starting bonding ping/pong", "id", id, "known", node != nil, "failcount", fails, "age", age) 651 652 tab.bondmu.Lock() 653 w := tab.bonding[id] 654 if w != nil { 655 // Wait for an existing bonding process to complete. 656 tab.bondmu.Unlock() 657 <-w.done 658 } else { 659 // Register a new bonding process. 660 w = &bondproc{done: make(chan struct{})} 661 tab.bonding[id] = w 662 tab.bondmu.Unlock() 663 // Do the ping/pong. The result goes into w. 664 tab.pingpong(w, pinged, id, addr, tcpPort, nType) 665 // Unregister the process after it's done. 666 tab.bondmu.Lock() 667 delete(tab.bonding, id) 668 tab.bondmu.Unlock() 669 } 670 // Retrieve the bonding results 671 result = w.err 672 tab.localLogger.Trace("Bond", "error", result) 673 if result == nil { 674 node = w.n 675 } 676 } 677 // Add the node to the table even if the bonding ping/pong 678 // fails. It will be replaced quickly if it continues to be 679 // unresponsive. 680 if node != nil { 681 tab.localLogger.Trace("Bond - Add", "id", node.ID, "type", node.NType, "sha", node.sha) 682 tab.add(node) 683 tab.db.updateFindFails(id, 0) 684 lenEntries := len(tab.GetBucketEntries()) 685 lenReplacements := len(tab.GetReplacements()) 686 bucketEntriesGauge.Update(int64(lenEntries)) 687 bucketReplacementsGauge.Update(int64(lenReplacements)) 688 } 689 return node, result 690 } 691 692 func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16, nType NodeType) { 693 // Request a bonding slot to limit network usage 694 <-tab.bondslots 695 defer func() { tab.bondslots <- struct{}{} }() 696 697 // Ping the remote side and wait for a pong. 698 if w.err = tab.ping(id, addr); w.err != nil { 699 close(w.done) 700 return 701 } 702 if !pinged { 703 // Give the remote node a chance to ping us before we start 704 // sending findnode requests. If they still remember us, 705 // waitping will simply time out. 706 tab.localLogger.Trace("pingpong-waitping", "to", id) 707 tab.net.waitping(id) 708 } 709 // Bonding succeeded, update the node database. 710 w.n = NewNode(id, addr.IP, uint16(addr.Port), tcpPort, nil, nType) 711 tab.localLogger.Trace("pingpong-success, make new node", "node", w.n) 712 close(w.done) 713 } 714 715 // ping a remote endpoint and wait for a reply, also updating the node 716 // database accordingly. 717 func (tab *Table) ping(id NodeID, addr *net.UDPAddr) error { 718 tab.localLogger.Trace("ping", "to", id) 719 tab.db.updateLastPing(id, time.Now()) 720 if err := tab.net.ping(id, addr); err != nil { 721 return err 722 } 723 tab.db.updateBondTime(id, time.Now()) 724 return nil 725 } 726 727 // bucket returns the bucket for the given node ID hash. 728 // This method is for only unit tests. 729 func (tab *Table) bucket(sha common.Hash, nType NodeType) *bucket { 730 tab.storagesMu.RLock() 731 defer tab.storagesMu.RUnlock() 732 733 if tab.storages[nType] == nil { 734 tab.localLogger.Warn("bucket(): Not Supported NodeType", "NodeType", nType) 735 return &bucket{} 736 } 737 if _, ok := tab.storages[nType].(*KademliaStorage); !ok { 738 tab.localLogger.Warn("bucket(): bucket() only allowed to use at KademliaStorage", "NodeType", nType) 739 return &bucket{} 740 } 741 ks := tab.storages[nType].(*KademliaStorage) 742 743 ks.bucketsMu.Lock() 744 defer ks.bucketsMu.Unlock() 745 return ks.bucket(sha) 746 } 747 748 // add attempts to add the given node its corresponding bucket. If the 749 // bucket has space available, adding the node succeeds immediately. 750 // Otherwise, the node is added if the least recently active node in 751 // the bucket does not respond to a ping packet. 752 // 753 // The caller must not hold tab.mutex. 754 func (tab *Table) add(new *Node) { 755 tab.localLogger.Trace("add(node)", "NodeType", new.NType, "node", new, "sha", new.sha) 756 tab.storagesMu.RLock() 757 defer tab.storagesMu.RUnlock() 758 if new.NType == NodeTypeBN { 759 for _, ds := range tab.storages { 760 ds.add(new) 761 } 762 } else { 763 if tab.storages[new.NType] == nil { 764 tab.localLogger.Warn("add(): Not Supported NodeType", "NodeType", new.NType) 765 return 766 } 767 tab.storages[new.NType].add(new) 768 } 769 } 770 771 // stuff adds nodes the table to the end of their corresponding bucket 772 // if the bucket is not full. 773 func (tab *Table) stuff(nodes []*Node, nType NodeType) { 774 tab.storagesMu.RLock() 775 defer tab.storagesMu.RUnlock() 776 if tab.storages[nType] == nil { 777 tab.localLogger.Warn("stuff(): Not Supported NodeType", "NodeType", nType) 778 return 779 } 780 tab.storages[nType].stuff(nodes) 781 } 782 783 // delete removes an entry from the node table (used to evacuate 784 // failed/non-bonded discovery peers). 785 func (tab *Table) delete(node *Node) { 786 tab.storagesMu.RLock() 787 defer tab.storagesMu.RUnlock() 788 for _, ds := range tab.storages { 789 ds.delete(node) 790 } 791 } 792 793 func (tab *Table) HasBond(id NodeID) bool { 794 return tab.db.hasBond(id) 795 } 796 797 // nodesByDistance is a list of nodes, ordered by 798 // distance to target. 799 type nodesByDistance struct { 800 entries []*Node 801 target common.Hash 802 } 803 804 // push adds the given node to the list, keeping the total size below maxElems. 805 func (h *nodesByDistance) push(n *Node, maxElems int) { 806 ix := sort.Search(len(h.entries), func(i int) bool { 807 return distcmp(h.target, h.entries[i].sha, n.sha) > 0 808 }) 809 if len(h.entries) < maxElems { 810 h.entries = append(h.entries, n) 811 } 812 if ix == len(h.entries) { 813 // farther away than all nodes we already have. 814 // if there was room for it, the node is now the last element. 815 } else { 816 // slide existing entries down to make room 817 // this will overwrite the entry we just appended. 818 copy(h.entries[ix+1:], h.entries[ix:]) 819 h.entries[ix] = n 820 } 821 } 822 823 func (h *nodesByDistance) String() string { 824 return fmt.Sprintf("nodeByDistance target: %s, entries: %s", h.target, h.entries) 825 }