aboutsummaryrefslogtreecommitdiffstats
path: root/p2p/discover/table.go
diff options
context:
space:
mode:
Diffstat (limited to 'p2p/discover/table.go')
-rw-r--r--p2p/discover/table.go60
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) {