diff options
Diffstat (limited to 'p2p/discover/table.go')
-rw-r--r-- | p2p/discover/table.go | 60 |
1 files changed, 50 insertions, 10 deletions
diff --git a/p2p/discover/table.go b/p2p/discover/table.go index e2e846456..d3fe373f4 100644 --- a/p2p/discover/table.go +++ b/p2p/discover/table.go @@ -11,6 +11,9 @@ import ( "sort" "sync" "time" + + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" ) const ( @@ -24,6 +27,7 @@ type Table struct { mutex sync.Mutex // protects buckets, their content, and nursery buckets [nBuckets]*bucket // index of known nodes by distance nursery []*Node // bootstrap nodes + db *nodeDB // database of known nodes bondmu sync.Mutex bonding map[NodeID]*bondproc @@ -31,7 +35,6 @@ type Table struct { net transport self *Node // metadata of the local node - db *nodeDB } type bondproc struct { @@ -58,10 +61,16 @@ type bucket struct { entries []*Node } -func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr) *Table { +func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr, nodeDBPath string) *Table { + // If no node database was given, use an in-memory one + db, err := newNodeDB(nodeDBPath, Version) + if err != nil { + glog.V(logger.Warn).Infoln("Failed to open node database:", err) + db, _ = newNodeDB("", Version) + } tab := &Table{ net: t, - db: new(nodeDB), + db: db, self: newNode(ourID, ourAddr), bonding: make(map[NodeID]*bondproc), bondslots: make(chan struct{}, maxBondingPingPongs), @@ -80,9 +89,10 @@ func (tab *Table) Self() *Node { return tab.self } -// Close terminates the network listener. +// Close terminates the network listener and flushes the node database. func (tab *Table) Close() { tab.net.close() + tab.db.close() } // Bootstrap sets the bootstrap nodes. These nodes are used to connect @@ -166,8 +176,13 @@ func (tab *Table) refresh() { result := tab.Lookup(randomID(tab.self.ID, ld)) if len(result) == 0 { - // bootstrap the table with a self lookup - all := tab.bondall(tab.nursery) + // Pick a batch of previously know seeds to lookup with + seeds := tab.db.querySeeds(10) + for _, seed := range seeds { + glog.V(logger.Debug).Infoln("Seeding network with", seed) + } + // Bootstrap the table with a self lookup + all := tab.bondall(append(tab.nursery, seeds...)) tab.mutex.Lock() tab.add(all) tab.mutex.Unlock() @@ -235,7 +250,7 @@ func (tab *Table) bondall(nodes []*Node) (result []*Node) { // of the process can be skipped. func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16) (*Node, error) { var n *Node - if n = tab.db.get(id); n == nil { + if n = tab.db.node(id); n == nil { tab.bondmu.Lock() w := tab.bonding[id] if w != nil { @@ -268,9 +283,12 @@ func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16 } func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16) { + // Request a bonding slot to limit network usage <-tab.bondslots defer func() { tab.bondslots <- struct{}{} }() - if w.err = tab.net.ping(id, addr); w.err != nil { + + // Ping the remote side and wait for a pong + if w.err = tab.ping(id, addr); w.err != nil { close(w.done) return } @@ -280,14 +298,21 @@ func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAdd // waitping will simply time out. tab.net.waitping(id) } - w.n = tab.db.add(id, addr, tcpPort) + // Bonding succeeded, update the node database + w.n = &Node{ + ID: id, + IP: addr.IP, + DiscPort: addr.Port, + TCPPort: int(tcpPort), + } + tab.db.updateNode(w.n) close(w.done) } func (tab *Table) pingreplace(new *Node, b *bucket) { if len(b.entries) == bucketSize { oldest := b.entries[bucketSize-1] - if err := tab.net.ping(oldest.ID, oldest.addr()); err == nil { + if err := tab.ping(oldest.ID, oldest.addr()); err == nil { // The node responded, we don't need to replace it. return } @@ -300,6 +325,21 @@ func (tab *Table) pingreplace(new *Node, b *bucket) { b.entries[0] = new } +// ping a remote endpoint and wait for a reply, also updating the node database +// accordingly. +func (tab *Table) ping(id NodeID, addr *net.UDPAddr) error { + // Update the last ping and send the message + tab.db.updateLastPing(id, time.Now()) + if err := tab.net.ping(id, addr); err != nil { + return err + } + // Pong received, update the database and return + tab.db.updateLastPong(id, time.Now()) + tab.db.ensureExpirer() + + return nil +} + // add puts the entries into the table if their corresponding // bucket is not full. The caller must hold tab.mutex. func (tab *Table) add(entries []*Node) { |