swarm/pss: Improve network tests

Signed-off-by: Lewis Marshall <lewis@lmars.net>
This commit is contained in:
Lewis Marshall 2017-12-14 15:55:46 +00:00
parent a12c003114
commit 41d3011ebe
16 changed files with 1106 additions and 147 deletions

View file

@ -104,7 +104,7 @@ func defaultNodeConfig() node.Config {
cfg.Name = clientIdentifier cfg.Name = clientIdentifier
cfg.Version = params.VersionWithCommit(gitCommit) cfg.Version = params.VersionWithCommit(gitCommit)
cfg.HTTPModules = append(cfg.HTTPModules, "eth", "shh") cfg.HTTPModules = append(cfg.HTTPModules, "eth", "shh")
cfg.WSModules = append(cfg.WSModules, "eth", "shh") cfg.WSModules = append(cfg.WSModules, "eth", "shh", "pss")
cfg.IPCPath = "geth.ipc" cfg.IPCPath = "geth.ipc"
return cfg return cfg
} }

View file

@ -17,11 +17,14 @@
package adapters package adapters
import ( import (
"crypto/rand"
"errors" "errors"
"fmt" "fmt"
"math" "math"
"net" "net"
"os"
"sync" "sync"
"syscall"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -31,9 +34,15 @@ import (
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
) )
const (
socketReadBuffer = 5000 * 1024
socketWriteBuffer = 5000 * 1024
)
// SimAdapter is a NodeAdapter which creates in-memory simulation nodes and // SimAdapter is a NodeAdapter which creates in-memory simulation nodes and
// connects them using in-memory net.Pipe connections // connects them using net.Pipe or OS socket connections
type SimAdapter struct { type SimAdapter struct {
pipe func() (net.Conn, net.Conn, error)
mtx sync.RWMutex mtx sync.RWMutex
nodes map[discover.NodeID]*SimNode nodes map[discover.NodeID]*SimNode
services map[string]ServiceFunc services map[string]ServiceFunc
@ -42,8 +51,30 @@ type SimAdapter struct {
// NewSimAdapter creates a SimAdapter which is capable of running in-memory // NewSimAdapter creates a SimAdapter which is capable of running in-memory
// simulation nodes running any of the given services (the services to run on a // simulation nodes running any of the given services (the services to run on a
// particular node are passed to the NewNode function in the NodeConfig) // particular node are passed to the NewNode function in the NodeConfig)
// the adapter uses a net.Pipe for in-memory simulated network connections
func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter { func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter {
return &SimAdapter{ return &SimAdapter{
pipe: netPipe,
nodes: make(map[discover.NodeID]*SimNode),
services: services,
}
}
// NewSocketAdapter creates a SimAdapter which is capable of running in-memory
// simulation nodes running any of the given services (the services to run on a
// particular node are passed to the NewNode function in the NodeConfig)
// the adapter uses a OS socketpairs for in-memory simulated network connections
func NewSocketAdapter(services map[string]ServiceFunc) *SimAdapter {
return &SimAdapter{
pipe: socketPipe,
nodes: make(map[discover.NodeID]*SimNode),
services: services,
}
}
func NewTCPAdapter(services map[string]ServiceFunc) *SimAdapter {
return &SimAdapter{
pipe: tcpPipe,
nodes: make(map[discover.NodeID]*SimNode), nodes: make(map[discover.NodeID]*SimNode),
services: services, services: services,
} }
@ -102,7 +133,7 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
} }
// Dial implements the p2p.NodeDialer interface by connecting to the node using // Dial implements the p2p.NodeDialer interface by connecting to the node using
// an in-memory net.Pipe connection // an in-memory net.Pipe or OS socket connection
func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) { func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) {
node, ok := s.GetNode(dest.ID) node, ok := s.GetNode(dest.ID)
if !ok { if !ok {
@ -112,7 +143,14 @@ func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) {
if srv == nil { if srv == nil {
return nil, fmt.Errorf("node not running: %s", dest.ID) return nil, fmt.Errorf("node not running: %s", dest.ID)
} }
pipe1, pipe2 := net.Pipe() // SimAdapter.pipe is either net.Pipe (NewSimAdapter) or socketPipe (NewSocketAdapter)
pipe1, pipe2, err := s.pipe()
if err != nil {
return nil, err
}
// this is simulated 'listening'
// asynchronously call the dialed destintion node's p2p server
// to set up connection on the 'listening' side
go srv.SetupConn(pipe1, 0, nil) go srv.SetupConn(pipe1, 0, nil)
return pipe2, nil return pipe2, nil
} }
@ -140,7 +178,7 @@ func (s *SimAdapter) GetNode(id discover.NodeID) (*SimNode, bool) {
} }
// SimNode is an in-memory simulation node which connects to other nodes using // SimNode is an in-memory simulation node which connects to other nodes using
// an in-memory net.Pipe connection (see SimAdapter.Dial), running devp2p // net.Pipe or OS socket connection (see SimAdapter.Dial), running devp2p
// protocols directly over that pipe // protocols directly over that pipe
type SimNode struct { type SimNode struct {
lock sync.RWMutex lock sync.RWMutex
@ -314,3 +352,117 @@ func (self *SimNode) NodeInfo() *p2p.NodeInfo {
} }
return server.NodeInfo() return server.NodeInfo()
} }
// socketPipe creates an in process full duplex pipe based on OS sockets
// credit to @lmars & Flynn
// https://github.com/flynn/flynn/blob/master/host/containerinit/init.go#L743-L749
// using this in large simulations requires raising OS's max open file limit
func socketPipe() (net.Conn, net.Conn, error) {
pair, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
if err != nil {
return nil, nil, err
}
nameb := make([]byte, 8)
_, err = rand.Read(nameb)
if err != nil {
return nil, nil, err
}
f1 := os.NewFile(uintptr(pair[0]), string(nameb)+".out")
f2 := os.NewFile(uintptr(pair[1]), string(nameb)+".in")
pipe1, err := net.FileConn(f1)
if err != nil {
return nil, nil, err
}
pipe2, err := net.FileConn(f2)
if err != nil {
return nil, nil, err
}
err = setSocketBuffer(pipe1)
if err != nil {
return nil, nil, err
}
err = setSocketBuffer(pipe2)
if err != nil {
return nil, nil, err
}
return pipe1, pipe2, nil
}
func setSocketBuffer(conn net.Conn) error {
switch v := conn.(type) {
case *net.UnixConn:
err := v.SetReadBuffer(socketReadBuffer)
if err != nil {
return err
}
err = v.SetWriteBuffer(socketWriteBuffer)
if err != nil {
return err
}
}
return nil
}
// netPipe wraps net.Pipe in a signature returning an error
func netPipe() (net.Conn, net.Conn, error) {
p1, p2 := net.Pipe()
return p1, p2, nil
}
// tcpPipe creates an in process full duplex pipe based on a localhost TCP socket
func tcpPipe() (net.Conn, net.Conn, error) {
type result struct {
conn net.Conn
err error
}
cl := make(chan result)
cd := make(chan result)
start := make(chan net.Addr)
go func(res chan result, start chan net.Addr) {
// resolve
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
if err != nil {
res <- result{err: err}
return
}
// listen
l, err := net.ListenTCP("tcp", addr)
if err != nil {
res <- result{err: err}
return
}
start <- l.Addr()
c, err := l.AcceptTCP()
if err != nil {
res <- result{err: err}
return
}
res <- result{conn: c}
}(cl, start)
go func(res chan result, start chan net.Addr) {
addr := <-start
c, err := net.DialTCP("tcp", nil, addr.(*net.TCPAddr))
if err != nil {
res <- result{err: err}
return
}
res <- result{conn: c}
}(cd, start)
a := <-cl
if a.err != nil {
return nil, nil, a.err
}
b := <-cd
if b.err != nil {
return nil, nil, b.err
}
return a.conn, b.conn, nil
}

View file

@ -0,0 +1,344 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package adapters
import (
"bytes"
"encoding/binary"
"fmt"
"testing"
"time"
)
func TestSocketPipe(t *testing.T) {
c1, c2, _ := socketPipe()
done := make(chan struct{})
go func() {
msgs := 20
size := 8
for i := 0; i < msgs; i++ {
msg := make([]byte, size)
_ = binary.PutUvarint(msg, uint64(i))
_, err := c1.Write(msg)
if err != nil {
t.Fatal(err)
}
}
for i := 0; i < msgs; i++ {
msg := make([]byte, size)
_ = binary.PutUvarint(msg, uint64(i))
out := make([]byte, size)
_, err := c2.Read(out)
if err != nil {
t.Fatal(err)
}
if bytes.Compare(msg, out) != 0 {
t.Fatalf("expected %#v, got %#v", msg, out)
}
}
done <- struct{}{}
}()
select {
case <-done:
case <-time.After(1 * time.Second):
t.Fatal("test timeout")
}
}
func TestSocketPipeBidirections(t *testing.T) {
c1, c2, _ := socketPipe()
done := make(chan struct{})
go func() {
msgs := 100
size := 4
for i := 0; i < msgs; i++ {
msg := []byte(`ping`)
_, err := c1.Write(msg)
if err != nil {
t.Fatal(err)
}
}
for i := 0; i < msgs; i++ {
out := make([]byte, size)
_, err := c2.Read(out)
if err != nil {
t.Fatal(err)
}
if bytes.Compare(out, []byte(`ping`)) == 0 {
msg := []byte(`pong`)
_, err := c2.Write(msg)
if err != nil {
t.Fatal(err)
}
}
}
for i := 0; i < msgs; i++ {
expected := []byte(`pong`)
out := make([]byte, size)
_, err := c1.Read(out)
if err != nil {
t.Fatal(err)
}
if bytes.Compare(out, expected) != 0 {
t.Fatalf("expected %#v, got %#v", expected, out)
}
}
done <- struct{}{}
}()
select {
case <-done:
case <-time.After(1 * time.Second):
t.Fatal("test timeout")
}
}
func TestTcpPipe(t *testing.T) {
c1, c2, _ := tcpPipe()
done := make(chan struct{})
go func() {
msgs := 50
size := 1024
for i := 0; i < msgs; i++ {
msg := make([]byte, size)
_ = binary.PutUvarint(msg, uint64(i))
_, err := c1.Write(msg)
if err != nil {
t.Fatal(err)
}
}
for i := 0; i < msgs; i++ {
msg := make([]byte, size)
_ = binary.PutUvarint(msg, uint64(i))
out := make([]byte, size)
_, err := c2.Read(out)
if err != nil {
t.Fatal(err)
}
if bytes.Compare(msg, out) != 0 {
t.Fatalf("expected %#v, got %#v", msg, out)
}
}
done <- struct{}{}
}()
select {
case <-done:
case <-time.After(1 * time.Second):
t.Fatal("test timeout")
}
}
func TestTcpPipeBidirections(t *testing.T) {
c1, c2, _ := tcpPipe()
done := make(chan struct{})
go func() {
msgs := 50
size := 7
for i := 0; i < msgs; i++ {
msg := []byte(fmt.Sprintf("ping %02d", i))
_, err := c1.Write(msg)
if err != nil {
t.Fatal(err)
}
}
for i := 0; i < msgs; i++ {
expected := []byte(fmt.Sprintf("ping %02d", i))
out := make([]byte, size)
_, err := c2.Read(out)
if err != nil {
t.Fatal(err)
}
if bytes.Compare(expected, out) != 0 {
t.Fatalf("expected %#v, got %#v", out, expected)
} else {
msg := []byte(fmt.Sprintf("pong %02d", i))
_, err := c2.Write(msg)
if err != nil {
t.Fatal(err)
}
}
}
for i := 0; i < msgs; i++ {
expected := []byte(fmt.Sprintf("pong %02d", i))
out := make([]byte, size)
_, err := c1.Read(out)
if err != nil {
t.Fatal(err)
}
if bytes.Compare(expected, out) != 0 {
t.Fatalf("expected %#v, got %#v", out, expected)
}
}
done <- struct{}{}
}()
select {
case <-done:
case <-time.After(1 * time.Second):
t.Fatal("test timeout")
}
}
func TestNetPipe(t *testing.T) {
c1, c2, _ := netPipe()
done := make(chan struct{})
go func() {
msgs := 50
size := 1024
// netPipe is blocking, so writes are emitted asynchronously
go func() {
for i := 0; i < msgs; i++ {
msg := make([]byte, size)
_ = binary.PutUvarint(msg, uint64(i))
_, err := c1.Write(msg)
if err != nil {
t.Fatal(err)
}
}
}()
for i := 0; i < msgs; i++ {
msg := make([]byte, size)
_ = binary.PutUvarint(msg, uint64(i))
out := make([]byte, size)
_, err := c2.Read(out)
if err != nil {
t.Fatal(err)
}
if bytes.Compare(msg, out) != 0 {
t.Fatalf("expected %#v, got %#v", msg, out)
}
}
done <- struct{}{}
}()
select {
case <-done:
case <-time.After(1 * time.Second):
t.Fatal("test timeout")
}
}
func TestNetPipeBidirections(t *testing.T) {
c1, c2, _ := netPipe()
done := make(chan struct{})
go func() {
msgs := 1000
size := 8
pingTemplate := "ping %03d"
pongTemplate := "pong %03d"
// netPipe is blocking, so writes are emitted asynchronously
go func() {
for i := 0; i < msgs; i++ {
msg := []byte(fmt.Sprintf(pingTemplate, i))
_, err := c1.Write(msg)
if err != nil {
t.Fatal(err)
}
}
}()
// netPipe is blocking, so reads for pong are emitted asynchronously
go func() {
for i := 0; i < msgs; i++ {
expected := []byte(fmt.Sprintf(pongTemplate, i))
out := make([]byte, size)
_, err := c1.Read(out)
if err != nil {
t.Fatal(err)
}
if bytes.Compare(expected, out) != 0 {
t.Fatalf("expected %#v, got %#v", expected, out)
}
}
done <- struct{}{}
}()
// expect to read pings, and respond with pongs to the alternate connection
for i := 0; i < msgs; i++ {
expected := []byte(fmt.Sprintf(pingTemplate, i))
out := make([]byte, size)
_, err := c2.Read(out)
if err != nil {
t.Fatal(err)
}
if bytes.Compare(expected, out) != 0 {
t.Fatalf("expected %#v, got %#v", expected, out)
} else {
msg := []byte(fmt.Sprintf(pongTemplate, i))
_, err := c2.Write(msg)
if err != nil {
t.Fatal(err)
}
}
}
}()
select {
case <-done:
case <-time.After(1 * time.Second):
t.Fatal("test timeout")
}
}

View file

@ -60,7 +60,7 @@ const (
// The approach taken here is to maintain a per-subscription linked list buffer // The approach taken here is to maintain a per-subscription linked list buffer
// shrinks on demand. If the buffer reaches the size below, the subscription is // shrinks on demand. If the buffer reaches the size below, the subscription is
// dropped. // dropped.
maxClientSubscriptionBuffer = 8000 maxClientSubscriptionBuffer = 20000
) )
// BatchElem is an element in a batch request. // BatchElem is an element in a batch request.

View file

@ -29,18 +29,18 @@ func NewControl(api *Api, hive *network.Hive) *Control {
return &Control{api, hive} return &Control{api, hive}
} }
func (self *Control) BlockNetworkRead(on bool) { //func (self *Control) BlockNetworkRead(on bool) {
self.hive.BlockNetworkRead(on) // self.hive.BlockNetworkRead(on)
} //}
//
func (self *Control) SyncEnabled(on bool) { //func (self *Control) SyncEnabled(on bool) {
self.hive.SyncEnabled(on) // self.hive.SyncEnabled(on)
} //}
//
func (self *Control) SwapEnabled(on bool) { //func (self *Control) SwapEnabled(on bool) {
self.hive.SwapEnabled(on) // self.hive.SwapEnabled(on)
} //}
//
func (self *Control) Hive() string { func (self *Control) Hive() string {
return self.hive.String() return self.hive.String()
} }

