aboutsummaryrefslogtreecommitdiffstats
path: root/p2p/discover
diff options
context:
space:
mode:
Diffstat (limited to 'p2p/discover')
-rw-r--r--p2p/discover/database.go5
-rw-r--r--p2p/discover/table.go2
-rw-r--r--p2p/discover/udp.go46
-rw-r--r--p2p/discover/udp_test.go25
4 files changed, 56 insertions, 22 deletions
diff --git a/p2p/discover/database.go b/p2p/discover/database.go
index dc0b97ddf..2b9da0423 100644
--- a/p2p/discover/database.go
+++ b/p2p/discover/database.go
@@ -17,6 +17,7 @@ import (
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/errors"
"github.com/syndtr/goleveldb/leveldb/iterator"
+ "github.com/syndtr/goleveldb/leveldb/opt"
"github.com/syndtr/goleveldb/leveldb/storage"
"github.com/syndtr/goleveldb/leveldb/util"
)
@@ -72,8 +73,8 @@ func newMemoryNodeDB() (*nodeDB, error) {
// newPersistentNodeDB creates/opens a leveldb backed persistent node database,
// also flushing its contents in case of a version mismatch.
func newPersistentNodeDB(path string, version int) (*nodeDB, error) {
- // Try to open the cache, recovering any corruption
- db, err := leveldb.OpenFile(path, nil)
+ opts := &opt.Options{OpenFilesCacheCapacity: 5}
+ db, err := leveldb.OpenFile(path, opts)
if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted {
db, err = leveldb.RecoverFile(path, nil)
}
diff --git a/p2p/discover/table.go b/p2p/discover/table.go
index 2c9cb80d5..5e6dd8d0d 100644
--- a/p2p/discover/table.go
+++ b/p2p/discover/table.go
@@ -25,7 +25,7 @@ const (
hashBits = len(common.Hash{}) * 8
nBuckets = hashBits + 1 // Number of buckets
- maxBondingPingPongs = 10
+ maxBondingPingPongs = 16
)
type Table struct {
diff --git a/p2p/discover/udp.go b/p2p/discover/udp.go
index 1213c12c8..539ccd460 100644
--- a/p2p/discover/udp.go
+++ b/p2p/discover/udp.go
@@ -363,7 +363,31 @@ const (
headSize = macSize + sigSize // space of packet frame data
)
-var headSpace = make([]byte, headSize)
+var (
+ headSpace = make([]byte, headSize)
+
+ // Neighbors responses are sent across multiple packets to
+ // stay below the 1280 byte limit. We compute the maximum number
+ // of entries by stuffing a packet until it grows too large.
+ maxNeighbors int
+)
+
+func init() {
+ p := neighbors{Expiration: ^uint64(0)}
+ maxSizeNode := rpcNode{IP: make(net.IP, 16), UDP: ^uint16(0), TCP: ^uint16(0)}
+ for n := 0; ; n++ {
+ p.Nodes = append(p.Nodes, maxSizeNode)
+ size, _, err := rlp.EncodeToReader(p)
+ if err != nil {
+ // If this ever happens, it will be caught by the unit tests.
+ panic("cannot encode: " + err.Error())
+ }
+ if headSize+size+1 >= 1280 {
+ maxNeighbors = n
+ break
+ }
+ }
+}
func (t *udp) send(toaddr *net.UDPAddr, ptype byte, req interface{}) error {
packet, err := encodePacket(t.priv, ptype, req)
@@ -402,7 +426,10 @@ func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) ([]byte,
// readLoop runs in its own goroutine. it handles incoming UDP packets.
func (t *udp) readLoop() {
defer t.conn.Close()
- buf := make([]byte, 4096) // TODO: good buffer size
+ // Discovery packets are defined to be no larger than 1280 bytes.
+ // Packets larger than this size will be cut at the end and treated
+ // as invalid because their hash won't match.
+ buf := make([]byte, 1280)
for {
nbytes, from, err := t.conn.ReadFromUDP(buf)
if err != nil {
@@ -504,15 +531,16 @@ func (req *findnode) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte
closest := t.closest(target, bucketSize).entries
t.mutex.Unlock()
- // TODO: this conversion could use a cached version of the slice
- closestrpc := make([]rpcNode, len(closest))
+ p := neighbors{Expiration: uint64(time.Now().Add(expiration).Unix())}
+ // Send neighbors in chunks with at most maxNeighbors per packet
+ // to stay below the 1280 byte limit.
for i, n := range closest {
- closestrpc[i] = nodeToRPC(n)
+ p.Nodes = append(p.Nodes, nodeToRPC(n))
+ if len(p.Nodes) == maxNeighbors || i == len(closest)-1 {
+ t.send(from, neighborsPacket, p)
+ p.Nodes = p.Nodes[:0]
+ }
}
- t.send(from, neighborsPacket, neighbors{
- Nodes: closestrpc,
- Expiration: uint64(time.Now().Add(expiration).Unix()),
- })
return nil
}
diff --git a/p2p/discover/udp_test.go b/p2p/discover/udp_test.go
index f175835a8..11fa31d7c 100644
--- a/p2p/discover/udp_test.go
+++ b/p2p/discover/udp_test.go
@@ -163,17 +163,22 @@ func TestUDP_findnode(t *testing.T) {
))
// check that closest neighbors are returned.
test.packetIn(nil, findnodePacket, &findnode{Target: testTarget, Expiration: futureExp})
- test.waitPacketOut(func(p *neighbors) {
- expected := test.table.closest(targetHash, bucketSize)
- if len(p.Nodes) != bucketSize {
- t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), bucketSize)
- }
- for i := range p.Nodes {
- if p.Nodes[i].ID != expected.entries[i].ID {
- t.Errorf("result mismatch at %d:\n got: %v\n want: %v", i, p.Nodes[i], expected.entries[i])
+ expected := test.table.closest(targetHash, bucketSize)
+
+ waitNeighbors := func(want []*Node) {
+ test.waitPacketOut(func(p *neighbors) {
+ if len(p.Nodes) != len(want) {
+ t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), bucketSize)
}
- }
- })
+ for i := range p.Nodes {
+ if p.Nodes[i].ID != want[i].ID {
+ t.Errorf("result mismatch at %d:\n got: %v\n want: %v", i, p.Nodes[i], expected.entries[i])
+ }
+ }
+ })
+ }
+ waitNeighbors(expected.entries[:maxNeighbors])
+ waitNeighbors(expected.entries[maxNeighbors:])
}
func TestUDP_findnodeMultiReply(t *testing.T) {