diff options
Diffstat (limited to 'p2p/discover')
-rw-r--r-- | p2p/discover/database.go | 5 | ||||
-rw-r--r-- | p2p/discover/table.go | 2 | ||||
-rw-r--r-- | p2p/discover/udp.go | 46 | ||||
-rw-r--r-- | p2p/discover/udp_test.go | 25 |
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) { |