View file

@ -79,7 +79,7 @@ func NewHiveParams() *HiveParams {
type Hive struct { type Hive struct {
*HiveParams // settings *HiveParams // settings
Overlay // the overlay connectiviy driver Overlay // the overlay connectiviy driver
store StateStore // storage interface to save peers across sessions Store StateStore // storage interface to save peers across sessions
addPeer func(*discover.Node) // server callback to connect to a peer addPeer func(*discover.Node) // server callback to connect to a peer
// bookkeeping // bookkeeping
lock sync.Mutex lock sync.Mutex
@ -94,7 +94,7 @@ func NewHive(params *HiveParams, overlay Overlay, store StateStore) *Hive {
return &Hive{ return &Hive{
HiveParams: params, HiveParams: params,
Overlay: overlay, Overlay: overlay,
store: store, Store: store,
} }
} }
@ -104,7 +104,7 @@ func NewHive(params *HiveParams, overlay Overlay, store StateStore) *Hive {
func (h *Hive) Start(server *p2p.Server) error { func (h *Hive) Start(server *p2p.Server) error {
log.Trace(fmt.Sprintf("%08x hive starting", h.BaseAddr()[:4])) log.Trace(fmt.Sprintf("%08x hive starting", h.BaseAddr()[:4]))
// if state store is specified, load peers to prepopulate the overlay address book // if state store is specified, load peers to prepopulate the overlay address book
if h.store != nil { if h.Store != nil {
if err := h.loadPeers(); err != nil { if err := h.loadPeers(); err != nil {
return err return err
} }
@ -122,7 +122,7 @@ func (h *Hive) Start(server *p2p.Server) error {
func (h *Hive) Stop() error { func (h *Hive) Stop() error {
log.Info(fmt.Sprintf("%08x hive stopping, saving peers", h.BaseAddr()[:4])) log.Info(fmt.Sprintf("%08x hive stopping, saving peers", h.BaseAddr()[:4]))
h.ticker.Stop() h.ticker.Stop()
if h.store != nil { if h.Store != nil {
return h.savePeers() return h.savePeers()
} }
log.Info(fmt.Sprintf("%08x hive stopped, dropping peers", h.BaseAddr()[:4])) log.Info(fmt.Sprintf("%08x hive stopped, dropping peers", h.BaseAddr()[:4]))
@ -196,7 +196,7 @@ func ToAddr(pa OverlayPeer) *BzzAddr {
// loadPeers, savePeer implement persistence callback/ // loadPeers, savePeer implement persistence callback/
func (h *Hive) loadPeers() error { func (h *Hive) loadPeers() error {
data, err := h.store.Load("peers") data, err := h.Store.Load("peers")
if err != nil { if err != nil {
return err return err
} }
@ -233,7 +233,7 @@ func (h *Hive) savePeers() error {
if err != nil { if err != nil {
return fmt.Errorf("could not encode peers: %v", err) return fmt.Errorf("could not encode peers: %v", err)
} }
if err := h.store.Save("peers", data); err != nil { if err := h.Store.Save("peers", data); err != nil {
return fmt.Errorf("could not save peers: %v", err) return fmt.Errorf("could not save peers: %v", err)
} }
return nil return nil

View file

@ -100,7 +100,8 @@ func TestDiscoverySimulationSimAdapter(t *testing.T) {
} }
func testDiscoverySimulationSimAdapter(t *testing.T, nodes, conns int) { func testDiscoverySimulationSimAdapter(t *testing.T, nodes, conns int) {
testDiscoverySimulation(t, nodes, conns, adapters.NewSimAdapter(services)) testDiscoverySimulation(t, nodes, conns, adapters.NewSocketAdapter(services))
// testDiscoverySimulation(t, nodes, conns, adapters.NewSimAdapter(services))
} }
func testDiscoverySimulation(t *testing.T, nodes, conns int, adapter adapters.NodeAdapter) { func testDiscoverySimulation(t *testing.T, nodes, conns int, adapter adapters.NodeAdapter) {
@ -310,7 +311,7 @@ func newService(ctx *adapters.ServiceContext) (node.Service, error) {
kad := network.NewKademlia(addr.Over(), kp) kad := network.NewKademlia(addr.Over(), kp)
hp := network.NewHiveParams() hp := network.NewHiveParams()
hp.KeepAliveInterval = 500 * time.Millisecond hp.KeepAliveInterval = 200 * time.Millisecond
config := &network.BzzConfig{ config := &network.BzzConfig{
OverlayAddr: addr.Over(), OverlayAddr: addr.Over(),

View file

@ -5,11 +5,12 @@ package pss
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"time"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/protocols" "github.com/ethereum/go-ethereum/p2p/protocols"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"time"
) )
const ( const (
@ -197,8 +198,6 @@ func ToP2pMsg(msg []byte) (p2p.Msg, error) {
// to link the peer to. // to link the peer to.
// The key must exist in the pss store prior to adding the peer. // The key must exist in the pss store prior to adding the peer.
func (self *Protocol) AddPeer(p *p2p.Peer, run func(*p2p.Peer, p2p.MsgReadWriter) error, topic Topic, asymmetric bool, key string) (p2p.MsgReadWriter, error) { func (self *Protocol) AddPeer(p *p2p.Peer, run func(*p2p.Peer, p2p.MsgReadWriter) error, topic Topic, asymmetric bool, key string) (p2p.MsgReadWriter, error) {
self.Pss.lock.Lock()
defer self.Pss.lock.Unlock()
rw := &PssReadWriter{ rw := &PssReadWriter{
Pss: self.Pss, Pss: self.Pss,
rw: make(chan p2p.Msg), rw: make(chan p2p.Msg),
@ -212,11 +211,14 @@ func (self *Protocol) AddPeer(p *p2p.Peer, run func(*p2p.Peer, p2p.MsgReadWriter
rw.sendFunc = self.Pss.SendSym rw.sendFunc = self.Pss.SendSym
} }
if asymmetric { if asymmetric {
self.Pss.pubKeyPoolMu.Lock()
if _, ok := self.Pss.pubKeyPool[key]; !ok { if _, ok := self.Pss.pubKeyPool[key]; !ok {
return nil, fmt.Errorf("asym key does not exist: %s", key) return nil, fmt.Errorf("asym key does not exist: %s", key)
} }
self.Pss.pubKeyPoolMu.Unlock()
self.pubKeyRWPool[key] = rw self.pubKeyRWPool[key] = rw
} else { } else {
self.Pss.symKeyPoolMu.Lock()
if _, ok := self.Pss.symKeyPool[key]; !ok { if _, ok := self.Pss.symKeyPool[key]; !ok {
return nil, fmt.Errorf("symkey does not exist: %s", key) return nil, fmt.Errorf("symkey does not exist: %s", key)
} }

View file

@ -94,25 +94,29 @@ type Pss struct {
auxAPIs []rpc.API // builtins (handshake, test) can add APIs auxAPIs []rpc.API // builtins (handshake, test) can add APIs
// sending and forwarding // sending and forwarding
fwdPool map[string]*protocols.Peer // keep track of all peers sitting on the pssmsg routing layer fwdPool map[string]*protocols.Peer // keep track of all peers sitting on the pssmsg routing layer
fwdPoolMu sync.Mutex
fwdCache map[pssDigest]pssCacheEntry // checksum of unique fields from pssmsg mapped to expiry, cache to determine whether to drop msg fwdCache map[pssDigest]pssCacheEntry // checksum of unique fields from pssmsg mapped to expiry, cache to determine whether to drop msg
cacheTTL time.Duration // how long to keep messages in fwdCache (not implemented) fwdCacheMu sync.Mutex
cacheTTL time.Duration // how long to keep messages in fwdCache (not implemented)
msgTTL time.Duration msgTTL time.Duration
paddingByteSize int paddingByteSize int
capstring string capstring string
// keys and peers // keys and peers
pubKeyPool map[string]map[Topic]*pssPeer // mapping of hex public keys to peer address by topic. pubKeyPool map[string]map[Topic]*pssPeer // mapping of hex public keys to peer address by topic.
pubKeyPoolMu sync.Mutex
symKeyPool map[string]map[Topic]*pssPeer // mapping of symkeyids to peer address by topic. symKeyPool map[string]map[Topic]*pssPeer // mapping of symkeyids to peer address by topic.
symKeyDecryptCache []*string // fast lookup of symkeys recently used for decryption; last used is on top of stack symKeyPoolMu sync.Mutex
symKeyDecryptCacheCursor int // modular cursor pointing to last used, wraps on symKeyDecryptCache array symKeyDecryptCache []*string // fast lookup of symkeys recently used for decryption; last used is on top of stack
symKeyDecryptCacheCapacity int // max amount of symkeys to keep. symKeyDecryptCacheCursor int // modular cursor pointing to last used, wraps on symKeyDecryptCache array
symKeyDecryptCacheCapacity int // max amount of symkeys to keep.
// message handling // message handling
handlers map[Topic]map[*Handler]bool // topic and version based pss payload handlers. See pss.Handle() handlers map[Topic]map[*Handler]bool // topic and version based pss payload handlers. See pss.Handle()
handlersMu sync.Mutex
// process // process
lock sync.Mutex
quitC chan struct{} quitC chan struct{}
} }
@ -197,7 +201,9 @@ func (self *Pss) Protocols() []p2p.Protocol {
func (self *Pss) Run(p *p2p.Peer, rw p2p.MsgReadWriter) error { func (self *Pss) Run(p *p2p.Peer, rw p2p.MsgReadWriter) error {
pp := protocols.NewPeer(p, rw, pssSpec) pp := protocols.NewPeer(p, rw, pssSpec)
self.fwdPoolMu.Lock()
self.fwdPool[p.Info().ID] = pp self.fwdPool[p.Info().ID] = pp
self.fwdPoolMu.Unlock()
return pp.Run(self.handlePssMsg) return pp.Run(self.handlePssMsg)
} }
@ -246,8 +252,8 @@ func (self *Pss) PublicKey() *ecdsa.PublicKey {
// Returns a deregister function which needs to be called to // Returns a deregister function which needs to be called to
// deregister the handler, // deregister the handler,
func (self *Pss) Register(topic *Topic, handler Handler) func() { func (self *Pss) Register(topic *Topic, handler Handler) func() {
self.lock.Lock() self.handlersMu.Lock()
defer self.lock.Unlock() defer self.handlersMu.Unlock()
handlers := self.handlers[*topic] handlers := self.handlers[*topic]
if handlers == nil { if handlers == nil {
handlers = make(map[*Handler]bool) handlers = make(map[*Handler]bool)
@ -257,8 +263,8 @@ func (self *Pss) Register(topic *Topic, handler Handler) func() {
return func() { self.deregister(topic, &handler) } return func() { self.deregister(topic, &handler) }
} }
func (self *Pss) deregister(topic *Topic, h *Handler) { func (self *Pss) deregister(topic *Topic, h *Handler) {
self.lock.Lock() self.handlersMu.Lock()
defer self.lock.Unlock() defer self.handlersMu.Unlock()
handlers := self.handlers[*topic] handlers := self.handlers[*topic]
if len(handlers) == 1 { if len(handlers) == 1 {
delete(self.handlers, *topic) delete(self.handlers, *topic)
@ -269,8 +275,8 @@ func (self *Pss) deregister(topic *Topic, h *Handler) {
// get all registered handlers for respective topics // get all registered handlers for respective topics
func (self *Pss) getHandlers(topic Topic) map[*Handler]bool { func (self *Pss) getHandlers(topic Topic) map[*Handler]bool {
self.lock.Lock() self.handlersMu.Lock()
defer self.lock.Unlock() defer self.handlersMu.Unlock()
return self.handlers[topic] return self.handlers[topic]
} }
@ -374,8 +380,6 @@ func (self *Pss) isSelfPossibleRecipient(msg *PssMsg) bool {
// The value in `address` will be used as a routing hint for the // The value in `address` will be used as a routing hint for the
// public key / topic association // public key / topic association
func (self *Pss) SetPeerPublicKey(pubkey *ecdsa.PublicKey, topic Topic, address *PssAddress) error { func (self *Pss) SetPeerPublicKey(pubkey *ecdsa.PublicKey, topic Topic, address *PssAddress) error {
self.lock.Lock()
defer self.lock.Unlock()
pubkeybytes := crypto.FromECDSAPub(pubkey) pubkeybytes := crypto.FromECDSAPub(pubkey)
if len(pubkeybytes) == 0 { if len(pubkeybytes) == 0 {
return fmt.Errorf("invalid public key: %v", pubkey) return fmt.Errorf("invalid public key: %v", pubkey)
@ -384,10 +388,12 @@ func (self *Pss) SetPeerPublicKey(pubkey *ecdsa.PublicKey, topic Topic, address
psp := &pssPeer{ psp := &pssPeer{
address: address, address: address,
} }
self.pubKeyPoolMu.Lock()
if _, ok := self.pubKeyPool[pubkeyid]; ok == false { if _, ok := self.pubKeyPool[pubkeyid]; ok == false {
self.pubKeyPool[pubkeyid] = make(map[Topic]*pssPeer) self.pubKeyPool[pubkeyid] = make(map[Topic]*pssPeer)
} }
self.pubKeyPool[pubkeyid][topic] = psp self.pubKeyPool[pubkeyid][topic] = psp
self.pubKeyPoolMu.Unlock()
log.Trace("added pubkey", "pubkeyid", pubkeyid, "topic", topic, "address", common.ToHex(*address)) log.Trace("added pubkey", "pubkeyid", pubkeyid, "topic", topic, "address", common.ToHex(*address))
return nil return nil
} }
@ -427,15 +433,15 @@ func (self *Pss) SetSymmetricKey(key []byte, topic Topic, address *PssAddress, a
// to the collection of keys used to attempt symmetric decryption of // to the collection of keys used to attempt symmetric decryption of
// incoming messages // incoming messages
func (self *Pss) addSymmetricKeyToPool(keyid string, topic Topic, address *PssAddress, addtocache bool) { func (self *Pss) addSymmetricKeyToPool(keyid string, topic Topic, address *PssAddress, addtocache bool) {
self.lock.Lock()
defer self.lock.Unlock()
psp := &pssPeer{ psp := &pssPeer{
address: address, address: address,
} }
self.symKeyPoolMu.Lock()
if _, ok := self.symKeyPool[keyid]; !ok { if _, ok := self.symKeyPool[keyid]; !ok {
self.symKeyPool[keyid] = make(map[Topic]*pssPeer) self.symKeyPool[keyid] = make(map[Topic]*pssPeer)
} }
self.symKeyPool[keyid][topic] = psp self.symKeyPool[keyid][topic] = psp
self.symKeyPoolMu.Unlock()
if addtocache { if addtocache {
self.symKeyDecryptCacheCursor++ self.symKeyDecryptCacheCursor++
self.symKeyDecryptCache[self.symKeyDecryptCacheCursor%cap(self.symKeyDecryptCache)] = &keyid self.symKeyDecryptCache[self.symKeyDecryptCacheCursor%cap(self.symKeyDecryptCache)] = &keyid
@ -476,7 +482,9 @@ func (self *Pss) processSym(envelope *whisper.Envelope) (*whisper.ReceivedMessag
if !recvmsg.Validate() { if !recvmsg.Validate() {
return nil, "", nil, fmt.Errorf("symmetrically encrypted message has invalid signature or is corrupt") return nil, "", nil, fmt.Errorf("symmetrically encrypted message has invalid signature or is corrupt")
} }
self.symKeyPoolMu.Lock()
from := self.symKeyPool[*symkeyid][Topic(envelope.Topic)].address from := self.symKeyPool[*symkeyid][Topic(envelope.Topic)].address
self.symKeyPoolMu.Unlock()
self.symKeyDecryptCacheCursor++ self.symKeyDecryptCacheCursor++
self.symKeyDecryptCache[self.symKeyDecryptCacheCursor%cap(self.symKeyDecryptCache)] = symkeyid self.symKeyDecryptCache[self.symKeyDecryptCacheCursor%cap(self.symKeyDecryptCache)] = symkeyid
return recvmsg, *symkeyid, from, nil return recvmsg, *symkeyid, from, nil
@ -501,9 +509,11 @@ func (self *Pss) processAsym(envelope *whisper.Envelope) (*whisper.ReceivedMessa
} }
pubkeyid := common.ToHex(crypto.FromECDSAPub(recvmsg.Src)) pubkeyid := common.ToHex(crypto.FromECDSAPub(recvmsg.Src))
var from *PssAddress var from *PssAddress
self.pubKeyPoolMu.Lock()
if self.pubKeyPool[pubkeyid][Topic(envelope.Topic)] != nil { if self.pubKeyPool[pubkeyid][Topic(envelope.Topic)] != nil {
from = self.pubKeyPool[pubkeyid][Topic(envelope.Topic)].address from = self.pubKeyPool[pubkeyid][Topic(envelope.Topic)].address
} }
self.pubKeyPoolMu.Unlock()
return recvmsg, pubkeyid, from, nil return recvmsg, pubkeyid, from, nil
} }
@ -533,8 +543,10 @@ func (self *Pss) cleanKeys() (count int) {
} }
} }
for _, topic := range expiredtopics { for _, topic := range expiredtopics {
self.symKeyPoolMu.Lock()
delete(self.symKeyPool[keyid], topic) delete(self.symKeyPool[keyid], topic)
log.Trace("symkey cleanup deletion", "symkeyid", keyid, "topic", topic, "val", self.symKeyPool[keyid]) log.Trace("symkey cleanup deletion", "symkeyid", keyid, "topic", topic, "val", self.symKeyPool[keyid])
self.symKeyPoolMu.Unlock()
count++ count++
} }
} }
@ -553,7 +565,9 @@ func (self *Pss) SendSym(symkeyid string, topic Topic, msg []byte) error {
if err != nil { if err != nil {
return fmt.Errorf("missing valid send symkey %s: %v", symkeyid, err) return fmt.Errorf("missing valid send symkey %s: %v", symkeyid, err)
} }
self.symKeyPoolMu.Lock()
psp, ok := self.symKeyPool[symkeyid][topic] psp, ok := self.symKeyPool[symkeyid][topic]
self.symKeyPoolMu.Unlock()
if !ok { if !ok {
return fmt.Errorf("invalid topic '%s' for symkey '%s'", topic, symkeyid) return fmt.Errorf("invalid topic '%s' for symkey '%s'", topic, symkeyid)
} else if psp.address == nil { } else if psp.address == nil {
@ -567,18 +581,21 @@ func (self *Pss) SendSym(symkeyid string, topic Topic, msg []byte) error {
// //
// Fails if the key id does not match any in of the stored public keys // Fails if the key id does not match any in of the stored public keys
func (self *Pss) SendAsym(pubkeyid string, topic Topic, msg []byte) error { func (self *Pss) SendAsym(pubkeyid string, topic Topic, msg []byte) error {
//pubkey := self.pubKeyIndex[pubkeyid]
pubkey := crypto.ToECDSAPub(common.FromHex(pubkeyid)) pubkey := crypto.ToECDSAPub(common.FromHex(pubkeyid))
if pubkey == nil { if pubkey == nil {
return fmt.Errorf("Invalid public key id %x", pubkey) return fmt.Errorf("Invalid public key id %x", pubkey)
} }
self.pubKeyPoolMu.Lock()
psp, ok := self.pubKeyPool[pubkeyid][topic] psp, ok := self.pubKeyPool[pubkeyid][topic]
self.pubKeyPoolMu.Unlock()
if !ok { if !ok {
return fmt.Errorf("invalid topic '%s' for pubkey '%s'", topic, pubkeyid) return fmt.Errorf("invalid topic '%s' for pubkey '%s'", topic, pubkeyid)
} else if psp.address == nil { } else if psp.address == nil {
return fmt.Errorf("no address hint for topic '%s' pubkey '%s'", topic, pubkeyid) return fmt.Errorf("no address hint for topic '%s' pubkey '%s'", topic, pubkeyid)
} }
self.send(*psp.address, topic, msg, true, common.FromHex(pubkeyid)) go func() {
self.send(*psp.address, topic, msg, true, common.FromHex(pubkeyid))
}()
return nil return nil
} }
@ -688,11 +705,12 @@ func (self *Pss) forward(msg *PssMsg) error {
return true return true
} }
// attempt to send the message // attempt to send the message
err := pp.Send(msg) go func() {
if err != nil { err := pp.Send(msg)
log.Debug(fmt.Sprintf("%v: failed forwarding: %v", sendMsg, err)) if err != nil {
return true log.Debug(fmt.Sprintf("%v: failed forwarding: %v", sendMsg, err))
} }
}()
log.Trace(fmt.Sprintf("%v: successfully forwarded", sendMsg)) log.Trace(fmt.Sprintf("%v: successfully forwarded", sendMsg))
sent++ sent++
// continue forwarding if: // continue forwarding if:
@ -728,8 +746,8 @@ func (self *Pss) forward(msg *PssMsg) error {
// add a message to the cache // add a message to the cache
func (self *Pss) addFwdCache(digest pssDigest) error { func (self *Pss) addFwdCache(digest pssDigest) error {
self.lock.Lock() self.fwdCacheMu.Lock()
defer self.lock.Unlock() defer self.fwdCacheMu.Unlock()
var entry pssCacheEntry var entry pssCacheEntry
var ok bool var ok bool
if entry, ok = self.fwdCache[digest]; !ok { if entry, ok = self.fwdCache[digest]; !ok {
@ -742,8 +760,8 @@ func (self *Pss) addFwdCache(digest pssDigest) error {
// check if message is in the cache // check if message is in the cache
func (self *Pss) checkFwdCache(addr []byte, digest pssDigest) bool { func (self *Pss) checkFwdCache(addr []byte, digest pssDigest) bool {
self.lock.Lock() self.fwdCacheMu.Lock()
defer self.lock.Unlock() defer self.fwdCacheMu.Unlock()
entry, ok := self.fwdCache[digest] entry, ok := self.fwdCache[digest]
if ok { if ok {
if entry.expiresAt.After(time.Now()) { if entry.expiresAt.After(time.Now()) {

View file

@ -59,12 +59,14 @@ var (
var services = newServices() var services = newServices()
func init() { func init() {
flag.Parse() flag.Parse()
rand.Seed(time.Now().Unix()) rand.Seed(time.Now().Unix())
adapters.RegisterServices(services) adapters.RegisterServices(services)
initTest()
}
func initTest() {
initOnce.Do( initOnce.Do(
func() { func() {
loglevel := log.LvlInfo loglevel := log.LvlInfo
@ -609,94 +611,84 @@ func testAsymSend(t *testing.T) {
} }
} }
type networkParams struct { type Job struct {
snapshotFile string Msg []byte
numMessages int SendNode discover.NodeID
addressSize int RecvNode discover.NodeID
adapterType string
messageDelay int
} }
func (n *networkParams) String() string { func worker(id int, jobs <-chan Job, rpcs map[discover.NodeID]*rpc.Client, pubkeys map[discover.NodeID]string, topic string) {
return fmt.Sprintf(":%s:%d:%d:%d:%s", n.snapshotFile, n.numMessages, n.addressSize, n.messageDelay, n.adapterType) for j := range jobs {
rpcs[j.SendNode].Call(nil, "pss_sendAsym", pubkeys[j.RecvNode], topic, hexutil.Encode(j.Msg))
}
} }
// Tests random message sending in network snapshots
//
// params in run name: // params in run name:
// #nodes/#msgs/#addrbytes/adaptertype // nodes/msgs/addrbytes/adaptertype
// if adaptertype is exec uses execadapter, simadapter otherwise // if adaptertype is exec uses execadapter, simadapter otherwise
func TestNetwork(t *testing.T) { func TestNetwork(t *testing.T) {
var tests []*networkParams t.Run("3/2000/4/sock", testNetwork)
if *snapshotflag != "" { t.Run("4/2000/4/sock", testNetwork)
if *addresssizeflag < 0 || *addresssizeflag > 32 { t.Run("8/2000/4/sock", testNetwork)
t.Fatal("invalid address size") t.Run("16/2000/4/sock", testNetwork)
} t.Run("32/2000/4/sock", testNetwork)
_, err := os.Stat(*snapshotflag) t.Run("64/2000/4/sim", testNetwork)
if err != nil {
t.Fatal(err)
}
tests = append(tests, &networkParams{
snapshotFile: *snapshotflag,
numMessages: *messagesflag,
addressSize: *addresssizeflag,
adapterType: *adaptertypeflag,
messageDelay: *messagedelayflag,
})
} else {
tests = append(tests, &networkParams{
snapshotFile: "testdata/snapshot_8.json",
numMessages: 2,
addressSize: 2,
adapterType: "sim",
messageDelay: 1000,
})
}
for _, p := range tests {
t.Run(p.String(), testNetwork)
}
} }
func testNetwork(t *testing.T) { func testNetwork(t *testing.T) {
lock := &sync.Mutex{}
type msgnotifyC struct { type msgnotifyC struct {
id discover.NodeID id discover.NodeID
msgIdx int msgIdx int
} }
paramstring := strings.Split(t.Name(), ":") paramstring := strings.Split(t.Name(), "/")
nodecount, _ := strconv.ParseInt(paramstring[1], 10, 0)
msgcount, _ := strconv.ParseInt(paramstring[2], 10, 0) msgcount, _ := strconv.ParseInt(paramstring[2], 10, 0)
addrsize, _ := strconv.ParseInt(paramstring[3], 10, 0) addrsize, _ := strconv.ParseInt(paramstring[3], 10, 0)
messagedelaymax, _ := strconv.ParseInt(paramstring[4], 10, 0) adapter := paramstring[4]
log.Info("network test", "snapshot", paramstring[1], "msgcount", msgcount, "addrhintsize", addrsize, "messagedelay", messagedelaymax)
log.Info("network test", "nodecount", nodecount, "msgcount", msgcount, "addrhintsize", addrsize)
nodes := make([]discover.NodeID, nodecount)
bzzaddrs := make(map[discover.NodeID]string, nodecount)
rpcs := make(map[discover.NodeID]*rpc.Client, nodecount)
pubkeys := make(map[discover.NodeID]string, nodecount)
sentmsgs := make([][]byte, msgcount) sentmsgs := make([][]byte, msgcount)
recvmsgs := make([]bool, msgcount) recvmsgs := make([]bool, msgcount)
nodemsgcount := make(map[discover.NodeID]int, nodecount)
trigger := make(chan discover.NodeID) trigger := make(chan discover.NodeID)
var adapter adapters.NodeAdapter var a adapters.NodeAdapter
if paramstring[5] == "exec" { if adapter == "exec" {
dirname, err := ioutil.TempDir(".", "") dirname, err := ioutil.TempDir(".", "")
defer os.RemoveAll(dirname)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
adapter = adapters.NewExecAdapter(dirname) a = adapters.NewExecAdapter(dirname)
} else { } else if adapter == "sock" {
adapter = adapters.NewSimAdapter(services) a = adapters.NewSocketAdapter(services)
} else if adapter == "tcp" {
a = adapters.NewTCPAdapter(services)
} else if adapter == "sim" {
a = adapters.NewSimAdapter(services)
} }
net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{ net := simulations.NewNetwork(a, &simulations.NetworkConfig{
ID: "0", ID: "0",
}) })
defer net.Shutdown() defer net.Shutdown()
f, err := os.Open(paramstring[1]) f, err := os.Open(fmt.Sprintf("testdata/snapshot_%d.json", nodecount))
if err != nil {
t.Fatal(err)
}
jsonbyte, err := ioutil.ReadAll(f)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
var snap simulations.Snapshot var snap simulations.Snapshot
err = json.NewDecoder(f).Decode(&snap) err = json.Unmarshal(jsonbyte, &snap)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -705,12 +697,6 @@ func testNetwork(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
nodes := make([]discover.NodeID, len(snap.Nodes))
bzzaddrs := make(map[discover.NodeID]string, len(snap.Nodes))
rpcs := make(map[discover.NodeID]*rpc.Client, len(snap.Nodes))
pubkeys := make(map[discover.NodeID]string, len(snap.Nodes))
nodemsgcount := make(map[discover.NodeID]int, len(snap.Nodes))
triggerChecks := func(trigger chan discover.NodeID, id discover.NodeID, rpcclient *rpc.Client, topic string) error { triggerChecks := func(trigger chan discover.NodeID, id discover.NodeID, rpcclient *rpc.Client, topic string) error {
msgC := make(chan APIMsg) msgC := make(chan APIMsg)
ctx, cancel := context.WithTimeout(context.Background(), time.Second) ctx, cancel := context.WithTimeout(context.Background(), time.Second)
@ -725,13 +711,11 @@ func testNetwork(t *testing.T) {
select { select {
case recvmsg := <-msgC: case recvmsg := <-msgC:
idx, _ := binary.Uvarint(recvmsg.Msg) idx, _ := binary.Uvarint(recvmsg.Msg)
lock.Lock()
if recvmsgs[idx] == false { if recvmsgs[idx] == false {
log.Debug("msg recv", "idx", idx, "id", id) log.Debug("msg recv", "idx", idx, "id", id)
recvmsgs[idx] = true recvmsgs[idx] = true
trigger <- id trigger <- id
} }
lock.Unlock()
case <-sub.Err(): case <-sub.Err():
return return
} }
@ -771,10 +755,15 @@ func testNetwork(t *testing.T) {
} }
} }
messagedelayhigh := 0 // setup workers
jobs := make(chan Job, 10)
for w := 1; w <= 10; w++ {
go worker(w, jobs, rpcs, pubkeys, topic)
}
for i := 0; i < int(msgcount); i++ { for i := 0; i < int(msgcount); i++ {
sendnodeidx := rand.Intn(int(len(snap.Nodes))) sendnodeidx := rand.Intn(int(nodecount))
recvnodeidx := rand.Intn(int(len(snap.Nodes) - 1)) recvnodeidx := rand.Intn(int(nodecount - 1))
if recvnodeidx >= sendnodeidx { if recvnodeidx >= sendnodeidx {
recvnodeidx++ recvnodeidx++
} }
@ -784,38 +773,23 @@ func testNetwork(t *testing.T) {
if c == 0 { if c == 0 {
t.Fatal("0 byte message") t.Fatal("0 byte message")
} }
if err != nil {
t.Fatal(err)
}
err = rpcs[nodes[sendnodeidx]].Call(nil, "pss_setPeerPublicKey", pubkeys[nodes[recvnodeidx]], topic, bzzaddrs[nodes[recvnodeidx]]) err = rpcs[nodes[sendnodeidx]].Call(nil, "pss_setPeerPublicKey", pubkeys[nodes[recvnodeidx]], topic, bzzaddrs[nodes[recvnodeidx]])
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = rpcs[nodes[recvnodeidx]].Call(nil, "pss_setPeerPublicKey", pubkeys[nodes[sendnodeidx]], topic, bzzaddrs[nodes[sendnodeidx]])
if err != nil {
t.Fatal(err)
}
messagedelay := rand.Intn(int(messagedelaymax))
if messagedelay > messagedelayhigh {
messagedelayhigh = messagedelay
}
messagedelayduration, err := time.ParseDuration(fmt.Sprintf("%dus", messagedelay))
if err != nil {
t.Fatal(err)
}
go func(rpcclient *rpc.Client, pubkey string, msg []byte) {
time.Sleep(messagedelayduration)
err = rpcclient.Call(nil, "pss_sendAsym", pubkey, topic, hexutil.Encode(msg))
if err != nil {
log.Error("Send asym rpc fail", "pubkey", pubkey, "topic", topic, "err", err)
}
}(rpcs[nodes[sendnodeidx]], pubkeys[nodes[recvnodeidx]], sentmsgs[i])
}
timeout, err := time.ParseDuration(fmt.Sprintf("%dus", 60000000+messagedelayhigh)) jobs <- Job{
if err != nil { Msg: sentmsgs[i],
t.Fatal(err) SendNode: nodes[sendnodeidx],
RecvNode: nodes[recvnodeidx],
}
} }
finalmsgcount := 0 finalmsgcount := 0
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel() defer cancel()
outer: outer:
for i := 0; i < int(msgcount); i++ { for i := 0; i < int(msgcount); i++ {
@ -996,7 +970,7 @@ func benchmarkSymkeyBruteforceChangeaddr(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
if !ps.process(pssmsgs[len(pssmsgs)-(i%len(pssmsgs))-1]) { if !ps.process(pssmsgs[len(pssmsgs)-(i%len(pssmsgs))-1]) {
b.Fatalf("pss processing failed") b.Fatalf("pss processing failed: %v", err)
} }
} }
} }
@ -1154,6 +1128,9 @@ func newServices() adapters.Services {
return nil, fmt.Errorf("local dpa creation failed", "error", err) return nil, fmt.Errorf("local dpa creation failed", "error", err)
} }
// execadapter does not exec init()
initTest()
ctxlocal, _ := context.WithTimeout(context.Background(), time.Second) ctxlocal, _ := context.WithTimeout(context.Background(), time.Second)
keys, err := wapi.NewKeyPair(ctxlocal) keys, err := wapi.NewKeyPair(ctxlocal)
privkey, err := w.GetPrivateKey(keys) privkey, err := w.GetPrivateKey(keys)

View file

@ -0,0 +1,28 @@
#!/usr/bin/perl
use JSON;
my $f;
my $jsontext;
my $nodelist;
my $network;
open($f, "<", $ARGV[0]) || die "cant open " . $ARGV[0];
while (<$f>) {
$jsontext .= $_;
}
close($f);
$network = decode_json($jsontext);
$nodelist = $network->{'nodes'};
for ($i = 0; $i < 0+@$nodelist; $i++) {
#my $protocollist = $$nodelist[$i]{'node'}{'info'}{'protocols'};
#$$protocollist{'pss'} = "pss";
my $svc = $$nodelist[$i]{'node'}{'config'}{'services'};
pop(@$svc);
push(@$svc, "pss");
push(@$svc, "bzz");
}
print encode_json($network);

67
swarm/pss/testdata/snapshot_2.json vendored Normal file
View file

@ -0,0 +1,67 @@
{
"conns":[
{
"other":"0eec333dd211c2ea81db614fe58bf0300c15e50e1b044e47ef93067a6cdbc3bc666b40bdcc515bbf580355dbef9370294ef1ee92ee0525e78a8beed00c2b99f5",
"one":"7b12f55c7c012104e006775d03b89722b403fb0e1ecb79af8cadfa6947425aedb323fb9416c84b782d35f3216acb5d94a1dd31d60a3eba45f9051bf503de1b66",
"up":true
}
],
"nodes":[
{
"node":{
"config":{
"private_key":"e567b7d9c554e5102cdc99b6523bace02dbb8951415c8816d82ba2d2e97fa23b",
"name":"node01",
"id":"7b12f55c7c012104e006775d03b89722b403fb0e1ecb79af8cadfa6947425aedb323fb9416c84b782d35f3216acb5d94a1dd31d60a3eba45f9051bf503de1b66",
"services":[
"pss","bzz"
]
},
"info":{
"ip":"0.0.0.0",
"listenAddr":"",
"protocols":{
"hive":"\n=========================================================================\nFri Sep 29 21:22:53 UTC 2017 KΛÐΞMLIΛ hive: queen's address: 73d6ad\npopulation: 5 (7), MinProxBinSize: 2, MinBinSize: 1, MaxBinSize: 3\n000 1 dfd4 | 3 8a1e (0) d776 (0) dfd4 (0)\n============ DEPTH: 1 ==========================================\n001 3 05da 159c 3451 | 3 05da (0) 159c (0) 3451 (0)\n002 0 | 0\n003 1 6e8d | 1 6e8d (0)\n004 0 | 0\n005 0 | 0\n006 0 | 0\n007 0 | 0\n008 0 | 0\n009 0 | 0\n010 0 | 0\n011 0 | 0\n012 0 | 0\n013 0 | 0\n014 0 | 0\n015 0 | 0\n=========================================================================",
"bzz":"c9atSnUGnc7WYPpMuYFD7lVz33yxXZopWs8WVeloM4Q="
},
"ports":{
"listener":0,
"discovery":0
},
"name":"node01",
"id":"7b12f55c7c012104e006775d03b89722b403fb0e1ecb79af8cadfa6947425aedb323fb9416c84b782d35f3216acb5d94a1dd31d60a3eba45f9051bf503de1b66",
"enode":"enode://7b12f55c7c012104e006775d03b89722b403fb0e1ecb79af8cadfa6947425aedb323fb9416c84b782d35f3216acb5d94a1dd31d60a3eba45f9051bf503de1b66@0.0.0.0:0"
},
"up":true
}
},
{
"node":{
"info":{
"listenAddr":"",
"ip":"0.0.0.0",
"ports":{
"discovery":0,
"listener":0
},
"protocols":{
"hive":"\n=========================================================================\nFri Sep 29 21:22:53 UTC 2017 KΛÐΞMLIΛ hive: queen's address: 6e8da8\npopulation: 5 (7), MinProxBinSize: 2, MinBinSize: 1, MaxBinSize: 3\n000 1 8a1e | 3 8a1e (0) d776 (0) dfd4 (0)\n============ DEPTH: 1 ==========================================\n001 3 3451 159c 05da | 3 05da (0) 159c (0) 3451 (0)\n002 0 | 0\n003 1 73d6 | 1 73d6 (0)\n004 0 | 0\n005 0 | 0\n006 0 | 0\n007 0 | 0\n008 0 | 0\n009 0 | 0\n010 0 | 0\n011 0 | 0\n012 0 | 0\n013 0 | 0\n014 0 | 0\n015 0 | 0\n=========================================================================",
"bzz":"bo2oaruJSrNQRMjEVRRyJd+WyrSY2gZ6EY8fuaQX+eM="
},
"id":"0eec333dd211c2ea81db614fe58bf0300c15e50e1b044e47ef93067a6cdbc3bc666b40bdcc515bbf580355dbef9370294ef1ee92ee0525e78a8beed00c2b99f5",
"name":"node02",
"enode":"enode://0eec333dd211c2ea81db614fe58bf0300c15e50e1b044e47ef93067a6cdbc3bc666b40bdcc515bbf580355dbef9370294ef1ee92ee0525e78a8beed00c2b99f5@0.0.0.0:0"
},
"config":{
"name":"node02",
"id":"0eec333dd211c2ea81db614fe58bf0300c15e50e1b044e47ef93067a6cdbc3bc666b40bdcc515bbf580355dbef9370294ef1ee92ee0525e78a8beed00c2b99f5",
"services":[
"pss","bzz"
],
"private_key":"c7526db70acd02f36d3b201ef3e1d85e38c52bee6931453213dbc5edec4d0976"
},
"up":true
}
}
]
}

100
swarm/pss/testdata/snapshot_3.json vendored Normal file
View file

@ -0,0 +1,100 @@
{
"conns":[
{
"one":"0eec333dd211c2ea81db614fe58bf0300c15e50e1b044e47ef93067a6cdbc3bc666b40bdcc515bbf580355dbef9370294ef1ee92ee0525e78a8beed00c2b99f5",
"other":"6f6ee658538ea66a68c9cb914d09f228f6ee9942c337a8d5a2cb3a0f021e83dd0fab481ca8ebf56ed913f6ddf69caa3249459d43e61e5e5b162ded7e1c918c9c",
"up":true
},
{
"other":"0eec333dd211c2ea81db614fe58bf0300c15e50e1b044e47ef93067a6cdbc3bc666b40bdcc515bbf580355dbef9370294ef1ee92ee0525e78a8beed00c2b99f5",
"one":"7b12f55c7c012104e006775d03b89722b403fb0e1ecb79af8cadfa6947425aedb323fb9416c84b782d35f3216acb5d94a1dd31d60a3eba45f9051bf503de1b66",
"up":true
}
],
"nodes":[
{
"node":{
"config":{
"private_key":"e567b7d9c554e5102cdc99b6523bace02dbb8951415c8816d82ba2d2e97fa23b",
"name":"node01",
"id":"7b12f55c7c012104e006775d03b89722b403fb0e1ecb79af8cadfa6947425aedb323fb9416c84b782d35f3216acb5d94a1dd31d60a3eba45f9051bf503de1b66",
"services":[
"bzz","pss"
]
},
"info":{
"ip":"0.0.0.0",
"listenAddr":"",
"protocols":{
"hive":"\n=========================================================================\nFri Sep 29 21:22:53 UTC 2017 KΛÐΞMLIΛ hive: queen's address: 73d6ad\npopulation: 5 (7), MinProxBinSize: 2, MinBinSize: 1, MaxBinSize: 3\n000 1 dfd4 | 3 8a1e (0) d776 (0) dfd4 (0)\n============ DEPTH: 1 ==========================================\n001 3 05da 159c 3451 | 3 05da (0) 159c (0) 3451 (0)\n002 0 | 0\n003 1 6e8d | 1 6e8d (0)\n004 0 | 0\n005 0 | 0\n006 0 | 0\n007 0 | 0\n008 0 | 0\n009 0 | 0\n010 0 | 0\n011 0 | 0\n012 0 | 0\n013 0 | 0\n014 0 | 0\n015 0 | 0\n=========================================================================",
"bzz":"c9atSnUGnc7WYPpMuYFD7lVz33yxXZopWs8WVeloM4Q="
},
"ports":{
"listener":0,
"discovery":0
},
"name":"node01",
"id":"7b12f55c7c012104e006775d03b89722b403fb0e1ecb79af8cadfa6947425aedb323fb9416c84b782d35f3216acb5d94a1dd31d60a3eba45f9051bf503de1b66",
"enode":"enode://7b12f55c7c012104e006775d03b89722b403fb0e1ecb79af8cadfa6947425aedb323fb9416c84b782d35f3216acb5d94a1dd31d60a3eba45f9051bf503de1b66@0.0.0.0:0"
},
"up":true
}
},
{
"node":{
"info":{
"listenAddr":"",
"ip":"0.0.0.0",
"ports":{
"discovery":0,
"listener":0
},
"protocols":{
"hive":"\n=========================================================================\nFri Sep 29 21:22:53 UTC 2017 KΛÐΞMLIΛ hive: queen's address: 6e8da8\npopulation: 5 (7), MinProxBinSize: 2, MinBinSize: 1, MaxBinSize: 3\n000 1 8a1e | 3 8a1e (0) d776 (0) dfd4 (0)\n============ DEPTH: 1 ==========================================\n001 3 3451 159c 05da | 3 05da (0) 159c (0) 3451 (0)\n002 0 | 0\n003 1 73d6 | 1 73d6 (0)\n004 0 | 0\n005 0 | 0\n006 0 | 0\n007 0 | 0\n008 0 | 0\n009 0 | 0\n010 0 | 0\n011 0 | 0\n012 0 | 0\n013 0 | 0\n014 0 | 0\n015 0 | 0\n=========================================================================",
"bzz":"bo2oaruJSrNQRMjEVRRyJd+WyrSY2gZ6EY8fuaQX+eM="
},
"id":"0eec333dd211c2ea81db614fe58bf0300c15e50e1b044e47ef93067a6cdbc3bc666b40bdcc515bbf580355dbef9370294ef1ee92ee0525e78a8beed00c2b99f5",
"name":"node02",
"enode":"enode://0eec333dd211c2ea81db614fe58bf0300c15e50e1b044e47ef93067a6cdbc3bc666b40bdcc515bbf580355dbef9370294ef1ee92ee0525e78a8beed00c2b99f5@0.0.0.0:0"
},
"config":{
"name":"node02",
"id":"0eec333dd211c2ea81db614fe58bf0300c15e50e1b044e47ef93067a6cdbc3bc666b40bdcc515bbf580355dbef9370294ef1ee92ee0525e78a8beed00c2b99f5",
"services":[
"bzz","pss"
],
"private_key":"c7526db70acd02f36d3b201ef3e1d85e38c52bee6931453213dbc5edec4d0976"
},
"up":true
}
},
{
"node":{
"config":{
"private_key":"61b5728f59bc43080c3b8eb0458fb30d7723e2747355b6dc980f35f3ed431199",
"id":"6f6ee658538ea66a68c9cb914d09f228f6ee9942c337a8d5a2cb3a0f021e83dd0fab481ca8ebf56ed913f6ddf69caa3249459d43e61e5e5b162ded7e1c918c9c",
"name":"node03",
"services":[
"bzz","pss"
]
},
"info":{
"ip":"0.0.0.0",
"listenAddr":"",
"protocols":{
"hive":"\n=========================================================================\nFri Sep 29 21:22:53 UTC 2017 KΛÐΞMLIΛ hive: queen's address: 8a1eb7\npopulation: 3 (7), MinProxBinSize: 2, MinBinSize: 1, MaxBinSize: 3\n000 1 6e8d | 5 05da (0) 159c (0) 3451 (0) 73d6 (0)\n============ DEPTH: 1 ==========================================\n001 2 dfd4 d776 | 2 dfd4 (0) d776 (0)\n002 0 | 0\n003 0 | 0\n004 0 | 0\n005 0 | 0\n006 0 | 0\n007 0 | 0\n008 0 | 0\n009 0 | 0\n010 0 | 0\n011 0 | 0\n012 0 | 0\n013 0 | 0\n014 0 | 0\n015 0 | 0\n=========================================================================",
"bzz":"ih63j/E98xjn+BFt/+6YzX2ZBWUPpT8Wdmt1SmPzh6w="
},
"ports":{
"discovery":0,
"listener":0
},
"name":"node03",
"id":"6f6ee658538ea66a68c9cb914d09f228f6ee9942c337a8d5a2cb3a0f021e83dd0fab481ca8ebf56ed913f6ddf69caa3249459d43e61e5e5b162ded7e1c918c9c",
"enode":"enode://6f6ee658538ea66a68c9cb914d09f228f6ee9942c337a8d5a2cb3a0f021e83dd0fab481ca8ebf56ed913f6ddf69caa3249459d43e61e5e5b162ded7e1c918c9c@0.0.0.0:0"
},
"up":true
}
}
]
}

133
swarm/pss/testdata/snapshot_4.json vendored Normal file
View file

@ -0,0 +1,133 @@
{
"conns":[
{
"one":"0eec333dd211c2ea81db614fe58bf0300c15e50e1b044e47ef93067a6cdbc3bc666b40bdcc515bbf580355dbef9370294ef1ee92ee0525e78a8beed00c2b99f5",
"other":"6f6ee658538ea66a68c9cb914d09f228f6ee9942c337a8d5a2cb3a0f021e83dd0fab481ca8ebf56ed913f6ddf69caa3249459d43e61e5e5b162ded7e1c918c9c",
"up":true
},
{
"other":"0eec333dd211c2ea81db614fe58bf0300c15e50e1b044e47ef93067a6cdbc3bc666b40bdcc515bbf580355dbef9370294ef1ee92ee0525e78a8beed00c2b99f5",
"one":"7b12f55c7c012104e006775d03b89722b403fb0e1ecb79af8cadfa6947425aedb323fb9416c84b782d35f3216acb5d94a1dd31d60a3eba45f9051bf503de1b66",
"up":true
},
{
"up":true,
"other":"83388147883592bab4fdaddb4e56f8cb1c56dc5c2e910fc6a7277ac89b77cc7ce24892ed6984f3414589cb3b8c4b69356ff9aab7ca52fdd58f12dee2a2152523",
"one":"6f6ee658538ea66a68c9cb914d09f228f6ee9942c337a8d5a2cb3a0f021e83dd0fab481ca8ebf56ed913f6ddf69caa3249459d43e61e5e5b162ded7e1c918c9c"
}
],
"nodes":[
{
"node":{
"config":{
"private_key":"e567b7d9c554e5102cdc99b6523bace02dbb8951415c8816d82ba2d2e97fa23b",
"name":"node01",
"id":"7b12f55c7c012104e006775d03b89722b403fb0e1ecb79af8cadfa6947425aedb323fb9416c84b782d35f3216acb5d94a1dd31d60a3eba45f9051bf503de1b66",
"services":[
"bzz","pss"
]
},
"info":{
"ip":"0.0.0.0",
"listenAddr":"",
"protocols":{
"hive":"\n=========================================================================\nFri Sep 29 21:22:53 UTC 2017 KΛÐΞMLIΛ hive: queen's address: 73d6ad\npopulation: 5 (7), MinProxBinSize: 2, MinBinSize: 1, MaxBinSize: 3\n000 1 dfd4 | 3 8a1e (0) d776 (0) dfd4 (0)\n============ DEPTH: 1 ==========================================\n001 3 05da 159c 3451 | 3 05da (0) 159c (0) 3451 (0)\n002 0 | 0\n003 1 6e8d | 1 6e8d (0)\n004 0 | 0\n005 0 | 0\n006 0 | 0\n007 0 | 0\n008 0 | 0\n009 0 | 0\n010 0 | 0\n011 0 | 0\n012 0 | 0\n013 0 | 0\n014 0 | 0\n015 0 | 0\n=========================================================================",
"bzz":"c9atSnUGnc7WYPpMuYFD7lVz33yxXZopWs8WVeloM4Q="
},
"ports":{
"listener":0,
"discovery":0
},
"name":"node01",
"id":"7b12f55c7c012104e006775d03b89722b403fb0e1ecb79af8cadfa6947425aedb323fb9416c84b782d35f3216acb5d94a1dd31d60a3eba45f9051bf503de1b66",
"enode":"enode://7b12f55c7c012104e006775d03b89722b403fb0e1ecb79af8cadfa6947425aedb323fb9416c84b782d35f3216acb5d94a1dd31d60a3eba45f9051bf503de1b66@0.0.0.0:0"
},
"up":true
}
},
{
"node":{
"info":{
"listenAddr":"",
"ip":"0.0.0.0",
"ports":{
"discovery":0,
"listener":0
},
"protocols":{
"hive":"\n=========================================================================\nFri Sep 29 21:22:53 UTC 2017 KΛÐΞMLIΛ hive: queen's address: 6e8da8\npopulation: 5 (7), MinProxBinSize: 2, MinBinSize: 1, MaxBinSize: 3\n000 1 8a1e | 3 8a1e (0) d776 (0) dfd4 (0)\n============ DEPTH: 1 ==========================================\n001 3 3451 159c 05da | 3 05da (0) 159c (0) 3451 (0)\n002 0 | 0\n003 1 73d6 | 1 73d6 (0)\n004 0 | 0\n005 0 | 0\n006 0 | 0\n007 0 | 0\n008 0 | 0\n009 0 | 0\n010 0 | 0\n011 0 | 0\n012 0 | 0\n013 0 | 0\n014 0 | 0\n015 0 | 0\n=========================================================================",
"bzz":"bo2oaruJSrNQRMjEVRRyJd+WyrSY2gZ6EY8fuaQX+eM="
},
"id":"0eec333dd211c2ea81db614fe58bf0300c15e50e1b044e47ef93067a6cdbc3bc666b40bdcc515bbf580355dbef9370294ef1ee92ee0525e78a8beed00c2b99f5",
"name":"node02",
"enode":"enode://0eec333dd211c2ea81db614fe58bf0300c15e50e1b044e47ef93067a6cdbc3bc666b40bdcc515bbf580355dbef9370294ef1ee92ee0525e78a8beed00c2b99f5@0.0.0.0:0"
},
"config":{
"name":"node02",
"id":"0eec333dd211c2ea81db614fe58bf0300c15e50e1b044e47ef93067a6cdbc3bc666b40bdcc515bbf580355dbef9370294ef1ee92ee0525e78a8beed00c2b99f5",
"services":[
"bzz","pss"
],
"private_key":"c7526db70acd02f36d3b201ef3e1d85e38c52bee6931453213dbc5edec4d0976"
},
"up":true
}
},
{
"node":{
"config":{
"private_key":"61b5728f59bc43080c3b8eb0458fb30d7723e2747355b6dc980f35f3ed431199",
"id":"6f6ee658538ea66a68c9cb914d09f228f6ee9942c337a8d5a2cb3a0f021e83dd0fab481ca8ebf56ed913f6ddf69caa3249459d43e61e5e5b162ded7e1c918c9c",
"name":"node03",
"services":[
"bzz","pss"
]
},
"info":{
"ip":"0.0.0.0",
"listenAddr":"",
"protocols":{
"hive":"\n=========================================================================\nFri Sep 29 21:22:53 UTC 2017 KΛÐΞMLIΛ hive: queen's address: 8a1eb7\npopulation: 3 (7), MinProxBinSize: 2, MinBinSize: 1, MaxBinSize: 3\n000 1 6e8d | 5 05da (0) 159c (0) 3451 (0) 73d6 (0)\n============ DEPTH: 1 ==========================================\n001 2 dfd4 d776 | 2 dfd4 (0) d776 (0)\n002 0 | 0\n003 0 | 0\n004 0 | 0\n005 0 | 0\n006 0 | 0\n007 0 | 0\n008 0 | 0\n009 0 | 0\n010 0 | 0\n011 0 | 0\n012 0 | 0\n013 0 | 0\n014 0 | 0\n015 0 | 0\n=========================================================================",
"bzz":"ih63j/E98xjn+BFt/+6YzX2ZBWUPpT8Wdmt1SmPzh6w="
},
"ports":{
"discovery":0,
"listener":0
},
"name":"node03",
"id":"6f6ee658538ea66a68c9cb914d09f228f6ee9942c337a8d5a2cb3a0f021e83dd0fab481ca8ebf56ed913f6ddf69caa3249459d43e61e5e5b162ded7e1c918c9c",
"enode":"enode://6f6ee658538ea66a68c9cb914d09f228f6ee9942c337a8d5a2cb3a0f021e83dd0fab481ca8ebf56ed913f6ddf69caa3249459d43e61e5e5b162ded7e1c918c9c@0.0.0.0:0"
},
"up":true
}
},
{
"node":{
"info":{
"name":"node04",
"id":"83388147883592bab4fdaddb4e56f8cb1c56dc5c2e910fc6a7277ac89b77cc7ce24892ed6984f3414589cb3b8c4b69356ff9aab7ca52fdd58f12dee2a2152523",
"enode":"enode://83388147883592bab4fdaddb4e56f8cb1c56dc5c2e910fc6a7277ac89b77cc7ce24892ed6984f3414589cb3b8c4b69356ff9aab7ca52fdd58f12dee2a2152523@0.0.0.0:0",
"ip":"0.0.0.0",
"listenAddr":"",
"protocols":{
"bzz":"13aDNPedYmrbQz9EtwOoGFVeMzEFYDbvP40Sglhr8EQ=",
"hive":"\n=========================================================================\nFri Sep 29 21:22:53 UTC 2017 KΛÐΞMLIΛ hive: queen's address: d77683\npopulation: 5 (7), MinProxBinSize: 2, MinBinSize: 1, MaxBinSize: 3\n000 3 3451 159c 05da | 5 6e8d (0) 73d6 (0) 3451 (0) 159c (0)\n============ DEPTH: 1 ==========================================\n001 1 8a1e | 1 8a1e (0)\n002 0 | 0\n003 0 | 0\n004 1 dfd4 | 1 dfd4 (0)\n005 0 | 0\n006 0 | 0\n007 0 | 0\n008 0 | 0\n009 0 | 0\n010 0 | 0\n011 0 | 0\n012 0 | 0\n013 0 | 0\n014 0 | 0\n015 0 | 0\n========================================================================="
},
"ports":{
"listener":0,
"discovery":0
}
},
"config":{
"services":[
"bzz","pss"
],
"id":"83388147883592bab4fdaddb4e56f8cb1c56dc5c2e910fc6a7277ac89b77cc7ce24892ed6984f3414589cb3b8c4b69356ff9aab7ca52fdd58f12dee2a2152523",
"name":"node04",
"private_key":"075b07c29ceac4ffa2a114afd67b21dfc438126bc169bf7c154be6d81d86ed38"
},
"up":true
}
}
]
}

125
swarm/pss/writeup.md Normal file
View file

@ -0,0 +1,125 @@
## PSS tests failures explanation
This document aims to explain the changes in https://github.com/ethersphere/go-ethereum/pull/126 and how those changes affect the pss_test.go TestNetwork tests.
### Problem
When running the TestNetwork test, execution sometimes:
* deadlocks
* panics
* failures with wrong result, such as:
```
$ go test -v ./swarm/pss -cpu 4 -run TestNetwork
```
```
--- FAIL: TestNetwork (68.13s)
--- FAIL: TestNetwork/3/10/4/sim (68.13s)
pss_test.go:697: 7 of 10 messages received
pss_test.go:700: 3 messages were not received
FAIL
```
Moreover execution almost always deadlocks with `sim` adapter, and `sock` adapter (when buffer is low), but is mostly stable with `exec` and `tcp` adapters.
### Findings and Fixes
#### 1. Addressing panics
Panics were caused due to concurrent map read/writes and unsynchronised access to shared memory by multiple goroutines. This is visible when running the test with the `-race` flag.
```
go test -race -v ./swarm/pss -cpu 4 -run TestNetwork
1 ==================
2 WARNING: DATA RACE
3 Read at 0x00c424d456a0 by goroutine 1089:
4 github.com/ethereum/go-ethereum/swarm/pss.(*Pss).forward.func1()
5 /Users/nonsense/code/src/github.com/ethereum/go-ethereum/swarm/pss/pss.go:654 +0x44f
6 github.com/ethereum/go-ethereum/swarm/network.(*Kademlia).eachConn.func1()
7 /Users/nonsense/code/src/github.com/ethereum/go-ethereum/swarm/network/kademlia.go:350 +0xc9
8 github.com/ethereum/go-ethereum/pot.(*Pot).eachNeighbour.func1()
9 /Users/nonsense/code/src/github.com/ethereum/go-ethereum/pot/pot.go:599 +0x59
...
28
29 Previous write at 0x00c424d456a0 by goroutine 829:
30 github.com/ethereum/go-ethereum/swarm/pss.(*Pss).Run()
31 /Users/nonsense/code/src/github.com/ethereum/go-ethereum/swarm/pss/pss.go:192 +0x16a
32 github.com/ethereum/go-ethereum/swarm/pss.(*Pss).Run-fm()
33 /Users/nonsense/code/src/github.com/ethereum/go-ethereum/swarm/pss/pss.go:185 +0x63
34 github.com/ethereum/go-ethereum/p2p.(*Peer).startProtocols.func1()
35 /Users/nonsense/code/src/github.com/ethereum/go-ethereum/p2p/peer.go:347 +0x8b
...
```
##### Current solution
Adding a mutex around all shared data.
#### 2. Failures with wrong result
The validation phase of the TestNetwork test is done using an RPC subscription:
```
...
triggerChecks := func(trigger chan discover.NodeID, id discover.NodeID, rpcclient *rpc.Client) error {
msgC := make(chan APIMsg)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
sub, err := rpcclient.Subscribe(ctx, "pss", msgC, "receive", hextopic)
...
```
By design the RPC uses a subscription buffer with a max length. When this length is reached, the subscription is dropped. The current config value is not suitable for stress tests.
##### Current solution
Increase the max length of the RPC subscription buffer.
```
const (
// Subscriptions are removed when the subscriber cannot keep up.
//
// This can be worked around by supplying a channel with sufficiently sized buffer,
// but this can be inconvenient and hard to explain in the docs. Another issue with
// buffered channels is that the buffer is static even though it might not be needed
// most of the time.
//
// The approach taken here is to maintain a per-subscription linked list buffer
// shrinks on demand. If the buffer reaches the size below, the subscription is
// dropped.
maxClientSubscriptionBuffer = 20000
)
```
#### 3. Deadlocks
Deadlocks are triggered when using:
* `sim` adapter - synchronous, unbuffered channel
* `sock` adapter - asynchronous, buffered channel (when using a 1K buffer)
No deadlocks were triggered when using:
* `tcp` adapter - asynchronous, buffered channel
* `exec` adapter - asynchronous, buffered channel
Ultimately the deadlocks happen due to blocking `pp.Send()` call at:
// attempt to send the message
err := pp.Send(msg)
if err != nil {
log.Debug(fmt.Sprintf("%v: failed forwarding: %v", sendMsg, err))
return true
}
`p2p` request handling is synchronous (as discussed at https://github.com/ethersphere/go-ethereum/issues/130), `pss` is also synchronous, therefore if two nodes happen to be processing a request, while at the same time waiting for response on `pp.Send(msg)`, deadlock occurs.
`pp.Send(msg)` is only blocking when the underlying adapter is blocking (read `sim` or `sock`) or the buffer of the connection is full.
##### Current solution
Make no assumption on the undelying connection, and call `pp.Send` asynchronously in a go-routine.
Alternatively, get rid of the `sim` and `sock` adapters, and use `tcp` adapter for testing.

12
swarm/state.go Normal file
View file

@ -0,0 +1,12 @@
package swarm
type Voidstore struct {
}
func (self Voidstore) Load(string) ([]byte, error) {
return nil, nil
}
func (self Voidstore) Save(string, []byte) error {
return nil
}