Merge pull request #92 from ethersphere/swarm-pss-rpcclient

swarm/pss: rpcclient + pss test fixes
This commit is contained in:
Viktor Trón 2017-05-17 23:45:23 -07:00 committed by GitHub
commit ebd6199dfc
58 changed files with 3421 additions and 2374 deletions

View file

@ -459,7 +459,7 @@ func getPassPhrase(prompt string, i int, passwords []string) string {
return password return password
} }
func injectBootnodes(srv p2p.Server, nodes []string) { func injectBootnodes(srv *p2p.Server, nodes []string) {
for _, url := range nodes { for _, url := range nodes {
n, err := discover.ParseNode(url) n, err := discover.ParseNode(url)
if err != nil { if err != nil {

View file

@ -51,7 +51,7 @@ const quitCommand = "~Q"
// singletons // singletons
var ( var (
server p2p.Server server *p2p.Server
shh *whisper.Whisper shh *whisper.Whisper
done chan struct{} done chan struct{}
mailServer mailserver.WMailServer mailServer mailserver.WMailServer
@ -253,17 +253,19 @@ func initialize() {
maxPeers = 800 maxPeers = 800
} }
server = p2p.NewServer(p2p.Config{ server = &p2p.Server{
PrivateKey: nodeid, Config: p2p.Config{
MaxPeers: maxPeers, PrivateKey: nodeid,
Name: common.MakeName("wnode", "5.0"), MaxPeers: maxPeers,
Protocols: shh.Protocols(), Name: common.MakeName("wnode", "5.0"),
ListenAddr: *argIP, Protocols: shh.Protocols(),
NAT: nat.Any(), ListenAddr: *argIP,
BootstrapNodes: peers, NAT: nat.Any(),
StaticNodes: peers, BootstrapNodes: peers,
TrustedNodes: peers, StaticNodes: peers,
}) TrustedNodes: peers,
},
}
} }
func startServer() { func startServer() {

View file

@ -94,7 +94,7 @@ func (r *ReleaseService) Protocols() []p2p.Protocol { return nil }
func (r *ReleaseService) APIs() []rpc.API { return nil } func (r *ReleaseService) APIs() []rpc.API { return nil }
// Start spawns the periodic version checker goroutine // Start spawns the periodic version checker goroutine
func (r *ReleaseService) Start(server p2p.Server) error { func (r *ReleaseService) Start(server *p2p.Server) error {
go r.checker() go r.checker()
return nil return nil
} }

View file

@ -49,7 +49,7 @@ import (
) )
type LesServer interface { type LesServer interface {
Start(srvr p2p.Server) Start(srvr *p2p.Server)
Stop() Stop()
Protocols() []p2p.Protocol Protocols() []p2p.Protocol
} }
@ -362,7 +362,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol {
// Start implements node.Service, starting all internal goroutines needed by the // Start implements node.Service, starting all internal goroutines needed by the
// Ethereum protocol implementation. // Ethereum protocol implementation.
func (s *Ethereum) Start(srvr p2p.Server) error { func (s *Ethereum) Start(srvr *p2p.Server) error {
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion()) s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion())
s.protocolManager.Start() s.protocolManager.Start()

View file

@ -52,7 +52,7 @@ const historyUpdateRange = 50
type Service struct { type Service struct {
stack *node.Node // Temporary workaround, remove when API finalized stack *node.Node // Temporary workaround, remove when API finalized
server p2p.Server // Peer-to-peer server to retrieve networking infos server *p2p.Server // Peer-to-peer server to retrieve networking infos
eth *eth.Ethereum // Full Ethereum service if monitoring a full node eth *eth.Ethereum // Full Ethereum service if monitoring a full node
les *les.LightEthereum // Light Ethereum service if monitoring a light node les *les.LightEthereum // Light Ethereum service if monitoring a light node
engine consensus.Engine // Consensus engine to retrieve variadic block fields engine consensus.Engine // Consensus engine to retrieve variadic block fields
@ -101,7 +101,7 @@ func (s *Service) Protocols() []p2p.Protocol { return nil }
func (s *Service) APIs() []rpc.API { return nil } func (s *Service) APIs() []rpc.API { return nil }
// Start implements node.Service, starting up the monitoring and reporting daemon. // Start implements node.Service, starting up the monitoring and reporting daemon.
func (s *Service) Start(server p2p.Server) error { func (s *Service) Start(server *p2p.Server) error {
s.server = server s.server = server
go s.loop() go s.loop()

View file

@ -1434,12 +1434,12 @@ func (api *PrivateDebugAPI) SetHead(number hexutil.Uint64) {
// PublicNetAPI offers network related RPC methods // PublicNetAPI offers network related RPC methods
type PublicNetAPI struct { type PublicNetAPI struct {
net p2p.Server net *p2p.Server
networkVersion uint64 networkVersion uint64
} }
// NewPublicNetAPI creates a new net API instance. // NewPublicNetAPI creates a new net API instance.
func NewPublicNetAPI(net p2p.Server, networkVersion uint64) *PublicNetAPI { func NewPublicNetAPI(net *p2p.Server, networkVersion uint64) *PublicNetAPI {
return &PublicNetAPI{net, networkVersion} return &PublicNetAPI{net, networkVersion}
} }

View file

@ -185,7 +185,7 @@ func (s *LightEthereum) Protocols() []p2p.Protocol {
// Start implements node.Service, starting all internal goroutines needed by the // Start implements node.Service, starting all internal goroutines needed by the
// Ethereum protocol implementation. // Ethereum protocol implementation.
func (s *LightEthereum) Start(srvr p2p.Server) error { func (s *LightEthereum) Start(srvr *p2p.Server) error {
log.Warn("Light client mode is an experimental feature") log.Warn("Light client mode is an experimental feature")
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.networkId) s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.networkId)
s.protocolManager.Start(srvr) s.protocolManager.Start(srvr)

View file

@ -61,10 +61,6 @@ const (
disableClientRemovePeer = false disableClientRemovePeer = false
) )
type discV5Server interface {
DiscV5() *discv5.Network
}
// errIncompatibleConfig is returned if the requested protocols and configs are // errIncompatibleConfig is returned if the requested protocols and configs are
// not compatible (low protocol version restrictions and high requirements). // not compatible (low protocol version restrictions and high requirements).
var errIncompatibleConfig = errors.New("incompatible configuration") var errIncompatibleConfig = errors.New("incompatible configuration")
@ -260,10 +256,10 @@ func (pm *ProtocolManager) removePeer(id string) {
} }
} }
func (pm *ProtocolManager) Start(srvr p2p.Server) { func (pm *ProtocolManager) Start(srvr *p2p.Server) {
var topicDisc *discv5.Network var topicDisc *discv5.Network
if v, ok := srvr.(discV5Server); ok { if srvr != nil {
topicDisc = v.DiscV5() topicDisc = srvr.DiscV5
} }
lesTopic := discv5.Topic("LES@" + common.Bytes2Hex(pm.blockchain.Genesis().Hash().Bytes()[0:8])) lesTopic := discv5.Topic("LES@" + common.Bytes2Hex(pm.blockchain.Genesis().Hash().Bytes()[0:8]))
if pm.lightSync { if pm.lightSync {

View file

@ -68,7 +68,7 @@ func (s *LesServer) Protocols() []p2p.Protocol {
} }
// Start starts the LES server // Start starts the LES server
func (s *LesServer) Start(srvr p2p.Server) { func (s *LesServer) Start(srvr *p2p.Server) {
s.protocolManager.Start(srvr) s.protocolManager.Start(srvr)
} }

View file

@ -97,7 +97,7 @@ const (
type serverPool struct { type serverPool struct {
db ethdb.Database db ethdb.Database
dbKey []byte dbKey []byte
server p2p.Server server *p2p.Server
quit chan struct{} quit chan struct{}
wg *sync.WaitGroup wg *sync.WaitGroup
connWg sync.WaitGroup connWg sync.WaitGroup
@ -118,7 +118,7 @@ type serverPool struct {
} }
// newServerPool creates a new serverPool instance // newServerPool creates a new serverPool instance
func newServerPool(db ethdb.Database, dbPrefix []byte, server p2p.Server, topic discv5.Topic, quit chan struct{}, wg *sync.WaitGroup) *serverPool { func newServerPool(db ethdb.Database, dbPrefix []byte, server *p2p.Server, topic discv5.Topic, quit chan struct{}, wg *sync.WaitGroup) *serverPool {
pool := &serverPool{ pool := &serverPool{
db: db, db: db,
dbKey: append(dbPrefix, []byte(topic)...), dbKey: append(dbPrefix, []byte(topic)...),
@ -139,11 +139,11 @@ func newServerPool(db ethdb.Database, dbPrefix []byte, server p2p.Server, topic
pool.loadNodes() pool.loadNodes()
pool.checkDial() pool.checkDial()
if srv, ok := pool.server.(discV5Server); ok && srv.DiscV5() != nil { if pool.server.DiscV5 != nil {
pool.discSetPeriod = make(chan time.Duration, 1) pool.discSetPeriod = make(chan time.Duration, 1)
pool.discNodes = make(chan *discv5.Node, 100) pool.discNodes = make(chan *discv5.Node, 100)
pool.discLookups = make(chan bool, 100) pool.discLookups = make(chan bool, 100)
go srv.DiscV5().SearchTopic(topic, pool.discSetPeriod, pool.discNodes, pool.discLookups) go pool.server.DiscV5.SearchTopic(topic, pool.discSetPeriod, pool.discNodes, pool.discLookups)
} }
go pool.eventLoop() go pool.eventLoop()

View file

@ -375,7 +375,7 @@ func NewPublicWeb3API(stack *Node) *PublicWeb3API {
// ClientVersion returns the node name // ClientVersion returns the node name
func (s *PublicWeb3API) ClientVersion() string { func (s *PublicWeb3API) ClientVersion() string {
return s.stack.serverConfig.Name return s.stack.Server().Name
} }
// Sha3 applies the ethereum sha3 implementation on the input. // Sha3 applies the ethereum sha3 implementation on the input.

View file

@ -56,7 +56,7 @@ type Node struct {
instanceDirLock storage.Storage // prevents concurrent use of instance directory instanceDirLock storage.Storage // prevents concurrent use of instance directory
serverConfig p2p.Config serverConfig p2p.Config
server p2p.Server // Currently running P2P networking layer server *p2p.Server // Currently running P2P networking layer
serviceFuncs []ServiceConstructor // Service constructors (in dependency order) serviceFuncs []ServiceConstructor // Service constructors (in dependency order)
services map[reflect.Type]Service // Currently running services services map[reflect.Type]Service // Currently running services
@ -165,6 +165,8 @@ func (n *Node) Start() error {
if n.serverConfig.NodeDatabase == "" { if n.serverConfig.NodeDatabase == "" {
n.serverConfig.NodeDatabase = n.config.NodeDB() n.serverConfig.NodeDatabase = n.config.NodeDB()
} }
running := &p2p.Server{Config: n.serverConfig}
log.Info("Starting peer-to-peer node", "instance", n.serverConfig.Name)
// Otherwise copy and specialize the P2P configuration // Otherwise copy and specialize the P2P configuration
services := make(map[reflect.Type]Service) services := make(map[reflect.Type]Service)
@ -192,10 +194,8 @@ func (n *Node) Start() error {
} }
// Gather the protocols and start the freshly assembled P2P server // Gather the protocols and start the freshly assembled P2P server
for _, service := range services { for _, service := range services {
n.serverConfig.Protocols = append(n.serverConfig.Protocols, service.Protocols()...) running.Protocols = append(running.Protocols, service.Protocols()...)
} }
running := p2p.NewServer(n.serverConfig)
log.Info("Starting peer-to-peer node", "instance", n.serverConfig.Name)
if err := running.Start(); err != nil { if err := running.Start(); err != nil {
if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] { if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] {
return ErrDatadirUsed return ErrDatadirUsed
@ -582,7 +582,7 @@ func (n *Node) RPCHandler() (*rpc.Server, error) {
// Server retrieves the currently running P2P network layer. This method is meant // Server retrieves the currently running P2P network layer. This method is meant
// only to inspect fields of the currently running server, life cycle management // only to inspect fields of the currently running server, life cycle management
// should be left to this Node entity. // should be left to this Node entity.
func (n *Node) Server() p2p.Server { func (n *Node) Server() *p2p.Server {
n.lock.RLock() n.lock.RLock()
defer n.lock.RUnlock() defer n.lock.RUnlock()

View file

@ -37,7 +37,7 @@ type SampleService struct{}
func (s *SampleService) Protocols() []p2p.Protocol { return nil } func (s *SampleService) Protocols() []p2p.Protocol { return nil }
func (s *SampleService) APIs() []rpc.API { return nil } func (s *SampleService) APIs() []rpc.API { return nil }
func (s *SampleService) Start(p2p.Server) error { fmt.Println("Service starting..."); return nil } func (s *SampleService) Start(*p2p.Server) error { fmt.Println("Service starting..."); return nil }
func (s *SampleService) Stop() error { fmt.Println("Service stopping..."); return nil } func (s *SampleService) Stop() error { fmt.Println("Service stopping..."); return nil }
func ExampleService() { func ExampleService() {

View file

@ -154,7 +154,7 @@ func TestServiceLifeCycle(t *testing.T) {
id := id // Closure for the constructor id := id // Closure for the constructor
constructor := func(*ServiceContext) (Service, error) { constructor := func(*ServiceContext) (Service, error) {
return &InstrumentedService{ return &InstrumentedService{
startHook: func(p2p.Server) { started[id] = true }, startHook: func(*p2p.Server) { started[id] = true },
stopHook: func() { stopped[id] = true }, stopHook: func() { stopped[id] = true },
}, nil }, nil
} }
@ -200,7 +200,7 @@ func TestServiceRestarts(t *testing.T) {
running = false running = false
return &InstrumentedService{ return &InstrumentedService{
startHook: func(p2p.Server) { startHook: func(*p2p.Server) {
if running { if running {
panic("already running") panic("already running")
} }
@ -250,7 +250,7 @@ func TestServiceConstructionAbortion(t *testing.T) {
id := id // Closure for the constructor id := id // Closure for the constructor
constructor := func(*ServiceContext) (Service, error) { constructor := func(*ServiceContext) (Service, error) {
return &InstrumentedService{ return &InstrumentedService{
startHook: func(p2p.Server) { started[id] = true }, startHook: func(*p2p.Server) { started[id] = true },
}, nil }, nil
} }
if err := stack.Register(maker(constructor)); err != nil { if err := stack.Register(maker(constructor)); err != nil {
@ -299,7 +299,7 @@ func TestServiceStartupAbortion(t *testing.T) {
id := id // Closure for the constructor id := id // Closure for the constructor
constructor := func(*ServiceContext) (Service, error) { constructor := func(*ServiceContext) (Service, error) {
return &InstrumentedService{ return &InstrumentedService{
startHook: func(p2p.Server) { started[id] = true }, startHook: func(*p2p.Server) { started[id] = true },
stopHook: func() { stopped[id] = true }, stopHook: func() { stopped[id] = true },
}, nil }, nil
} }
@ -352,7 +352,7 @@ func TestServiceTerminationGuarantee(t *testing.T) {
id := id // Closure for the constructor id := id // Closure for the constructor
constructor := func(*ServiceContext) (Service, error) { constructor := func(*ServiceContext) (Service, error) {
return &InstrumentedService{ return &InstrumentedService{
startHook: func(p2p.Server) { started[id] = true }, startHook: func(*p2p.Server) { started[id] = true },
stopHook: func() { stopped[id] = true }, stopHook: func() { stopped[id] = true },
}, nil }, nil
} }

View file

@ -86,7 +86,7 @@ type Service interface {
// Start is called after all services have been constructed and the networking // Start is called after all services have been constructed and the networking
// layer was also initialized to spawn any goroutines required by the service. // layer was also initialized to spawn any goroutines required by the service.
Start(server p2p.Server) error Start(server *p2p.Server) error
// Stop terminates all goroutines belonging to the service, blocking until they // Stop terminates all goroutines belonging to the service, blocking until they
// are all terminated. // are all terminated.

View file

@ -31,7 +31,7 @@ type NoopService struct{}
func (s *NoopService) Protocols() []p2p.Protocol { return nil } func (s *NoopService) Protocols() []p2p.Protocol { return nil }
func (s *NoopService) APIs() []rpc.API { return nil } func (s *NoopService) APIs() []rpc.API { return nil }
func (s *NoopService) Start(p2p.Server) error { return nil } func (s *NoopService) Start(*p2p.Server) error { return nil }
func (s *NoopService) Stop() error { return nil } func (s *NoopService) Stop() error { return nil }
func NewNoopService(*ServiceContext) (Service, error) { return new(NoopService), nil } func NewNoopService(*ServiceContext) (Service, error) { return new(NoopService), nil }
@ -57,7 +57,7 @@ type InstrumentedService struct {
stop error stop error
protocolsHook func() protocolsHook func()
startHook func(p2p.Server) startHook func(*p2p.Server)
stopHook func() stopHook func()
} }
@ -74,7 +74,7 @@ func (s *InstrumentedService) APIs() []rpc.API {
return s.apis return s.apis
} }
func (s *InstrumentedService) Start(server p2p.Server) error { func (s *InstrumentedService) Start(server *p2p.Server) error {
if s.startHook != nil { if s.startHook != nil {
s.startHook(server) s.startHook(server)
} }

View file

@ -47,6 +47,19 @@ const (
maxResolveDelay = time.Hour maxResolveDelay = time.Hour
) )
type NodeDialer interface {
Dial(*discover.Node) (net.Conn, error)
}
type TCPDialer struct {
*net.Dialer
}
func (t TCPDialer) Dial(dest *discover.Node) (net.Conn, error) {
addr := &net.TCPAddr{IP: dest.IP, Port: int(dest.TCP)}
return t.Dialer.Dial("tcp", addr.String())
}
// dialstate schedules dials and discovery lookups. // dialstate schedules dials and discovery lookups.
// it get's a chance to compute new tasks on every iteration // it get's a chance to compute new tasks on every iteration
// of the main loop in server.run. // of the main loop in server.run.
@ -84,7 +97,7 @@ type pastDial struct {
} }
type task interface { type task interface {
Do(*server) Do(*Server)
} }
// A dialTask is generated for each node that is dialed. Its // A dialTask is generated for each node that is dialed. Its
@ -267,7 +280,7 @@ func (s *dialstate) taskDone(t task, now time.Time) {
} }
} }
func (t *dialTask) Do(srv *server) { func (t *dialTask) Do(srv *Server) {
if t.dest.Incomplete() { if t.dest.Incomplete() {
if !t.resolve(srv) { if !t.resolve(srv) {
return return
@ -288,7 +301,7 @@ func (t *dialTask) Do(srv *server) {
// Resolve operations are throttled with backoff to avoid flooding the // Resolve operations are throttled with backoff to avoid flooding the
// discovery network with useless queries for nodes that don't exist. // discovery network with useless queries for nodes that don't exist.
// The backoff delay resets when the node is found. // The backoff delay resets when the node is found.
func (t *dialTask) resolve(srv *server) bool { func (t *dialTask) resolve(srv *Server) bool {
if srv.ntab == nil { if srv.ntab == nil {
log.Debug("Can't resolve node", "id", t.dest.ID, "err", "discovery is disabled") log.Debug("Can't resolve node", "id", t.dest.ID, "err", "discovery is disabled")
return false return false
@ -317,15 +330,14 @@ func (t *dialTask) resolve(srv *server) bool {
} }
// dial performs the actual connection attempt. // dial performs the actual connection attempt.
func (t *dialTask) dial(srv *server, dest *discover.Node) bool { func (t *dialTask) dial(srv *Server, dest *discover.Node) bool {
addr := &net.TCPAddr{IP: dest.IP, Port: int(dest.TCP)} fd, err := srv.Dialer.Dial(dest)
fd, err := srv.Dialer.Dial("tcp", addr.String())
if err != nil { if err != nil {
log.Trace("Dial error", "task", t, "err", err) log.Trace("Dial error", "task", t, "err", err)
return false return false
} }
mfd := newMeteredConn(fd, false) mfd := newMeteredConn(fd, false)
srv.setupConn(mfd, t.flags, dest) srv.SetupConn(mfd, t.flags, dest)
return true return true
} }
@ -333,7 +345,7 @@ func (t *dialTask) String() string {
return fmt.Sprintf("%v %x %v:%d", t.flags, t.dest.ID[:8], t.dest.IP, t.dest.TCP) return fmt.Sprintf("%v %x %v:%d", t.flags, t.dest.ID[:8], t.dest.IP, t.dest.TCP)
} }
func (t *discoverTask) Do(srv *server) { func (t *discoverTask) Do(srv *Server) {
// newTasks generates a lookup task whenever dynamic dials are // newTasks generates a lookup task whenever dynamic dials are
// necessary. Lookups need to take some time, otherwise the // necessary. Lookups need to take some time, otherwise the
// event loop spins too fast. // event loop spins too fast.
@ -355,7 +367,7 @@ func (t *discoverTask) String() string {
return s return s
} }
func (t waitExpireTask) Do(*server) { func (t waitExpireTask) Do(*Server) {
time.Sleep(t.Duration) time.Sleep(t.Duration)
} }
func (t waitExpireTask) String() string { func (t waitExpireTask) String() string {

View file

@ -597,8 +597,8 @@ func TestDialResolve(t *testing.T) {
} }
// Now run the task, it should resolve the ID once. // Now run the task, it should resolve the ID once.
config := Config{Dialer: &net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}} config := Config{Dialer: TCPDialer{&net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}}}
srv := &server{ntab: table, Config: config} srv := &Server{ntab: table, Config: config}
tasks[0].Do(srv) tasks[0].Do(srv)
if !reflect.DeepEqual(table.resolveCalls, []discover.NodeID{dest.ID}) { if !reflect.DeepEqual(table.resolveCalls, []discover.NodeID{dest.ID}) {
t.Fatalf("wrong resolve calls, got %v", table.resolveCalls) t.Fatalf("wrong resolve calls, got %v", table.resolveCalls)

View file

@ -30,12 +30,13 @@ Standard protocol supports:
package protocols package protocols
import ( import (
"context"
"fmt" "fmt"
"reflect" "reflect"
"sync"
"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/discover"
) )
// error codes used by this protocol scheme // error codes used by this protocol scheme
@ -45,23 +46,21 @@ const (
ErrWrite ErrWrite
ErrInvalidMsgCode ErrInvalidMsgCode
ErrInvalidMsgType ErrInvalidMsgType
ErrLocalHandshake ErrHandshake
ErrRemoteHandshake
ErrNoHandler ErrNoHandler
ErrHandler ErrHandler
) )
// error description strings associated with the codes // error description strings associated with the codes
var errorToString = map[int]string{ var errorToString = map[int]string{
ErrMsgTooLong: "Message too long", ErrMsgTooLong: "Message too long",
ErrDecode: "Invalid message (RLP error)", ErrDecode: "Invalid message (RLP error)",
ErrWrite: "Error sending message", ErrWrite: "Error sending message",
ErrInvalidMsgCode: "Invalid message code", ErrInvalidMsgCode: "Invalid message code",
ErrInvalidMsgType: "Invalid message type", ErrInvalidMsgType: "Invalid message type",
ErrLocalHandshake: "Local handshake error", ErrHandshake: "Handshake error",
ErrRemoteHandshake: "Remote handshake error", ErrNoHandler: "No handler registered error",
ErrNoHandler: "No handler registered error", ErrHandler: "Message handler error",
ErrHandler: "Message handler error",
} }
/* /*
@ -110,93 +109,74 @@ func errorf(code int, format string, params ...interface{}) *Error {
return self return self
} }
// implements the code table spec // Spec is a protocol specification including its name and version as well as
// listing the message codes and types etc // the types of messages which are exchanged
// and further metadata about the protocol type Spec struct {
type CodeMap struct { // Name is the name of the protocol, often a three-letter word
Name string // name of the protocol Name string
Version uint // version
MaxMsgSize int // max length of message payload size // Version is the version number of the protocol
codes []reflect.Type // index of codes to msg types - to create zero values Version uint
messages map[reflect.Type]uint64 // index of types to codes, for sending by type
// MaxMsgSize is the maximum accepted length of the message payload
MaxMsgSize uint32
// Messages is a list of message types which this protocol uses, with
// each message type being sent with its array index as the code (so
// [&foo{}, &bar{}, &baz{}] would send foo, bar and baz with codes
// 0, 1 and 2 respectively)
Messages []interface{}
initOnce sync.Once
codes map[reflect.Type]uint64
types map[uint64]reflect.Type
} }
func (self *CodeMap) GetInterface(code uint64) (interface{}, bool) { func (s *Spec) init() {
if int(code) > len(self.codes)-1 { s.initOnce.Do(func() {
s.codes = make(map[reflect.Type]uint64, len(s.Messages))
s.types = make(map[uint64]reflect.Type, len(s.Messages))
for i, msg := range s.Messages {
code := uint64(i)
typ := reflect.TypeOf(msg)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
s.codes[typ] = code
s.types[code] = typ
}
})
}
func (s *Spec) Length() uint64 {
return uint64(len(s.Messages))
}
func (s *Spec) GetCode(msg interface{}) (uint64, bool) {
s.init()
typ := reflect.TypeOf(msg)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
code, ok := s.codes[typ]
return code, ok
}
func (s *Spec) NewMsg(code uint64) (interface{}, bool) {
s.init()
typ, ok := s.types[code]
if !ok {
return nil, false return nil, false
} }
typ := self.codes[code] return reflect.New(typ).Interface(), true
val := reflect.New(typ)
return val.Interface(), true
}
func (self *CodeMap) GetCode(msg interface{}) (uint64, bool) {
code, found := self.messages[reflect.TypeOf(msg)]
return code, found
}
func NewCodeMap(name string, version uint, maxMsgSize int, msgs ...interface{}) *CodeMap {
self := &CodeMap{
Name: name,
Version: version,
MaxMsgSize: maxMsgSize,
messages: make(map[reflect.Type]uint64),
}
self.Register(msgs...)
return self
}
func (self *CodeMap) Length() uint64 {
return uint64(len(self.codes))
}
func (self *CodeMap) Register(msgs ...interface{}) {
code := uint64(len(self.codes))
for _, msg := range msgs {
typ := reflect.TypeOf(msg)
_, found := self.messages[typ]
if found {
// ignore duplicates
continue
}
// next code assigned to message type typ
self.messages[typ] = code
self.codes = append(self.codes, typ)
code++
}
}
func NewProtocol(protocolname string, protocolversion uint, run func(*Peer) error, ct *CodeMap, peerInfo func(id discover.NodeID) interface{}, nodeInfo func() interface{}) *p2p.Protocol {
// PeerInfo is an optional helper method to retrieve protocol specific metadata
// about a certain peer in the network. If an info retrieval function is set,
// but returns nil, it is assumed that the protocol handshake is still running.
r := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
return run(NewPeer(p, ct, rw))
}
return &p2p.Protocol{
Name: protocolname,
Version: protocolversion,
Length: ct.Length(),
Run: r,
PeerInfo: peerInfo,
NodeInfo: nodeInfo,
}
}
type Disconnect struct {
err error
} }
// A Peer represents a remote peer or protocol instance that is running on a peer connection with // A Peer represents a remote peer or protocol instance that is running on a peer connection with
// a remote peer // a remote peer
type Peer struct { type Peer struct {
ct *CodeMap // CodeMap for the protocol *p2p.Peer // the p2p.Peer object representing the remote
*p2p.Peer // the p2p.Peer object representing the remote rw p2p.MsgReadWriter // p2p.MsgReadWriter to send messages to and read messages from
rw p2p.MsgReadWriter // p2p.MsgReadWriter to send messages to and read messages from spec *Spec
handlers map[reflect.Type][]func(interface{}) error // message type -> message handler callback(s) map
Errc chan error Errc chan error
wErrc chan error // write error channel wErrc chan error // write error channel
} }
@ -205,55 +185,28 @@ type Peer struct {
// this constructor is called by the p2p.Protocol#Run function // this constructor is called by the p2p.Protocol#Run function
// the first two arguments are comming the arguments passed to p2p.Protocol.Run function // the first two arguments are comming the arguments passed to p2p.Protocol.Run function
// the third argument is the CodeMap describing the protocol messages and options // the third argument is the CodeMap describing the protocol messages and options
func NewPeer(p *p2p.Peer, ct *CodeMap, rw p2p.MsgReadWriter) *Peer { func NewPeer(p *p2p.Peer, rw p2p.MsgReadWriter, spec *Spec) *Peer {
return &Peer{ return &Peer{
ct: ct, Peer: p,
Peer: p, rw: rw,
rw: rw, spec: spec,
Errc: make(chan error), Errc: make(chan error),
wErrc: make(chan error), wErrc: make(chan error),
handlers: make(map[reflect.Type][]func(interface{}) error),
} }
} }
// Register is called on the peer typically within the constructor of service instances running on peer connections
// These constructors are called by the p2p.Protocol#Run function
// It ties handler callbackss for specific message types
// A message type can have several handlers registered by the same or different protocol services
// Register is meant to be called once, deregistering is not currently supported therefore
// handlers are assumed to be static across handshake renegotiations
// i.e., a service instance either handles a message or not (irrespective of the handshake)
// it panics if the message type is not defined in the CodeMap
func (self *Peer) Register(msg interface{}, handler func(interface{}) error) uint64 {
typ := reflect.TypeOf(msg)
code, found := self.ct.messages[typ]
if !found {
panic(fmt.Sprintf("message type '%v' unknown ", typ))
}
log.Trace(fmt.Sprintf("register handle for %v", typ))
self.handlers[typ] = append(self.handlers[typ], handler)
return code
}
// Run starts the forever loop that handles incoming messages // Run starts the forever loop that handles incoming messages
// called within the p2p.Protocol#Run function // called within the p2p.Protocol#Run function
func (self *Peer) Run() error { func (self *Peer) Run(handler func(msg interface{}) error) error {
go func() { go func() {
for { for {
_, err := self.handleIncoming() if err := self.handleIncoming(handler); err != nil {
if err != nil {
self.Errc <- err self.Errc <- err
return return
} }
} }
}() }()
err := <-self.Errc return <-self.Errc
d := &Disconnect{err}
for _, f := range self.handlers[reflect.TypeOf(d)] {
log.Trace(fmt.Sprintf("disconnect hook for %v", d))
f(err)
}
return err
} }
// Drop disconnects a peer. // Drop disconnects a peer.
@ -271,20 +224,12 @@ func (self *Peer) Drop(err error) {
// this low level call will be wrapped by libraries providing routed or broadcast sends // this low level call will be wrapped by libraries providing routed or broadcast sends
// but often just used to forward and push messages to directly connected peers // but often just used to forward and push messages to directly connected peers
func (self *Peer) Send(msg interface{}) error { func (self *Peer) Send(msg interface{}) error {
code, found := self.ct.GetCode(msg) code, found := self.spec.GetCode(msg)
if !found { if !found {
return errorf(ErrInvalidMsgType, "%v", code) return errorf(ErrInvalidMsgType, "%v", code)
} }
log.Trace(fmt.Sprintf("=> msg #%d TO %v : %v", code, self.ID(), msg)) log.Trace(fmt.Sprintf("=> msg #%d TO %v : %v", code, self.ID(), msg))
return p2p.Send(self.rw, uint64(code), msg) return p2p.Send(self.rw, code, msg)
}
func (self *Peer) DisconnectHook(f func(error)) {
typ := reflect.TypeOf(&Disconnect{})
self.handlers[typ] = append(self.handlers[typ], func(e interface{}) error {
f(e.(error))
return nil
})
} }
// handleIncoming(code) // handleIncoming(code)
@ -292,79 +237,72 @@ func (self *Peer) DisconnectHook(f func(error)) {
// if this returns an error the loop returns and the peer is disconnected with the error // if this returns an error the loop returns and the peer is disconnected with the error
// checks message size, out-of-range message codes, handles decoding with reflection, // checks message size, out-of-range message codes, handles decoding with reflection,
// call handlers as callback onside // call handlers as callback onside
func (self *Peer) handleIncoming() (interface{}, error) { func (self *Peer) handleIncoming(handle func(msg interface{}) error) error {
msg, err := self.rw.ReadMsg() msg, err := self.rw.ReadMsg()
if err != nil { if err != nil {
return nil, err return err
} }
log.Trace(fmt.Sprintf("<= %v", msg)) log.Trace(fmt.Sprintf("<= %v", msg))
// make sure that the payload has been fully consumed // make sure that the payload has been fully consumed
defer msg.Discard() defer msg.Discard()
if msg.Size > uint32(self.ct.MaxMsgSize) { if msg.Size > self.spec.MaxMsgSize {
return nil, errorf(ErrMsgTooLong, "%v > %v", msg.Size, self.ct.MaxMsgSize) return errorf(ErrMsgTooLong, "%v > %v", msg.Size, self.spec.MaxMsgSize)
} }
// check if the message code is correct val, ok := self.spec.NewMsg(msg.Code)
maxMsgCode := uint(len(self.ct.messages)) if !ok {
if msg.Code >= uint64(maxMsgCode) { return errorf(ErrInvalidMsgCode, "%v", msg.Code)
return nil, errorf(ErrInvalidMsgCode, "%v (>=%v)", msg.Code, maxMsgCode)
} }
if err := msg.Decode(val); err != nil {
// it is safe to be unsafe here return errorf(ErrDecode, "<= %v: %v", msg, err)
typ := self.ct.codes[msg.Code]
val := reflect.New(typ)
req := val.Elem()
req.Set(reflect.Zero(typ))
if err := msg.Decode(val.Interface()); err != nil {
return nil, errorf(ErrDecode, "<= %v: %v", msg, err)
} }
log.Trace(fmt.Sprintf("<= %v FROM %v %v %v", msg, self.ID(), req, typ)) log.Trace(fmt.Sprintf("<= %v FROM %v %T %v", msg, self.ID(), val, val))
// call the registered handler callbacks // call the registered handler callbacks
// a registered callback take the decoded message as argument as an interface // a registered callback take the decoded message as argument as an interface
// which the handler is supposed to cast to the appropriate type // which the handler is supposed to cast to the appropriate type
// it is entirely safe not to check the cast in the handler since the handler is // it is entirely safe not to check the cast in the handler since the handler is
// chosen based on the proper type in the first place // chosen based on the proper type in the first place
handlers := self.handlers[typ] if err := handle(val); err != nil {
if len(handlers) == 0 { return errorf(ErrHandler, "(msg code %v): %v", msg.Code, err)
log.Trace(fmt.Sprintf("no handler (msg code %v)", msg.Code))
// return nil, errorf(ErrNoHandler, "(msg code %v)", msg.Code)
} else {
for i, f := range handlers {
log.Trace(fmt.Sprintf("handler %v for %v", i, typ))
err = f(req.Interface())
if err != nil {
return nil, errorf(ErrHandler, "(msg code %v): %v", msg.Code, err)
}
}
} }
return req.Interface(), nil return nil
} }
// Handshake initiates a handshake on the peer connection // Handshake initiates a handshake on the peer connection
// * the argument is the local handshake to be sent to the remote peer // * the argument is the local handshake to be sent to the remote peer
// * expects a remote handshake back of the same type // * expects a remote handshake back of the same type
// returns the remote hs and an error // returns the remote hs and an error
func (self *Peer) Handshake(hs interface{}) (interface{}, error) { func (self *Peer) Handshake(ctx context.Context, hs interface{}) (interface{}, error) {
typ := reflect.TypeOf(hs) if _, ok := self.spec.GetCode(hs); !ok {
_, found := self.ct.messages[typ] return nil, errorf(ErrHandshake, "unknown handshake message type: %T", hs)
if !found {
return nil, errorf(ErrLocalHandshake, "unknown handshake message type: %v", typ)
} }
errc := make(chan error) errc := make(chan error, 2)
go func() { go func() {
err := self.Send(hs) if err := self.Send(hs); err != nil {
if err != nil { errc <- errorf(ErrHandshake, "cannot send: %v", err)
err = errorf(ErrLocalHandshake, "cannot send: %v", err)
} }
errc <- err
}() }()
// receiving and validating remote handshake, expect code hsc := make(chan interface{})
rhs, err := self.handleIncoming() go func() {
if err != nil { var rhs interface{}
return nil, errorf(ErrRemoteHandshake, "'%v': %v", self.ct.Name, err) err := self.handleIncoming(func(msg interface{}) error {
rhs = msg
return nil
})
if err != nil {
errc <- err
return
}
hsc <- rhs
}()
select {
case rhs := <-hsc:
return rhs, nil
case <-ctx.Done():
return nil, ctx.Err()
case err := <-errc:
return nil, err
} }
err = <-errc
return rhs, err
} }

View file

@ -1,6 +1,8 @@
package protocols package protocols
import ( import (
"context"
"errors"
"fmt" "fmt"
"os" "os"
"testing" "testing"
@ -13,7 +15,7 @@ import (
) )
func init() { func init() {
log.Root().SetHandler(log.LvlFilterHandler(log.LvlError, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
} }
// handshake message type // handshake message type
@ -55,28 +57,26 @@ const networkId = "420"
// newProtocol sets up a protocol // newProtocol sets up a protocol
// the run function here demonstrates a typical protocol using peerPool, handshake // the run function here demonstrates a typical protocol using peerPool, handshake
// and messages registered to handlers // and messages registered to handlers
func newProtocol(pp *p2ptest.TestPeerPool) adapters.RunProtocol { func newProtocol(pp *p2ptest.TestPeerPool) func(*p2p.Peer, p2p.MsgReadWriter) error {
ct := NewCodeMap("test", 42, 1024, &protoHandshake{}, &hs0{}, &kill{}, &drop{}) spec := &Spec{
Name: "test",
Version: 42,
MaxMsgSize: 10 * 1024,
Messages: []interface{}{
protoHandshake{},
hs0{},
kill{},
drop{},
},
}
return func(p *p2p.Peer, rw p2p.MsgReadWriter) error { return func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
peer := NewPeer(p, ct, rw) peer := NewPeer(p, rw, spec)
// demonstrates use of peerPool, killing another peer connection as a response to a message
peer.Register(&kill{}, func(msg interface{}) error {
id := msg.(*kill).C
pp.Get(id).Drop(fmt.Errorf("killed"))
log.Trace(fmt.Sprintf("id %v killed", id))
return nil
})
// for testing we can trigger self induced disconnect upon receiving drop message
peer.Register(&drop{}, func(msg interface{}) error {
log.Trace("dropped")
return fmt.Errorf("dropped")
})
// initiate one-off protohandshake and check validity // initiate one-off protohandshake and check validity
phs := &protoHandshake{ct.Version, networkId} ctx, cancel := context.WithTimeout(context.Background(), time.Second)
hs, err := peer.Handshake(phs) defer cancel()
phs := &protoHandshake{42, networkId}
hs, err := peer.Handshake(ctx, phs)
if err != nil { if err != nil {
return err return err
} }
@ -88,7 +88,7 @@ func newProtocol(pp *p2ptest.TestPeerPool) adapters.RunProtocol {
lhs := &hs0{42} lhs := &hs0{42}
// module handshake demonstrating a simple repeatable exchange of same-type message // module handshake demonstrating a simple repeatable exchange of same-type message
hs, err = peer.Handshake(lhs) hs, err = peer.Handshake(ctx, lhs)
if err != nil { if err != nil {
return err return err
} }
@ -97,19 +97,40 @@ func newProtocol(pp *p2ptest.TestPeerPool) adapters.RunProtocol {
return fmt.Errorf("handshake mismatch remote %v > local %v", rmhs.C, lhs.C) return fmt.Errorf("handshake mismatch remote %v > local %v", rmhs.C, lhs.C)
} }
peer.Register(lhs, func(msg interface{}) error { handle := func(msg interface{}) error {
rhs := msg.(*hs0) switch msg := msg.(type) {
if rhs.C > lhs.C {
return fmt.Errorf("handshake mismatch remote %v > local %v", rhs.C, lhs.C) case *protoHandshake:
return errors.New("duplicate handshake")
case *hs0:
rhs := msg
if rhs.C > lhs.C {
return fmt.Errorf("handshake mismatch remote %v > local %v", rhs.C, lhs.C)
}
lhs.C += rhs.C
return peer.Send(lhs)
case *kill:
// demonstrates use of peerPool, killing another peer connection as a response to a message
id := msg.C
pp.Get(id).Drop(errors.New("killed"))
log.Trace(fmt.Sprintf("id %v killed", id))
return nil
case *drop:
// for testing we can trigger self induced disconnect upon receiving drop message
return errors.New("dropped")
default:
return fmt.Errorf("unknown message type: %T", msg)
} }
lhs.C += rhs.C }
return peer.Send(lhs)
})
log.Trace(fmt.Sprintf("adding peer %v", peer)) log.Trace(fmt.Sprintf("adding peer %v", peer))
pp.Add(peer) pp.Add(peer)
defer pp.Remove(peer) defer pp.Remove(peer)
err = peer.Run() err = peer.Run(handle)
log.Trace(fmt.Sprintf("peer %v protocol quitting: %v", peer, err)) log.Trace(fmt.Sprintf("peer %v protocol quitting: %v", peer, err))
return err return err

View file

@ -131,7 +131,7 @@ type Config struct {
// If Dialer is set to a non-nil value, the given Dialer // If Dialer is set to a non-nil value, the given Dialer
// is used to dial outbound peer connections. // is used to dial outbound peer connections.
Dialer *net.Dialer `toml:"-"` Dialer NodeDialer `toml:"-"`
// If NoDial is true, the server will not dial any peers. // If NoDial is true, the server will not dial any peers.
NoDial bool `toml:",omitempty"` NoDial bool `toml:",omitempty"`
@ -141,23 +141,8 @@ type Config struct {
EnableMsgEvents bool EnableMsgEvents bool
} }
type Server interface {
Start() error
Stop() error
AddPeer(node *discover.Node)
RemovePeer(node *discover.Node)
SubscribeEvents(ch chan *PeerEvent) event.Subscription
PeerCount() int
NodeInfo() *NodeInfo
PeersInfo() []*PeerInfo
}
func NewServer(conf Config) Server {
return &server{Config: conf}
}
// Server manages all peer connections. // Server manages all peer connections.
type server struct { type Server struct {
// Config fields may not be modified while the server is running. // Config fields may not be modified while the server is running.
Config Config
@ -266,7 +251,7 @@ func (c *conn) is(f connFlag) bool {
} }
// Peers returns all connected peers. // Peers returns all connected peers.
func (srv *server) Peers() []*Peer { func (srv *Server) Peers() []*Peer {
var ps []*Peer var ps []*Peer
select { select {
// Note: We'd love to put this function into a variable but // Note: We'd love to put this function into a variable but
@ -284,7 +269,7 @@ func (srv *server) Peers() []*Peer {
} }
// PeerCount returns the number of connected peers. // PeerCount returns the number of connected peers.
func (srv *server) PeerCount() int { func (srv *Server) PeerCount() int {
var count int var count int
select { select {
case srv.peerOp <- func(ps map[discover.NodeID]*Peer) { count = len(ps) }: case srv.peerOp <- func(ps map[discover.NodeID]*Peer) { count = len(ps) }:
@ -297,7 +282,7 @@ func (srv *server) PeerCount() int {
// AddPeer connects to the given node and maintains the connection until the // AddPeer connects to the given node and maintains the connection until the
// server is shut down. If the connection fails for any reason, the server will // server is shut down. If the connection fails for any reason, the server will
// attempt to reconnect the peer. // attempt to reconnect the peer.
func (srv *server) AddPeer(node *discover.Node) { func (srv *Server) AddPeer(node *discover.Node) {
select { select {
case srv.addstatic <- node: case srv.addstatic <- node:
case <-srv.quit: case <-srv.quit:
@ -305,7 +290,7 @@ func (srv *server) AddPeer(node *discover.Node) {
} }
// RemovePeer disconnects from the given node // RemovePeer disconnects from the given node
func (srv *server) RemovePeer(node *discover.Node) { func (srv *Server) RemovePeer(node *discover.Node) {
select { select {
case srv.removestatic <- node: case srv.removestatic <- node:
case <-srv.quit: case <-srv.quit:
@ -313,12 +298,12 @@ func (srv *server) RemovePeer(node *discover.Node) {
} }
// SubscribePeers subscribes the given channel to peer events // SubscribePeers subscribes the given channel to peer events
func (srv *server) SubscribeEvents(ch chan *PeerEvent) event.Subscription { func (srv *Server) SubscribeEvents(ch chan *PeerEvent) event.Subscription {
return srv.peerFeed.Subscribe(ch) return srv.peerFeed.Subscribe(ch)
} }
// Self returns the local node's endpoint information. // Self returns the local node's endpoint information.
func (srv *server) Self() *discover.Node { func (srv *Server) Self() *discover.Node {
srv.lock.Lock() srv.lock.Lock()
defer srv.lock.Unlock() defer srv.lock.Unlock()
@ -328,7 +313,7 @@ func (srv *server) Self() *discover.Node {
return srv.makeSelf(srv.listener, srv.ntab) return srv.makeSelf(srv.listener, srv.ntab)
} }
func (srv *server) makeSelf(listener net.Listener, ntab discoverTable) *discover.Node { func (srv *Server) makeSelf(listener net.Listener, ntab discoverTable) *discover.Node {
// If the server's not running, return an empty node. // If the server's not running, return an empty node.
// If the node is running but discovery is off, manually assemble the node infos. // If the node is running but discovery is off, manually assemble the node infos.
if ntab == nil { if ntab == nil {
@ -350,7 +335,7 @@ func (srv *server) makeSelf(listener net.Listener, ntab discoverTable) *discover
// Stop terminates the server and all active peer connections. // Stop terminates the server and all active peer connections.
// It blocks until all active connections have been closed. // It blocks until all active connections have been closed.
func (srv *server) Stop() error { func (srv *Server) Stop() error {
srv.lock.Lock() srv.lock.Lock()
defer srv.lock.Unlock() defer srv.lock.Unlock()
if !srv.running { if !srv.running {
@ -368,7 +353,7 @@ func (srv *server) Stop() error {
// Start starts running the server. // Start starts running the server.
// Servers can not be re-used after stopping. // Servers can not be re-used after stopping.
func (srv *server) Start() (err error) { func (srv *Server) Start() (err error) {
srv.lock.Lock() srv.lock.Lock()
defer srv.lock.Unlock() defer srv.lock.Unlock()
if srv.running { if srv.running {
@ -385,7 +370,7 @@ func (srv *server) Start() (err error) {
srv.newTransport = newRLPX srv.newTransport = newRLPX
} }
if srv.Dialer == nil { if srv.Dialer == nil {
srv.Dialer = &net.Dialer{Timeout: defaultDialTimeout} srv.Dialer = TCPDialer{&net.Dialer{Timeout: defaultDialTimeout}}
} }
srv.quit = make(chan struct{}) srv.quit = make(chan struct{})
srv.addpeer = make(chan *conn) srv.addpeer = make(chan *conn)
@ -446,7 +431,7 @@ func (srv *server) Start() (err error) {
return nil return nil
} }
func (srv *server) startListening() error { func (srv *Server) startListening() error {
// Launch the TCP listener. // Launch the TCP listener.
listener, err := net.Listen("tcp", srv.ListenAddr) listener, err := net.Listen("tcp", srv.ListenAddr)
if err != nil { if err != nil {
@ -475,7 +460,7 @@ type dialer interface {
removeStatic(*discover.Node) removeStatic(*discover.Node)
} }
func (srv *server) run(dialstate dialer) { func (srv *Server) run(dialstate dialer) {
defer srv.loopWG.Done() defer srv.loopWG.Done()
var ( var (
peers = make(map[discover.NodeID]*Peer) peers = make(map[discover.NodeID]*Peer)
@ -616,7 +601,7 @@ running:
} }
} }
func (srv *server) protoHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn) error { func (srv *Server) protoHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn) error {
// Drop connections with no matching protocols. // Drop connections with no matching protocols.
if len(srv.Protocols) > 0 && countMatchingProtocols(srv.Protocols, c.caps) == 0 { if len(srv.Protocols) > 0 && countMatchingProtocols(srv.Protocols, c.caps) == 0 {
return DiscUselessPeer return DiscUselessPeer
@ -626,7 +611,7 @@ func (srv *server) protoHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn
return srv.encHandshakeChecks(peers, c) return srv.encHandshakeChecks(peers, c)
} }
func (srv *server) encHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn) error { func (srv *Server) encHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn) error {
switch { switch {
case !c.is(trustedConn|staticDialedConn) && len(peers) >= srv.MaxPeers: case !c.is(trustedConn|staticDialedConn) && len(peers) >= srv.MaxPeers:
return DiscTooManyPeers return DiscTooManyPeers
@ -645,7 +630,7 @@ type tempError interface {
// listenLoop runs in its own goroutine and accepts // listenLoop runs in its own goroutine and accepts
// inbound connections. // inbound connections.
func (srv *server) listenLoop() { func (srv *Server) listenLoop() {
defer srv.loopWG.Done() defer srv.loopWG.Done()
log.Info("RLPx listener up", "self", srv.makeSelf(srv.listener, srv.ntab)) log.Info("RLPx listener up", "self", srv.makeSelf(srv.listener, srv.ntab))
@ -697,7 +682,7 @@ func (srv *server) listenLoop() {
// Spawn the handler. It will give the slot back when the connection // Spawn the handler. It will give the slot back when the connection
// has been established. // has been established.
go func() { go func() {
srv.setupConn(fd, inboundConn, nil) srv.SetupConn(fd, inboundConn, nil)
slots <- struct{}{} slots <- struct{}{}
}() }()
} }
@ -706,7 +691,7 @@ func (srv *server) listenLoop() {
// setupConn runs the handshakes and attempts to add the connection // setupConn runs the handshakes and attempts to add the connection
// as a peer. It returns when the connection has been added as a peer // as a peer. It returns when the connection has been added as a peer
// or the handshakes have failed. // or the handshakes have failed.
func (srv *server) setupConn(fd net.Conn, flags connFlag, dialDest *discover.Node) { func (srv *Server) SetupConn(fd net.Conn, flags connFlag, dialDest *discover.Node) {
// Prevent leftover pending conns from entering the handshake. // Prevent leftover pending conns from entering the handshake.
srv.lock.Lock() srv.lock.Lock()
running := srv.running running := srv.running
@ -766,7 +751,7 @@ func truncateName(s string) string {
// checkpoint sends the conn to run, which performs the // checkpoint sends the conn to run, which performs the
// post-handshake checks for the stage (posthandshake, addpeer). // post-handshake checks for the stage (posthandshake, addpeer).
func (srv *server) checkpoint(c *conn, stage chan<- *conn) error { func (srv *Server) checkpoint(c *conn, stage chan<- *conn) error {
select { select {
case stage <- c: case stage <- c:
case <-srv.quit: case <-srv.quit:
@ -783,7 +768,7 @@ func (srv *server) checkpoint(c *conn, stage chan<- *conn) error {
// runPeer runs in its own goroutine for each peer. // runPeer runs in its own goroutine for each peer.
// it waits until the Peer logic returns and removes // it waits until the Peer logic returns and removes
// the peer. // the peer.
func (srv *server) runPeer(p *Peer) { func (srv *Server) runPeer(p *Peer) {
if srv.newPeerHook != nil { if srv.newPeerHook != nil {
srv.newPeerHook(p) srv.newPeerHook(p)
} }
@ -824,7 +809,7 @@ type NodeInfo struct {
} }
// NodeInfo gathers and returns a collection of metadata known about the host. // NodeInfo gathers and returns a collection of metadata known about the host.
func (srv *server) NodeInfo() *NodeInfo { func (srv *Server) NodeInfo() *NodeInfo {
node := srv.Self() node := srv.Self()
// Gather and assemble the generic node infos // Gather and assemble the generic node infos
@ -853,7 +838,7 @@ func (srv *server) NodeInfo() *NodeInfo {
} }
// PeersInfo returns an array of metadata objects describing connected peers. // PeersInfo returns an array of metadata objects describing connected peers.
func (srv *server) PeersInfo() []*PeerInfo { func (srv *Server) PeersInfo() []*PeerInfo {
// Gather all the generic and sub-protocol specific infos // Gather all the generic and sub-protocol specific infos
infos := make([]*PeerInfo, 0, srv.PeerCount()) infos := make([]*PeerInfo, 0, srv.PeerCount())
for _, peer := range srv.Peers() { for _, peer := range srv.Peers() {
@ -872,6 +857,6 @@ func (srv *server) PeersInfo() []*PeerInfo {
return infos return infos
} }
func (srv *server) DiscV5() *discv5.Network { func (srv *Server) DiscV5() *discv5.Network {
return srv.discV5 return srv.discV5
} }

View file

@ -72,7 +72,7 @@ func startTestServer(t *testing.T, id discover.NodeID, pf func(*Peer)) *Server {
ListenAddr: "127.0.0.1:0", ListenAddr: "127.0.0.1:0",
PrivateKey: newkey(), PrivateKey: newkey(),
} }
server := &server{ server := &Server{
Config: config, Config: config,
newPeerHook: pf, newPeerHook: pf,
newTransport: func(fd net.Conn) transport { return newTestTransport(id, fd) }, newTransport: func(fd net.Conn) transport { return newTestTransport(id, fd) },
@ -201,7 +201,7 @@ func TestServerTaskScheduling(t *testing.T) {
// The Server in this test isn't actually running // The Server in this test isn't actually running
// because we're only interested in what run does. // because we're only interested in what run does.
srv := &server{ srv := &Server{
Config: Config{MaxPeers: 10}, Config: Config{MaxPeers: 10},
quit: make(chan struct{}), quit: make(chan struct{}),
ntab: fakeTable{}, ntab: fakeTable{},
@ -246,7 +246,7 @@ func TestServerManyTasks(t *testing.T) {
} }
var ( var (
srv = &server{quit: make(chan struct{}), ntab: fakeTable{}, running: true} srv = &Server{quit: make(chan struct{}), ntab: fakeTable{}, running: true}
done = make(chan *testTask) done = make(chan *testTask)
start, end = 0, 0 start, end = 0, 0
) )
@ -317,7 +317,7 @@ func (t *testTask) Do(srv *Server) {
// at capacity. Trusted connections should still be accepted. // at capacity. Trusted connections should still be accepted.
func TestServerAtCap(t *testing.T) { func TestServerAtCap(t *testing.T) {
trustedID := randomID() trustedID := randomID()
srv := &server{ srv := &Server{
Config: Config{ Config: Config{
PrivateKey: newkey(), PrivateKey: newkey(),
MaxPeers: 10, MaxPeers: 10,
@ -420,7 +420,7 @@ func TestServerSetupConn(t *testing.T) {
} }
for i, test := range tests { for i, test := range tests {
srv := &server{ srv := &Server{
Config: Config{ Config: Config{
PrivateKey: srvkey, PrivateKey: srvkey,
MaxPeers: 10, MaxPeers: 10,
@ -435,7 +435,7 @@ func TestServerSetupConn(t *testing.T) {
} }
} }
p1, _ := net.Pipe() p1, _ := net.Pipe()
srv.setupConn(p1, test.flags, test.dialDest) srv.SetupConn(p1, test.flags, test.dialDest)
if !reflect.DeepEqual(test.tt.closeErr, test.wantCloseErr) { if !reflect.DeepEqual(test.tt.closeErr, test.wantCloseErr) {
t.Errorf("test %d: close error mismatch: got %q, want %q", i, test.tt.closeErr, test.wantCloseErr) t.Errorf("test %d: close error mismatch: got %q, want %q", i, test.tt.closeErr, test.wantCloseErr)
} }

View file

@ -9,6 +9,7 @@ import (
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings"
"github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/reexec"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
@ -81,12 +82,16 @@ func (n *DockerNode) dockerCommand() *exec.Cmd {
return exec.Command( return exec.Command(
"sh", "-c", "sh", "-c",
fmt.Sprintf( fmt.Sprintf(
`exec docker run --interactive --env _P2P_NODE_CONFIG="${_P2P_NODE_CONFIG}" %s p2p-node %s %s`, `exec docker run --interactive --env _P2P_NODE_CONFIG="${_P2P_NODE_CONFIG}" --env _P2P_NODE_KEY="${_P2P_NODE_KEY}" %s p2p-node %s %s`,
dockerImage, n.Config.Node.Service, n.ID.String(), dockerImage, strings.Join(n.Services, " "), n.ID.String(),
), ),
) )
} }
func (n *DockerNode) GetService(name string) node.Service {
return nil
}
// dockerImage is the name of the docker image // dockerImage is the name of the docker image
const dockerImage = "p2p-node" const dockerImage = "p2p-node"

View file

@ -63,7 +63,7 @@ func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) {
Node: config, Node: config,
} }
conf.Stack.DataDir = filepath.Join(dir, "data") conf.Stack.DataDir = filepath.Join(dir, "data")
conf.Stack.P2P.EnableMsgEvents = true conf.Stack.P2P.EnableMsgEvents = false
conf.Stack.P2P.NoDiscovery = true conf.Stack.P2P.NoDiscovery = true
conf.Stack.P2P.NAT = nil conf.Stack.P2P.NAT = nil
@ -88,11 +88,12 @@ func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) {
// (so for example we can run the node in a remote Docker container and // (so for example we can run the node in a remote Docker container and
// still communicate with it). // still communicate with it).
type ExecNode struct { type ExecNode struct {
ID *NodeId ID *NodeId
Dir string Dir string
Config *execNodeConfig Config *execNodeConfig
Cmd *exec.Cmd Cmd *exec.Cmd
Info *p2p.NodeInfo Info *p2p.NodeInfo
Services []string
client *rpc.Client client *rpc.Client
rpcMux *rpcMux rpcMux *rpcMux
@ -164,13 +165,17 @@ func (n *ExecNode) Start(snapshot []byte) (err error) {
return nil return nil
} }
func (n *ExecNode) GetService(name string) node.Service {
return nil
}
// execCommand returns a command which runs the node locally by exec'ing // execCommand returns a command which runs the node locally by exec'ing
// the current binary but setting argv[0] to "p2p-node" so that the child // the current binary but setting argv[0] to "p2p-node" so that the child
// runs execP2PNode // runs execP2PNode
func (n *ExecNode) execCommand() *exec.Cmd { func (n *ExecNode) execCommand() *exec.Cmd {
return &exec.Cmd{ return &exec.Cmd{
Path: reexec.Self(), Path: reexec.Self(),
Args: []string{"p2p-node", n.Config.Node.Service, n.ID.String()}, Args: []string{"p2p-node", n.Services[0], n.ID.String()},
} }
} }
@ -278,7 +283,7 @@ func execP2PNode() {
if !exists { if !exists {
log.Crit(fmt.Sprintf("unknown node service %q", serviceName)) log.Crit(fmt.Sprintf("unknown node service %q", serviceName))
} }
service := serviceFunc(id, conf.Snapshot) services := serviceFunc(id, conf.Snapshot)
// use explicit IP address in ListenAddr so that Enode URL is usable // use explicit IP address in ListenAddr so that Enode URL is usable
if strings.HasPrefix(conf.Stack.P2P.ListenAddr, ":") { if strings.HasPrefix(conf.Stack.P2P.ListenAddr, ":") {
@ -295,7 +300,7 @@ func execP2PNode() {
} }
// start the devp2p stack // start the devp2p stack
stack, err := startP2PNode(&conf.Stack, service) stack, err := startP2PNode(&conf.Stack, services)
if err != nil { if err != nil {
log.Crit("error starting p2p node", "err", err) log.Crit("error starting p2p node", "err", err)
} }
@ -321,17 +326,20 @@ func execP2PNode() {
stack.Wait() stack.Wait()
} }
func startP2PNode(conf *node.Config, service node.Service) (*node.Node, error) { func startP2PNode(conf *node.Config, services []node.Service) (*node.Node, error) {
stack, err := node.New(conf) stack, err := node.New(conf)
if err != nil { if err != nil {
return nil, err return nil, err
} }
constructor := func(ctx *node.ServiceContext) (node.Service, error) { for _, svc := range services {
return &snapshotService{service}, nil constructor := func(ctx *node.ServiceContext) (node.Service, error) {
} return &snapshotService{svc}, nil
if err := stack.Register(constructor); err != nil { }
return nil, err if err := stack.Register(constructor); err != nil {
return nil, err
}
} }
if err := stack.Start(); err != nil { if err := stack.Start(); err != nil {
return nil, err return nil, err
} }

View file

@ -17,10 +17,11 @@
package adapters package adapters
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"math"
"net" "net"
"reflect"
"sync" "sync"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
@ -36,7 +37,6 @@ import (
type SimAdapter struct { type SimAdapter struct {
mtx sync.RWMutex mtx sync.RWMutex
nodes map[discover.NodeID]*SimNode nodes map[discover.NodeID]*SimNode
services map[string]ServiceFunc
} }
// NewSimAdapter creates a SimAdapter which is capable of running in-memory // NewSimAdapter creates a SimAdapter which is capable of running in-memory
@ -45,7 +45,6 @@ type SimAdapter struct {
func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter { func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter {
return &SimAdapter{ return &SimAdapter{
nodes: make(map[discover.NodeID]*SimNode), nodes: make(map[discover.NodeID]*SimNode),
services: services,
} }
} }
@ -56,6 +55,8 @@ func (s *SimAdapter) Name() string {
// NewNode returns a new SimNode using the given config // NewNode returns a new SimNode using the given config
func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) { func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
var nodeprotos []p2p.Protocol
s.mtx.Lock() s.mtx.Lock()
defer s.mtx.Unlock() defer s.mtx.Unlock()
@ -66,6 +67,7 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
} }
// check the service is valid and initialize it // check the service is valid and initialize it
/*
serviceFunc, exists := s.services[config.Service] serviceFunc, exists := s.services[config.Service]
if !exists { if !exists {
return nil, fmt.Errorf("unknown node service %q", config.Service) return nil, fmt.Errorf("unknown node service %q", config.Service)
@ -73,13 +75,60 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
node := &SimNode{ node := &SimNode{
Id: id, Id: id,
config: config,
adapter: s, adapter: s,
serviceFunc: serviceFunc, serviceFunc: serviceFunc,
peers: make(map[discover.NodeID]MsgReadWriteCloser), */
dropPeers: make(chan struct{}), //serviceFunc, exists := s.services[config.Service]
//if !exists {
// return nil, fmt.Errorf("unknown node service %q", config.Service)
//}
//service := serviceFunc(id)
_, err := node.New(&node.Config{
P2P: p2p.Config{
PrivateKey: config.PrivateKey,
MaxPeers: math.MaxInt32,
NoDiscovery: true,
Protocols: nodeprotos,
Dialer: s,
EnableMsgEvents: true,
},
})
if err != nil {
return nil, err
} }
s.nodes[id.NodeID] = node
return node, nil for _, service := range serviceFuncs[config.Service](id, nil) {
for _, proto := range service.Protocols() {
nodeprotos = append(nodeprotos, proto)
}
}
simnode := &SimNode{
Id: id,
serviceFunc: serviceFuncs[config.Service],
adapter: s,
config: config,
running: []node.Service{},
}
s.nodes[id.NodeID] = simnode
return simnode, nil
}
func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) {
node, ok := s.GetNode(dest.ID)
if !ok {
return nil, fmt.Errorf("unknown node: %s", dest.ID)
}
srv := node.Server()
if srv == nil {
return nil, fmt.Errorf("node not running: %s", dest.ID)
}
pipe1, pipe2 := net.Pipe()
go srv.SetupConn(pipe1, 0, nil)
return pipe2, nil
} }
// GetNode returns the node with the given ID if it exists // GetNode returns the node with the given ID if it exists
@ -90,14 +139,6 @@ func (s *SimAdapter) GetNode(id discover.NodeID) (*SimNode, bool) {
return node, ok return node, ok
} }
// MsgReadWriteCloser wraps a MsgReadWriter with the addition of a Close method
// so we can simulate the closing of a p2p connection (which usually happens by
/// closing the underlying TCP connection)
type MsgReadWriteCloser interface {
p2p.MsgReadWriter
Close() error
}
// SimNode is an in-memory node which connects to other SimNodes using an // SimNode is an in-memory node which connects to other SimNodes using an
// in-memory p2p.MsgReadWriter pipe, running an underlying service protocol // in-memory p2p.MsgReadWriter pipe, running an underlying service protocol
// directly over that pipe. // directly over that pipe.
@ -107,17 +148,13 @@ type MsgReadWriteCloser interface {
type SimNode struct { type SimNode struct {
lock sync.RWMutex lock sync.RWMutex
Id *NodeId Id *NodeId
config *NodeConfig
adapter *SimAdapter adapter *SimAdapter
running node.Service serviceFunc ServiceFunc
serviceFunc ServiceFunc node *node.Node
peers map[discover.NodeID]MsgReadWriteCloser
peerFeed event.Feed
client *rpc.Client client *rpc.Client
rpcMux *rpcMux rpcMux *rpcMux
running []node.Service
// dropPeers is used to force peer disconnects when
// the node is stopped
dropPeers chan struct{}
} }
// Addr returns the node's discovery address // Addr returns the node's discovery address
@ -127,7 +164,7 @@ func (self *SimNode) Addr() []byte {
// Node returns a discover.Node representing the SimNode // Node returns a discover.Node representing the SimNode
func (self *SimNode) Node() *discover.Node { func (self *SimNode) Node() *discover.Node {
return discover.NewNode(self.Id.NodeID, nil, 0, 0) return discover.NewNode(self.Id.NodeID, net.IP{127, 0, 0, 1}, 30303, 30303)
} }
// Client returns an rpc.Client which can be used to communicate with the // Client returns an rpc.Client which can be used to communicate with the
@ -154,82 +191,68 @@ func (self *SimNode) ServeRPC(conn net.Conn) error {
return nil return nil
} }
// Start initializes the service, starts the RPC handler and then starts // Snapshot creates a snapshot of the service state by calling the
// the service // simulation_snapshot RPC method
func (self *SimNode) Start(snapshot []byte) error { func (self *SimNode) Snapshot() ([]byte, error) {
service := self.serviceFunc(self.Id, snapshot) self.lock.Lock()
defer self.lock.Unlock()
if self.client == nil {
return nil, errors.New("RPC not started")
}
var snapshot []byte
return snapshot, self.client.Call(&snapshot, "simulation_snapshot")
}
// for simplicity, only support single protocol services (simulating // Start starts the RPC handler and the underlying service
// multiple protocols on the same peer is extra effort, and we don't func (self *SimNode) Start(snapshot []byte) error {
// currently run any simulations which run multiple protocols) self.lock.Lock()
if len(service.Protocols()) != 1 { defer self.lock.Unlock()
return errors.New("service must have a single protocol") if self.node != nil {
return errors.New("node already started")
}
services := []node.ServiceConstructor{}
sf := self.serviceFunc(self.Id, snapshot)
for i, _ := range sf {
service := sf[i]
sc := func(ctx *node.ServiceContext) (node.Service, error) {
return service, nil
}
log.Debug(fmt.Sprintf("servicefunc yield: %v %p %p", reflect.TypeOf(sf[i]), sf[i], sc))
services = append(services, sc)
self.running = append(self.running, sf[i])
} }
self.dropPeers = make(chan struct{}) node, err := node.New(&node.Config{
if err := self.startRPC(service); err != nil { P2P: p2p.Config{
PrivateKey: self.config.PrivateKey,
MaxPeers: math.MaxInt32,
NoDiscovery: true,
Dialer: self.adapter,
EnableMsgEvents: false,
},
NoUSB: true,
})
if err != nil {
return err return err
} }
self.running = service
return service.Start(&simServer{self}) for _, service := range services {
} log.Debug(fmt.Sprintf("service %v", service))
if err := node.Register(service); err != nil {
// simServer wraps a SimNode but modifies the Start method signature so that return err
// it implements the p2p.Server interface (the Start method is never actually }
// called when using the SimAdapter)
type simServer struct {
*SimNode
}
func (s *simServer) Start() error {
return nil
}
// Stop stops the RPC handler, stops the underlying service and disconnects
// any currently connected peers
func (self *SimNode) Stop() error {
self.stopRPC()
close(self.dropPeers)
return self.running.Stop()
}
// Running returns whether or not the service is running
func (self *SimNode) Running() bool {
self.lock.Lock()
defer self.lock.Unlock()
return self.running != nil
}
// Service returns the running node.Service
func (self *SimNode) Service() node.Service {
return self.running
}
// startRPC starts an RPC server and connects to it using an in-process RPC
// client
func (self *SimNode) startRPC(service node.Service) error {
self.lock.Lock()
defer self.lock.Unlock()
if self.client != nil {
return errors.New("RPC already started")
} }
// add SimAdminAPI so that the network can call the if err := node.Start(); err != nil {
// AddPeer, RemovePeer and PeerEvents RPC methods return err
apis := append(service.APIs(), []rpc.API{ }
{
Namespace: "admin",
Version: "1.0",
Service: &SimAdminAPI{self},
},
}...)
// start the RPC handler handler, err := node.RPCHandler()
handler := rpc.NewServer() if err != nil {
for _, api := range apis { return err
if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
return fmt.Errorf("error registering RPC: %s", err)
}
} }
// create an in-process RPC multiplexer // create an in-process RPC multiplexer
@ -240,197 +263,61 @@ func (self *SimNode) startRPC(service node.Service) error {
// create an in-process RPC client // create an in-process RPC client
self.client = self.rpcMux.Client() self.client = self.rpcMux.Client()
self.node = node
return nil return nil
} }
// stopRPC closes the node's RPC client func (self *SimNode) Stop() error {
func (self *SimNode) stopRPC() {
self.lock.Lock() self.lock.Lock()
defer self.lock.Unlock() defer self.lock.Unlock()
if self.client != nil { if self.node == nil {
self.client.Close() return nil
self.client = nil
self.rpcMux = nil
} }
if err := self.node.Stop(); err != nil {
return err
}
self.node = nil
return nil
} }
// RemovePeer removes the given node as a peer by looking up the corresponding // Service returns the underlying running node.Service matching the supplied servuce type
// p2p.MsgReadWriter pipe and closing it (which will cause both the local func (self *SimNode) Service(servicetype interface{}) node.Service {
// and peer Protocol.Run functions to exit)
func (self *SimNode) RemovePeer(peer *discover.Node) {
self.lock.Lock() self.lock.Lock()
defer self.lock.Unlock() defer self.lock.Unlock()
peerRW, exists := self.peers[peer.ID] typ := reflect.TypeOf(servicetype)
if !exists { for _, service := range self.running {
return if reflect.TypeOf(service) == typ {
return service
}
} }
peerRW.Close() return nil
delete(self.peers, peer.ID)
log.Trace(fmt.Sprintf("dropped peer %v", peer.ID))
} }
// AddPeer adds the given node as a peer by creating a p2p.MsgReadWriter pipe func (self *SimNode) Server() *p2p.Server {
// and running both the local and peer's Protocol.Run function over the pipe
func (self *SimNode) AddPeer(peer *discover.Node) {
self.lock.Lock() self.lock.Lock()
defer self.lock.Unlock() defer self.lock.Unlock()
if _, exists := self.peers[peer.ID]; exists { if self.node == nil {
return return nil
} }
peerNode, exists := self.adapter.GetNode(peer.ID) return self.node.Server()
if !exists {
panic(fmt.Sprintf("unknown peer: %s", peer.ID))
}
if !peerNode.Running() {
return
}
p1, p2 := p2p.MsgPipe()
localRW := p2p.NewMsgEventer(p1, &self.peerFeed, peer.ID)
peerRW := p2p.NewMsgEventer(p2, &self.peerFeed, self.Id.NodeID)
self.peers[peer.ID] = peerRW
peerNode.RunProtocol(self, peerRW)
self.RunProtocol(peerNode, localRW)
} }
// SubscribeEvents subscribes the given channel to p2p peer events
func (self *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription { func (self *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription {
return self.peerFeed.Subscribe(ch) srv := self.Server()
if srv == nil {
panic("node not running")
}
return srv.SubscribeEvents(ch)
} }
// PeerCount returns the number of currently connected peers
func (self *SimNode) PeerCount() int {
self.lock.Lock()
defer self.lock.Unlock()
return len(self.peers)
}
// NodeInfo returns information about the node
func (self *SimNode) NodeInfo() *p2p.NodeInfo { func (self *SimNode) NodeInfo() *p2p.NodeInfo {
self.lock.Lock() server := self.Server()
defer self.lock.Unlock() if server == nil {
info := &p2p.NodeInfo{ return &p2p.NodeInfo{
ID: self.Id.String(), ID: self.Id.String(),
Enode: self.Node().String(), Enode: self.Node().String(),
Protocols: make(map[string]interface{}),
}
if self.running != nil {
for _, proto := range self.running.Protocols() {
nodeInfo := interface{}("unknown")
if query := proto.NodeInfo; query != nil {
nodeInfo = proto.NodeInfo()
}
info.Protocols[proto.Name] = nodeInfo
} }
} }
return info return server.NodeInfo()
}
// PeersInfo is a stub so that SimNode implements p2p.Server
func (self *SimNode) PeersInfo() (info []*p2p.PeerInfo) {
return nil
}
// Snapshot creates a snapshot of the running service
func (self *SimNode) Snapshot() ([]byte, error) {
self.lock.Lock()
service := self.running
self.lock.Unlock()
if service == nil {
return nil, errors.New("service not running")
}
return SnapshotAPI{service}.Snapshot()
}
// RunProtocol runs the underlying service's protocol with the peer using the
// given MsgReadWriteCloser, emitting peer add / drop events for peer event
// subscribers
func (self *SimNode) RunProtocol(peer *SimNode, rw MsgReadWriteCloser) {
// close the rw if the node is stopped to disconnect the peer
go func() {
<-self.dropPeers
log.Trace("dropping peer", "self.id", self.Id, "peer.id", peer.Id)
rw.Close()
}()
id := peer.Id
log.Trace(fmt.Sprintf("protocol starting on peer %v (connection with %v)", self.Id, id))
protocol := self.running.Protocols()[0]
p := p2p.NewPeer(id.NodeID, id.Label(), []p2p.Cap{})
go func() {
// emit peer add event
self.peerFeed.Send(&p2p.PeerEvent{
Type: p2p.PeerEventTypeAdd,
Peer: id.NodeID,
})
// run the protocol
err := protocol.Run(p, rw)
// remove the peer
self.RemovePeer(peer.Node())
log.Trace(fmt.Sprintf("protocol quit on peer %v (connection with %v broken: %v)", self.Id, id, err))
// emit peer drop event
self.peerFeed.Send(&p2p.PeerEvent{
Type: p2p.PeerEventTypeDrop,
Peer: id.NodeID,
Error: err.Error(),
})
}()
}
// SimAdminAPI implements the AddPeer and RemovePeer RPC methods (API
// compatible with node.PrivateAdminAPI)
type SimAdminAPI struct {
*SimNode
}
func (api *SimAdminAPI) AddPeer(url string) (bool, error) {
node, err := discover.ParseNode(url)
if err != nil {
return false, fmt.Errorf("invalid enode: %v", err)
}
api.SimNode.AddPeer(node)
return true, nil
}
func (api *SimAdminAPI) RemovePeer(url string) (bool, error) {
node, err := discover.ParseNode(url)
if err != nil {
return false, fmt.Errorf("invalid enode: %v", err)
}
api.SimNode.RemovePeer(node)
return true, nil
}
// PeerEvents creates an RPC subscription which receives peer events from the
// underlying p2p.Server
func (api *SimAdminAPI) PeerEvents(ctx context.Context) (*rpc.Subscription, error) {
notifier, supported := rpc.NotifierFromContext(ctx)
if !supported {
return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
}
rpcSub := notifier.CreateSubscription()
go func() {
events := make(chan *p2p.PeerEvent)
sub := api.SubscribeEvents(events)
defer sub.Unsubscribe()
for {
select {
case event := <-events:
notifier.Notify(rpcSub.ID, event)
case <-sub.Err():
return
case <-rpcSub.Err():
return
case <-notifier.Closed():
return
}
}
}()
return rpcSub, nil
} }

View file

@ -0,0 +1,20 @@
package adapters
type SimStateStore struct {
m map[string][]byte
}
func (self *SimStateStore) Load(s string) ([]byte, error) {
return self.m[s], nil
}
func (self *SimStateStore) Save(s string, data []byte) error {
self.m[s] = data
return nil
}
func NewSimStateStore() *SimStateStore {
return &SimStateStore{
make(map[string][]byte),
}
}

View file

@ -127,9 +127,9 @@ type NodeConfig struct {
// Name is a human friendly name for the node like "node01" // Name is a human friendly name for the node like "node01"
Name string Name string
// Service is the name of the service which should be run when starting // Service is the name of the services which should be run when starting
// the node (for SimNodes it should be the name of a service contained // the node (for SimNodes it should be the names of services contained
// in SimAdapter.services, for other nodes it should be a service // in SimAdapter.services, for other nodes it should be services
// registered by calling the RegisterService function) // registered by calling the RegisterService function)
Service string Service string
} }
@ -140,7 +140,7 @@ type nodeConfigJSON struct {
Id string `json:"id"` Id string `json:"id"`
PrivateKey string `json:"private_key"` PrivateKey string `json:"private_key"`
Name string `json:"name"` Name string `json:"name"`
Service string `json:"service"` Service string `json:"service"`
} }
func (n *NodeConfig) MarshalJSON() ([]byte, error) { func (n *NodeConfig) MarshalJSON() ([]byte, error) {
@ -202,10 +202,11 @@ func RandomNodeConfig() *NodeConfig {
} }
// Services is a collection of services which can be run in a simulation // Services is a collection of services which can be run in a simulation
// it is mapped to strings representing TYPES of nodes
type Services map[string]ServiceFunc type Services map[string]ServiceFunc
// ServiceFunc returns a node.Service which can be used to boot devp2p nodes // ServiceFunc returns a node.Service which can be used to boot devp2p nodes
type ServiceFunc func(id *NodeId, snapshot []byte) node.Service type ServiceFunc func(id *NodeId, snapshot []byte) []node.Service
// serviceFuncs is a map of registered services which are used to boot devp2p // serviceFuncs is a map of registered services which are used to boot devp2p
// nodes // nodes

View file

@ -99,7 +99,7 @@ func (p *pingPongService) APIs() []rpc.API {
return nil return nil
} }
func (p *pingPongService) Start(server p2p.Server) error { func (p *pingPongService) Start(server *p2p.Server) error {
p.log.Info("ping-pong service starting") p.log.Info("ping-pong service starting")
return nil return nil
} }

View file

@ -59,6 +59,21 @@ func (c *Client) GetNetwork(networkID string) (*Network, error) {
return network, c.Get(fmt.Sprintf("/networks/%s", networkID), network) return network, c.Get(fmt.Sprintf("/networks/%s", networkID), network)
} }
// StartNetwork starts all existing nodes in a simulation network
func (c *Client) StartNetwork(networkID string) error {
return c.Post(fmt.Sprintf("/networks/%s/start", networkID), nil, nil)
}
// StopNetwork stops all existing nodes in a simulation network
func (c *Client) StopNetwork(networkID string) error {
return c.Post(fmt.Sprintf("/networks/%s/stop", networkID), nil, nil)
}
// DeleteNetwork stops and deletes a simulation network
func (c *Client) DeleteNetwork(networkID string) error {
return c.Delete(fmt.Sprintf("/networks/%s", networkID))
}
// CreateSnapshot creates a network snapshot // CreateSnapshot creates a network snapshot
func (c *Client) CreateSnapshot(networkID string) (*Snapshot, error) { func (c *Client) CreateSnapshot(networkID string) (*Snapshot, error) {
snap := &Snapshot{} snap := &Snapshot{}
@ -276,6 +291,9 @@ func NewServer(config *ServerConfig) *Server {
s.POST("/networks", s.CreateNetwork) s.POST("/networks", s.CreateNetwork)
s.GET("/networks", s.GetNetworks) s.GET("/networks", s.GetNetworks)
s.GET("/networks/:netid", s.GetNetwork) s.GET("/networks/:netid", s.GetNetwork)
s.POST("/networks/:netid/start", s.StartNetwork)
s.POST("/networks/:netid/stop", s.StopNetwork)
s.DELETE("/networks/:netid", s.DeleteNetwork)
s.GET("/networks/:netid/events", s.StreamNetworkEvents) s.GET("/networks/:netid/events", s.StreamNetworkEvents)
s.GET("/networks/:netid/snapshot", s.CreateSnapshot) s.GET("/networks/:netid/snapshot", s.CreateSnapshot)
s.POST("/networks/:netid/snapshot", s.LoadSnapshot) s.POST("/networks/:netid/snapshot", s.LoadSnapshot)
@ -341,6 +359,46 @@ func (s *Server) GetNetwork(w http.ResponseWriter, req *http.Request) {
s.JSON(w, http.StatusOK, network) s.JSON(w, http.StatusOK, network)
} }
// StartNetwork starts all nodes in a network
func (s *Server) StartNetwork(w http.ResponseWriter, req *http.Request) {
network := req.Context().Value("network").(*Network)
if err := network.StartAll(); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
// StopNetwork stops all nodes in a network
func (s *Server) StopNetwork(w http.ResponseWriter, req *http.Request) {
network := req.Context().Value("network").(*Network)
if err := network.StopAll(); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
// DeleteNetwork stops all nodes in a network and deletes it
func (s *Server) DeleteNetwork(w http.ResponseWriter, req *http.Request) {
network := req.Context().Value("network").(*Network)
if err := network.StopAll(); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
s.mtx.Lock()
delete(s.networks, network.Id)
s.mtx.Unlock()
w.WriteHeader(http.StatusOK)
}
//Get the info for a particular mocker //Get the info for a particular mocker
func (s *Server) GetMocker(w http.ResponseWriter, req *http.Request) { func (s *Server) GetMocker(w http.ResponseWriter, req *http.Request) {
m := make(map[string]string) m := make(map[string]string)

View file

@ -46,7 +46,7 @@ func (t *testService) APIs() []rpc.API {
}} }}
} }
func (t *testService) Start(server p2p.Server) error { func (t *testService) Start(server *p2p.Server) error {
return nil return nil
} }

View file

@ -260,6 +260,30 @@ func (self *Conn) nodesUp() error {
return nil return nil
} }
func (self *Network) StartAll() error {
for _, node := range self.Nodes {
if node.Up {
continue
}
if err := self.Start(node.ID()); err != nil {
return err
}
}
return nil
}
func (self *Network) StopAll() error {
for _, node := range self.Nodes {
if !node.Up {
continue
}
if err := self.Stop(node.ID()); err != nil {
return err
}
}
return nil
}
// Start(id) starts up the node (relevant only for instance with own p2p or remote) // Start(id) starts up the node (relevant only for instance with own p2p or remote)
func (self *Network) Start(id *adapters.NodeId) error { func (self *Network) Start(id *adapters.NodeId) error {
return self.startWithSnapshot(id, nil) return self.startWithSnapshot(id, nil)
@ -275,6 +299,7 @@ func (self *Network) startWithSnapshot(id *adapters.NodeId, snapshot []byte) err
} }
log.Trace(fmt.Sprintf("starting node %v: %v using %v", id, node.Up, self.nodeAdapter.Name())) log.Trace(fmt.Sprintf("starting node %v: %v using %v", id, node.Up, self.nodeAdapter.Name()))
if err := node.Start(snapshot); err != nil { if err := node.Start(snapshot); err != nil {
log.Warn(fmt.Sprintf("start up failed: %v", err))
return err return err
} }
node.Up = true node.Up = true

View file

@ -13,8 +13,7 @@ import (
) )
type ProtocolSession struct { type ProtocolSession struct {
*adapters.SimNode Server *p2p.Server
Ids []*adapters.NodeId Ids []*adapters.NodeId
adapter *adapters.SimAdapter adapter *adapters.SimAdapter
events chan *p2p.PeerEvent events chan *p2p.PeerEvent
@ -58,7 +57,7 @@ func (self *ProtocolSession) trigger(trig Trigger) error {
if !ok { if !ok {
return fmt.Errorf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(self.Ids)) return fmt.Errorf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(self.Ids))
} }
mockNode, ok := simNode.Service().(*mockNode) mockNode, ok := simNode.Service(&mockNode{}).(*mockNode)
if !ok { if !ok {
return fmt.Errorf("trigger: peer %v is not a mock", trig.Peer) return fmt.Errorf("trigger: peer %v is not a mock", trig.Peer)
} }
@ -93,7 +92,7 @@ func (self *ProtocolSession) expect(exp Expect) error {
if !ok { if !ok {
return fmt.Errorf("trigger: peer %v does not exist (1- %v)", exp.Peer, len(self.Ids)) return fmt.Errorf("trigger: peer %v does not exist (1- %v)", exp.Peer, len(self.Ids))
} }
mockNode, ok := simNode.Service().(*mockNode) mockNode, ok := simNode.Service(&mockNode{}).(*mockNode)
if !ok { if !ok {
return fmt.Errorf("trigger: peer %v is not a mock", exp.Peer) return fmt.Errorf("trigger: peer %v is not a mock", exp.Peer)
} }

View file

@ -19,14 +19,16 @@ type ProtocolTester struct {
} }
func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester { func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester {
services := map[string]adapters.ServiceFunc{ //func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester {
"test": func(id *adapters.NodeId) node.Service { services := adapters.Services {
return &testNode{run} "test": func(id *adapters.NodeId, _ []byte) []node.Service {
return []node.Service{&testNode{run}}
}, },
"mock": func(id *adapters.NodeId) node.Service { "mock": func(id *adapters.NodeId, _ []byte) []node.Service {
return newMockNode() return []node.Service{newMockNode()}
}, },
} }
adapters.RegisterServices(services)
adapter := adapters.NewSimAdapter(services) adapter := adapters.NewSimAdapter(services)
net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{}) net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{})
if _, err := net.NewNodeWithConfig(&adapters.NodeConfig{Id: id, Service: "test"}); err != nil { if _, err := net.NewNodeWithConfig(&adapters.NodeConfig{Id: id, Service: "test"}); err != nil {
@ -47,7 +49,7 @@ func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.P
events := make(chan *p2p.PeerEvent, 1000) events := make(chan *p2p.PeerEvent, 1000)
node.SubscribeEvents(events) node.SubscribeEvents(events)
ps := &ProtocolSession{ ps := &ProtocolSession{
SimNode: node, Server: node.Server(),
Ids: peerIDs, Ids: peerIDs,
adapter: adapter, adapter: adapter,
events: events, events: events,
@ -62,6 +64,10 @@ func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.P
return self return self
} }
func (self *ProtocolTester) Stop() error {
return self.Server.Stop()
}
func (self *ProtocolTester) Connect(selfId *adapters.NodeId, peers ...*adapters.NodeConfig) { func (self *ProtocolTester) Connect(selfId *adapters.NodeId, peers ...*adapters.NodeConfig) {
for _, peer := range peers { for _, peer := range peers {
log.Trace(fmt.Sprintf("start node %v", peer.Id)) log.Trace(fmt.Sprintf("start node %v", peer.Id))
@ -86,14 +92,17 @@ type testNode struct {
} }
func (t *testNode) Protocols() []p2p.Protocol { func (t *testNode) Protocols() []p2p.Protocol {
return []p2p.Protocol{{Run: t.run}} return []p2p.Protocol{{
Length: 100,
Run: t.run,
}}
} }
func (t *testNode) APIs() []rpc.API { func (t *testNode) APIs() []rpc.API {
return nil return nil
} }
func (t *testNode) Start(server p2p.Server) error { func (t *testNode) Start(server *p2p.Server) error {
return nil return nil
} }

View file

@ -287,3 +287,62 @@ func (self *BoolAddress) PO(val PotVal, pos int) (po int, eq bool) {
} }
return po, true return po, true
} }
type BytesAddress interface {
Address() []byte
}
type bytesAddress struct {
bytes []byte
toBytes func(v AnyVal) []byte
}
func NewBytesVal(v AnyVal, f func(v AnyVal) []byte) *bytesAddress {
if f == nil {
f = ToBytes
}
b := f(v)
return &bytesAddress{b, f}
}
func ToBytes(v AnyVal) []byte {
b, ok := v.([]byte)
if !ok {
ba, ok := v.(BytesAddress)
if !ok {
panic(fmt.Sprintf("unsupported value type %T", v))
}
b = ba.Address()
}
return b
}
func (a *bytesAddress) String() string {
return fmt.Sprintf("%08b", a.bytes)
}
func (a *bytesAddress) Address() []byte {
return a.bytes
}
func (a *bytesAddress) PO(val PotVal, i int) (int, bool) {
return proximityOrder(a.bytes, a.toBytes(val), i)
}
func proximityOrder(one, other []byte, pos int) (int, bool) {
for i := pos / 8; i < len(one); i++ {
if one[i] == other[i] {
continue
}
oxo := one[i] ^ other[i]
start := 0
if i == pos/8 {
start = pos % 8
}
for j := start; j < 8; j++ {
if (uint8(oxo)>>uint8(7-j))&0x01 != 0 {
return i*8 + j, false
}
}
}
return len(one) * 8, true
}

View file

@ -45,6 +45,8 @@ type PotVal interface {
String() string String() string
} }
type AnyVal interface{}
// Pot constructor. Requires value of type PotVal to pin // Pot constructor. Requires value of type PotVal to pin
// and po to point to a span in the PotVal key // and po to point to a span in the PotVal key
// The pinned item counts towards the size // The pinned item counts towards the size
@ -245,11 +247,12 @@ func remove(t *pot, val PotVal) (r *pot, po int, found bool) {
// if f returns v' <> v then v' is inserted into the Pot // if f returns v' <> v then v' is inserted into the Pot
// if v' == v the pot is not changed // if v' == v the pot is not changed
// it panics if v'.PO(k, 0) says v and k are not equal // it panics if v'.PO(k, 0) says v and k are not equal
func (t *Pot) Swap(val PotVal, f func(v PotVal) PotVal) (po int, found bool, change bool) { func (t *Pot) Swap(val AnyVal, f func(v PotVal) PotVal) (po int, found bool, change bool) {
t.lock.Lock() t.lock.Lock()
defer t.lock.Unlock() defer t.lock.Unlock()
ba := NewBytesVal(val, nil)
var t0 *pot var t0 *pot
t0, po, found, change = swap(t.pot, val, f) t0, po, found, change = swap(t.pot, ba, f)
if change { if change {
t.pot = t0 t.pot = t0
} }

View file

@ -8,59 +8,62 @@ import (
// discovery bzz overlay extension doing peer relaying // discovery bzz overlay extension doing peer relaying
// messages related to peer discovery
var DiscoveryMsgs = []interface{}{
&getPeersMsg{},
&peersMsg{},
&subPeersMsg{},
}
type discPeer struct { type discPeer struct {
Peer *bzzPeer
overlay Overlay overlay Overlay
peers map[string]bool peers map[string]bool
proxLimit uint8 // the proximity radius advertised by remote to subscribe to peers depth uint8 // the proximity radius advertised by remote to subscribe to peers
sentPeers bool // set to true when the peer is first notifed of peers close to them sentPeers bool // set to true when the peer is first notifed of peers close to them
} }
// discovery peer contructor // NewDiscovery discovery peer contructor
// registers the handlers for discovery messages func NewDiscovery(p *bzzPeer, o Overlay) *discPeer {
func NewDiscovery(p Peer, o Overlay) *discPeer {
self := &discPeer{ self := &discPeer{
overlay: o, overlay: o,
Peer: p, bzzPeer: p,
peers: make(map[string]bool), peers: make(map[string]bool),
} }
self.seen(self) self.seen(self)
p.Register(&peersMsg{}, self.handlePeersMsg)
p.Register(&getPeersMsg{}, self.handleGetPeersMsg)
p.Register(&subPeersMsg{}, self.handleSubPeersMsg)
return self return self
} }
func (self *discPeer) HandleMsg(msg interface{}) error {
switch msg := msg.(type) {
case *peersMsg:
return self.handlePeersMsg(msg)
case *getPeersMsg:
return self.handleGetPeersMsg(msg)
case *subPeersMsg:
return self.handleSubPeersMsg(msg)
default:
return fmt.Errorf("unknown message type: %T", msg)
}
}
// NotifyPeer notifies the receiver remote end of a peer p or PO po. // NotifyPeer notifies the receiver remote end of a peer p or PO po.
// callback for overlay driver // callback for overlay driver
func (self *discPeer) NotifyPeer(p Peer, po uint8) error { func (self *discPeer) NotifyPeer(a OverlayAddr, po uint8) error {
log.Warn(fmt.Sprintf("peer %#v peers %v", p, self.peers)) if po < self.depth || self.seen(a) {
if po < self.proxLimit || self.seen(p) {
return nil return nil
} }
log.Warn(fmt.Sprintf("notification about %x", p.OverlayAddr())) log.Warn(fmt.Sprintf("notification about %x", a.Address()))
resp := &peersMsg{ resp := &peersMsg{
Peers: []*peerAddr{&peerAddr{OAddr: p.OverlayAddr(), UAddr: p.UnderlayAddr()}}, // perhaps the PeerAddr interface is unnecessary generalization Peers: []*bzzAddr{ToAddr(a)}, // perhaps the PeerAddr interface is unnecessary generalization
} }
return self.Send(resp) return self.Send(resp)
} }
// NotifyProx sends a subPeers Msg to the receiver notifying them about // NotifyDepth sends a subPeers Msg to the receiver notifying them about
// a change in the prox limit (radius of the set including the nearest X peers // a change in the prox limit (radius of the set including the nearest X peers
// or first empty row) // or first empty row)
// callback for overlay driver // callback for overlay driver
func (self *discPeer) NotifyProx(po uint8) error { func (self *discPeer) NotifyDepth(po uint8) error {
return self.Send(&subPeersMsg{ProxLimit: po}) return self.Send(&subPeersMsg{Depth: po})
} }
/* /*
@ -83,7 +86,7 @@ disconnected
// used for communicating about known peers // used for communicating about known peers
// relevant for bootstrapping connectivity and updating peersets // relevant for bootstrapping connectivity and updating peersets
type peersMsg struct { type peersMsg struct {
Peers []*peerAddr Peers []*bzzAddr
} }
func (self peersMsg) String() string { func (self peersMsg) String() string {
@ -102,28 +105,27 @@ func (self getPeersMsg) String() string {
// subPeers msg is communicating the depth/sharpness/focus of the overlay table of a peer // subPeers msg is communicating the depth/sharpness/focus of the overlay table of a peer
type subPeersMsg struct { type subPeersMsg struct {
ProxLimit uint8 Depth uint8
} }
func (self subPeersMsg) String() string { func (self subPeersMsg) String() string {
return fmt.Sprintf("%T: request peers > PO%02d. ", self, self.ProxLimit) return fmt.Sprintf("%T: request peers > PO%02d. ", self, self.Depth)
} }
func (self *discPeer) handleSubPeersMsg(msg interface{}) error { func (self *discPeer) handleSubPeersMsg(msg *subPeersMsg) error {
spm := msg.(*subPeersMsg) self.depth = msg.Depth
self.proxLimit = spm.ProxLimit
if !self.sentPeers { if !self.sentPeers {
var peers []*peerAddr var peers []*bzzAddr
self.overlay.EachLivePeer(self.OverlayAddr(), 255, func(p Peer, po int, isproxbin bool) bool { self.overlay.EachConn(self.Over(), 255, func(p OverlayConn, po int, isproxbin bool) bool {
if uint8(po) < self.proxLimit { if uint8(po) < self.depth {
return false return false
} }
log.Warn(fmt.Sprintf("peer %#v proxlimit %v", p, self.proxLimit)) if !self.seen(p) {
self.seen(p.(*discPeer).Peer) peers = append(peers, ToAddr(p.Off()))
peers = append(peers, &peerAddr{p.OverlayAddr(), p.UnderlayAddr()}) }
return true return true
}) })
log.Warn(fmt.Sprintf("found initial %v peers not farther than %v", len(peers), self.proxLimit)) log.Warn(fmt.Sprintf("found initial %v peers not farther than %v", len(peers), self.depth))
if len(peers) > 0 { if len(peers) > 0 {
if err := self.Send(&peersMsg{Peers: peers}); err != nil { if err := self.Send(&peersMsg{Peers: peers}); err != nil {
return err return err
@ -137,39 +139,41 @@ func (self *discPeer) handleSubPeersMsg(msg interface{}) error {
// handlePeersMsg called by the protocol when receiving peerset (for target address) // handlePeersMsg called by the protocol when receiving peerset (for target address)
// list of nodes ([]PeerAddr in peersMsg) is added to the overlay db using the // list of nodes ([]PeerAddr in peersMsg) is added to the overlay db using the
// Register interface method // Register interface method
func (self *discPeer) handlePeersMsg(msg interface{}) error { func (self *discPeer) handlePeersMsg(msg *peersMsg) error {
// register all addresses // register all addresses
var nas []PeerAddr if len(msg.Peers) == 0 {
for _, na := range msg.(*peersMsg).Peers {
addr := PeerAddr(na)
nas = append(nas, addr)
self.seen(addr)
}
if len(nas) == 0 {
log.Debug(fmt.Sprintf("whoops, no peers in incoming peersMsg from %v", self)) log.Debug(fmt.Sprintf("whoops, no peers in incoming peersMsg from %v", self))
return nil return nil
} }
log.Debug(fmt.Sprintf("got peer addresses from %x, %v (%v)", self.OverlayAddr(), nas, len(nas)))
return self.overlay.Register(nas...) c := make(chan OverlayAddr)
go func() {
defer close(c)
for _, a := range msg.Peers {
self.seen(a)
c <- a
}
}()
log.Info("discovery overlay register")
return self.overlay.Register(c)
} }
// handleGetPeersMsg is called by the protocol when receiving a // handleGetPeersMsg is called by the protocol when receiving a
// peerset (for target address) request // peerset (for target address) request
// peers suggestions are retrieved from the overlay topology driver // peers suggestions are retrieved from the overlay topology driver
// using the EachLivePeer interface iterator method // using the EachConn interface iterator method
// peers sent are remembered throughout a session and not sent twice // peers sent are remembered throughout a session and not sent twice
func (self *discPeer) handleGetPeersMsg(msg interface{}) error { func (self *discPeer) handleGetPeersMsg(msg *getPeersMsg) error {
var peers []*peerAddr var peers []*bzzAddr
req := msg.(*getPeersMsg)
i := 0 i := 0
self.overlay.EachLivePeer(self.OverlayAddr(), int(req.Order), func(n Peer, po int, isproxbin bool) bool { self.overlay.EachConn(self.Over(), int(msg.Order), func(p OverlayConn, po int, isproxbin bool) bool {
i++ i++
// only send peers we have not sent before in this session // only send peers we have not sent before in this session
if self.seen(n) { a := ToAddr(p.Off())
peers = append(peers, &peerAddr{n.OverlayAddr(), n.UnderlayAddr()}) if self.seen(a) {
peers = append(peers, a)
} }
return len(peers) < int(req.Max) return len(peers) < int(msg.Max)
}) })
if len(peers) == 0 { if len(peers) == 0 {
log.Debug(fmt.Sprintf("no peers found for %v", self)) log.Debug(fmt.Sprintf("no peers found for %v", self))
@ -189,9 +193,8 @@ func RequestOrder(k Overlay, order, broadcastSize, maxPeers uint8) {
} }
var i uint8 var i uint8
//var err error //var err error
k.EachLivePeer(nil, 255, func(n Peer, po int, isproxbin bool) bool { k.EachConn(nil, 255, func(p OverlayConn, po int, isproxbin bool) bool {
log.Trace(fmt.Sprintf("%T sent to %v", req, n)) if err := p.(Conn).Send(req); err == nil {
if err := n.Send(req); err == nil {
i++ i++
if i >= broadcastSize { if i >= broadcastSize {
return false return false
@ -202,8 +205,8 @@ func RequestOrder(k Overlay, order, broadcastSize, maxPeers uint8) {
log.Info(fmt.Sprintf("requesting bees of PO%03d from %v/%v (each max %v)", order, i, broadcastSize, maxPeers)) log.Info(fmt.Sprintf("requesting bees of PO%03d from %v/%v (each max %v)", order, i, broadcastSize, maxPeers))
} }
func (self *discPeer) seen(p PeerAddr) bool { func (self *discPeer) seen(p OverlayPeer) bool {
k := NodeId(p).NodeID.String() k := string(p.Address())
if self.peers[k] { if self.peers[k] {
return true return true
} }

View file

@ -16,29 +16,24 @@ import (
func TestDiscovery(t *testing.T) { func TestDiscovery(t *testing.T) {
addr := RandomAddr() addr := RandomAddr()
to := NewKademlia(addr.OAddr, NewKadParams()) to := NewKademlia(addr.OAddr, NewKadParams())
ct := BzzCodeMap(DiscoveryMsgs...)
services := func(p Peer) error { run := func(p *bzzPeer) error {
dp := NewDiscovery(p, to) dp := NewDiscovery(p, to)
to.On(dp) to.On(p)
defer to.Off(p)
log.Trace(fmt.Sprintf("kademlia on %v", p)) log.Trace(fmt.Sprintf("kademlia on %v", p))
p.DisconnectHook(func(err error) { return p.Run(dp.HandleMsg)
to.Off(p)
})
return nil
} }
s := newBzzBaseTester(t, 1, addr, ct, services) s := newBzzBaseTester(t, 1, addr, DiscoverySpec, run)
defer s.Stop() defer s.Stop()
s.runHandshakes()
// o := 0
s.TestExchanges(p2ptest.Exchange{ s.TestExchanges(p2ptest.Exchange{
Label: "outgoing SubPeersMsg", Label: "outgoing SubPeersMsg",
Expects: []p2ptest.Expect{ Expects: []p2ptest.Expect{
p2ptest.Expect{ p2ptest.Expect{
Code: 3, Code: 3,
Msg: &subPeersMsg{ProxLimit: 0}, Msg: &subPeersMsg{Depth: 0},
Peer: s.ProtocolTester.Ids[0], Peer: s.ProtocolTester.Ids[0],
}, },
}, },

View file

@ -17,6 +17,7 @@
package network package network
import ( import (
"encoding/json"
"fmt" "fmt"
"sync" "sync"
"time" "time"
@ -33,75 +34,89 @@ it uses an Overlay Topology driver (e.g., generic kademlia nodetable)
to find best peer list for any target to find best peer list for any target
this is used by the netstore to search for content in the swarm this is used by the netstore to search for content in the swarm
It handles the bzz protocol getPeersMsg peersMsg exchange It handles the hive protocol getPeersMsg peersMsg exchange
and relay the peer request process to the Overlay module and relay the peer request process to the Overlay module
peer connections and disconnections are reported and registered peer connections and disconnections are reported and registered
to keep the nodetable uptodate to keep the nodetable uptodate
*/ */
// Overlay is the interface to Jaak ahd ka)a
type Overlay interface { type Overlay interface {
Register(...PeerAddr) error Register(chan OverlayAddr) error
On(Peer) On(OverlayConn)
Off(Peer) Off(OverlayConn)
EachLivePeer([]byte, int, func(Peer, int, bool) bool) EachConn([]byte, int, func(OverlayConn, int, bool) bool)
EachPeer([]byte, int, func(PeerAddr, int) bool) EachAddr([]byte, int, func(OverlayAddr, int) bool)
SuggestPeer() (PeerAddr, int, bool) SuggestPeer() (OverlayAddr, int, bool)
String() string String() string
GetAddr() PeerAddr BaseAddr() []byte
Healthy([][]byte) bool
}
// HiveParams holds the config options to hive
type HiveParams struct {
Discovery bool // if want discovery of not
PeersBroadcastSetSize uint8 // how many peers to use when relaying
MaxPeersPerRequest uint8 // max size for peer address batches
KeepAliveInterval time.Duration
}
// NewHiveParams returns hive config with only the
func NewHiveParams() *HiveParams {
return &HiveParams{
Discovery: true,
PeersBroadcastSetSize: 2,
MaxPeersPerRequest: 5,
KeepAliveInterval: time.Second,
}
} }
// Hive implements the PeerPool interface // Hive implements the PeerPool interface
type Hive struct { type Hive struct {
*HiveParams // settings *HiveParams // settings
Overlay // the overlay topology driver Overlay // the overlay topology driver
lock sync.Mutex store StateStore
quit chan bool
toggle chan bool
more chan bool
}
type HiveParams struct { // bookkeeping
Discovery bool lock sync.Mutex
PeersBroadcastSetSize uint8 quit chan bool
MaxPeersPerRequest uint8 more chan bool
CallInterval uint
}
func NewHiveParams() *HiveParams { tick <-chan time.Time
return &HiveParams{
Discovery: true,
PeersBroadcastSetSize: 2,
MaxPeersPerRequest: 5,
CallInterval: 1000,
}
} }
// Hive constructor embeds both arguments // Hive constructor embeds both arguments
// HiveParams config parameters // HiveParams: config parameters
// Overlay Topology Driver Interface // Overlay: Topology Driver Interface
func NewHive(params *HiveParams, overlay Overlay) *Hive { func NewHive(params *HiveParams, overlay Overlay, store StateStore) *Hive {
return &Hive{ return &Hive{
HiveParams: params, HiveParams: params,
Overlay: overlay, Overlay: overlay,
store: store,
} }
} }
// Start receives network info only at startup // Start receives network info only at startup
// connectPeer is a function to connect to a peer based on its NodeID or enode URL // server is used to connect to a peer based on its NodeID or enode URL
// these are called on the p2p.Server which runs on the node // these are called on the p2p.Server which runs on the node
// af() returns an arbitrary ticker channel // af() returns an arbitrary ticker channel
func (self *Hive) Start(server p2p.Server, af func() <-chan time.Time) error { // rw is a read writer for json configs
func (self *Hive) Start(server *p2p.Server) error {
self.toggle = make(chan bool) if self.store != nil {
if err := self.loadPeers(); err != nil {
return err
}
}
self.more = make(chan bool, 1) self.more = make(chan bool, 1)
self.quit = make(chan bool) self.quit = make(chan bool)
log.Debug("hive started") log.Debug("hive started")
// this loop is doing bootstrapping and maintains a healthy table // this loop is doing bootstrapping and maintains a healthy table
go self.keepAlive(af) go self.keepAlive()
go func() { go func() {
// each iteration, ask kademlia about most preferred peer // each iteration, ask kademlia about most preferred peer
for more := range self.more { for more := range self.more {
@ -110,14 +125,15 @@ func (self *Hive) Start(server p2p.Server, af func() <-chan time.Time) error {
// to attempt to write to more (remove Peer when shutting down) // to attempt to write to more (remove Peer when shutting down)
return return
} }
log.Trace("hive delegate to overlay driver: suggest addr to connect to") log.Debug("hive delegate to overlay driver: suggest addr to connect to")
// log.Trace("hive delegate to overlay driver: suggest addr to connect to")
addr, order, want := self.SuggestPeer() addr, order, want := self.SuggestPeer()
if addr != nil { if addr != nil {
log.Info(fmt.Sprintf("========> connect to bee %v", addr)) log.Info(fmt.Sprintf("========> connect to bee %v", addr))
node, err := discover.ParseNode(NodeId(addr).NodeID.String()) under, err := discover.ParseNode(string(addr.(Addr).Under()))
if err == nil { if err == nil {
server.AddPeer(node) server.AddPeer(under)
} else { } else {
log.Error(fmt.Sprintf("===X====> connect to bee %v failed: invalid node URL: %v", addr, err)) log.Error(fmt.Sprintf("===X====> connect to bee %v failed: invalid node URL: %v", addr, err))
} }
@ -127,75 +143,44 @@ func (self *Hive) Start(server p2p.Server, af func() <-chan time.Time) error {
want = want && self.Discovery want = want && self.Discovery
if want { if want {
go RequestOrder(self.Overlay, uint8(order), self.PeersBroadcastSetSize, self.MaxPeersPerRequest) log.Debug(fmt.Sprintf("========> request peers nearest %v", addr))
RequestOrder(self.Overlay, uint8(order), self.PeersBroadcastSetSize, self.MaxPeersPerRequest)
} }
log.Info(fmt.Sprintf("%v", self))
select { select {
case self.toggle <- want:
log.Trace(fmt.Sprintf("keep hive alive: %v", want))
case <-self.quit: case <-self.quit:
return return
default:
} }
log.Info(fmt.Sprintf("%v", self))
} }
}() }()
return nil return nil
} }
func (self *Hive) ticker() <-chan time.Time { // Stop terminates the updateloop and saves the peers
return time.NewTicker(time.Duration(self.CallInterval) * time.Millisecond).C func (self *Hive) Stop() {
} if self.store != nil {
self.savePeers()
// keepAlive is a forever loop
// in its awake state it periodically triggers connection attempts
// by writing to self.more until Kademlia Table is saturated
// wake state is toggled by writing to self.toggle
// it restarts if the table becomes non-full again due to disconnections
func (self *Hive) keepAlive(af func() <-chan time.Time) {
log.Trace("keep alive loop started")
alarm := af()
for {
select {
case <-alarm:
log.Trace("wake up: make hive alive")
self.wake()
case need := <-self.toggle:
if alarm == nil && need {
alarm = af()
}
// if hive saturated, no more peers asked
if alarm != nil && !need {
alarm = nil
}
case <-self.quit:
return
}
} }
// closing toggle channel quits the updateloop
close(self.quit)
} }
// Add is called at the end of a successful protocol handshake func (self *Hive) Run(p *bzzPeer) error {
// to register a connected (live) peer dp := NewDiscovery(p, self)
func (self *Hive) Add(p Peer) error {
defer self.wake()
dp := NewDiscovery(p, self.Overlay)
log.Debug(fmt.Sprintf("to add new bee %v", p)) log.Debug(fmt.Sprintf("to add new bee %v", p))
self.On(dp) self.On(dp)
self.String() self.wake()
log.Debug(fmt.Sprintf("%v", self))
return nil
}
// Remove called after peer is disconnected
func (self *Hive) Remove(p Peer) {
defer self.wake() defer self.wake()
log.Debug(fmt.Sprintf("remove bee %v", p)) defer self.Off(dp)
self.Off(p) return p.Run(dp.HandleMsg)
} }
// NodeInfo function is used by the p2p.server RPC interface to display // NodeInfo function is used by the p2p.server RPC interface to display
// protocol specific node information // protocol specific node information
func (self *Hive) NodeInfo() interface{} { func (self *Hive) NodeInfo() interface{} {
return interface{}(self.String()) return self.String()
} }
// PeerInfo function is used by the p2p.server RPC interface to display // PeerInfo function is used by the p2p.server RPC interface to display
@ -203,22 +188,16 @@ func (self *Hive) NodeInfo() interface{} {
func (self *Hive) PeerInfo(id discover.NodeID) interface{} { func (self *Hive) PeerInfo(id discover.NodeID) interface{} {
self.lock.Lock() self.lock.Lock()
defer self.lock.Unlock() defer self.lock.Unlock()
addr := NewPeerAddrFromNodeId(adapters.NewNodeId(id[:])) addr := NewAddrFromNodeId(adapters.NewNodeId(id[:]))
return interface{}(addr) return interface{}(addr)
} }
// Stop terminates the updateloop func (self *Hive) Register(peers chan OverlayAddr) error {
func (self *Hive) Stop() { defer self.wake()
// closing toggle channel quits the updateloop return self.Overlay.Register(peers)
close(self.quit)
}
func (self *Hive) Healthy() bool {
// TODO: determine if we have enough peers to consider the network
// to be healthy
return true
} }
// wake triggers
func (self *Hive) wake() { func (self *Hive) wake() {
select { select {
case self.more <- true: case self.more <- true:
@ -229,7 +208,100 @@ func (self *Hive) wake() {
} }
} }
// HexToBytes reads a hex string ontp
func HexToBytes(s string) []byte { func HexToBytes(s string) []byte {
id := discover.MustHexID(s) id := discover.MustHexID(s)
return id[:] return id[:]
} }
// ToAddr returns the serialisable version of u
func ToAddr(pa OverlayPeer) *bzzAddr {
if addr, ok := pa.(*bzzAddr); ok {
return addr
}
if p, ok := pa.(*discPeer); ok {
return p.bzzAddr
}
return pa.(*bzzPeer).bzzAddr
}
type hiveTicker interface {
Ch() <-chan time.Time
Stop()
}
type timeTicker struct {
*time.Ticker
}
func (t *timeTicker) Ch() <-chan time.Time {
return t.C
}
// keepAlive is a forever loop
// in its awake state it periodically triggers connection attempts
// by writing to self.more until Kademlia Table is saturated
// wake state is toggled by writing to self.toggle
// it goes to sleep mode if table is saturated
// it restarts if the table becomes non-full again due to disconnections
func (self *Hive) keepAlive() {
if self.tick == nil {
ticker := time.NewTicker(self.KeepAliveInterval)
defer ticker.Stop()
self.tick = ticker.C
}
for {
select {
case <-self.tick:
log.Debug("wake up: make hive alive")
self.wake()
case <-self.quit:
return
}
}
}
// loadPeers, savePeer implement persistence callback/
func (self *Hive) loadPeers() error {
data, err := self.store.Load("peers")
if err != nil {
return err
}
if data == nil {
return nil
}
var as []*bzzAddr
if err := json.Unmarshal(data, &as); err != nil {
return err
}
c := make(chan OverlayAddr)
go func() {
defer close(c)
for _, a := range as {
c <- a
}
}()
return self.Overlay.Register(c)
}
// savePeers, savePeer implement persistence callback/
func (self *Hive) savePeers() error {
var peers []*bzzAddr
self.Overlay.EachAddr(nil, 256, func(pa OverlayAddr, i int) bool {
if pa == nil {
log.Warn(fmt.Sprintf("empty addr: %v", i))
return true
}
peers = append(peers, ToAddr(pa))
return true
})
data, err := json.Marshal(peers)
if err != nil {
return fmt.Errorf("could not encode peers: %v", err)
}
if err := self.store.Save("peers", data); err != nil {
return fmt.Errorf("could not save peers: %v", err)
}
return nil
}

View file

@ -1,67 +1,19 @@
package network package network
import ( import (
"sync"
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
p2ptest "github.com/ethereum/go-ethereum/p2p/testing" p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
) )
type testConnect struct {
mu sync.Mutex
conns []string
connectf func(c string) error
ticker chan time.Time
}
func (self *testConnect) ping() <-chan time.Time {
return self.ticker
}
func (self *testConnect) connect(na string) error {
self.mu.Lock()
defer self.mu.Unlock()
self.conns = append(self.conns, na)
self.connectf(na)
return nil
}
func newHiveTester(t *testing.T, params *HiveParams) (*bzzTester, *Hive) { func newHiveTester(t *testing.T, params *HiveParams) (*bzzTester, *Hive) {
// setup // setup
addr := RandomAddr() // tested peers peer address addr := RandomAddr() // tested peers peer address
to := NewTestOverlay(addr.OverlayAddr()) // overlay topology drive to := NewKademlia(addr.OAddr, NewKadParams())
pp := NewHive(params, to) // hive pp := NewHive(params, to, nil) // hive
ct := BzzCodeMap(DiscoveryMsgs...) // bzz protocol code map
services := func(p Peer) error {
pp.Add(p)
p.DisconnectHook(func(err error) {
pp.Remove(p)
})
return nil
}
return newBzzBaseTester(t, 1, addr, ct, services), pp
}
func TestOverlayRegistration(t *testing.T) {
params := NewHiveParams()
params.Discovery = false
s, pp := newHiveTester(t, params)
defer s.Stop()
id := s.Ids[0]
raddr := NewPeerAddrFromNodeId(id)
s.runHandshakes()
// hive should have called the overlay
if pp.Overlay.(*testOverlay).posMap[string(raddr.OverlayAddr())] == nil {
t.Fatalf("Overlay#On not called on new peer")
}
return newBzzBaseTester(t, 1, addr, DiscoverySpec, pp.Run), pp
} }
func TestRegisterAndConnect(t *testing.T) { func TestRegisterAndConnect(t *testing.T) {
@ -70,30 +22,23 @@ func TestRegisterAndConnect(t *testing.T) {
defer s.Stop() defer s.Stop()
id := s.Ids[0] id := s.Ids[0]
raddr := NewPeerAddrFromNodeId(id) raddr := NewAddrFromNodeId(id)
pp.Register(raddr) ch := make(chan OverlayAddr)
go func() {
ch <- raddr
close(ch)
}()
pp.Register(ch)
// start the hive and wait for the connection // start the hive and wait for the connection
tc := &testConnect{ tick := make(chan time.Time)
connectf: func(c string) error { pp.tick = tick
s.Connect(adapters.NewNodeIdFromHex(c)) pp.Start(s.Server)
return nil
},
ticker: make(chan time.Time),
}
pp.Start(s, tc.ping)
defer pp.Stop() defer pp.Stop()
tc.ticker <- time.Now() tick <- time.Now()
s.runHandshakes()
if pp.Overlay.(*testOverlay).posMap[string(raddr.OverlayAddr())] == nil {
t.Fatalf("Overlay#On not called on new peer")
}
// retrieve and broadcast // retrieve and broadcast
ord := order(raddr.OverlayAddr()) ord := raddr.Over()[0] / 32
o := 0 o := 0
if ord == 0 { if ord == 0 {
o = 1 o = 1

View file

@ -23,6 +23,8 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/pot" "github.com/ethereum/go-ethereum/pot"
) )
@ -46,24 +48,20 @@ a guaranteed constant maximum limit on the number of hops needed to reach one
node from the other. node from the other.
*/ */
type KadDiscovery interface { // KadParams holds the config params for Kademlia
NotifyPeer(Peer, uint8) error
NotifyProx(uint8) error
}
type KadParams struct { type KadParams struct {
// adjustable parameters // adjustable parameters
MaxProxDisplay int MaxProxDisplay int // number of rows the table shows
MinProxBinSize int MinProxBinSize int // nearest neighbour core minimum cardinality
MinBinSize int MinBinSize int // minimum number of peers in a row
MaxBinSize int MaxBinSize int // maximum number of peers in a row before pruning
RetryInterval int RetryInterval int // initial interval before a peer is first redialed
RetryExponent int RetryExponent int // exponent to multiply retry intervals with
MaxRetries int MaxRetries int // maximum number of redial attempts
PruneInterval int PruneInterval int // interval between peer pruning cycles
} }
// NewKadParams() returns a params struct with default values // NewKadParams returns a params struct with default values
func NewKadParams() *KadParams { func NewKadParams() *KadParams {
return &KadParams{ return &KadParams{
MaxProxDisplay: 8, MaxProxDisplay: 8,
@ -79,141 +77,103 @@ func NewKadParams() *KadParams {
// Kademlia is a table of live peers and a db of known peers // Kademlia is a table of live peers and a db of known peers
type Kademlia struct { type Kademlia struct {
addr PeerAddr // immutable baseaddress of the table *KadParams // Kademlia configuration parameters
// addr *pot.HashAddress // immutable baseaddress of the table base []byte // immutable baseaddress of the table
*KadParams // Kademlia configuration parameters addrs *pot.Pot // pots container for known peer addresses
addrs, peers *pot.Pot // pots container for peers conns *pot.Pot // pots container for live peer connections
lastProxLimit uint8 // stores the last calculated proxlimit depth uint8 // stores the last calculated depth
} }
// NewKademlia(addr, params) creates a Kademlia table for base address addr // NewKademlia creates a Kademlia table for base address addr
// with parameters as in params // with parameters as in params
// if params is nil, it uses default values // if params is nil, it uses default values
func NewKademlia(addr []byte, params *KadParams) *Kademlia { func NewKademlia(addr []byte, params *KadParams) *Kademlia {
if params == nil { if params == nil {
params = NewKadParams() params = NewKadParams()
} }
self := &Kademlia{ return &Kademlia{
addr: &peerAddr{OAddr: addr}, base: addr,
KadParams: params, KadParams: params,
addrs: pot.NewPot(nil, 0), addrs: pot.NewPot(nil, 0),
peers: pot.NewPot(nil, 0), conns: pot.NewPot(nil, 0),
} }
return self
} }
// Prune implements a forever loop reacting to a ticker time channel given type Notifier interface {
// as the first argument NotifyPeer(OverlayAddr, uint8) error
// the loop quits if the channel is closed NotifyDepth(uint8) error
// it checks each kademlia bin and if the peer count is higher than
// the MaxBinSize parameter it drops the oldest n peers such that
// the bin is reduced to MinBinSize peers thus leaving slots to newly
// connecting peers
func (self *Kademlia) Prune(c <-chan time.Time) {
go func() {
for _ = range c {
log.Debug("pruning...")
total := 0
self.peers.EachBin(self.addr, 0, func(po, size int, f func(func(pot.PotVal, int) bool) bool) bool {
extra := size - self.MinBinSize
if size > self.MaxBinSize {
n := 0
f(func(v pot.PotVal, po int) bool {
p := v.(*KadPeer).Peer
if p != nil {
p.Drop(fmt.Errorf("bucket full"))
}
n++
return n < extra
})
total += extra
}
return true
})
log.Debug(fmt.Sprintf("pruned %v peers", total))
}
}()
} }
// KadPeer represents a Kademlia Peer and extends // OverlayPeer interface captures the common aspect of view of a peer from the Overlay
// * PeerAddr interface (overlay and underlay addresses) // topology driver
// * Peer interface (id, last seen, drop) type OverlayPeer interface {
type KadPeer struct { Address() []byte
PeerAddr }
Peer Peer
// OverlayConn represents a connected peer
type OverlayConn interface {
OverlayPeer
Drop(error) // call to indicate a peer should be expunged
Off() OverlayAddr // call to return a persitent OverlayAddr
}
type OverlayAddr interface {
OverlayPeer
Update(OverlayAddr) OverlayAddr // returns the updated version of the original
}
// entry represents a Kademlia table entry (an extension of OverlayPeer)
// implements the pot.PotVal interface via BytesAddress, so entry can be
// used directly as a pot element
type entry struct {
pot.PotVal
OverlayPeer
seenAt time.Time seenAt time.Time
retries int retries int
} }
func (self *KadPeer) String() string { // newEntry creates a kademlia peer from an OverlayPeer interface
if self == nil { func newEntry(p OverlayPeer) *entry {
return "<nil>" return &entry{
} PotVal: pot.NewBytesVal(p, nil),
return fmt.Sprintf("%x", self.OverlayAddr()) OverlayPeer: p,
} seenAt: time.Now(),
func (self *Kademlia) callable(val pot.PotVal) *KadPeer {
kp := val.(*KadPeer)
// not callable if peer is live or exceeded maxRetries
if kp.Peer != nil || kp.retries > self.MaxRetries {
log.Trace(fmt.Sprintf("peer %v (%T) not callable", kp, kp.Peer))
return nil
}
// calculate the allowed number of retries based on time lapsed since last seen
timeAgo := time.Since(kp.seenAt)
var retries int
for delta := int(timeAgo) / self.RetryInterval; delta > 0; delta /= self.RetryExponent {
log.Trace(fmt.Sprintf("delta: %v", delta))
retries++
}
// this is never called concurrently, so safe to increment
// peer can be retried again
if retries < kp.retries {
log.Trace(fmt.Sprintf("log time needed before retry %v, wait only warrants %v", kp.retries, retries))
return nil
}
kp.retries++
log.Trace(fmt.Sprintf("peer %v is callable", kp))
return kp
}
// NewKadPeer creates a kademlia peer from a PeerAddr interface
func NewKadPeer(na PeerAddr) *KadPeer {
return &KadPeer{
PeerAddr: na,
seenAt: time.Now(),
} }
} }
// retrieve the base address func (self *entry) addr() OverlayAddr {
// which is the overlayaddress used by peers to reach us a, _ := self.OverlayPeer.(OverlayAddr)
func (self *Kademlia) GetAddr() PeerAddr { return a
return self.addr
} }
// Register(nas) enters each PeerAddr as kademlia peers into the func (self *entry) conn() OverlayConn {
// database of known peers c, _ := self.OverlayPeer.(OverlayConn)
func (self *Kademlia) Register(nas ...PeerAddr) error { return c
label := fmt.Sprintf("%x", RandomAddr().OverlayAddr()) }
func (self *entry) String() string {
return fmt.Sprintf("%x", self.OverlayPeer.Address())
}
// Register enters each OverlayAddr as kademlia peer record into the
// database of known peer addresses
func (self *Kademlia) Register(peers chan OverlayAddr) error {
np := pot.NewPot(nil, 0) np := pot.NewPot(nil, 0)
for _, na := range nas { for p := range peers {
if bytes.Equal(na.OverlayAddr(), self.addr.OverlayAddr()) { // error if self received, peer should know better
log.Warn(fmt.Sprintf("[%06s] add peers: %x is self.. skipped ", label, self.addr.OverlayAddr())) if bytes.Equal(p.Address(), self.base) {
continue return fmt.Errorf("add peers: %x is self", self.base)
} }
p := NewKadPeer(na) np, _, _ = pot.Add(np, newEntry(p))
np, _, _ = pot.Add(np, pot.PotVal(p))
} }
oldpeers := pot.NewPot(nil, 0) com := self.addrs.Merge(np)
oldpeers.Merge(self.addrs) log.Debug(fmt.Sprintf("merged %v peers, %v known, total: %v", np.Size(), com, self.addrs.Size()))
self.addrs.Merge(np) // log.Trace(fmt.Sprintf("merged %v peers, %v known", np.Size(), com))
// TODO: remove this check
m := make(map[string]bool) m := make(map[string]bool)
self.addrs.Each(func(val pot.PotVal, i int) bool { self.addrs.Each(func(val pot.PotVal, i int) bool {
_, found := m[val.String()] _, found := m[val.String()]
// TODO: remove this check
// log.Debug(fmt.Sprintf("-> %v %v", val, i))
if found { if found {
panic("duplicate found") panic("duplicate found")
} }
@ -223,148 +183,34 @@ func (self *Kademlia) Register(nas ...PeerAddr) error {
return nil return nil
} }
// On(p) inserts the peer as a kademlia peer into the live peers
func (self *Kademlia) On(p Peer) {
kp := NewKadPeer(p)
kp.Peer = p
self.peers.Swap(kp, func(v pot.PotVal) pot.PotVal {
// if not found live
if v == nil {
// switch the offline peer
self.addrs.Swap(kp, func(v pot.PotVal) pot.PotVal {
return pot.PotVal(kp)
})
// insert new peer
return pot.PotVal(kp)
}
// found among live peers, do nothing
return v
})
prox := self.proxLimit()
vp, ok := kp.Peer.(KadDiscovery)
if !ok {
// log.Trace(fmt.Sprintf("not discovery peer %T", kp))
return
}
go vp.NotifyProx(uint8(prox))
f := func(val pot.PotVal, po int) {
dp := val.(*KadPeer).Peer.(KadDiscovery)
log.Debug(fmt.Sprintf("peer %v notified of %v (%v)", dp, kp, po))
dp.NotifyPeer(kp.Peer, uint8(po))
if uint8(prox) != self.lastProxLimit {
self.lastProxLimit = uint8(prox)
dp.NotifyProx(uint8(prox))
}
log.Debug("peer notified")
}
self.peers.EachNeighbourAsync(kp, 1024, 255, f, false)
}
// Off removes a peer from among live peers
func (self *Kademlia) Off(p Peer) {
kp := NewKadPeer(p)
self.addrs.Swap(kp, func(v pot.PotVal) pot.PotVal {
if v != nil {
self.peers.Swap(kp, func(v pot.PotVal) pot.PotVal {
return nil
})
}
return nil
})
}
type ByteAddr struct {
key []byte
}
// EachLivePeer(base, po, f) is an iterator applying f to each live peer
// that has proximity order po or less as measured from the base
// if base is nil, kademlia base address is used
func (self *Kademlia) EachLivePeer(base []byte, o int, f func(Peer, int, bool) bool) {
var p pot.PotVal
if base == nil {
p = pot.PotVal(self.addr)
} else {
p = pot.PotVal(&peerAddr{OAddr: base})
}
self.peers.EachNeighbour(p, func(val pot.PotVal, po int) bool {
if po > o {
return true
}
isproxbin := false
if l, _ := p.PO(val, 0); l >= self.proxLimit() {
isproxbin = true
}
return f(val.(*KadPeer).Peer, po, isproxbin)
})
}
// EachPeer(base, po, f) is an iterator applying f to each known peer
// that has proximity order po or less as measured from the base
// if base is nil, kademlia base address is used
func (self *Kademlia) EachPeer(base []byte, o int, f func(PeerAddr, int) bool) {
var p pot.PotVal
if base == nil {
p = pot.PotVal(self.addr)
} else {
p = pot.NewHashAddressFromBytes(base)
}
self.addrs.EachNeighbour(p, func(val pot.PotVal, po int) bool {
if po > o {
return true
}
return f(val.(*KadPeer).Peer, po)
})
}
// proxLimit() returns the proximity order that defines the distance of
// the nearest neighbour set with cardinality >= MinProxBinSize
// if there is altogether less than MinProxBinSize peers it returns 0
func (self *Kademlia) proxLimit() int {
if self.peers.Size() < self.MinProxBinSize {
return 0
}
var proxLimit int
var size int
f := func(v pot.PotVal, i int) bool {
size++
proxLimit = i
return size < self.MinProxBinSize
}
self.peers.EachNeighbour(pot.PotVal(self.addr), f)
return proxLimit
}
// SuggestPeer returns a known peer for the lowest proximity bin for the // SuggestPeer returns a known peer for the lowest proximity bin for the
// lowest bincount below proxLimit // lowest bincount below depth
// naturally if there is an empty row it returns a peer for that // naturally if there is an empty row it returns a peer for that
// //
func (self *Kademlia) SuggestPeer() (p PeerAddr, o int, want bool) { func (self *Kademlia) SuggestPeer() (a OverlayAddr, o int, want bool) {
minsize := self.MinBinSize minsize := self.MinBinSize
proxLimit := self.proxLimit() depth := self.Depth()
// if there is a callable neighbour within the current proxBin, connect // if there is a callable neighbour within the current proxBin, connect
// this makes sure nearest neighbour set is fully connected // this makes sure nearest neighbour set is fully connected
log.Trace(fmt.Sprintf("candidate prox peer checking above PO %v", proxLimit)) log.Debug(fmt.Sprintf("candidate prox peer checking above PO %v", depth))
// log.Trace(fmt.Sprintf("candidate prox peer checking above PO %v", depth))
var ppo int var ppo int
self.addrs.EachNeighbour(self.addr, func(val pot.PotVal, po int) bool { ba := pot.NewBytesVal(self.base, nil)
r := self.callable(val) self.addrs.EachNeighbour(ba, func(val pot.PotVal, po int) bool {
if r == nil { a = self.callable(val)
return po >= proxLimit log.Trace(fmt.Sprintf("candidate prox peer at %x: %v (%v). a == nil is %v", val.(*entry).Address(), a, po, a == nil))
}
p = r
ppo = po ppo = po
return false return a == nil && po >= depth
}) })
if p != nil { if a != nil {
log.Trace(fmt.Sprintf("candidate prox peer found: %v (%v), %v", p, ppo, p)) log.Trace(fmt.Sprintf("candidate prox peer found: %v (%v)", a, ppo))
return p, 0, false return a, 0, false
} }
log.Trace(fmt.Sprintf("no candidate prox peers to connect to (ProxLimit: %v, minProxSize: %v)", proxLimit, self.MinProxBinSize)) log.Trace(fmt.Sprintf("no candidate prox peers to connect to (Depth: %v, minProxSize: %v) %#v", depth, self.MinProxBinSize, a))
var bpo []int var bpo []int
prev := -1 prev := -1
self.peers.EachBin(self.addr, 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool { self.conns.EachBin(pot.NewBytesVal(self.base, nil), 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool {
log.Trace(fmt.Sprintf("check PO%02d: ", po)) log.Trace(fmt.Sprintf("check PO%02d: ", po))
prev++ prev++
if po > prev { if po > prev {
@ -375,7 +221,7 @@ func (self *Kademlia) SuggestPeer() (p PeerAddr, o int, want bool) {
minsize = size minsize = size
bpo = append(bpo, po) bpo = append(bpo, po)
} }
return size > 0 && po < proxLimit return size > 0 && po < depth
}) })
// all buckets are full // all buckets are full
// minsize == self.MinBinSize // minsize == self.MinBinSize
@ -387,46 +233,186 @@ func (self *Kademlia) SuggestPeer() (p PeerAddr, o int, want bool) {
// try to select a candidate peer // try to select a candidate peer
for i := len(bpo) - 1; i >= 0; i-- { for i := len(bpo) - 1; i >= 0; i-- {
// find the first callable peer // find the first callable peer
self.addrs.EachBin(self.addr, bpo[i], func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool { self.addrs.EachBin(ba, bpo[i], func(po, size int, f func(func(pot.PotVal, int) bool) bool) bool {
// for each bin we find callable candidate peers // for each bin we find callable candidate peers
f(func(val pot.PotVal, i int) bool { log.Trace(fmt.Sprintf("check PO%02d: ", po))
r := self.callable(val) f(func(val pot.PotVal, j int) bool {
log.Trace(fmt.Sprintf("check PO%02d: ", po)) a = self.callable(val)
if r == nil { return a == nil && po < depth
return i < proxLimit
}
p = r
return false
}) })
return false return false
}) })
// found a candidate // found a candidate
if p != nil { if a != nil {
break break
} }
// cannot find a candidate, ask for more for this proximity bin specifically // cannot find a candidate, ask for more for this proximity bin specifically
o = bpo[i] o = bpo[i]
want = true want = true
} }
return p, o, want return a, o, want
} }
// kademlia table + kaddb table displayed with ascii // On inserts the peer as a kademlia peer into the live peers
func (self *Kademlia) On(p OverlayConn) {
e := newEntry(p)
self.conns.Swap(p, func(v pot.PotVal) pot.PotVal {
// if not found live
if v == nil {
// insert new online peer into addrs
self.addrs.Swap(p, func(v pot.PotVal) pot.PotVal {
return e
})
// insert new online peer into conns
return e
}
// found among live peers, do nothing
return v
})
np, ok := p.(Notifier)
if !ok {
return
}
depth := uint8(self.Depth())
if depth != self.depth {
self.depth = depth
} else {
depth = 0
}
go np.NotifyDepth(depth)
f := func(val pot.PotVal, po int) {
dp := val.(*entry).OverlayPeer.(Notifier)
dp.NotifyPeer(p.Off(), uint8(po))
// log.Trace(fmt.Sprintf("peer %v notified of %v (%v)", dp, p, po))
log.Debug(fmt.Sprintf("peer %v notified of %v (%v)", dp, p, po))
if depth > 0 {
dp.NotifyDepth(depth)
log.Debug(fmt.Sprintf("peer %v notified of new depth %v", dp, depth))
// log.Trace(fmt.Sprintf("peer %v notified of new depth %v", dp, depth))
}
}
self.conns.EachNeighbourAsync(e, 1024, 255, f, false)
}
// Off removes a peer from among live peers
func (self *Kademlia) Off(p OverlayConn) {
self.addrs.Swap(p, func(v pot.PotVal) pot.PotVal {
// v cannot be nil, must check otherwise we overwrite entry
if v == nil {
panic(fmt.Sprintf("connected peer not found %v", p))
}
self.conns.Swap(p, func(v pot.PotVal) pot.PotVal {
// v cannot be nil, but no need to check
return nil
})
return newEntry(p.Off())
})
}
// EachConn is an iterator with args (base, po, f) applies f to each live peer
// that has proximity order po or less as measured from the base
// if base is nil, kademlia base address is used
func (self *Kademlia) EachConn(base []byte, o int, f func(OverlayConn, int, bool) bool) {
if len(base) == 0 {
base = self.base
}
p := pot.NewBytesVal(base, nil)
self.conns.EachNeighbour(p, func(val pot.PotVal, po int) bool {
if po > o {
return true
}
isproxbin := false
if l, _ := p.PO(val, 0); l >= self.Depth() {
isproxbin = true
}
return f(val.(*entry).conn(), po, isproxbin)
})
}
// EachAddr(base, po, f) is an iterator applying f to each known peer
// that has proximity order po or less as measured from the base
// if base is nil, kademlia base address is used
func (self *Kademlia) EachAddr(base []byte, o int, f func(OverlayAddr, int) bool) {
if len(base) == 0 {
base = self.base
}
p := pot.NewBytesVal(base, nil)
self.addrs.EachNeighbour(p, func(val pot.PotVal, po int) bool {
if po > o {
return true
}
return f(val.(*entry).addr(), po)
})
}
// Depth returns the proximity order that defines the distance of
// the nearest neighbour set with cardinality >= MinProxBinSize
// if there is altogether less than MinProxBinSize peers it returns 0
func (self *Kademlia) Depth() (depth int) {
if self.conns.Size() < self.MinProxBinSize {
return 0
}
var size int
f := func(v pot.PotVal, i int) bool {
size++
depth = i
return size < self.MinProxBinSize
}
self.conns.EachNeighbour(pot.NewBytesVal(self.base, nil), f)
return depth
}
func (self *Kademlia) callable(val pot.PotVal) OverlayAddr {
e := val.(*entry)
// not callable if peer is live or exceeded maxRetries
if e.conn() != nil || e.retries > self.MaxRetries {
log.Trace(fmt.Sprintf("peer %v (%T) not callable", e, e.OverlayPeer))
return nil
}
// calculate the allowed number of retries based on time lapsed since last seen
timeAgo := time.Since(e.seenAt)
var retries int
for delta := int(timeAgo) / self.RetryInterval; delta > 0; delta /= self.RetryExponent {
log.Trace(fmt.Sprintf("delta: %v", delta))
retries++
}
// this is never called concurrently, so safe to increment
// peer can be retried again
if retries < e.retries {
log.Trace(fmt.Sprintf("long time since last try (at %v) needed before retry %v, wait only warrants %v", timeAgo, e.retries, retries))
return nil
}
e.retries++
log.Trace(fmt.Sprintf("peer %v is callable", e))
return e.addr()
}
// BaseAddr return the kademlia base addres
func (self *Kademlia) BaseAddr() []byte {
return self.base
}
// String returns kademlia table + kaddb table displayed with ascii
func (self *Kademlia) String() string { func (self *Kademlia) String() string {
var rows []string var rows []string
rows = append(rows, "=========================================================================") rows = append(rows, "=========================================================================")
rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %v", time.Now().UTC().Format(time.UnixDate), fmt.Sprintf("%x", self.addr.OverlayAddr()[:3]))) rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %x", time.Now().UTC().Format(time.UnixDate), self.BaseAddr()[:3]))
rows = append(rows, fmt.Sprintf("population: %d (%d), MinProxBinSize: %d, MinBinSize: %d, MaxBinSize: %d", self.peers.Size(), self.addrs.Size(), self.MinProxBinSize, self.MinBinSize, self.MaxBinSize)) rows = append(rows, fmt.Sprintf("population: %d (%d), MinProxBinSize: %d, MinBinSize: %d, MaxBinSize: %d", self.conns.Size(), self.addrs.Size(), self.MinProxBinSize, self.MinBinSize, self.MaxBinSize))
liverows := make([]string, self.MaxProxDisplay) liverows := make([]string, self.MaxProxDisplay)
peersrows := make([]string, self.MaxProxDisplay) peersrows := make([]string, self.MaxProxDisplay)
var proxLimit int var depth int
prev := -1 prev := -1
var proxLimitSet bool var depthSet bool
rest := self.peers.Size() rest := self.conns.Size()
self.peers.EachBin(self.addr, 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool { self.conns.EachBin(pot.NewBytesVal(self.base, nil), 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool {
var rowlen int var rowlen int
if po >= self.MaxProxDisplay { if po >= self.MaxProxDisplay {
po = self.MaxProxDisplay - 1 po = self.MaxProxDisplay - 1
@ -434,13 +420,13 @@ func (self *Kademlia) String() string {
row := []string{fmt.Sprintf("%2d", size)} row := []string{fmt.Sprintf("%2d", size)}
rest -= size rest -= size
f(func(val pot.PotVal, vpo int) bool { f(func(val pot.PotVal, vpo int) bool {
row = append(row, val.(*KadPeer).String()[:6]) row = append(row, val.(*entry).String()[:6])
rowlen++ rowlen++
return rowlen < 4 return rowlen < 4
}) })
if !proxLimitSet && (po > prev+1 || rest < self.MinProxBinSize) { if !depthSet && (po > prev+1 || rest < self.MinProxBinSize) {
proxLimitSet = true depthSet = true
proxLimit = prev + 1 depth = prev + 1
} }
for ; rowlen <= 5; rowlen++ { for ; rowlen <= 5; rowlen++ {
row = append(row, " ") row = append(row, " ")
@ -450,7 +436,7 @@ func (self *Kademlia) String() string {
return true return true
}) })
self.addrs.EachBin(self.addr, 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool { self.addrs.EachBin(pot.NewBytesVal(self.base, nil), 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool {
var rowlen int var rowlen int
if po >= self.MaxProxDisplay { if po >= self.MaxProxDisplay {
po = self.MaxProxDisplay - 1 po = self.MaxProxDisplay - 1
@ -461,8 +447,7 @@ func (self *Kademlia) String() string {
row := []string{fmt.Sprintf("%2d", size)} row := []string{fmt.Sprintf("%2d", size)}
// we are displaying live peers too // we are displaying live peers too
f(func(val pot.PotVal, vpo int) bool { f(func(val pot.PotVal, vpo int) bool {
kp := val.(*KadPeer) row = append(row, val.(*entry).String()[:6])
row = append(row, kp.String()[:6])
rowlen++ rowlen++
return rowlen < 4 return rowlen < 4
}) })
@ -471,7 +456,7 @@ func (self *Kademlia) String() string {
}) })
for i := 0; i < self.MaxProxDisplay; i++ { for i := 0; i < self.MaxProxDisplay; i++ {
if i == proxLimit { if i == depth {
rows = append(rows, fmt.Sprintf("============ PROX LIMIT: %d ==========================================", i)) rows = append(rows, fmt.Sprintf("============ PROX LIMIT: %d ==========================================", i))
} }
left := liverows[i] left := liverows[i]
@ -487,3 +472,95 @@ func (self *Kademlia) String() string {
rows = append(rows, "=========================================================================") rows = append(rows, "=========================================================================")
return "\n" + strings.Join(rows, "\n") return "\n" + strings.Join(rows, "\n")
} }
// Prune implements a forever loop reacting to a ticker time channel given
// as the first argument
// the loop quits if the channel is closed
// it checks each kademlia bin and if the peer count is higher than
// the MaxBinSize parameter it drops the oldest n peers such that
// the bin is reduced to MinBinSize peers thus leaving slots to newly
// connecting peers
func (self *Kademlia) Prune(c <-chan time.Time) {
go func() {
for range c {
total := 0
self.conns.EachBin(pot.NewBytesVal(self.base, nil), 0, func(po, size int, f func(func(pot.PotVal, int) bool) bool) bool {
extra := size - self.MinBinSize
if size > self.MaxBinSize {
n := 0
f(func(v pot.PotVal, po int) bool {
v.(*entry).conn().Drop(fmt.Errorf("bucket full"))
n++
return n < extra
})
total += extra
}
return true
})
log.Debug(fmt.Sprintf("pruned %v peers", total))
}
}()
}
func NewPeerPot(kadMinProxSize int, ids ...*adapters.NodeId) map[discover.NodeID][][]byte {
// create a table of all nodes for health check
np := pot.NewPot(nil, 0)
for _, id := range ids {
o := ToOverlayAddr(id.Bytes())
np, _, _ = pot.Add(np, pot.NewBytesVal(o, nil))
}
nnmap := make(map[discover.NodeID][][]byte)
for _, id := range ids {
pl := 0
var nns [][]byte
np.EachNeighbour(pot.NewBytesVal(id.Bytes(), nil), func(val pot.PotVal, po int) bool {
a := val.(pot.BytesAddress).Address()
nns = append(nns, a)
if len(nns) >= kadMinProxSize {
pl = po
}
return pl == 0 || pl == po
})
nnmap[id.NodeID] = nns
}
return nnmap
}
func (self *Kademlia) FirstEmptyBin() (i int) {
i = -1
self.conns.EachBin(pot.NewBytesVal(self.base, nil), 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool {
if po > i+1 {
i = po
return false
}
i = po
return true
})
return i
}
func (self *Kademlia) Full() bool {
return self.FirstEmptyBin() >= self.Depth()
}
// Healthy reports the health state of the kademlia connectivity
//
func (self *Kademlia) Healthy(peers [][]byte) bool {
return self.gotNearestNeighbours(peers) && self.Full()
}
func (self *Kademlia) gotNearestNeighbours(peers [][]byte) (got bool) {
pm := make(map[string]bool)
for _, p := range peers {
pm[string(p)] = true
}
self.EachConn(nil, 255, func(p OverlayConn, po int, nn bool) bool {
if !nn {
return false
}
_, got = pm[string(p.Address())]
return got
})
return got
}

View file

@ -17,6 +17,7 @@ package network
import ( import (
"fmt" "fmt"
"os"
"sync" "sync"
"testing" "testing"
"time" "time"
@ -25,9 +26,15 @@ import (
"github.com/ethereum/go-ethereum/pot" "github.com/ethereum/go-ethereum/pot"
) )
func testKadPeerAddr(s string) *peerAddr { func init() {
h := log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))
// h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true)))
log.Root().SetHandler(h)
}
func testKadPeerAddr(s string) *bzzAddr {
a := pot.NewHashAddress(s).Bytes() a := pot.NewHashAddress(s).Bytes()
return &peerAddr{OAddr: a, UAddr: a} return &bzzAddr{OAddr: a, UAddr: a}
} }
type testDropPeer struct { type testDropPeer struct {
@ -58,23 +65,24 @@ type dropError struct {
} }
func (self *testDropPeer) Drop(err error) { func (self *testDropPeer) Drop(err error) {
err2 := &dropError{err, overlayStr(self)} err2 := &dropError{err, binStr(self)}
self.dropc <- err2 self.dropc <- err2
} }
func (self *testDiscPeer) NotifyProx(po uint8) error { func (self *testDiscPeer) NotifyDepth(po uint8) error {
key := overlayStr(self) key := binStr(self)
self.lock.Lock() self.lock.Lock()
defer self.lock.Unlock() defer self.lock.Unlock()
self.notifications[key] = po self.notifications[key] = po
return nil return nil
} }
func (self *testDiscPeer) NotifyPeer(p Peer, po uint8) error { func (self *testDiscPeer) NotifyPeer(p OverlayAddr, po uint8) error {
key := overlayStr(self) key := binStr(self)
key += overlayStr(p) key += binStr(p)
self.lock.Lock() self.lock.Lock()
defer self.lock.Unlock() defer self.lock.Unlock()
log.Trace(fmt.Sprintf("key %v=>%v", key, po))
self.notifications[key] = po self.notifications[key] = po
return nil return nil
} }
@ -102,40 +110,13 @@ func newTestKademlia(b string) *testKademlia {
} }
func (k *testKademlia) newTestKadPeer(s string) Peer { func (k *testKademlia) newTestKadPeer(s string) Peer {
dp := &testDropPeer{&bzzPeer{peerAddr: testKadPeerAddr(s)}, k.dropc} dp := &testDropPeer{&bzzPeer{bzzAddr: testKadPeerAddr(s)}, k.dropc}
if k.Discovery { if k.Discovery {
return Peer(&testDiscPeer{dp, k.lock, k.notifications}) return Peer(&testDiscPeer{dp, k.lock, k.notifications})
} }
return Peer(dp) return Peer(dp)
} }
func overlayStr(a PeerAddr) string {
log.Error(fmt.Sprintf("PeerAddr: %v (%T)", a, a))
// if a == (*KadPeer)(nil) || a == (*testDiscPeer)(nil) || a == (*bzzPeer)(nil) || a == nil {
// return "<nil>"
// }
// var p Peer
// s, ok := a.(*KadPeer)
// if ok {
// p = s.Peer
// } else {
// p = a.(*testDiscPeer).Peer
// }
// log.Error(fmt.Sprintf("PeerAddr: %v (%T)", p, p))
// if p == (Peer)(nil) || p == (*testDiscPeer)(nil) || p == (*bzzPeer)(nil) {
// return "<nil>"
// }
// return pot.NewHashAddressFromBytes(p.OverlayAddr()).Bin()[:6]
if a == nil {
return "<nil>"
}
k, ok := a.(*KadPeer)
if ok && k.Peer != nil {
return pot.ToBin(a.(*KadPeer).Peer.OverlayAddr())[:6]
}
return pot.ToBin(a.OverlayAddr())[:6]
}
func (k *testKademlia) On(ons ...string) *testKademlia { func (k *testKademlia) On(ons ...string) *testKademlia {
for _, s := range ons { for _, s := range ons {
p := k.newTestKadPeer(s) p := k.newTestKadPeer(s)
@ -146,25 +127,30 @@ func (k *testKademlia) On(ons ...string) *testKademlia {
func (k *testKademlia) Off(offs ...string) *testKademlia { func (k *testKademlia) Off(offs ...string) *testKademlia {
for _, s := range offs { for _, s := range offs {
k.Kademlia.Off(k.newTestKadPeer(s)) k.Kademlia.Off(k.newTestKadPeer(s).(OverlayConn))
} }
return k return k
} }
func (k *testKademlia) Register(regs ...string) *testKademlia { func (k *testKademlia) Register(regs ...string) *testKademlia {
var ps []PeerAddr ch := make(chan OverlayAddr)
for _, s := range regs { go func() {
ps = append(ps, PeerAddr(testKadPeerAddr(s))) defer close(ch)
} for _, s := range regs {
k.Kademlia.Register(ps...) ch <- testKadPeerAddr(s)
}
}()
err := k.Kademlia.Register(ch)
log.Trace(fmt.Sprintf("register %v addresses: %v", len(regs), err))
return k return k
} }
func testSuggestPeer(t *testing.T, k *testKademlia, expAddr string, expPo int, expWant bool) error { func testSuggestPeer(t *testing.T, k *testKademlia, expAddr string, expPo int, expWant bool) error {
addr, o, want := k.SuggestPeer() addr, o, want := k.SuggestPeer()
if overlayStr(addr) != expAddr { if binStr(addr) != expAddr {
return fmt.Errorf("incorrect peer address suggested. expected %v, got %v", expAddr, overlayStr(addr)) return fmt.Errorf("incorrect peer address suggested. expected %v, got %v", expAddr, binStr(addr))
} }
if o != expPo { if o != expPo {
return fmt.Errorf("incorrect prox order suggested. expected %v, got %v", expPo, o) return fmt.Errorf("incorrect prox order suggested. expected %v, got %v", expPo, o)
@ -175,6 +161,13 @@ func testSuggestPeer(t *testing.T, k *testKademlia, expAddr string, expPo int, e
return nil return nil
} }
func binStr(a OverlayPeer) string {
if a == nil {
return "<nil>"
}
return pot.ToBin(a.Address())[:6]
}
func TestSuggestPeerFindPeers(t *testing.T) { func TestSuggestPeerFindPeers(t *testing.T) {
// 2 row gap, unsaturated proxbin, no callables -> want PO 0 // 2 row gap, unsaturated proxbin, no callables -> want PO 0
k := newTestKademlia("000000").On("001000") k := newTestKademlia("000000").On("001000")
@ -222,7 +215,6 @@ func TestSuggestPeerFindPeers(t *testing.T) {
// second time disconnected peer not callable // second time disconnected peer not callable
// with reasonably set Interval // with reasonably set Interval
// err = testSuggestPeer(t, k, "010000", 2, true)
err = testSuggestPeer(t, k, "<nil>", 1, true) err = testSuggestPeer(t, k, "<nil>", 1, true)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatal(err.Error())
@ -237,16 +229,16 @@ func TestSuggestPeerFindPeers(t *testing.T) {
} }
k.On("010000") k.On("010000")
k.Off("010000")
// PO1 disconnects
// new closer peer appears, it is immediately wanted // new closer peer appears, it is immediately wanted
// k.Off("010000")
k.Register("000101") k.Register("000101")
err = testSuggestPeer(t, k, "000101", 0, false) err = testSuggestPeer(t, k, "000101", 0, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatal(err.Error())
} }
// PO1 disconnects
k.On("000101")
k.Off("010000")
// second time, gap filling // second time, gap filling
err = testSuggestPeer(t, k, "010000", 0, false) err = testSuggestPeer(t, k, "010000", 0, false)
if err != nil { if err != nil {
@ -265,6 +257,19 @@ func TestSuggestPeerFindPeers(t *testing.T) {
t.Fatal(err.Error()) t.Fatal(err.Error())
} }
k.Register("010001")
err = testSuggestPeer(t, k, "<nil>", 0, true)
if err != nil {
t.Fatal(err.Error())
}
k.On("100001")
log.Trace("Kad:\n%v", k.String())
err = testSuggestPeer(t, k, "010001", 0, false)
if err != nil {
t.Fatal(err.Error())
}
k.On("100001") k.On("100001")
k.On("010001") k.On("010001")
err = testSuggestPeer(t, k, "<nil>", 0, false) err = testSuggestPeer(t, k, "<nil>", 0, false)
@ -273,7 +278,18 @@ func TestSuggestPeerFindPeers(t *testing.T) {
} }
k.MinBinSize = 3 k.MinBinSize = 3
k.Register("100010")
err = testSuggestPeer(t, k, "100010", 0, false)
if err != nil {
t.Fatal(err.Error())
}
k.On("100010") k.On("100010")
err = testSuggestPeer(t, k, "<nil>", 1, true)
if err != nil {
t.Fatal(err.Error())
}
k.On("010010") k.On("010010")
err = testSuggestPeer(t, k, "<nil>", 2, true) err = testSuggestPeer(t, k, "<nil>", 2, true)
if err != nil { if err != nil {
@ -281,8 +297,16 @@ func TestSuggestPeerFindPeers(t *testing.T) {
} }
k.On("001010") k.On("001010")
err = testSuggestPeer(t, k, "<nil>", 3, true)
if err != nil {
log.Trace("Kad:\n%v", k.String())
t.Fatal(err.Error())
}
k.On("000110")
err = testSuggestPeer(t, k, "<nil>", 0, false) err = testSuggestPeer(t, k, "<nil>", 0, false)
if err != nil { if err != nil {
log.Trace("Kad:\n%v", k.String())
t.Fatal(err.Error()) t.Fatal(err.Error())
} }
@ -440,7 +464,7 @@ func TestNotifications(t *testing.T) {
k.Discovery = true k.Discovery = true
k.MinProxBinSize = 3 k.MinProxBinSize = 3
k.On("010000", "001000") k.On("010000", "001000")
time.Sleep(100 * time.Millisecond) time.Sleep(1000 * time.Millisecond)
err := k.checkNotifications( err := k.checkNotifications(
[]*testPeerNotification{ []*testPeerNotification{
&testPeerNotification{"010000", "001000", 1}, &testPeerNotification{"010000", "001000", 1},

View file

@ -17,7 +17,11 @@
package network package network
import ( import (
"context"
"errors"
"fmt" "fmt"
"net"
"sync"
"time" "time"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -26,83 +30,213 @@ import (
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/protocols" "github.com/ethereum/go-ethereum/p2p/protocols"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters" "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/pot" "github.com/ethereum/go-ethereum/rpc"
) )
const ( const (
ProtocolName = "bzz"
Version = 0
NetworkId = 322 // BZZ in l33t NetworkId = 322 // BZZ in l33t
ProtocolMaxMsgSize = 10 * 1024 * 1024 ProtocolMaxMsgSize = 10 * 1024 * 1024
) )
// bzz is the bzz protocol view of a protocols.Peer (itself an extension of p2p.Peer) var BzzHandshakeSpec = &protocols.Spec{
type bzzPeer struct { Name: "bzz",
*protocols.Peer Version: 1,
localAddr *peerAddr MaxMsgSize: 10 * 1024 * 1024,
*peerAddr // remote address Messages: []interface{}{
lastActive time.Time bzzHandshake{},
},
} }
func (self *bzzPeer) LastActive() time.Time { var DiscoverySpec = &protocols.Spec{
return self.lastActive Name: "hive",
Version: 1,
MaxMsgSize: 10 * 1024 * 1024,
Messages: []interface{}{
peersMsg{},
getPeersMsg{},
subPeersMsg{},
},
} }
// implemented by peerAddr // the Addr interface that peerPool needs
type PeerAddr interface { type Addr interface {
OverlayAddr() []byte OverlayPeer
UnderlayAddr() []byte Over() []byte
PO(pot.PotVal, int) (int, bool) Under() []byte
String() string String() string
Update(OverlayAddr) OverlayAddr
} }
// the Peer interface that peerPool needs // Peer interface represents an live peer connection
type Peer interface { type Peer interface {
PeerAddr Addr // the address of a peer
// String() string // pretty printable the Node Conn // the live connection (protocols.Peer)
ID() discover.NodeID // the key that uniquely identifies the Node for the peerPool LastActive() time.Time // last time active
Send(interface{}) error // can send messages
Drop(error) // disconnect this peer
Register(interface{}, func(interface{}) error) uint64 // register message-handler callbacks
DisconnectHook(func(error))
} }
func BzzCodeMap(msgs ...interface{}) *protocols.CodeMap { // Conn interface represents an live peer connection
ct := protocols.NewCodeMap(ProtocolName, Version, ProtocolMaxMsgSize) type Conn interface {
ct.Register(&bzzHandshake{}) ID() discover.NodeID // the key that uniquely identifies the Node for the peerPool
ct.Register(msgs...) Handshake(context.Context, interface{}) (interface{}, error) // can send messages
return ct Send(interface{}) error // can send messages
Drop(error) // disconnect this peer
Run(func(interface{}) error) error // the run function to run a protocol
Off() OverlayAddr
} }
// Bzz is the protocol constructor // TODO: implement store for exec nodes
// returns p2p.Protocol that is to be offered by the node.Service type StateStore interface {
func Bzz(oAddr, uAddr []byte, ct *protocols.CodeMap, services func(Peer) error, peerInfo func(id discover.NodeID) interface{}, nodeInfo func() interface{}) *p2p.Protocol { Load(string) ([]byte, error)
run := func(p *protocols.Peer) error { Save(string, []byte) error
bee := &bzzPeer{ }
Peer: p,
localAddr: &peerAddr{oAddr, uAddr}, // BzzConfig captures the config params used by the hive
} type BzzConfig struct {
// protocol handshake and its validation OverlayAddr []byte
// sets remote peer address UnderlayAddr []byte
err := bee.bzzHandshake() HiveParams *HiveParams
if err != nil { }
log.Error(fmt.Sprintf("handshake error in peer %v: %v", bee.ID(), err))
// Bzz is the swarm protocol bundle
type Bzz struct {
Hive *Hive
localAddr *bzzAddr
mtx sync.Mutex
handshakes map[discover.NodeID]*bzzHandshake
}
// NewBzz is the swarm protocol constructor
func NewBzz(config *BzzConfig, kad Overlay, store StateStore) *Bzz {
return &Bzz{
Hive: NewHive(config.HiveParams, kad, store),
localAddr: &bzzAddr{config.OverlayAddr, config.UnderlayAddr},
handshakes: make(map[discover.NodeID]*bzzHandshake),
}
}
// Bzz implements the node.Service interface, offers Protocols
// * handshake/hive
// * discovery
func (b *Bzz) Protocols() []p2p.Protocol {
return []p2p.Protocol{
{
Name: BzzHandshakeSpec.Name,
Version: BzzHandshakeSpec.Version,
Length: BzzHandshakeSpec.Length(),
Run: b.runHandshake,
},
{
Name: DiscoverySpec.Name,
Version: DiscoverySpec.Version,
Length: DiscoverySpec.Length(),
Run: b.runProtocol(DiscoverySpec, b.Hive.Run),
NodeInfo: b.Hive.NodeInfo,
PeerInfo: b.Hive.PeerInfo,
},
}
}
// Bzz implements the node.Service interface, offers APIs:
// * hive
func (b *Bzz) APIs() []rpc.API {
return []rpc.API{{
Namespace: "hive",
Version: "1.0",
Service: b.Hive,
}}
}
func (b *Bzz) Start(server *p2p.Server) error {
return b.Hive.Start(server)
}
func (b *Bzz) Stop() error {
b.Hive.Stop()
return nil
}
func (b *Bzz) runHandshake(p *p2p.Peer, rw p2p.MsgReadWriter) error {
handshake := b.getHandshake(p.ID())
defer b.removeHandshake(p.ID())
if err := handshake.Perform(p, rw); err != nil {
log.Error("handshake failed", "peer", p.ID(), "err", err)
return err
}
// fail if we get another handshake
msg, err := rw.ReadMsg()
if err != nil {
return err
}
msg.Discard()
return errors.New("received multiple handshakes")
}
func (b *Bzz) runProtocol(spec *protocols.Spec, run func(*bzzPeer) error) func(*p2p.Peer, p2p.MsgReadWriter) error {
return func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
// wait for the bzz protocol to perform the handshake
handshake := b.getHandshake(p.ID())
if err := handshake.Wait(); err != nil {
return err return err
} }
// mount external service models on the peer connection (swap, sync, hive) // the handshake has succeeded so run the service
if services != nil { peer := &bzzPeer{
err = services(bee) Peer: protocols.NewPeer(p, rw, spec),
if err != nil { localAddr: b.localAddr,
log.Error(fmt.Sprintf("protocol service error for peer %v: %v", bee.ID(), err)) bzzAddr: handshake.peerAddr,
return err
}
} }
return run(peer)
return bee.Run()
} }
}
return protocols.NewProtocol(ProtocolName, Version, run, ct, peerInfo, nodeInfo) func (b *Bzz) removeHandshake(peerID discover.NodeID) {
b.mtx.Lock()
defer b.mtx.Unlock()
delete(b.handshakes, peerID)
}
func (b *Bzz) getHandshake(peerID discover.NodeID) *bzzHandshake {
b.mtx.Lock()
defer b.mtx.Unlock()
handshake, ok := b.handshakes[peerID]
if !ok {
handshake = &bzzHandshake{
Version: uint64(BzzHandshakeSpec.Version),
NetworkId: uint64(NetworkId),
Addr: b.localAddr,
done: make(chan struct{}),
}
b.handshakes[peerID] = handshake
}
return handshake
}
// bzzPeer is the bzz protocol view of a protocols.Peer (itself an extension of p2p.Peer)
// implements the Peer interface and all interfaces Peer implements: Addr, OverlayPeer
type bzzPeer struct {
*protocols.Peer // represents the connection for online peers
localAddr *bzzAddr // local Peers address
*bzzAddr // remote address -> implements Addr interface = protocols.Peer
lastActive time.Time // time is updated whenever mutexes are releasing
}
func newBzzPeer(p *protocols.Peer, over, under []byte) *bzzPeer {
return &bzzPeer{
Peer: p,
localAddr: &bzzAddr{over, under},
}
}
// Off returns the overlay peer record for offline persistance
func (self *bzzPeer) Off() OverlayAddr {
return self.bzzAddr
}
// LastActive returns the time the peer was last active
func (self *bzzPeer) LastActive() time.Time {
return self.lastActive
} }
/* /*
@ -115,105 +249,84 @@ func Bzz(oAddr, uAddr []byte, ct *protocols.CodeMap, services func(Peer) error,
type bzzHandshake struct { type bzzHandshake struct {
Version uint64 Version uint64
NetworkId uint64 NetworkId uint64
Addr *peerAddr Addr *bzzAddr
// peerAddr is the address received in the peer handshake
peerAddr *bzzAddr
done chan struct{}
err error
} }
func (self *bzzHandshake) String() string { func (self *bzzHandshake) String() string {
return fmt.Sprintf("Handshake: Version: %v, NetworkId: %v, Addr: %v", self.Version, self.NetworkId, self.Addr) return fmt.Sprintf("Handshake: Version: %v, NetworkId: %v, Addr: %v", self.Version, self.NetworkId, self.Addr)
} }
// peerAddr implements the PeerAddress interface const bzzHandshakeTimeout = time.Second
type peerAddr struct {
func (self *bzzHandshake) Perform(p *p2p.Peer, rw p2p.MsgReadWriter) (err error) {
defer func() {
self.err = err
close(self.done)
}()
peer := protocols.NewPeer(p, rw, BzzHandshakeSpec)
ctx, cancel := context.WithTimeout(context.Background(), bzzHandshakeTimeout)
defer cancel()
hs, err := peer.Handshake(ctx, self)
if err != nil {
return err
}
rhs := hs.(*bzzHandshake)
if rhs.NetworkId != self.NetworkId {
return fmt.Errorf("network id mismatch %d (!= %d)", rhs.NetworkId, self.NetworkId)
}
if rhs.Version != self.Version {
return fmt.Errorf("version mismatch %d (!= %d)", rhs.Version, self.Version)
}
self.peerAddr = rhs.Addr
return nil
}
func (self *bzzHandshake) Wait() error {
select {
case <-self.done:
return self.err
case <-time.After(bzzHandshakeTimeout):
return errors.New("timed out waiting for bzz handshake")
}
}
// bzzAddr implements the PeerAddr interface
type bzzAddr struct {
OAddr []byte OAddr []byte
UAddr []byte UAddr []byte
} }
func (self *peerAddr) OverlayAddr() []byte { // implements OverlayPeer interface to be used in pot package
func (self *bzzAddr) Address() []byte {
return self.OAddr return self.OAddr
} }
func (self *peerAddr) UnderlayAddr() []byte { // Over returns the overlay address
func (self *bzzAddr) Over() []byte {
return self.OAddr
}
// Under retrun the underlay address
func (self *bzzAddr) Under() []byte {
return self.UAddr return self.UAddr
} }
func (self *peerAddr) PO(val pot.PotVal, pos int) (int, bool) { func (self *bzzAddr) Update(a OverlayAddr) OverlayAddr {
kp := val.(PeerAddr) return &bzzAddr{self.OAddr, a.(Addr).Under()}
one := kp.OverlayAddr()
other := self.OAddr
for i := pos / 8; i < len(one); i++ {
if one[i] == other[i] {
continue
}
oxo := one[i] ^ other[i]
start := 0
if i == pos/8 {
start = pos % 8
}
for j := start; j < 8; j++ {
if (uint8(oxo)>>uint8(7-j))&0x01 != 0 {
return i*8 + j, false
}
}
}
return len(one) * 8, true
// var ha *pot.HashAddress
// var left, right string
// if ok {
// ha = kp.HashAddress
// } else {
// ha = val.(*pot.HashAddress)
// }
} }
func (self *peerAddr) String() string { func (self *bzzAddr) String() string {
return fmt.Sprintf("%x <%x>", self.OAddr, self.UAddr) return fmt.Sprintf("%x <%s>", self.OAddr, self.UAddr)
}
// bzzHandshake negotiates the bzz master handshake
// and validates the response, returns error when
// mismatch/incompatibility is evident
func (self *bzzPeer) bzzHandshake() error {
lhs := &bzzHandshake{
Version: uint64(Version),
NetworkId: uint64(NetworkId),
Addr: self.localAddr,
}
hs, err := self.Handshake(lhs)
if err != nil {
log.Error(fmt.Sprintf("handshake failed: %v", err))
return err
}
rhs := hs.(*bzzHandshake)
self.peerAddr = rhs.Addr
err = checkBzzHandshake(rhs)
if err != nil {
log.Error(fmt.Sprintf("handshake between %v and %v failed: %v", self.localAddr, self.peerAddr, err))
return err
}
return nil
}
// checkBzzHandshake checks for the validity and compatibility of the remote handshake
func checkBzzHandshake(rhs *bzzHandshake) error {
if NetworkId != rhs.NetworkId {
return fmt.Errorf("network id mismatch %d (!= %d)", rhs.NetworkId, NetworkId)
}
if Version != rhs.Version {
return fmt.Errorf("version mismatch %d (!= %d)", rhs.Version, Version)
}
return nil
} }
// RandomAddr is a utility method generating an address from a public key // RandomAddr is a utility method generating an address from a public key
func RandomAddr() *peerAddr { func RandomAddr() *bzzAddr {
key, err := crypto.GenerateKey() key, err := crypto.GenerateKey()
if err != nil { if err != nil {
panic("unable to generate key") panic("unable to generate key")
@ -221,23 +334,28 @@ func RandomAddr() *peerAddr {
pubkey := crypto.FromECDSAPub(&key.PublicKey) pubkey := crypto.FromECDSAPub(&key.PublicKey)
var id discover.NodeID var id discover.NodeID
copy(id[:], pubkey[1:]) copy(id[:], pubkey[1:])
return &peerAddr{ return &bzzAddr{
OAddr: crypto.Keccak256(pubkey[1:]), OAddr: crypto.Keccak256(pubkey[1:]),
UAddr: id[:], UAddr: id[:],
} }
} }
// NodeId transforms the underlay address to an adapters.NodeId // NewNodeIdFromAddr transforms the underlay address to an adapters.NodeId
func NodeId(addr PeerAddr) *adapters.NodeId { func NewNodeIdFromAddr(addr Addr) *adapters.NodeId {
return adapters.NewNodeId(addr.UnderlayAddr()) return adapters.NewNodeId(addr.Under())
} }
// NewPeerAddrFromNodeId constucts a peerAddr from an adapters.NodeId // NewAddrFromNodeId constucts a bzzAddr from an adapters.NodeId
// the overlay address is derived as the hash of the nodeId // the overlay address is derived as the hash of the nodeId
func NewPeerAddrFromNodeId(n *adapters.NodeId) *peerAddr { func NewAddrFromNodeId(n *adapters.NodeId) Addr {
id := n.NodeID id := n.NodeID
return &peerAddr{ return &bzzAddr{
OAddr: crypto.Keccak256(id[:]), OAddr: ToOverlayAddr(n.Bytes()),
UAddr: id[:], UAddr: []byte(discover.NewNode(id, net.IP{127, 0, 0, 1}, 30303, 30303).String()),
} }
} }
// ToOverlayAddr creates an overlayaddress from NodeID
func ToOverlayAddr(id []byte) []byte {
return crypto.Keccak256(id)
}

View file

@ -2,14 +2,42 @@ package network
import ( import (
"fmt" "fmt"
"sync"
"testing" "testing"
"github.com/ethereum/go-ethereum/log" "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/p2p/simulations/adapters" "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
p2ptest "github.com/ethereum/go-ethereum/p2p/testing" p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
) )
type testStore struct {
sync.Mutex
values map[string][]byte
}
func newTestStore() *testStore {
return &testStore{values: make(map[string][]byte)}
}
func (t *testStore) Load(key string) ([]byte, error) {
t.Lock()
defer t.Unlock()
v, ok := t.values[key]
if !ok {
return nil, fmt.Errorf("key not found: %s", key)
}
return v, nil
}
func (t *testStore) Save(key string, v []byte) error {
t.Lock()
defer t.Unlock()
t.values[key] = v
return nil
}
func bzzHandshakeExchange(lhs, rhs *bzzHandshake, id *adapters.NodeId) []p2ptest.Exchange { func bzzHandshakeExchange(lhs, rhs *bzzHandshake, id *adapters.NodeId) []p2ptest.Exchange {
return []p2ptest.Exchange{ return []p2ptest.Exchange{
@ -34,21 +62,23 @@ func bzzHandshakeExchange(lhs, rhs *bzzHandshake, id *adapters.NodeId) []p2ptest
} }
} }
func newBzzBaseTester(t *testing.T, n int, addr *peerAddr, ct *protocols.CodeMap, services func(Peer) error) *bzzTester { func newBzzBaseTester(t *testing.T, n int, addr *bzzAddr, spec *protocols.Spec, run func(*bzzPeer) error) *bzzTester {
if ct == nil {
ct = BzzCodeMap()
}
cs := make(map[string]chan bool) cs := make(map[string]chan bool)
srv := func(p Peer) error { srv := func(p *bzzPeer) error {
defer close(cs[p.ID().String()]) defer close(cs[p.ID().String()])
return services(p) return run(p)
} }
protocall := Bzz(addr.OverlayAddr(), addr.UnderlayAddr(), ct, srv, nil, nil).Run protocall := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
return srv(&bzzPeer{
Peer: protocols.NewPeer(p, rw, spec),
localAddr: addr,
bzzAddr: NewAddrFromNodeId(&adapters.NodeId{NodeID: p.ID()}),
})
}
s := p2ptest.NewProtocolTester(t, NodeId(addr), n, protocall) s := p2ptest.NewProtocolTester(t, NewNodeIdFromAddr(addr), n, protocall)
for _, id := range s.Ids { for _, id := range s.Ids {
cs[id.NodeID.String()] = make(chan bool) cs[id.NodeID.String()] = make(chan bool)
@ -63,32 +93,27 @@ func newBzzBaseTester(t *testing.T, n int, addr *peerAddr, ct *protocols.CodeMap
type bzzTester struct { type bzzTester struct {
*p2ptest.ProtocolTester *p2ptest.ProtocolTester
addr *peerAddr addr *bzzAddr
cs map[string]chan bool cs map[string]chan bool
} }
func newBzzTester(t *testing.T, n int, addr *peerAddr, pp *p2ptest.TestPeerPool, ct *protocols.CodeMap, services func(Peer) error) *bzzTester { func newBzzTester(t *testing.T, n int, addr *bzzAddr, pp *p2ptest.TestPeerPool, spec *protocols.Spec, services func(Peer) error) *bzzTester {
extraservices := func(p Peer) error { extraservices := func(p *bzzPeer) error {
pp.Add(p) pp.Add(p)
p.DisconnectHook(func(err error) { defer pp.Remove(p)
pp.Remove(p) if services == nil {
}) return nil
if services != nil {
err := services(p)
if err != nil {
return err
}
} }
return nil return services(p)
} }
return newBzzBaseTester(t, n, addr, ct, extraservices) return newBzzBaseTester(t, n, addr, spec, extraservices)
} }
// should test handshakes in one exchange? parallelisation // should test handshakes in one exchange? parallelisation
func (s *bzzTester) testHandshake(lhs, rhs *bzzHandshake, disconnects ...*p2ptest.Disconnect) { func (s *bzzTester) testHandshake(lhs, rhs *bzzHandshake, disconnects ...*p2ptest.Disconnect) {
var peers []*adapters.NodeId var peers []*adapters.NodeId
id := NodeId(rhs.Addr) id := NewNodeIdFromAddr(rhs.Addr)
if len(disconnects) > 0 { if len(disconnects) > 0 {
for _, d := range disconnects { for _, d := range disconnects {
peers = append(peers, d.Peer) peers = append(peers, d.Peer)
@ -106,14 +131,18 @@ func (s *bzzTester) runHandshakes(ids ...*adapters.NodeId) {
ids = s.Ids ids = s.Ids
} }
for _, id := range ids { for _, id := range ids {
s.testHandshake(correctBzzHandshake(s.addr), correctBzzHandshake(NewPeerAddrFromNodeId(id))) s.testHandshake(correctBzzHandshake(s.addr), correctBzzHandshake(NewAddrFromNodeId(id)))
<-s.cs[id.NodeID.String()] <-s.cs[id.NodeID.String()]
} }
} }
func correctBzzHandshake(addr *peerAddr) *bzzHandshake { func correctBzzHandshake(addr *bzzAddr) *bzzHandshake {
return &bzzHandshake{0, 322, addr} return &bzzHandshake{
Version: 0,
NetworkId: 322,
Addr: addr,
}
} }
func TestBzzHandshakeNetworkIdMismatch(t *testing.T) { func TestBzzHandshakeNetworkIdMismatch(t *testing.T) {
@ -125,7 +154,7 @@ func TestBzzHandshakeNetworkIdMismatch(t *testing.T) {
id := s.Ids[0] id := s.Ids[0]
s.testHandshake( s.testHandshake(
correctBzzHandshake(addr), correctBzzHandshake(addr),
&bzzHandshake{0, 321, NewPeerAddrFromNodeId(id)}, &bzzHandshake{Version: 0, NetworkId: 321, Addr: NewAddrFromNodeId(id)},
&p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("network id mismatch 321 (!= 322)")}, &p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("network id mismatch 321 (!= 322)")},
) )
} }
@ -139,7 +168,7 @@ func TestBzzHandshakeVersionMismatch(t *testing.T) {
id := s.Ids[0] id := s.Ids[0]
s.testHandshake( s.testHandshake(
correctBzzHandshake(addr), correctBzzHandshake(addr),
&bzzHandshake{1, 322, NewPeerAddrFromNodeId(id)}, &bzzHandshake{Version: 1, NetworkId: 322, Addr: NewAddrFromNodeId(id)},
&p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("version mismatch 1 (!= 0)")}, &p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("version mismatch 1 (!= 0)")},
) )
} }
@ -153,70 +182,6 @@ func TestBzzHandshakeSuccess(t *testing.T) {
id := s.Ids[0] id := s.Ids[0]
s.testHandshake( s.testHandshake(
correctBzzHandshake(addr), correctBzzHandshake(addr),
&bzzHandshake{0, 322, NewPeerAddrFromNodeId(id)}, &bzzHandshake{Version: 0, NetworkId: 322, Addr: NewAddrFromNodeId(id)},
) )
} }
func TestBzzPeerPoolAdd(t *testing.T) {
pp := p2ptest.NewTestPeerPool()
addr := RandomAddr()
s := newBzzTester(t, 1, addr, pp, nil, nil)
defer s.Stop()
id := s.Ids[0]
log.Trace(fmt.Sprintf("handshake with %v", id))
s.runHandshakes()
if !pp.Has(id) {
t.Fatalf("peer '%v' not added: %v", id, pp)
}
}
func TestBzzPeerPoolRemove(t *testing.T) {
addr := RandomAddr()
pp := p2ptest.NewTestPeerPool()
s := newBzzTester(t, 1, addr, pp, nil, nil)
defer s.Stop()
s.runHandshakes()
id := s.Ids[0]
pp.Get(id).Drop(fmt.Errorf("p2p: read or write on closed message pipe"))
s.TestDisconnected(&p2ptest.Disconnect{id, fmt.Errorf("p2p: read or write on closed message pipe")})
if pp.Has(id) {
t.Fatalf("peer '%v' not removed: %v", id, pp)
}
}
func TestBzzPeerPoolBothAddRemove(t *testing.T) {
addr := RandomAddr()
pp := p2ptest.NewTestPeerPool()
s := newBzzTester(t, 1, addr, pp, nil, nil)
defer s.Stop()
s.runHandshakes()
id := s.Ids[0]
if !pp.Has(id) {
t.Fatalf("peer '%v' not added: %v", id, pp)
}
pp.Get(id).Drop(fmt.Errorf("p2p: read or write on closed message pipe"))
s.TestDisconnected(&p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("p2p: read or write on closed message pipe")})
if pp.Has(id) {
t.Fatalf("peer '%v' not removed: %v", id, pp)
}
}
func TestBzzPeerPoolNotAdd(t *testing.T) {
addr := RandomAddr()
pp := p2ptest.NewTestPeerPool()
s := newBzzTester(t, 1, addr, pp, nil, nil)
defer s.Stop()
id := s.Ids[0]
s.testHandshake(correctBzzHandshake(addr), &bzzHandshake{0, 321, NewPeerAddrFromNodeId(id)}, &p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("network id mismatch 321 (!= 322)")})
if pp.Has(id) {
t.Fatalf("peer %v incorrectly added: %v", id, pp)
}
}

View file

@ -1,66 +0,0 @@
package network
import (
"context"
"fmt"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
)
type PssApi struct {
*Pss
}
func NewPssApi(ps *Pss) *PssApi {
return &PssApi{Pss: ps}
}
func (self *PssApi) NewMsg(ctx context.Context, topic PssTopic) (*rpc.Subscription, error) {
notifier, supported := rpc.NotifierFromContext(ctx)
if !supported {
return nil, fmt.Errorf("Subscribe not supported")
}
sub := notifier.CreateSubscription()
ch := make(chan []byte)
psssub, err := self.Pss.Subscribe(&topic, ch)
if err != nil {
return nil, fmt.Errorf("pss subscription topic %v (rpc sub id %v) failed: %v", topic, sub.ID, err)
}
go func(topic PssTopic) error {
for {
select {
case msg := <-ch:
if err := notifier.Notify(sub.ID, msg); err != nil {
log.Warn(fmt.Sprintf("notification on pss sub topic %v rpc (sub %v) msg %v failed!", topic, sub.ID, msg))
return err
}
case err := <-psssub.Err():
log.Warn(fmt.Sprintf("caught subscription error in pss sub topic: %v", topic, err))
return err
case <-notifier.Closed():
log.Warn(fmt.Sprintf("rpc sub notifier closed"))
psssub.Unsubscribe()
return nil
case err := <-sub.Err():
log.Warn(fmt.Sprintf("rpc sub closed: %v", err))
psssub.Unsubscribe()
return nil
}
}
return nil
}(topic)
return sub, nil
}
func (self *PssApi) SendRaw(to []byte, topic PssTopic, msg []byte) error {
err := self.Pss.Send(to, topic, msg)
if err != nil {
return fmt.Errorf("send error: %v", err)
}
return fmt.Errorf("ok sent")
}

View file

@ -9,22 +9,20 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
p2pnode "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/simulations" "github.com/ethereum/go-ethereum/p2p/simulations"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters" "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/swarm/network" "github.com/ethereum/go-ethereum/swarm/network"
) )
// serviceName is used with the exec adapter so the exec'd binary knows which // serviceName is used with the exec adapter so the exec'd binary knows which
// service to execute // service to execute
const serviceName = "discovery" const serviceName = "discovery"
const testMinProxBinSize = 2
var services = adapters.Services{ var services = adapters.Services{
serviceName: func(id *adapters.NodeId, snapshot []byte) p2pnode.Service { serviceName: newService,
return newNode(id)
},
} }
func init() { func init() {
@ -70,7 +68,7 @@ func testDiscoverySimulation(t *testing.T, adapter adapters.NodeAdapter) {
for i := 0; i < nodeCount; i++ { for i := 0; i < nodeCount; i++ {
node, err := net.NewNode() node, err := net.NewNode()
if err != nil { if err != nil {
t.Fatalf("error starting node %s: %s", node.ID().Label(), err) t.Fatalf("error starting node: %s", err)
} }
if err := net.Start(node.ID()); err != nil { if err := net.Start(node.ID()); err != nil {
t.Fatalf("error starting node %s: %s", node.ID().Label(), err) t.Fatalf("error starting node %s: %s", node.ID().Label(), err)
@ -97,6 +95,7 @@ func testDiscoverySimulation(t *testing.T, adapter adapters.NodeAdapter) {
} }
return nil return nil
} }
nnmap := network.NewPeerPot(testMinProxBinSize, ids...)
check := func(ctx context.Context, id *adapters.NodeId) (bool, error) { check := func(ctx context.Context, id *adapters.NodeId) (bool, error) {
select { select {
case <-ctx.Done(): case <-ctx.Done():
@ -113,7 +112,7 @@ func testDiscoverySimulation(t *testing.T, adapter adapters.NodeAdapter) {
return false, fmt.Errorf("error getting node client: %s", err) return false, fmt.Errorf("error getting node client: %s", err)
} }
var healthy bool var healthy bool
if err := client.Call(&healthy, "hive_healthy", nil); err != nil { if err := client.Call(&healthy, "hive_healthy", nnmap[id.NodeID]); err != nil {
return false, fmt.Errorf("error getting node health: %s", err) return false, fmt.Errorf("error getting node health: %s", err)
} }
return healthy, nil return healthy, nil
@ -179,70 +178,26 @@ func triggerChecks(trigger chan *adapters.NodeId, net *simulations.Network, id *
return nil return nil
} }
type node struct { func newService(id *adapters.NodeId, snapshot []byte) node.Service {
*network.Hive addr := network.NewAddrFromNodeId(id)
protocol *p2p.Protocol kp := network.NewKadParams()
} kp.MinProxBinSize = testMinProxBinSize
kp.MaxBinSize = 3
kp.MinBinSize = 1
kp.MaxRetries = 1000
kp.RetryExponent = 2
kp.RetryInterval = 1000000
kad := network.NewKademlia(addr.Over(), kp)
func newNode(id *adapters.NodeId) *node { hp := network.NewHiveParams()
addr := network.NewPeerAddrFromNodeId(id) hp.KeepAliveInterval = time.Second
kademlia := newKademlia(addr.OverlayAddr())
hive := newHive(kademlia) config := &network.BzzConfig{
codeMap := network.BzzCodeMap(network.DiscoveryMsgs...) OverlayAddr: addr.Over(),
node := &node{Hive: hive} UnderlayAddr: addr.Under(),
services := func(peer network.Peer) error { HiveParams: hp,
discoveryPeer := network.NewDiscovery(peer, kademlia)
node.Add(discoveryPeer)
peer.DisconnectHook(func(err error) {
node.Remove(discoveryPeer)
})
return nil
} }
node.protocol = network.Bzz(addr.OverlayAddr(), addr.UnderlayAddr(), codeMap, services, nil, nil)
return node return network.NewBzz(config, kad, nil)
}
func newKademlia(overlayAddr []byte) *network.Kademlia {
params := network.NewKadParams()
params.MinProxBinSize = 2
params.MaxBinSize = 3
params.MinBinSize = 1
params.MaxRetries = 1000
params.RetryExponent = 2
params.RetryInterval = 1000000
return network.NewKademlia(overlayAddr, params)
}
func newHive(kademlia *network.Kademlia) *network.Hive {
params := network.NewHiveParams()
params.CallInterval = 5000
return network.NewHive(params, kademlia)
}
func (n *node) Protocols() []p2p.Protocol {
return []p2p.Protocol{*n.protocol}
}
func (n *node) APIs() []rpc.API {
return []rpc.API{{
Namespace: "hive",
Version: "1.0",
Service: n.Hive,
}}
}
func (n *node) Start(server p2p.Server) error {
return n.Hive.Start(server, n.hiveKeepAlive)
}
func (n *node) Stop() error {
n.Hive.Stop()
return nil
}
func (n *node) hiveKeepAlive() <-chan time.Time {
return time.Tick(time.Second)
} }

View file

@ -11,83 +11,58 @@ import (
"net/http" "net/http"
"os" "os"
"runtime" "runtime"
"sync"
"time" "time"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/simulations" "github.com/ethereum/go-ethereum/p2p/simulations"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters" "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/swarm/network" "github.com/ethereum/go-ethereum/swarm/network"
) )
// SimNode is the adapter used by Swarm simulations. type Simulation struct {
type SimNode struct { mtx sync.Mutex
hive *network.Hive stores map[discover.NodeID]*adapters.stateStore
protocol *p2p.Protocol
} }
func (s *SimNode) Protocols() []p2p.Protocol { func NewSimulation() *Simulation {
return []p2p.Protocol{*s.protocol} return &Simulation{
stores: make(map[discover.NodeID]*adapters.stateStore),
}
} }
func (s *SimNode) APIs() []rpc.API { func (s *Simulation) NewService(id *adapters.NodeId, snapshot []byte) node.Service {
return nil s.mtx.Lock()
} store, ok := s.stores[id.NodeID]
if !ok {
store = NewSimStore()
s.stores[id.NodeID] = store
}
s.mtx.Unlock()
// the hive update ticker for hive addr := network.NewAddrFromNodeId(id)
func af() <-chan time.Time {
return time.NewTicker(1 * time.Second).C
}
// Start() starts up the hive
// makes SimNode implement node.Service
func (self *SimNode) Start(server p2p.Server) error {
return self.hive.Start(server, af)
}
// Stop() shuts down the hive
// makes SimNode implement node.Service
func (self *SimNode) Stop() error {
self.hive.Stop()
return nil
}
// NewSimNode creates adapters for nodes in the simulation.
func NewSimNode(id *adapters.NodeId, snapshot []byte) node.Service {
addr := network.NewPeerAddrFromNodeId(id)
kp := network.NewKadParams() kp := network.NewKadParams()
kp.MinProxBinSize = 2 kp.MinProxBinSize = 2
kp.MaxBinSize = 3 kp.MaxBinSize = 8
kp.MinBinSize = 1 kp.MinBinSize = 2
kp.MaxRetries = 1000 kp.MaxRetries = 1000
kp.RetryExponent = 2 kp.RetryExponent = 2
kp.RetryInterval = 1000000 kp.RetryInterval = 1000
kad := network.NewKademlia(addr.Over(), kp)
to := network.NewKademlia(addr.OverlayAddr(), kp) // overlay topology driver
hp := network.NewHiveParams() hp := network.NewHiveParams()
hp.CallInterval = 5000 hp.KeepAliveInterval = 3 * time.Second
pp := network.NewHive(hp, to) // hive
services := func(p network.Peer) error { config := &network.BzzConfig{
dp := network.NewDiscovery(p, to) OverlayAddr: addr.Over(),
pp.Add(dp) UnderlayAddr: addr.Under(),
log.Trace(fmt.Sprintf("kademlia on %v", dp)) HiveParams: hp,
p.DisconnectHook(func(err error) {
pp.Remove(dp)
})
return nil
} }
ct := network.BzzCodeMap(network.DiscoveryMsgs...) // bzz protocol code map return network.NewBzz(config, kad, store)
nodeInfo := func() interface{} { return pp.String() }
return &SimNode{
hive: pp,
protocol: network.Bzz(addr.OverlayAddr(), addr.UnderlayAddr(), ct, services, nil, nodeInfo),
}
} }
func createMockers() map[string]*simulations.MockerConfig { func createMockers() map[string]*simulations.MockerConfig {
@ -119,8 +94,9 @@ func setupMocker(net *simulations.Network) []*adapters.NodeId {
conf := net.Config() conf := net.Config()
conf.DefaultService = "overlay" conf.DefaultService = "overlay"
ids := make([]*adapters.NodeId, 10) nodeCount := 50
for i := 0; i < 10; i++ { ids := make([]*adapters.NodeId, nodeCount)
for i := 0; i < nodeCount; i++ {
node, err := net.NewNode() node, err := net.NewNode()
if err != nil { if err != nil {
panic(err.Error()) panic(err.Error())
@ -129,8 +105,6 @@ func setupMocker(net *simulations.Network) []*adapters.NodeId {
} }
for _, id := range ids { for _, id := range ids {
n := rand.Intn(1000)
time.Sleep(time.Duration(n) * time.Millisecond)
if err := net.Start(id); err != nil { if err := net.Start(id); err != nil {
panic(err.Error()) panic(err.Error())
} }
@ -143,7 +117,12 @@ func setupMocker(net *simulations.Network) []*adapters.NodeId {
} else { } else {
peerId = ids[i-1] peerId = ids[i-1]
} }
if err := net.Connect(id, peerId); err != nil { ch := make(chan network.OverlayAddr)
go func() {
defer close(ch)
ch <- network.NewAddrFromNodeId(peerId)
}()
if err := net.GetNode(id).Node.(*adapters.SimNode).Service().(*network.Bzz).Hive.Register(ch); err != nil {
panic(err.Error()) panic(err.Error())
} }
} }
@ -191,23 +170,23 @@ func randomMocker(net *simulations.Network) {
func startStopMocker(net *simulations.Network) { func startStopMocker(net *simulations.Network) {
ids := setupMocker(net) ids := setupMocker(net)
for i, id := range ids { for range time.Tick(10 * time.Second) {
n := 3000 + i*1000 id := ids[rand.Intn(len(ids))]
go func(id *adapters.NodeId) { go func() {
for { log.Error("stopping node", "id", id)
// n := rand.Intn(5000) if err := net.Stop(id); err != nil {
// n := 3000 log.Error("error stopping node", "id", id, "err", err)
time.Sleep(time.Duration(n) * time.Millisecond) return
log.Debug(fmt.Sprintf("node %v shutting down", id))
net.Stop(id)
// n = rand.Intn(5000)
n = 2000
time.Sleep(time.Duration(n) * time.Millisecond)
log.Debug(fmt.Sprintf("node %v starting up", id))
net.Start(id)
n = 5000
} }
}(id)
time.Sleep(3 * time.Second)
log.Error("starting node", "id", id)
if err := net.Start(id); err != nil {
log.Error("error starting node", "id", id, "err", err)
return
}
}()
} }
} }
@ -215,10 +194,11 @@ func startStopMocker(net *simulations.Network) {
func main() { func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())
log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) log.Root().SetHandler(log.LvlFilterHandler(log.LvlDebug, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
s := NewSimulation()
services := adapters.Services{ services := adapters.Services{
"overlay": NewSimNode, "overlay": s.NewService,
} }
adapters.RegisterServices(services) adapters.RegisterServices(services)
@ -226,7 +206,7 @@ func main() {
config := &simulations.ServerConfig{ config := &simulations.ServerConfig{
NewAdapter: func() adapters.NodeAdapter { return adapters.NewSimAdapter(services) }, NewAdapter: func() adapters.NodeAdapter { return adapters.NewSimAdapter(services) },
DefaultMockerId: "start-stop", DefaultMockerId: "bootNet",
Mockers: mockers, Mockers: mockers,
} }

View file

@ -1,182 +1,193 @@
package network package network
import ( //
"fmt" // import (
"strings" // "fmt"
"sync" // "strings"
// "sync"
"github.com/ethereum/go-ethereum/log" //
) // "github.com/ethereum/go-ethereum/log"
// )
const orders = 8 //
// const orders = 8
type testOverlay struct { //
mu sync.Mutex // type testOverlay struct {
addr []byte // mu sync.Mutex
pos [][]*testPeerAddr // addr []byte
posMap map[string]*testPeerAddr // pos [][]OverlayAddr
} // posMap map[string]OverlayAddr
// }
type testPeerAddr struct { //
PeerAddr // type testPeerAddr struct {
Peer Peer // Addr
} // Peer
// }
func (self *testOverlay) Register(nas ...PeerAddr) error { //
self.mu.Lock() // func (self *testPeerAddr) Address() []byte {
defer self.mu.Unlock() // return nil
return self.register(nas...) // }
} //
// func (self *testPeerAddr) Update(a OverlayAddr) OverlayAddr {
func (self *testOverlay) GetAddr() PeerAddr { // return self
return &peerAddr{ // }
OAddr: self.addr, //
UAddr: []byte{}, // func (self *testPeerAddr) On(p OverlayConn) OverlayConn {
} // return self
} // }
//
func (self *testOverlay) register(nas ...PeerAddr) error { // func (self *testPeerAddr) Off() OverlayAddr {
for _, na := range nas { // return self
tna := &testPeerAddr{PeerAddr: na} // }
addr := na.OverlayAddr() //
if self.posMap[string(addr)] != nil { // func (self *testOverlay) Register(peers chan OverlayAddr) error {
continue // self.mu.Lock()
} // defer self.mu.Unlock()
self.posMap[string(addr)] = tna // var nas []OverlayAddr
o := order(addr) // for a := range peers {
log.Trace(fmt.Sprintf("PO: %v, orders: %v", o, orders)) // nas = append(nas, a)
self.pos[o] = append(self.pos[o], tna) // }
} // return self.register(nas...)
return nil // }
} //
// func (self *testOverlay) BaseAddr() []byte {
func order(addr []byte) int { // return nil
return int(addr[0]) / 32 // }
} //
// func (self *testOverlay) register(nas ...OverlayAddr) error {
func (self *testOverlay) On(n Peer) { // for _, na := range nas {
self.mu.Lock() // addr := na.Address()
defer self.mu.Unlock() // if self.posMap[string(addr)] != nil {
addr := n.OverlayAddr() // continue
na := self.posMap[string(addr)] // }
if na == nil { // self.posMap[string(addr)] = na
self.register(n) // o := order(addr)
na = self.posMap[string(addr)] // log.Trace(fmt.Sprintf("PO: %v, orders: %v", o, orders))
} else if na.Peer != nil { // self.pos[o] = append(self.pos[o], na)
return // }
} // return nil
log.Trace(fmt.Sprintf("Online: %x", addr[:4])) // }
na.Peer = n //
return // func order(addr []byte) int {
} // return int(addr[0]) / 32
// }
func (self *testOverlay) Off(n Peer) { //
self.mu.Lock() // func (self *testOverlay) On(n OverlayConn) {
defer self.mu.Unlock() // self.mu.Lock()
addr := n.OverlayAddr() // defer self.mu.Unlock()
na := self.posMap[string(addr)] // addr := n.Address()
if na == nil { // na := self.posMap[string(addr)]
return // if na == nil {
} // self.register(n)
delete(self.posMap, string(addr)) // na = self.posMap[string(addr)]
na.Peer = nil // } else if na.Peer != nil {
} // return
// }
// caller must hold the lock // log.Trace(fmt.Sprintf("Online: %x", addr[:4]))
func (self *testOverlay) on(po []*testPeerAddr) (nodes []Peer) { // na.Peer = n
for _, na := range po { // return
if na.Peer != nil { // }
nodes = append(nodes, na.Peer) //
} // func (self *testOverlay) Off(n OverlayConn) {
} // self.mu.Lock()
return nodes // defer self.mu.Unlock()
} // addr := n.Over()
// na := self.posMap[string(addr)]
// caller must hold the lock // if na == nil {
func (self *testOverlay) off(po []*testPeerAddr) (nas []PeerAddr) { // return
for _, na := range po { // }
if na.Peer == (*bzzPeer)(nil) { // delete(self.posMap, string(addr))
nas = append(nas, PeerAddr(na)) // na.Peer = nil
} // }
} //
return nas // // caller must hold the lock
} // func (self *testOverlay) on(po []*testPeerAddr) (nodes []OverlayConn) {
// for _, na := range po {
func (self *testOverlay) EachLivePeer(base []byte, o int, f func(Peer, int, bool) bool) { // if na.Peer != nil {
if base == nil { // nodes = append(nodes, na)
base = self.addr // }
} // }
for i := o; i < len(self.pos); i++ { // return nodes
for _, na := range self.pos[i] { // }
if na.Peer != nil { //
if !f(na.Peer, o, false) { // // caller must hold the lock
return // func (self *testOverlay) off(po []*testPeerAddr) (nas []OverlayAddr) {
} // for _, na := range po {
} // if na.Peer == (*bzzPeer)(nil) {
} // nas = append(nas, Addr(na))
} // }
} // }
// return nas
func (self *testOverlay) EachPeer(base []byte, o int, f func(PeerAddr, int) bool) { // }
if base == nil { //
base = self.addr // func (self *testOverlay) EachConn(base []byte, o int, f func(OverlayConn, int, bool) bool) {
} // for i := o; i < len(self.pos); i++ {
for i := o; i < len(self.pos); i++ { // for _, na := range self.pos[i] {
for _, na := range self.pos[i] { // if na.Peer != nil {
if !f(na, i) { // if !f(na, o, false) {
return // return
} // }
} // }
} // }
} // }
// }
func (self *testOverlay) SuggestPeer() (PeerAddr, int, bool) { //
self.mu.Lock() // func (self *testOverlay) EachAddr(base []byte, o int, f func(OverlayAddr, int) bool) {
defer self.mu.Unlock() // for i := o; i < len(self.pos); i++ {
for i, po := range self.pos { // for _, na := range self.pos[i] {
ons := self.on(po) // if !f(na, i) {
if len(ons) < 2 { // return
offs := self.off(po) // }
if len(offs) > 0 { // }
log.Trace(fmt.Sprintf("node %v is off", offs[0])) // }
return offs[0], i, true // }
} //
} // func (self *testOverlay) SuggestPeer() (OverlayAddr, int, bool) {
} // self.mu.Lock()
return nil, 0, true // defer self.mu.Unlock()
} // for i, po := range self.pos {
// ons := self.on(po)
func (self *testOverlay) String() string { // if len(ons) < 2 {
self.mu.Lock() // offs := self.off(po)
defer self.mu.Unlock() // if len(offs) > 0 {
var t []string // log.Trace(fmt.Sprintf("node %v is off", offs[0]))
var ons, offs int // return offs[0], i, true
var ns []Peer // }
var nas []PeerAddr // }
for o, po := range self.pos { // }
var row []string // return nil, 0, true
ns = self.on(po) // }
nas = self.off(po) //
ons = len(ns) // func (self *testOverlay) String() string {
for _, n := range ns { // self.mu.Lock()
addr := n.OverlayAddr() // defer self.mu.Unlock()
row = append(row, fmt.Sprintf("%x", addr[:4])) // var t []string
} // var ons, offs int
row = append(row, "|") // var ns []Peer
offs = len(nas) // var nas []Addr
for _, na := range nas { // for o, po := range self.pos {
addr := na.OverlayAddr() // var row []string
row = append(row, fmt.Sprintf("%x", addr[:4])) // ns = self.on(po)
} // nas = self.off(po)
t = append(t, fmt.Sprintf("%v: (%v/%v) %v", o, ons, offs, strings.Join(row, " "))) // ons = len(ns)
} // for _, n := range ns {
return strings.Join(t, "\n") // addr := n.Over()
} // row = append(row, fmt.Sprintf("%x", addr[:4]))
// }
func NewTestOverlay(addr []byte) *testOverlay { // row = append(row, "|")
return &testOverlay{ // offs = len(nas)
addr: addr, // for _, na := range nas {
posMap: make(map[string]*testPeerAddr), // addr := na.Over()
pos: make([][]*testPeerAddr, orders), // row = append(row, fmt.Sprintf("%x", addr[:4]))
} // }
} // t = append(t, fmt.Sprintf("%v: (%v/%v) %v", o, ons, offs, strings.Join(row, " ")))
// }
// return strings.Join(t, "\n")
// }
//
// func NewTestOverlay(addr []byte) *testOverlay {
// return &testOverlay{
// addr: addr,
// posMap: make(map[string]*testPeerAddr),
// pos: make([][]*testPeerAddr, orders),
// }
// }

228
swarm/pss/client.go Normal file
View file

@ -0,0 +1,228 @@
package pss
import (
"context"
"fmt"
"sync"
"sync/atomic"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/protocols"
"github.com/ethereum/go-ethereum/pot"
"github.com/ethereum/go-ethereum/rpc"
)
const (
inboxCapacity = 3000
outboxCapacity = 100
addrLen = common.HashLength
)
// implements p2p.Server
// implements net.Conn
type PssClient struct {
localuri string
remoteuri string
ctx context.Context
cancel func()
subscription *rpc.ClientSubscription
topicsC chan []byte
msgC chan PssAPIMsg
quitC chan struct{}
quitting uint32
ws *rpc.Client
lock sync.Mutex
peerPool map[PssTopic]map[pot.Address]*pssRPCRW
protos []*p2p.Protocol
}
type pssRPCRW struct {
*PssClient
topic *PssTopic
spec *protocols.Spec
msgC chan []byte
addr pot.Address
}
func (self *PssClient) newpssRPCRW(addr pot.Address, spec *protocols.Spec, topic *PssTopic) *pssRPCRW {
return &pssRPCRW {
PssClient: self,
topic: topic,
spec: spec,
msgC: make(chan []byte),
addr: addr,
}
}
func (rw *pssRPCRW) ReadMsg() (p2p.Msg, error) {
msg := <- rw.msgC
log.Warn("pssrpcrw read", "msg", msg)
pmsg, err := ToP2pMsg(msg)
if err != nil {
return p2p.Msg{}, err
}
return pmsg, nil
}
func (rw *pssRPCRW) WriteMsg(msg p2p.Msg) error {
ifc, found := rw.spec.NewMsg(msg.Code)
if !found {
return fmt.Errorf("could not find interface for msg #%d", msg.Code)
}
msg.Decode(ifc)
pmsg, err := newProtocolMsg(msg.Code, ifc)
if err != nil {
return fmt.Errorf("Could not render protocolmessage", "error", err)
}
return rw.PssClient.ws.CallContext(rw.PssClient.ctx, nil, "pss_sendRaw", rw.topic, PssAPIMsg{
Addr: rw.addr.Bytes(),
Msg: pmsg,
})
}
// remotehost: hostname of node running websockets proxy to pss (default localhost)
// remoteport: port of node running websockets proxy to pss (0 = go-ethereum node default)
// secure: whether or not to use secure connection
// originhost: local if host to connect from
func NewPssClient(ctx context.Context, cancel func(), remotehost string, remoteport int, secure bool, originhost string) *PssClient {
prefix := "ws"
if ctx == nil {
ctx = context.Background()
cancel = func() {return}
}
pssc := &PssClient{
msgC: make(chan PssAPIMsg),
quitC: make(chan struct{}),
peerPool: make(map[PssTopic]map[pot.Address]*pssRPCRW),
ctx: ctx,
cancel: cancel,
}
if remotehost == "" {
remotehost = "localhost"
}
if remoteport == 0 {
remoteport = node.DefaultWSPort
}
if originhost == "" {
originhost = "localhost"
}
if secure {
prefix = "wss"
}
pssc.remoteuri = fmt.Sprintf("%s://%s:%d", prefix, remotehost, remoteport)
pssc.localuri = fmt.Sprintf("%s://%s", prefix, originhost)
return pssc
}
func (self *PssClient) shutdown() {
atomic.StoreUint32(&self.quitting, 1)
self.cancel()
}
func (self *PssClient) Start() error {
log.Debug("Dialing ws", "src", self.localuri, "dst", self.remoteuri)
ws, err := rpc.DialWebsocket(self.ctx, self.remoteuri, self.localuri)
if err != nil {
return fmt.Errorf("Couldnt dial pss websocket: %v", err)
}
self.ws = ws
return nil
}
func (self *PssClient) RunProtocol(proto *p2p.Protocol, spec *protocols.Spec) error {
topic := NewTopic(spec.Name, int(spec.Version))
msgC := make(chan PssAPIMsg)
self.peerPool[topic] = make(map[pot.Address]*pssRPCRW)
sub, err := self.ws.Subscribe(self.ctx, "pss", msgC, "newMsg", topic)
if err != nil {
return fmt.Errorf("pss event subscription failed: %v", err)
}
self.subscription = sub
// dispatch incoming messages
go func() {
for {
select {
case msg := <- msgC:
var addr pot.Address
copy(addr[:], msg.Addr)
if self.peerPool[topic][addr] == nil {
self.peerPool[topic][addr] = self.newpssRPCRW(addr, spec, &topic)
nid, _ := discover.HexID("0x00")
p := p2p.NewPeer(nid, fmt.Sprintf("%v", addr), []p2p.Cap{})
go proto.Run(p, self.peerPool[topic][addr])
}
go func() {
self.peerPool[topic][addr].msgC <- msg.Msg
}()
case <-self.quitC:
self.shutdown()
return
}
}
}()
self.protos = append(self.protos, proto)
return nil
}
func (self *PssClient) Stop() error {
self.cancel()
return nil
}
func (self *PssClient) AddPssPeer(addr pot.Address, spec *protocols.Spec) {
topic := NewTopic(spec.Name, int(spec.Version))
if self.peerPool[topic][addr] == nil {
self.peerPool[topic][addr] = self.newpssRPCRW(addr, spec, &topic)
}
}
func (self *PssClient) RemovePssPeer(addr pot.Address, spec *protocols.Spec) {
topic := NewTopic(spec.Name, int(spec.Version))
delete(self.peerPool[topic], addr)
}
func (self *PssClient) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription {
log.Error("PSS client handles events internally, use the read functions instead")
return nil
}
func (self *PssClient) PeerCount() int {
return len(self.peerPool)
}
func (self *PssClient) NodeInfo() *p2p.NodeInfo {
return nil
}
func (self *PssClient) PeersInfo() []*p2p.PeerInfo {
return nil
}
func (self *PssClient) AddPeer(node *discover.Node) {
log.Error("Cannot add peer in PSS with discover.Node, need swarm overlay address")
}
func (self *PssClient) RemovePeer(node *discover.Node) {
log.Error("Cannot remove peer in PSS with discover.Node, need swarm overlay address")
}

173
swarm/pss/client_test.go Normal file
View file

@ -0,0 +1,173 @@
package pss
import (
"context"
"fmt"
"os"
"net"
"net/http"
"testing"
"time"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/protocols"
"github.com/ethereum/go-ethereum/pot"
"github.com/ethereum/go-ethereum/rpc"
)
func init() {
h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true)))
log.Root().SetHandler(h)
}
func TestRunProtocol(t *testing.T) {
quitC := make(chan struct{})
pss := newTestPss(nil)
ping := &pssPing{
quitC: make(chan struct{}),
}
proto := newProtocol(ping)
_, err := baseTester(t, proto, pss, nil, nil, quitC)
if err != nil {
t.Fatalf(err.Error())
}
quitC <- struct{}{}
}
func TestIncoming(t *testing.T) {
quitC := make(chan struct{})
pss := newTestPss(nil)
ctx, cancel := context.WithCancel(context.Background())
var addr []byte
ping := &pssPing{
quitC: make(chan struct{}),
}
proto := newProtocol(ping)
client, err := baseTester(t, proto, pss, ctx, cancel, quitC)
if err != nil {
t.Fatalf(err.Error())
}
client.ws.Call(&addr, "pss_baseAddr")
code, _ := pssPingProtocol.GetCode(&pssPingMsg{})
rlpbundle, err := newProtocolMsg(code, &pssPingMsg{
Created: time.Now(),
})
if err != nil {
t.Fatalf("couldn't make pssmsg")
}
pssenv := PssEnvelope{
From: addr,
Topic: NewTopic(proto.Name, int(proto.Version)),
TTL: DefaultTTL,
Payload: rlpbundle,
}
pssmsg := PssMsg{
To: addr,
Payload: &pssenv,
}
pss.Process(&pssmsg)
go func() {
<-ping.quitC
client.cancel()
}()
<-client.ctx.Done()
quitC <- struct{}{}
}
func TestOutgoing(t *testing.T) {
quitC := make(chan struct{})
pss := newTestPss(nil)
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond * 250)
var addr []byte
var potaddr pot.Address
ping := &pssPing{
quitC: make(chan struct{}),
}
proto := newProtocol(ping)
client, err := baseTester(t, proto, pss, ctx, cancel, quitC)
if err != nil {
t.Fatalf(err.Error())
}
client.ws.Call(&addr, "pss_baseAddr")
copy(potaddr[:], addr)
msg := &pssPingMsg{
Created: time.Now(),
}
topic := NewTopic(pssPingProtocol.Name, int(pssPingProtocol.Version))
client.AddPssPeer(potaddr, pssPingProtocol)
nid, _ := discover.HexID("0x00")
p := p2p.NewPeer(nid, fmt.Sprintf("%v", potaddr), []p2p.Cap{})
pp := protocols.NewPeer(p, client.peerPool[topic][potaddr], pssPingProtocol)
pp.Send(msg)
<-client.ctx.Done()
quitC <- struct{}{}
}
func baseTester(t *testing.T, proto *p2p.Protocol, pss *Pss, ctx context.Context, cancel func(), quitC chan struct{}) (*PssClient, error) {
var err error
client := newClient(t, pss, ctx, cancel, quitC)
err = client.Start()
if err != nil {
return nil, err
}
err = client.RunProtocol(proto, pssPingProtocol)
if err != nil {
return nil, err
}
return client, nil
}
func newProtocol(ping *pssPing) *p2p.Protocol {
return &p2p.Protocol{
Name: pssPingProtocol.Name,
Version: pssPingProtocol.Version,
Length: 1,
Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
pp := protocols.NewPeer(p, rw, pssPingProtocol)
pp.Run(ping.pssPingHandler)
return nil
},
}
}
func newClient(t *testing.T, pss *Pss, ctx context.Context, cancel func(), quitC chan struct{}) *PssClient {
pssclient := NewPssClient(ctx, cancel, "", 0, false, "")
srv := rpc.NewServer()
srv.RegisterName("pss", NewPssAPI(pss))
ws := srv.WebsocketHandler([]string{"*"})
uri := fmt.Sprintf("%s:%d", node.DefaultWSHost, node.DefaultWSPort)
sock, err := net.Listen("tcp", uri)
if err != nil {
t.Fatalf("Tcp (recv) on %s failed: %v", uri, err)
}
go func() {
http.Serve(sock, ws)
}()
go func() {
<-quitC
sock.Close()
}()
return pssclient
}

125
swarm/pss/common.go Normal file
View file

@ -0,0 +1,125 @@
package pss
import (
"fmt"
"io/ioutil"
"os"
"time"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/protocols"
"github.com/ethereum/go-ethereum/swarm/network"
"github.com/ethereum/go-ethereum/swarm/storage"
)
type pssPingMsg struct {
Created time.Time
}
type pssPing struct {
quitC chan struct{}
}
func (self *pssPing) pssPingHandler(msg interface{}) error {
log.Warn("got ping", "msg", msg)
self.quitC <- struct{}{}
return nil
}
var pssPingProtocol = &protocols.Spec{
Name: "psstest",
Version: 1,
MaxMsgSize: 10 * 1024 * 1024,
Messages: []interface{}{
pssPingMsg{},
},
}
var pssPingTopic = NewTopic(pssPingProtocol.Name, int(pssPingProtocol.Version))
func newTestPss(addr []byte) *Pss {
if addr == nil {
addr = network.RandomAddr().OAddr
}
// set up storage
cachedir, err := ioutil.TempDir("", "pss-cache")
if err != nil {
log.Error("create pss cache tmpdir failed", "error", err)
os.Exit(1)
}
dpa, err := storage.NewLocalDPA(cachedir)
if err != nil {
log.Error("local dpa creation failed", "error", err)
os.Exit(1)
}
// set up routing
kp := network.NewKadParams()
kp.MinProxBinSize = 3
// create pss
pp := NewPssParams()
overlay := network.NewKademlia(addr, kp)
ps := NewPss(overlay, dpa, pp)
return ps
}
func newPssPingMsg(ps *Pss, to []byte, spec *protocols.Spec, topic PssTopic, senderaddr []byte) PssMsg {
data := pssPingMsg{
Created: time.Now(),
}
code, found := spec.GetCode(&data)
if !found {
return PssMsg{}
}
rlpbundle, err := newProtocolMsg(code, data)
if err != nil {
return PssMsg{}
}
pssmsg := PssMsg{
To: to,
Payload: NewPssEnvelope(senderaddr, topic, rlpbundle),
}
return pssmsg
}
func newPssPingProtocol(handler func (interface{}) error) *p2p.Protocol {
return &p2p.Protocol{
Name: pssPingProtocol.Name,
Version: pssPingProtocol.Version,
Length: uint64(pssPingProtocol.MaxMsgSize),
Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
pp := protocols.NewPeer(p, rw, pssPingProtocol)
log.Trace(fmt.Sprintf("running pss vprotocol on peer %v", p))
err := pp.Run(handler)
return err
},
}
}
type testPssPeer struct {
*protocols.Peer
addr []byte
}
func (self *testPssPeer) Address() []byte {
return self.addr
}
func (self *testPssPeer) Off() network.OverlayAddr {
return self
}
func (self *testPssPeer) Drop(err error) {
}
func (self *testPssPeer) Update(o network.OverlayAddr) network.OverlayAddr {
return self
}

View file

@ -1,4 +1,4 @@
package network package pss
import ( import (
"bytes" "bytes"
@ -9,13 +9,16 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/crypto/sha3"
"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/discover"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters" "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/p2p/protocols"
"github.com/ethereum/go-ethereum/pot" "github.com/ethereum/go-ethereum/pot"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/swarm/network"
"github.com/ethereum/go-ethereum/swarm/storage" "github.com/ethereum/go-ethereum/swarm/storage"
) )
@ -25,19 +28,20 @@ const (
TopicResolverLength = 8 TopicResolverLength = 8
PssPeerCapacity = 256 PssPeerCapacity = 256
PssPeerTopicDefaultCapacity = 8 PssPeerTopicDefaultCapacity = 8
digestLength = 64 digestLength = 32
digestCapacity = 256 digestCapacity = 256
defaultDigestCacheTTL = time.Second defaultDigestCacheTTL = time.Second
pingTopicName = "pss"
pingTopicVersion = 1
) )
var ( var (
errorNoForwarder = errors.New("no available forwarders in routing table")
errorForwardToSelf = errors.New("forward to self") errorForwardToSelf = errors.New("forward to self")
errorBlockByCache = errors.New("message found in blocking cache")
) )
type senderPeer interface {
Address() []byte
Send(interface{}) error
}
// Defines params for Pss // Defines params for Pss
type PssParams struct { type PssParams struct {
Cachettl time.Duration Cachettl time.Duration
@ -51,50 +55,14 @@ func NewPssParams() *PssParams {
} }
// Encapsulates the message transported over pss. // Encapsulates the message transported over pss.
//
// Warning: do not access the To-member directly. Use *PssMsg.GetRecipient() and *PssMsg.SetRecipient() instead.
type PssMsg struct { type PssMsg struct {
// (we need the To-member exported for type inference)
To []byte To []byte
Payload pssEnvelope Payload *PssEnvelope
}
// Retrieve the remote peer receipient address of the message
func (self *PssMsg) GetRecipient() []byte {
return self.To
}
// Set the remote peer recipient address of the message
func (self *PssMsg) SetRecipient(to []byte) {
self.To = to
} }
// String representation of PssMsg // String representation of PssMsg
func (self *PssMsg) String() string { func (self *PssMsg) String() string {
return fmt.Sprintf("PssMsg: Recipient: %x", common.ByteLabel(self.GetRecipient())) return fmt.Sprintf("PssMsg: Recipient: %x", common.ByteLabel(self.To))
}
// Pre-Whisper placeholder
type pssEnvelope struct {
Topic PssTopic
TTL uint16
Payload []byte
SenderOAddr []byte
SenderUAddr []byte
}
// Pre-Whisper placeholder
type pssPayload struct {
Code uint64
Size uint32
Data []byte
ReceivedAt time.Time
}
// Pre-Whisper placeholder
type pssCacheEntry struct {
expiresAt time.Time
receivedFrom []byte
} }
// Topic defines the context of a message being transported over pss // Topic defines the context of a message being transported over pss
@ -102,14 +70,70 @@ type pssCacheEntry struct {
// Typically, one can map protocol handlers for the message payloads by mapping topic to them; see *Pss.Register() // Typically, one can map protocol handlers for the message payloads by mapping topic to them; see *Pss.Register()
type PssTopic [TopicLength]byte type PssTopic [TopicLength]byte
// Pre-Whisper placeholder func (self *PssTopic) String() string {
type pssDigest uint32 return fmt.Sprintf("%x", self)
}
// Pre-Whisper placeholder, payload of PssMsg
type PssEnvelope struct {
Topic PssTopic
TTL uint16
Payload []byte
From []byte
}
// creates Pss envelope from sender address, topic and raw payload
func NewPssEnvelope(addr []byte, topic PssTopic, payload []byte) *PssEnvelope {
return &PssEnvelope{
From: addr,
Topic: topic,
TTL: DefaultTTL,
Payload: payload,
}
}
func (msg *PssMsg) serialize() []byte {
rlpdata, _ := rlp.EncodeToBytes(msg)
/*buf := bytes.NewBuffer(nil)
buf.Write(self.PssEnvelope.Topic[:])
buf.Write(self.PssEnvelope.Payload)
buf.Write(self.PssEnvelope.From)
return buf.Bytes()*/
return rlpdata
}
var pssSpec = &protocols.Spec{
Name: "pss",
Version: 1,
MaxMsgSize: 10 * 1024 * 1024,
Messages: []interface{}{
PssMsg{},
},
}
// encapsulates a protocol msg as PssEnvelope data
type PssProtocolMsg struct {
Code uint64
Size uint32
Payload []byte
ReceivedAt time.Time
}
type pssCacheEntry struct {
expiresAt time.Time
receivedFrom []byte
}
type pssDigest [digestLength]byte
// Message handler func for a topic
type pssHandler func(msg []byte, p *p2p.Peer, from []byte) error
// pss provides sending messages to nodes without having to be directly connected to them. // pss provides sending messages to nodes without having to be directly connected to them.
// //
// The messages are wrapped in a PssMsg structure and routed using the swarm kademlia routing. // The messages are wrapped in a PssMsg structure and routed using the swarm kademlia routing.
// The structure is used by normal incoming message handlers on the nodes to determine which action to take, forward or process.
// Thus it is up to the implementer to write a handler, and link the PssMsg to this appropriate handler.
// //
// The top-level Pss object provides: // The top-level Pss object provides:
// //
@ -120,51 +144,118 @@ type pssDigest uint32
// - a dispatcher lookup, mapping protocols to topics // - a dispatcher lookup, mapping protocols to topics
// - a message cache to spot messages that previously have been forwarded // - a message cache to spot messages that previously have been forwarded
type Pss struct { type Pss struct {
Overlay // we can get the overlayaddress from this network.Overlay // we can get the overlayaddress from this
//peerPool map[pot.Address]map[PssTopic]*PssReadWriter // keep track of all virtual p2p.Peers we are currently speaking to peerPool map[pot.Address]map[PssTopic]p2p.MsgReadWriter // keep track of all virtual p2p.Peers we are currently speaking to
peerPool map[pot.Address]map[PssTopic]p2p.MsgReadWriter // keep track of all virtual p2p.Peers we are currently speaking to handlers map[PssTopic]map[*pssHandler]bool // topic and version based pss payload handlers
handlers map[PssTopic]func([]byte, *p2p.Peer, []byte) error // topic and version based pss payload handlers fwdcache map[pssDigest]pssCacheEntry // checksum of unique fields from pssmsg mapped to expiry, cache to determine whether to drop msg
events map[PssTopic]*event.Feed // subscriptions for each topic cachettl time.Duration // how long to keep messages in fwdcache
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
hasher func(string) storage.Hasher // hasher to digest message to cache
lock sync.Mutex lock sync.Mutex
dpa *storage.DPA
} }
func (self *Pss) hashMsg(msg *PssMsg) pssDigest { func (self *Pss) storeMsg(msg *PssMsg) (pssDigest, error) {
hasher := self.hasher("SHA3")() swg := &sync.WaitGroup{}
hasher.Reset() wwg := &sync.WaitGroup{}
hasher.Write(msg.GetRecipient()) buf := bytes.NewReader(msg.serialize())
hasher.Write(msg.Payload.SenderUAddr) key, err := self.dpa.Store(buf, int64(buf.Len()), swg, wwg)
hasher.Write(msg.Payload.SenderOAddr) if err != nil {
hasher.Write(msg.Payload.Topic[:]) log.Warn("Could not store in swarm", "err", err)
hasher.Write(msg.Payload.Payload) return pssDigest{}, err
b := hasher.Sum([]byte{}) }
return pssDigest(binary.BigEndian.Uint32(b)) log.Trace("Stored msg in swarm", "key", key)
digest := pssDigest{}
copy(digest[:], key[:digestLength])
return digest, nil
} }
// Creates a new Pss instance. A node should only need one of these // Creates a new Pss instance. A node should only need one of these
// //
// TODO error check overlay integrity // TODO: error check overlay integrity
func NewPss(k Overlay, params *PssParams) *Pss { func NewPss(k network.Overlay, dpa *storage.DPA, params *PssParams) *Pss {
return &Pss{ return &Pss{
Overlay: k, Overlay: k,
//peerPool: make(map[pot.Address]map[PssTopic]*PssReadWriter, PssPeerCapacity),
peerPool: make(map[pot.Address]map[PssTopic]p2p.MsgReadWriter, PssPeerCapacity), peerPool: make(map[pot.Address]map[PssTopic]p2p.MsgReadWriter, PssPeerCapacity),
handlers: make(map[PssTopic]func([]byte, *p2p.Peer, []byte) error), handlers: make(map[PssTopic]map[*pssHandler]bool),
events: make(map[PssTopic]*event.Feed),
fwdcache: make(map[pssDigest]pssCacheEntry), fwdcache: make(map[pssDigest]pssCacheEntry),
cachettl: params.Cachettl, cachettl: params.Cachettl,
hasher: storage.MakeHashFunc, dpa: dpa,
} }
} }
func (self *Pss) Start(srv *p2p.Server) error {
return nil
}
func (self *Pss) Stop() error {
return nil
}
func (self *Pss) Protocols() []p2p.Protocol {
return []p2p.Protocol{
p2p.Protocol{
Name: pssSpec.Name,
Version: pssSpec.Version,
Length: pssSpec.Length(),
Run: self.Run,
},
}
}
func (self *Pss) Run(p *p2p.Peer, rw p2p.MsgReadWriter) error {
pp := protocols.NewPeer(p, rw, pssSpec)
return pp.Run(self.handlePssMsg)
}
func (self *Pss) APIs() []rpc.API {
return []rpc.API{
rpc.API {
Namespace: "pss",
Version: "0.1",
Service: NewPssAPI(self),
Public: true,
},
}
}
// Takes the generated PssTopic of a protocol/chatroom etc, and links a handler function to it
// This allows the implementer to retrieve the right handler functions (invoke the right protocol)
// for an incoming message by inspecting the topic on it.
// a topic allows for multiple handlers
// returns a deregister function which needs to be called to deregister the handler
// (similar to event.Subscription.Unsubscribe())
func (self *Pss) Register(topic *PssTopic, handler pssHandler) func() {
self.lock.Lock()
defer self.lock.Unlock()
handlers := self.handlers[*topic]
if handlers == nil {
handlers = make(map[*pssHandler]bool)
self.handlers[*topic] = handlers
}
handlers[&handler] = true
return func() { self.deregister(topic, &handler) }
}
func (self *Pss) deregister(topic *PssTopic, h *pssHandler) {
self.lock.Lock()
defer self.lock.Unlock()
handlers := self.handlers[*topic]
if len(handlers) == 1 {
delete(self.handlers, *topic)
return
}
delete(handlers, h)
}
// enables to set address of node, to avoid backwards forwarding // enables to set address of node, to avoid backwards forwarding
// //
// currently not in use as forwarder address is not known in the handler function hooked to the pss dispatcher. // currently not in use as forwarder address is not known in the handler function hooked to the pss dispatcher.
// it is included as a courtesy to custom transport layers that may want to implement this // it is included as a courtesy to custom transport layers that may want to implement this
func (self *Pss) AddToCache(addr []byte, msg *PssMsg) error { func (self *Pss) AddToCache(addr []byte, msg *PssMsg) error {
digest := self.hashMsg(msg) //digest := self.hashMsg(msg)
digest, err := self.storeMsg(msg)
if err != nil {
return err
}
return self.addFwdCacheSender(addr, digest) return self.addFwdCacheSender(addr, digest)
} }
@ -210,35 +301,115 @@ func (self *Pss) checkFwdCache(addr []byte, digest pssDigest) bool {
return false return false
} }
// Takes the generated PssTopic of a protocol, and links a handler function to it func (self *Pss) getHandlers(topic PssTopic) map[*pssHandler]bool {
// This allows the implementer to retrieve the right handler function (invoke the right protocol) for an incoming message by inspecting the topic on it.
func (self *Pss) Register(topic PssTopic, handler func(msg []byte, p *p2p.Peer, from []byte) error) error {
self.lock.Lock()
defer self.lock.Unlock()
self.handlers[topic] = func(msg []byte, p *p2p.Peer, from []byte) error {
self.alertSubscribers(&topic, msg)
return handler(msg, p, from)
}
self.registerFeed(topic)
return nil
}
func (self *Pss) Subscribe(topic *PssTopic, ch chan []byte) (event.Subscription, error) {
_, ok := self.events[*topic]
if !ok {
return nil, fmt.Errorf("No feed registered for topic %v", topic)
}
sub := self.events[*topic].Subscribe(ch)
log.Trace("new pss subscribe", "topic", topic, "sub", sub)
return sub, nil
}
func (self *Pss) GetHandler(topic PssTopic) func([]byte, *p2p.Peer, []byte) error {
self.lock.Lock() self.lock.Lock()
defer self.lock.Unlock() defer self.lock.Unlock()
return self.handlers[topic] return self.handlers[topic]
} }
//
func (self *Pss) handlePssMsg(msg interface{}) error {
pssmsg := msg.(*PssMsg)
if !self.isSelfRecipient(pssmsg) {
log.Trace("pss was for someone else :'( ... forwarding")
return self.Forward(pssmsg)
}
log.Trace("pss for us, yay! ... let's process!")
return self.Process(pssmsg)
}
// processes a message with self as recipient
func (self *Pss) Process(pssmsg *PssMsg) error {
env := pssmsg.Payload
payload := env.Payload
handlers := self.getHandlers(env.Topic)
if len(handlers) == 0 {
return fmt.Errorf("No registered handler for topic '%s'", env.Topic)
}
nid, _ := discover.HexID("0x00")
p := p2p.NewPeer(nid, fmt.Sprintf("%x", env.From), []p2p.Cap{})
for f := range handlers {
err := (*f)(payload, p, env.From)
if err != nil {
return err
}
}
return nil
}
// Sends a message using pss. The message could be anything at all, and will be handled by whichever handler function is mapped to PssTopic using *Pss.Register()
//
// The to address is a swarm overlay address
func (self *Pss) Send(to []byte, topic PssTopic, msg []byte) error {
sender := self.Overlay.BaseAddr()
pssenv := NewPssEnvelope(sender, topic, msg)
pssmsg := &PssMsg{
To: to,
Payload: pssenv,
}
return self.Forward(pssmsg)
}
// Forwards a pss message to the peer(s) closest to the to address
//
// Handlers that want to pass on a message should call this directly
func (self *Pss) Forward(msg *PssMsg) error {
if self.isSelfRecipient(msg) {
return errorForwardToSelf
}
digest, err := self.storeMsg(msg)
if err != nil {
log.Warn(fmt.Sprintf("could not store message %v to cache: %v", msg, err))
}
if self.checkFwdCache(nil, digest) {
log.Trace(fmt.Sprintf("pss relay block-cache match: FROM %x TO %x", common.ByteLabel(self.Overlay.BaseAddr()), common.ByteLabel(msg.To)))
return nil
}
// TODO:check integrity of message
sent := 0
// send with kademlia
// find the closest peer to the recipient and attempt to send
self.Overlay.EachConn(msg.To, 256, func(op network.OverlayConn, po int, isproxbin bool) bool {
p, ok := op.(senderPeer)
if !ok {
return true
}
addr := self.Overlay.BaseAddr()
sendMsg := fmt.Sprintf("%x: msg to %x via %x", common.ByteLabel(addr), common.ByteLabel(msg.To), common.ByteLabel(p.Address()))
if self.checkFwdCache(p.Address(), digest) {
log.Info(fmt.Sprintf("%v: peer already forwarded to", sendMsg))
return true
}
err := p.Send(msg)
if err != nil {
log.Warn(fmt.Sprintf("%v: failed forwarding: %v", sendMsg, err))
return true
}
log.Trace(fmt.Sprintf("%v: successfully forwarded", sendMsg))
sent++
// if equality holds, p is always the first peer given in the iterator
if bytes.Equal(msg.To, p.Address()) || !isproxbin {
return false
}
log.Trace(fmt.Sprintf("%x is in proxbin, keep forwarding", common.ByteLabel(p.Address())))
return true
})
if sent == 0 {
log.Error("PSS: unable to forward to any peers")
return nil
}
self.addFwdCacheExpire(digest)
return nil
}
// Links a pss peer address and topic to a dedicated p2p.MsgReadWriter in the pss peerpool, and runs the specificed protocol on this p2p.MsgReadWriter and the specified peer // Links a pss peer address and topic to a dedicated p2p.MsgReadWriter in the pss peerpool, and runs the specificed protocol on this p2p.MsgReadWriter and the specified peer
// //
// The effect is that now we have a "virtual" protocol running on an artificial p2p.Peer, which can be looked up and piped to through Pss using swarm overlay address and topic // The effect is that now we have a "virtual" protocol running on an artificial p2p.Peer, which can be looked up and piped to through Pss using swarm overlay address and topic
@ -249,121 +420,32 @@ func (self *Pss) AddPeer(p *p2p.Peer, addr pot.Address, run adapters.RunProtocol
go func() { go func() {
err := run(p, rw) err := run(p, rw)
log.Warn(fmt.Sprintf("pss vprotocol quit on addr %v topic %v: %v", addr, topic, err)) log.Warn(fmt.Sprintf("pss vprotocol quit on addr %v topic %v: %v", addr, topic, err))
self.removePeerTopic(rw, topic)
}() }()
return nil return nil
} }
// Removes a pss peer from the pss peerpool
func (self *Pss) RemovePeer(id pot.Address) {
self.lock.Lock()
defer self.lock.Unlock()
self.peerPool[id] = nil
return
}
func (self *Pss) addPeerTopic(id pot.Address, topic PssTopic, rw p2p.MsgReadWriter) error { func (self *Pss) addPeerTopic(id pot.Address, topic PssTopic, rw p2p.MsgReadWriter) error {
if self.peerPool[id][topic] == nil { if self.peerPool[id] == nil {
self.peerPool[id] = make(map[PssTopic]p2p.MsgReadWriter, PssPeerTopicDefaultCapacity) self.peerPool[id] = make(map[PssTopic]p2p.MsgReadWriter, PssPeerTopicDefaultCapacity)
} }
self.peerPool[id][topic] = rw self.peerPool[id][topic] = rw
return nil return nil
} }
func (self *Pss) removePeerTopic(id pot.Address, topic PssTopic) { func (self *Pss) removePeerTopic(rw p2p.MsgReadWriter, topic PssTopic) {
self.peerPool[id][topic] = nil prw, ok := rw.(*PssReadWriter)
return if !ok {
return
}
delete(self.peerPool[prw.To], topic)
if len(self.peerPool[prw.To]) == 0 {
delete(self.peerPool, prw.To)
}
} }
func (self *Pss) isActive(id pot.Address, topic PssTopic) bool { func (self *Pss) isActive(id pot.Address, topic PssTopic) bool {
if self.peerPool[id][topic] == nil { return self.peerPool[id][topic] != nil
return false
}
return true
}
func (self *Pss) registerFeed(topic PssTopic) {
self.events[topic] = &event.Feed{}
}
func (self *Pss) alertSubscribers(topic *PssTopic, msg []byte) error {
feed, ok := self.events[*topic]
if !ok {
return fmt.Errorf("No subscriptions registered for topic %v", topic)
}
numsent := feed.Send(msg)
log.Trace(fmt.Sprintf("pss sent to %d subscribers", numsent))
return nil
}
// Sends a message using pss. The message could be anything at all, and will be handled by whichever handler function is mapped to PssTopic using *Pss.Register()
//
// The to address is a swarm overlay address
func (self *Pss) Send(to []byte, topic PssTopic, msg []byte) error {
pssenv := pssEnvelope{
SenderOAddr: self.Overlay.GetAddr().OverlayAddr(),
SenderUAddr: self.Overlay.GetAddr().UnderlayAddr(),
Topic: topic,
TTL: DefaultTTL,
Payload: msg,
}
pssmsg := &PssMsg{
Payload: pssenv,
}
pssmsg.SetRecipient(to)
return self.Forward(pssmsg)
}
// Forwards a pss message to the peer(s) closest to the to address
//
// Handlers that want to pass on a message should call this directly
func (self *Pss) Forward(msg *PssMsg) error {
if self.IsSelfRecipient(msg) {
return errorForwardToSelf
}
digest := self.hashMsg(msg)
if self.checkFwdCache(nil, digest) {
log.Trace(fmt.Sprintf("pss relay block-cache match: FROM %x TO %x", common.ByteLabel(self.Overlay.GetAddr().OverlayAddr()), common.ByteLabel(msg.GetRecipient())))
//return errorBlockByCache
return nil
}
// TODO:check integrity of message
sent := 0
// send with kademlia
// find the closest peer to the recipient and attempt to send
self.Overlay.EachLivePeer(msg.GetRecipient(), 256, func(p Peer, po int, isproxbin bool) bool {
if self.checkFwdCache(p.OverlayAddr(), digest) {
log.Warn(fmt.Sprintf("BOUNCE DEFER PSS-relay FROM %x TO %x THRU %x:", common.ByteLabel(self.Overlay.GetAddr().OverlayAddr()), common.ByteLabel(msg.GetRecipient()), common.ByteLabel(p.OverlayAddr())))
return true
}
log.Warn(fmt.Sprintf("Attempting PSS-relay FROM %x TO %x THRU %x", common.ByteLabel(self.Overlay.GetAddr().OverlayAddr()), common.ByteLabel(msg.GetRecipient()), common.ByteLabel(p.OverlayAddr())))
err := p.Send(msg)
if err != nil {
log.Warn(fmt.Sprintf("FAILED PSS-relay FROM %x TO %x THRU %x: %v", common.ByteLabel(self.Overlay.GetAddr().OverlayAddr()), common.ByteLabel(msg.GetRecipient()), common.ByteLabel(p.OverlayAddr()), err))
return true
}
sent++
if bytes.Equal(msg.GetRecipient(), p.OverlayAddr()) || !isproxbin {
return false
}
log.Trace(fmt.Sprintf("%x is in proxbin, so we continue sending", common.ByteLabel(p.OverlayAddr())))
return true
})
if sent == 0 {
return fmt.Errorf("PSS Was not able to send to any peers")
} else {
self.addFwdCacheExpire(digest)
}
return nil
} }
// Convenience object that: // Convenience object that:
@ -374,36 +456,34 @@ func (self *Pss) Forward(msg *PssMsg) error {
// Implements p2p.MsgReadWriter // Implements p2p.MsgReadWriter
type PssReadWriter struct { type PssReadWriter struct {
*Pss *Pss
RecipientOAddr pot.Address To pot.Address
LastActive time.Time LastActive time.Time
rw chan p2p.Msg rw chan p2p.Msg
ct *protocols.CodeMap spec *protocols.Spec
topic *PssTopic topic *PssTopic
} }
// Implements p2p.MsgReader // Implements p2p.MsgReader
func (prw PssReadWriter) ReadMsg() (p2p.Msg, error) { func (prw PssReadWriter) ReadMsg() (p2p.Msg, error) {
msg := <-prw.rw msg := <-prw.rw
log.Trace(fmt.Sprintf("pssrw readmsg: %v", msg)) log.Trace(fmt.Sprintf("pssrw readmsg: %v", msg))
return msg, nil return msg, nil
} }
// Implements p2p.MsgWriter // Implements p2p.MsgWriter
func (prw PssReadWriter) WriteMsg(msg p2p.Msg) error { func (prw PssReadWriter) WriteMsg(msg p2p.Msg) error {
log.Trace(fmt.Sprintf("pssrw writemsg: %v", msg)) log.Trace(fmt.Sprintf("pssrw writemsg: %v", msg))
ifc, found := prw.ct.GetInterface(msg.Code) ifc, found := prw.spec.NewMsg(msg.Code)
if !found { if !found {
return fmt.Errorf("Writemsg couldn't find matching interface for code %d", msg.Code) return fmt.Errorf("Writemsg couldn't find matching interface for code %d", msg.Code)
} }
msg.Decode(ifc) msg.Decode(ifc)
to := prw.RecipientOAddr.Bytes() pmsg, err := newProtocolMsg(msg.Code, ifc)
if err != nil {
pmsg, _ := makeMsg(msg.Code, ifc) return err
}
return prw.Pss.Send(to, *prw.topic, pmsg) return prw.Pss.Send(prw.To.Bytes(), *prw.topic, pmsg)
} }
// Injects a p2p.Msg into the MsgReadWriter, so that it appears on the associated p2p.MsgReader // Injects a p2p.Msg into the MsgReadWriter, so that it appears on the associated p2p.MsgReader
@ -416,50 +496,41 @@ func (prw PssReadWriter) injectMsg(msg p2p.Msg) error {
// Convenience object for passing messages in and out of the p2p layer // Convenience object for passing messages in and out of the p2p layer
type PssProtocol struct { type PssProtocol struct {
*Pss *Pss
virtualProtocol *p2p.Protocol proto *p2p.Protocol
topic *PssTopic topic *PssTopic
ct *protocols.CodeMap spec *protocols.Spec
} }
// Constructor // Constructor
func NewPssProtocol(pss *Pss, topic *PssTopic, ct *protocols.CodeMap, targetprotocol *p2p.Protocol) *PssProtocol { //func RegisterPssProtocol(pss *Pss, topic *PssTopic, spec *protocols.Spec, targetprotocol *p2p.Protocol) *PssProtocol {
func RegisterPssProtocol(pss *Pss, topic *PssTopic, spec *protocols.Spec, targetprotocol *p2p.Protocol) error {
pp := &PssProtocol{ pp := &PssProtocol{
Pss: pss, Pss: pss,
virtualProtocol: targetprotocol, proto: targetprotocol,
topic: topic, topic: topic,
ct: ct, spec: spec,
} }
return pp pss.Register(topic, pp.handle)
} //return pp
return nil
// Retrieves a convenience method for passing an incoming message into the p2p layer
//
// If the implementer wishes to use the p2p.Protocol (or p2p/protocols) message handling, this handler can be directly registered as a handler for the PssMsg structure
func (self *PssProtocol) GetHandler() func([]byte, *p2p.Peer, []byte) error {
return self.handle
} }
func (self *PssProtocol) handle(msg []byte, p *p2p.Peer, senderAddr []byte) error { func (self *PssProtocol) handle(msg []byte, p *p2p.Peer, senderAddr []byte) error {
hashoaddr := pot.NewHashAddressFromBytes(senderAddr).Address hashoaddr := pot.NewHashAddressFromBytes(senderAddr).Address
if !self.isActive(hashoaddr, *self.topic) { if !self.isActive(hashoaddr, *self.topic) {
rw := &PssReadWriter{ rw := &PssReadWriter{
Pss: self.Pss, Pss: self.Pss,
RecipientOAddr: hashoaddr, To: hashoaddr,
rw: make(chan p2p.Msg), rw: make(chan p2p.Msg),
ct: self.ct, spec: self.spec,
topic: self.topic, topic: self.topic,
} }
self.Pss.AddPeer(p, hashoaddr, self.virtualProtocol.Run, *self.topic, rw) self.Pss.AddPeer(p, hashoaddr, self.proto.Run, *self.topic, rw)
} }
payload := &pssPayload{} pmsg, err := ToP2pMsg(msg)
rlp.DecodeBytes(msg, payload) if err != nil {
return fmt.Errorf("could not decode pssmsg")
pmsg := p2p.Msg{
Code: payload.Code,
Size: uint32(len(payload.Data)),
ReceivedAt: time.Now(),
Payload: bytes.NewBuffer(payload.Data),
} }
vrw := self.Pss.peerPool[hashoaddr][*self.topic].(*PssReadWriter) vrw := self.Pss.peerPool[hashoaddr][*self.topic].(*PssReadWriter)
@ -468,26 +539,11 @@ func (self *PssProtocol) handle(msg []byte, p *p2p.Peer, senderAddr []byte) erro
return nil return nil
} }
func (self *Pss) IsSelfRecipient(msg *PssMsg) bool { func (self *Pss) isSelfRecipient(msg *PssMsg) bool {
if bytes.Equal(msg.GetRecipient(), self.Overlay.GetAddr().OverlayAddr()) { return bytes.Equal(msg.To, self.Overlay.BaseAddr())
return true
}
return false
} }
func (self *Pss) GetPingHandler() func([]byte, *p2p.Peer, []byte) error { func newProtocolMsg(code uint64, msg interface{}) ([]byte, error) {
pingtopic, _ := MakeTopic(pingTopicName, pingTopicVersion)
return func(msg []byte, p *p2p.Peer, from []byte) error {
if bytes.Equal([]byte("ping"), msg) {
log.Trace(fmt.Sprintf("swarm pss ping from %x sending pong", common.ByteLabel(from)))
self.Send(from, pingtopic, []byte("pong"))
}
return nil
}
}
// Pre-Whisper placeholder
func makeMsg(code uint64, msg interface{}) ([]byte, error) {
rlpdata, err := rlp.EncodeToBytes(msg) rlpdata, err := rlp.EncodeToBytes(msg)
if err != nil { if err != nil {
@ -497,30 +553,39 @@ func makeMsg(code uint64, msg interface{}) ([]byte, error) {
// previous attempts corrupted nested structs in the payload iself upon deserializing // previous attempts corrupted nested structs in the payload iself upon deserializing
// therefore we use two separate []byte fields instead of peerAddr // therefore we use two separate []byte fields instead of peerAddr
// TODO verify that nested structs cannot be used in rlp // TODO verify that nested structs cannot be used in rlp
smsg := &pssPayload{ smsg := &PssProtocolMsg{
Code: code, Code: code,
Size: uint32(len(rlpdata)), Size: uint32(len(rlpdata)),
Data: rlpdata, Payload: rlpdata,
} }
rlpbundle, err := rlp.EncodeToBytes(smsg) return rlp.EncodeToBytes(smsg)
if err != nil {
return nil, err
}
return rlpbundle, nil
} }
// Compiles a new PssTopic from a given name and version. // constructs a new PssTopic from a given name and version.
// //
// Analogous to the name and version members of p2p.Protocol // Analogous to the name and version members of p2p.Protocol
func MakeTopic(s string, v int) (PssTopic, error) { func NewTopic(s string, v int) (topic PssTopic) {
t := [TopicLength]byte{} h := sha3.NewKeccak256()
if len(s)+4 <= TopicLength { h.Write([]byte(s))
copy(t[4:len(s)+4], s) buf := make([]byte, TopicLength / 8)
} else { binary.PutUvarint(buf, uint64(v))
return t, fmt.Errorf("topic '%t' too long", s) h.Write(buf)
} copy(topic[:], h.Sum(buf)[:])
binary.PutVarint(t[:4], int64(v)) return topic
return t, nil }
func ToP2pMsg(msg []byte) (p2p.Msg, error) {
payload := &PssProtocolMsg{}
if err := rlp.DecodeBytes(msg, payload); err != nil {
return p2p.Msg{}, fmt.Errorf("pss protocol handler unable to decode payload as p2p message: %v", err)
}
return p2p.Msg{
Code: payload.Code,
Size: uint32(len(payload.Payload)),
ReceivedAt: time.Now(),
Payload: bytes.NewBuffer(payload.Payload),
}, nil
} }

View file

@ -1,37 +1,417 @@
package network package pss
import ( import (
"bytes"
"context" "context"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"math/rand" "io/ioutil"
"net"
"net/http"
"os" "os"
"strconv"
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"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/p2p/simulations" "github.com/ethereum/go-ethereum/p2p/simulations"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters" "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/rpc" p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
"github.com/ethereum/go-ethereum/swarm/network"
"github.com/ethereum/go-ethereum/swarm/storage"
) )
const ( const (
protocolName = "foo" pssServiceName = "pss"
protocolVersion = 42 bzzServiceName = "bzz"
) )
var services = newServices()
func init() { func init() {
adapters.RegisterServices(services)
h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true))) h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true)))
log.Root().SetHandler(h) log.Root().SetHandler(h)
} }
func TestPssCache(t *testing.T) {
var err error
to, _ := hex.DecodeString("08090a0b0c0d0e0f1011121314150001020304050607161718191a1b1c1d1e1f")
oaddr, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
//uaddr, _ := hex.DecodeString("101112131415161718191a1b1c1d1e1f000102030405060708090a0b0c0d0e0f")
//proofbytes := []byte{241, 172, 117, 105, 88, 154, 82, 33, 176, 188, 91, 244, 245, 85, 86, 16, 120, 232, 70, 45, 182, 188, 99, 103, 157, 3, 202, 121, 252, 21, 129, 22}
proofbytes, _ := hex.DecodeString("ad312dca94df401555cfdeb85a6a1f87fb8f240f08dc36af246bd9d4d41efd89")
ps := newTestPss(oaddr)
pp := NewPssParams()
data := []byte("foo")
datatwo := []byte("bar")
fwdaddr := network.RandomAddr()
msg := &PssMsg{
Payload: &PssEnvelope{
TTL: 0,
From: oaddr,
Topic: pssPingTopic,
Payload: data,
},
To: to,
}
msgtwo := &PssMsg{
Payload: &PssEnvelope{
TTL: 0,
From: oaddr,
Topic: pssPingTopic,
Payload: datatwo,
},
To: to,
}
digest, err := ps.storeMsg(msg)
if err != nil {
t.Fatalf("could not store cache msgone: %v", err)
}
digesttwo, err := ps.storeMsg(msgtwo)
if err != nil {
t.Fatalf("could not store cache msgtwo: %v", err)
}
if !bytes.Equal(digest[:], proofbytes) {
t.Fatalf("digest - got: %x, expected: %x", digest, proofbytes)
}
if digest == digesttwo {
t.Fatalf("different msgs return same crc: %d", digesttwo)
}
// check the sender cache
err = ps.addFwdCacheSender(fwdaddr.Over(), digest)
if err != nil {
t.Fatalf("write to pss sender cache failed: %v", err)
}
if !ps.checkFwdCache(fwdaddr.Over(), digest) {
t.Fatalf("message %v should have SENDER record in cache but checkCache returned false", msg)
}
if ps.checkFwdCache(fwdaddr.Over(), digesttwo) {
t.Fatalf("message %v should NOT have SENDER record in cache but checkCache returned true", msgtwo)
}
// check the expire cache
err = ps.addFwdCacheExpire(digest)
if err != nil {
t.Fatalf("write to pss expire cache failed: %v", err)
}
if !ps.checkFwdCache(nil, digest) {
t.Fatalf("message %v should have EXPIRE record in cache but checkCache returned false", msg)
}
if ps.checkFwdCache(nil, digesttwo) {
t.Fatalf("message %v should NOT have EXPIRE record in cache but checkCache returned true", msgtwo)
}
time.Sleep(pp.Cachettl)
if ps.checkFwdCache(nil, digest) {
t.Fatalf("message %v should have expired from cache but checkCache returned true", msg)
}
err = ps.AddToCache(fwdaddr.Over(), msgtwo)
if err != nil {
t.Fatalf("public accessor cache write failed: %v", err)
}
if !ps.checkFwdCache(fwdaddr.Over(), digesttwo) {
t.Fatalf("message %v should have SENDER record in cache but checkCache returned false", msgtwo)
}
}
func TestPssRegisterHandler(t *testing.T) {
var err error
addr := network.RandomAddr()
ps := newTestPss(addr.OAddr)
from := network.RandomAddr()
payload := []byte("payload")
topic := NewTopic(pssSpec.Name, int(pssSpec.Version))
wrongtopic := NewTopic("foo", 42)
checkMsg := func(msg []byte, p *p2p.Peer, sender []byte) error {
if !bytes.Equal(from.OAddr, sender) {
return fmt.Errorf("sender mismatch. expected %x, got %x", from.OAddr, sender)
}
if !bytes.Equal(msg, payload) {
return fmt.Errorf("sender mismatch. expected %x, got %x", msg, payload)
}
return nil
}
deregister := ps.Register(&topic, checkMsg)
pssmsg := &PssMsg{Payload: NewPssEnvelope(from.OAddr, topic, payload)}
err = ps.Process(pssmsg)
if err != nil {
t.Fatal(err)
}
var i int
err = ps.Process(&PssMsg{Payload: NewPssEnvelope(from.OAddr, wrongtopic, payload)})
expErr := ""
if err == nil || err.Error() == expErr {
t.Fatalf("unhandled topic expected '%v', got '%v'", expErr, err)
}
deregister2 := ps.Register(&topic, func(msg []byte, p *p2p.Peer, sender []byte) error { i++; return nil })
err = ps.Process(pssmsg)
if err != nil {
t.Fatal(err)
}
if i != 1 {
t.Fatalf("second registerer handler did not run")
}
deregister()
deregister2()
err = ps.Process(&PssMsg{Payload: NewPssEnvelope(from.OAddr, topic, payload)})
expErr = ""
if err == nil || err.Error() == expErr {
t.Fatalf("reregister handler expected %v, got %v", expErr, err)
}
}
func TestPssSimpleLinear(t *testing.T) {
var err error
nodeconfig := adapters.RandomNodeConfig()
addr := network.NewAddrFromNodeId(nodeconfig.Id)
_ = p2ptest.NewTestPeerPool()
ps := newTestPss(addr.Over())
ping := &pssPing{
quitC: make(chan struct{}),
}
err = RegisterPssProtocol(ps, &pssPingTopic, pssPingProtocol, newPssPingProtocol(ping.pssPingHandler))
if err != nil {
t.Fatalf("Failed to register virtual protocol in pss: %v", err)
}
run := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
id := p.ID()
bp := &testPssPeer{
Peer: protocols.NewPeer(p, rw, pssSpec),
addr: network.ToOverlayAddr(id[:]),
}
ps.Overlay.On(bp)
defer ps.Overlay.Off(bp)
log.Debug(fmt.Sprintf("%v", ps.Overlay))
return bp.Run(ps.handlePssMsg)
}
pt := p2ptest.NewProtocolTester(t, nodeconfig.Id, 2, run)
msg := newPssPingMsg(ps, network.ToOverlayAddr(pt.Ids[0].Bytes()), pssPingProtocol, pssPingTopic, []byte{1, 2, 3})
exchange := p2ptest.Exchange{
Expects: []p2ptest.Expect{
p2ptest.Expect{
Code: 0,
Msg: msg,
Peer: pt.Ids[0],
},
},
Triggers: []p2ptest.Trigger{
p2ptest.Trigger{
Code: 0,
Msg: msg,
Peer: pt.Ids[1],
},
},
}
err = pt.TestExchanges(exchange)
if err != nil {
t.Fatalf("exchange failed %v", err)
}
}
func TestPssFullRandom10_5_5(t *testing.T) {
adapter := adapters.NewSimAdapter(services)
testPssFullRandom(t, adapter, 10, 5, 5)
}
func testPssFullRandom(t *testing.T, adapter adapters.NodeAdapter, nodecount int, fullnodecount int, msgcount int) {
var lastid *adapters.NodeId = nil
nodeCount := 5
net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{
Id: "0",
DefaultService: "psstest",
})
defer net.Shutdown()
trigger := make(chan *adapters.NodeId)
ids := make([]*adapters.NodeId, nodeCount)
for i := 0; i < nodeCount; i++ {
nodeconfig := adapters.RandomNodeConfig()
nodeconfig.Service = "psstest"
node, err := net.NewNodeWithConfig(nodeconfig)
if err != nil {
t.Fatalf("error starting node: %s", err)
}
if err := net.Start(node.ID()); err != nil {
t.Fatalf("error starting node %s: %s", node.ID().Label(), err)
}
if err := triggerChecks(trigger, net, node.ID()); err != nil {
t.Fatal("error triggering checks for node %s: %s", node.ID().Label(), err)
}
ids[i] = node.ID()
}
// run a simulation which connects the 10 nodes in a ring and waits
// for full peer discovery
action := func(ctx context.Context) error {
for i, id := range ids {
var peerId *adapters.NodeId
if i == 0 {
peerId = ids[len(ids)-1]
} else {
peerId = ids[i-1]
}
if err := net.Connect(id, peerId); err != nil {
return err
}
}
return nil
}
check := func(ctx context.Context, id *adapters.NodeId) (bool, error) {
select {
case <-ctx.Done():
return false, ctx.Err()
default:
}
node := net.GetNode(id)
if node == nil {
return false, fmt.Errorf("unknown node: %s", id)
}
client, err := node.Client()
if err != nil {
return false, fmt.Errorf("error getting node client: %s", err)
}
log.Debug("in check", "node", id)
if lastid != nil {
//msg := pssPingMsg{Created: time.Now(),}
client.CallContext(context.Background(), nil, "pss_sendRaw", pssPingTopic, PssAPIMsg{
Addr: lastid.Bytes(),
Msg: []byte{1, 2, 3},
})
}
lastid = id
return true, nil
}
timeout := 5 * time.Second
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
result := simulations.NewSimulation(net).Run(ctx, &simulations.Step{
Action: action,
Trigger: trigger,
Expect: &simulations.Expectation{
Nodes: ids,
Check: check,
},
})
if result.Error != nil {
t.Fatalf("simulation failed: %s", result.Error)
}
t.Log("Simulation Passed:")
t.Logf("Duration: %s", result.FinishedAt.Sub(result.StartedAt))
time.Sleep(time.Second * 2)
}
// triggerChecks triggers a simulation step check whenever a peer is added or
// removed from the given node
func triggerChecks(trigger chan *adapters.NodeId, net *simulations.Network, id *adapters.NodeId) error {
node := net.GetNode(id)
if node == nil {
return fmt.Errorf("unknown node: %s", id)
}
go func() {
time.Sleep(time.Second)
trigger <- id
}()
/*
client, err := node.Client()
if err != nil {
return err
}
events := make(chan PssAPIMsg)
sub, err := client.Subscribe(context.Background(), "pss", events, "newMsg", topic)
if err != nil {
return fmt.Errorf("error getting peer events for node %v: %s", id, err)
}
go func() {
defer sub.Unsubscribe()
for {
select {
case msg := <-events:
log.Warn("pss rpc got msg", "msg", msg)
trigger <- id
case err := <-sub.Err():
if err != nil {
log.Error(fmt.Sprintf("error getting peer events for node %v", id), "err", err)
}
return
}
}
}()
*/
return nil
}
func newServices() adapters.Services {
return adapters.Services{
"psstest": func(id *adapters.NodeId, snapshot []byte) []node.Service {
addr := network.NewAddrFromNodeId(id)
kadparams := network.NewKadParams()
kadparams.MinProxBinSize = 2
kadparams.MaxBinSize = 3
kadparams.MinBinSize = 1
kadparams.MaxRetries = 1000
kadparams.RetryExponent = 2
kadparams.RetryInterval = 1000000
kademlia := network.NewKademlia(addr.Over(), kadparams)
config := &network.BzzConfig{
OverlayAddr: addr.Over(),
UnderlayAddr: addr.Under(),
HiveParams: network.NewHiveParams(),
}
config.HiveParams.KeepAliveInterval = time.Second
cachedir, err := ioutil.TempDir("", "pss-cache")
if err != nil {
log.Error("create pss cache tmpdir failed", "error", err)
return nil
}
dpa, err := storage.NewLocalDPA(cachedir)
if err != nil {
log.Error("local dpa creation failed", "error", err)
return nil
}
pssp := NewPssParams()
return []node.Service{network.NewBzz(config, kademlia, adapters.NewSimStateStore()), NewPss(kademlia, dpa, pssp)}
},
}
}
/*
// example protocol implementation peer // example protocol implementation peer
// message handlers are methods of this // message handlers are methods of this
// channels allow receipt reporting from p2p.Protocol message handler // channels allow receipt reporting from p2p.Protocol message handler
@ -59,27 +439,19 @@ type pssTestNode struct {
apifunc func() []rpc.API apifunc func() []rpc.API
} }
func (n *pssTestNode) Add(peer Peer) error { func (n *pssTestNode) Add(peer *bzzPeer) error {
err := n.Hive.Add(peer) err := n.Hive.Add(peer)
time.Sleep(time.Millisecond * 250) time.Sleep(time.Millisecond * 250)
n.triggerCheck() n.triggerCheck()
return err return err
} }
func (n *pssTestNode) Remove(peer Peer) {
n.Hive.Remove(peer)
}
func (n *pssTestNode) hiveKeepAlive() <-chan time.Time {
return time.Tick(time.Second * 10)
}
func (n *pssTestNode) triggerCheck() { func (n *pssTestNode) triggerCheck() {
go func() { n.trigger <- n.id }() go func() { n.trigger <- n.id }()
} }
func (n *pssTestNode) OverlayAddr() []byte { func (n *pssTestNode) OverlayAddr() []byte {
return n.Pss.Overlay.GetAddr().OverlayAddr() return n.Pss.Overlay.BaseAddr()
} }
func (n *pssTestNode) UnderlayAddr() []byte { func (n *pssTestNode) UnderlayAddr() []byte {
@ -102,8 +474,9 @@ type pssTestService struct {
func newPssTestService(t *testing.T, handlefunc func(interface{}) error, testnode *pssTestNode) *pssTestService { func newPssTestService(t *testing.T, handlefunc func(interface{}) error, testnode *pssTestNode) *pssTestService {
hp := NewHiveParams() hp := NewHiveParams()
//hp.CallInterval = 250 hp.KeepAliveInterval = 300
testnode.Hive = NewHive(hp, testnode.Pss.Overlay) bzz := NewBzz(testnode.OverlayAddr(), testnode.UnderlayAddr(), newTestStore())
testnode.Hive = NewHive(hp, testnode.Pss.Overlay, bzz)
return &pssTestService{ return &pssTestService{
//nid := adapters.NewNodeId(addr.UnderlayAddr()) //nid := adapters.NewNodeId(addr.UnderlayAddr())
msgFunc: handlefunc, msgFunc: handlefunc,
@ -111,8 +484,8 @@ func newPssTestService(t *testing.T, handlefunc func(interface{}) error, testnod
} }
} }
func (self *pssTestService) Start(server p2p.Server) error { func (self *pssTestService) Start(server *p2p.Server) error {
return self.node.Hive.Start(server, self.node.hiveKeepAlive) return self.node.Hive.Start(server)
} }
func (self *pssTestService) Stop() error { func (self *pssTestService) Stop() error {
@ -121,24 +494,13 @@ func (self *pssTestService) Stop() error {
} }
func (self *pssTestService) Protocols() []p2p.Protocol { func (self *pssTestService) Protocols() []p2p.Protocol {
ct := BzzCodeMap() bzz := NewBzz(self.node.OverlayAddr(), self.node.UnderlayAddr(), newTestStore())
for _, m := range DiscoveryMsgs { return append(self.node.Hive.Protocols(), p2p.Protocol{
ct.Register(m) Name: PssProtocolName,
} Version: PssProtocolVersion,
ct.Register(&PssMsg{}) Length: PssProtocol.Length(),
Run: bzz.RunProtocol(PssProtocol, self.Run),
srv := func(p Peer) error { })
p.Register(&PssMsg{}, self.msgFunc)
self.node.Add(p)
p.DisconnectHook(func(err error) {
self.node.Remove(p)
})
return nil
}
proto := Bzz(self.node.OverlayAddr(), self.node.UnderlayAddr(), ct, srv, nil, nil)
return []p2p.Protocol{*proto}
} }
func (self *pssTestService) APIs() []rpc.API { func (self *pssTestService) APIs() []rpc.API {
@ -153,142 +515,15 @@ func (self *pssTestService) APIs() []rpc.API {
return nil return nil
} }
func TestPssCache(t *testing.T) { func (self *pssTestService) Run(peer *bzzPeer) error {
var err error self.node.Add(peer)
to, _ := hex.DecodeString("08090a0b0c0d0e0f1011121314150001020304050607161718191a1b1c1d1e1f") defer self.node.Remove(peer)
oaddr, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") return peer.Run(self.msgFunc)
uaddr, _ := hex.DecodeString("101112131415161718191a1b1c1d1e1f000102030405060708090a0b0c0d0e0f")
ps := makePss(oaddr)
pp := NewPssParams()
topic, _ := MakeTopic(protocolName, protocolVersion)
data := []byte("foo")
fwdaddr := RandomAddr()
msg := &PssMsg{
Payload: pssEnvelope{
TTL: 0,
SenderOAddr: oaddr,
SenderUAddr: uaddr,
Topic: topic,
Payload: data,
},
}
msg.SetRecipient(to)
msgtwo := &PssMsg{
Payload: pssEnvelope{
TTL: 0,
SenderOAddr: uaddr,
SenderUAddr: oaddr,
Topic: topic,
Payload: data,
},
}
msgtwo.SetRecipient(to)
digest := ps.hashMsg(msg)
digesttwo := ps.hashMsg(msgtwo)
if digest != 3595343914 {
t.Fatalf("digest - got: %d, expected: %d", digest, 3595343914)
}
if digest == digesttwo {
t.Fatalf("different msgs return same crc: %d", digesttwo)
}
// check the sender cache
err = ps.addFwdCacheSender(fwdaddr.OverlayAddr(), digest)
if err != nil {
t.Fatalf("write to pss sender cache failed: %v", err)
}
if !ps.checkFwdCache(fwdaddr.OverlayAddr(), digest) {
t.Fatalf("message %v should have SENDER record in cache but checkCache returned false", msg)
}
if ps.checkFwdCache(fwdaddr.OverlayAddr(), digesttwo) {
t.Fatalf("message %v should NOT have SENDER record in cache but checkCache returned true", msgtwo)
}
// check the expire cache
err = ps.addFwdCacheExpire(digest)
if err != nil {
t.Fatalf("write to pss expire cache failed: %v", err)
}
if !ps.checkFwdCache(nil, digest) {
t.Fatalf("message %v should have EXPIRE record in cache but checkCache returned false", msg)
}
if ps.checkFwdCache(nil, digesttwo) {
t.Fatalf("message %v should NOT have EXPIRE record in cache but checkCache returned true", msgtwo)
}
time.Sleep(pp.Cachettl)
if ps.checkFwdCache(nil, digest) {
t.Fatalf("message %v should have expired from cache but checkCache returned true", msg)
}
err = ps.AddToCache(fwdaddr.OverlayAddr(), msgtwo)
if err != nil {
t.Fatalf("public accessor cache write failed: %v", err)
}
if !ps.checkFwdCache(fwdaddr.OverlayAddr(), digesttwo) {
t.Fatalf("message %v should have SENDER record in cache but checkCache returned false", msgtwo)
}
} }
*/
func TestPssRegisterHandler(t *testing.T) { /*
var topic PssTopic
var err error
addr := RandomAddr()
ps := makePss(addr.UnderlayAddr())
topic, _ = MakeTopic(protocolName, protocolVersion)
err = ps.Register(topic, func(msg []byte, p *p2p.Peer, sender []byte) error { return nil })
if err != nil {
t.Fatalf("couldnt register protocol 'foo' v 42: %v", err)
}
topic, _ = MakeTopic(protocolName, protocolVersion)
err = ps.Register(topic, func(msg []byte, p *p2p.Peer, sender []byte) error { return nil })
if err == nil {
t.Fatalf("register protocol 'abc..789' v 65536 should have failed")
}
}
func TestPssFullRandom10_10_5(t *testing.T) {
testPssFullRandom(t, 10, 10, 5)
}
func TestPssFullRandom50_50_5(t *testing.T) {
testPssFullRandom(t, 50, 50, 5)
}
func TestPssFullRandom50_50_25(t *testing.T) {
testPssFullRandom(t, 50, 50, 25)
}
func TestPssFullRandom10_100_50(t *testing.T) {
testPssFullRandom(t, 10, 100, 50)
}
func TestPssFullRandom50_100_50(t *testing.T) {
testPssFullRandom(t, 50, 100, 50)
}
func TestPssFullRandom100_100_5(t *testing.T) {
testPssFullRandom(t, 100, 100, 5)
}
func TestPssFullRandom100_100_25(t *testing.T) {
testPssFullRandom(t, 100, 100, 25)
}
func TestPssFullRandom100_100_50(t *testing.T) {
testPssFullRandom(t, 100, 100, 50)
}
func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes int) { func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes int) {
var action func(ctx context.Context) error var action func(ctx context.Context) error
@ -305,7 +540,9 @@ func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes in
expectnodesids := []*adapters.NodeId{} // the nodes to expect on (needed by checker) expectnodesids := []*adapters.NodeId{} // the nodes to expect on (needed by checker)
expectnodesresults := make(map[*adapters.NodeId][]int) // which messages expect actually got expectnodesresults := make(map[*adapters.NodeId][]int) // which messages expect actually got
vct := protocols.NewCodeMap(protocolName, protocolVersion, 65535, &pssTestPayload{}) vct := protocols.NewCodeMap(map[uint64]interface{}{
0: pssTestPayload{},
})
topic, _ := MakeTopic(protocolName, protocolVersion) topic, _ := MakeTopic(protocolName, protocolVersion)
trigger := make(chan *adapters.NodeId) trigger := make(chan *adapters.NodeId)
@ -471,8 +708,8 @@ func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes in
for i := 0; i < len(sends); i += 2 { for i := 0; i < len(sends); i += 2 {
t.Logf("Pss #%d: oaddr %x -> %x (uaddr %x -> %x)", i/2+1, t.Logf("Pss #%d: oaddr %x -> %x (uaddr %x -> %x)", i/2+1,
common.ByteLabel(nodes[fullnodes[sends[i]]].Pss.GetAddr().OverlayAddr()), common.ByteLabel(nodes[fullnodes[sends[i]]].Pss.BaseAddr()),
common.ByteLabel(nodes[fullnodes[sends[i+1]]].Pss.GetAddr().OverlayAddr()), common.ByteLabel(nodes[fullnodes[sends[i+1]]].Pss.BaseAddr()),
common.ByteLabel(fullnodes[sends[i]].Bytes()), common.ByteLabel(fullnodes[sends[i]].Bytes()),
common.ByteLabel(fullnodes[sends[i+1]].Bytes())) common.ByteLabel(fullnodes[sends[i+1]].Bytes()))
} }
@ -484,15 +721,15 @@ func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes in
fails++ fails++
} }
} }
t.Logf("Node oaddr %x (uaddr %x) was sent %d msgs, of which %d failed", common.ByteLabel(nodes[id].Pss.GetAddr().OverlayAddr()), common.ByteLabel(id.Bytes()), len(results), fails) t.Logf("Node oaddr %x (uaddr %x) was sent %d msgs, of which %d failed", common.ByteLabel(nodes[id].Pss.BaseAddr()), common.ByteLabel(id.Bytes()), len(results), fails)
totalfails += fails totalfails += fails
} }
t.Logf("Total sent: %d, total fail: %d (%.2f%%)", len(sends)/2, totalfails, (float32(totalfails)/float32(len(sends)/2+1))*100) t.Logf("Total sent: %d, total fail: %d (%.2f%%)", len(sends)/2, totalfails, (float32(totalfails)/float32(len(sends)/2+1))*100)
for _, node := range nodes { for _, node := range nodes {
logstring := fmt.Sprintf("Node oaddr %x kademlia: ", common.ByteLabel(node.Pss.Overlay.GetAddr().OverlayAddr())) logstring := fmt.Sprintf("Node oaddr %x kademlia: ", common.ByteLabel(node.Pss.Overlay.BaseAddr()))
node.Pss.Overlay.EachLivePeer(nil, 256, func(p Peer, po int, isprox bool) bool { node.Pss.Overlay.EachConn(nil, 256, func(p Peer, po int, isprox bool) bool {
logstring += fmt.Sprintf("%x ", common.ByteLabel(p.OverlayAddr())) logstring += fmt.Sprintf("%x ", common.ByteLabel(p.Over()))
return true return true
}) })
t.Log(logstring) t.Log(logstring)
@ -511,7 +748,8 @@ func TestPssFullLinearEcho(t *testing.T) {
var firstpssnode *adapters.NodeId var firstpssnode *adapters.NodeId
var secondpssnode *adapters.NodeId var secondpssnode *adapters.NodeId
vct := protocols.NewCodeMap(protocolName, protocolVersion, 65535, &pssTestPayload{}) vct := protocols.NewCodeMap(protocolName, protocolVersion, ProtocolMaxMsgSize)
vct.Register(0, &pssTestPayload{})
topic, _ := MakeTopic(protocolName, protocolVersion) topic, _ := MakeTopic(protocolName, protocolVersion)
fullnodes := []*adapters.NodeId{} fullnodes := []*adapters.NodeId{}
@ -538,15 +776,15 @@ func TestPssFullLinearEcho(t *testing.T) {
return err return err
} }
/*for i, id := range ids { // for i, id := range ids {
var peerId *adapters.NodeId // var peerId *adapters.NodeId
if i != 0 { // if i != 0 {
peerId = ids[i-1] // peerId = ids[i-1]
if err := net.Connect(id, peerId); err != nil { // if err := net.Connect(id, peerId); err != nil {
return err // return err
} // }
} // }
}*/ // }
return nil return nil
} }
check = func(ctx context.Context, id *adapters.NodeId) (bool, error) { check = func(ctx context.Context, id *adapters.NodeId) (bool, error) {
@ -559,9 +797,8 @@ func TestPssFullLinearEcho(t *testing.T) {
node, ok := nodes[id] node, ok := nodes[id]
if !ok { if !ok {
return false, fmt.Errorf("unknown node: %s (%v)", id, node) return false, fmt.Errorf("unknown node: %s (%v)", id, node)
} else {
log.Trace(fmt.Sprintf("sim check ok node %v", id))
} }
log.Trace(fmt.Sprintf("sim check ok node %v", id))
return true, nil return true, nil
} }
@ -589,7 +826,7 @@ func TestPssFullLinearEcho(t *testing.T) {
// first find a node that we're connected to // first find a node that we're connected to
for firstpssnode == nonode { for firstpssnode == nonode {
log.Debug(fmt.Sprintf("Waiting for pss relaypeer for %x close to %x ...", common.ByteLabel(nodes[fullnodes[0]].OverlayAddr()), common.ByteLabel(nodes[ids[1]].OverlayAddr()))) log.Debug(fmt.Sprintf("Waiting for pss relaypeer for %x close to %x ...", common.ByteLabel(nodes[fullnodes[0]].OverlayAddr()), common.ByteLabel(nodes[ids[1]].OverlayAddr())))
nodes[fullnodes[0]].Pss.Overlay.EachLivePeer(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool { nodes[fullnodes[0]].Pss.Overlay.EachConn(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool {
for _, id := range ids { for _, id := range ids {
if id.NodeID == p.ID() { if id.NodeID == p.ID() {
firstpssnode = id firstpssnode = id
@ -609,7 +846,7 @@ func TestPssFullLinearEcho(t *testing.T) {
// then find the node it's connected to // then find the node it's connected to
for secondpssnode == nonode { for secondpssnode == nonode {
log.Debug(fmt.Sprintf("PSS kademlia: Waiting for recipientpeer for %x close to %x ...", common.ByteLabel(nodes[firstpssnode].OverlayAddr()), common.ByteLabel(nodes[fullnodes[1]].OverlayAddr()))) log.Debug(fmt.Sprintf("PSS kademlia: Waiting for recipientpeer for %x close to %x ...", common.ByteLabel(nodes[firstpssnode].OverlayAddr()), common.ByteLabel(nodes[fullnodes[1]].OverlayAddr())))
nodes[firstpssnode].Pss.Overlay.EachLivePeer(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool { nodes[firstpssnode].Pss.Overlay.Eachc(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool {
for _, id := range ids { for _, id := range ids {
if id.NodeID == p.ID() && id.NodeID != fullnodes[0].NodeID { if id.NodeID == p.ID() && id.NodeID != fullnodes[0].NodeID {
secondpssnode = id secondpssnode = id
@ -692,7 +929,8 @@ func TestPssFullWS(t *testing.T) {
var firstpssnode, secondpssnode *adapters.NodeId var firstpssnode, secondpssnode *adapters.NodeId
fullnodes := []*adapters.NodeId{} fullnodes := []*adapters.NodeId{}
vct := protocols.NewCodeMap(protocolName, protocolVersion, 65535, &pssTestPayload{}) vct := protocols.NewCodeMap(protocolName, protocolVersion, ProtocolMaxMsgSize)
vct.Register(0, &pssTestPayload{})
topic, _ := MakeTopic(pingTopicName, pingTopicVersion) topic, _ := MakeTopic(pingTopicName, pingTopicVersion)
trigger := make(chan *adapters.NodeId) trigger := make(chan *adapters.NodeId)
@ -788,7 +1026,7 @@ func TestPssFullWS(t *testing.T) {
// then find the node it's connected to // then find the node it's connected to
for secondpssnode == nonode { for secondpssnode == nonode {
log.Debug(fmt.Sprintf("PSS kademlia: Waiting for recipientpeer for %x close to %x ...", common.ByteLabel(nodes[firstpssnode].OverlayAddr()), common.ByteLabel(nodes[fullnodes[1]].OverlayAddr()))) log.Debug(fmt.Sprintf("PSS kademlia: Waiting for recipientpeer for %x close to %x ...", common.ByteLabel(nodes[firstpssnode].OverlayAddr()), common.ByteLabel(nodes[fullnodes[1]].OverlayAddr())))
nodes[firstpssnode].Pss.Overlay.EachLivePeer(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool { nodes[firstpssnode].Pss.Overlay.EachConn(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool {
for _, id := range ids { for _, id := range ids {
if id.NodeID == p.ID() && id.NodeID != fullnodes[0].NodeID { if id.NodeID == p.ID() && id.NodeID != fullnodes[0].NodeID {
secondpssnode = id secondpssnode = id
@ -855,7 +1093,7 @@ func TestPssFullWS(t *testing.T) {
action = func(ctx context.Context) error { action = func(ctx context.Context) error {
go func() { go func() {
clientrecv.EthSubscribe(ctx, ch, "newMsg", topic) clientrecv.EthSubscribe(ctx, ch, "newMsg", topic)
clientsend.Call(nil, "eth_sendRaw", nodes[secondpssnode].Pss.Overlay.GetAddr().OverlayAddr(), topic, []byte("ping")) clientsend.Call(nil, "eth_sendRaw", nodes[secondpssnode].Pss.Overlay.BaseAddr(), topic, []byte("ping"))
trigger <- secondpssnode trigger <- secondpssnode
}() }()
return nil return nil
@ -935,7 +1173,7 @@ func newPssSimulationTester(t *testing.T, numnodes int, numfullnodes int, trigge
if testpeers[id] != nil { if testpeers[id] != nil {
handlefunc = makePssHandleProtocol(psss[id]) handlefunc = makePssHandleProtocol(psss[id])
log.Trace(fmt.Sprintf("Making full protocol id %x addr %x (testpeers %p)", common.ByteLabel(id.Bytes()), common.ByteLabel(addr.OverlayAddr()), testpeers)) log.Trace(fmt.Sprintf("Making full protocol id %x addr %x (testpeers %p)", common.ByteLabel(id.Bytes()), common.ByteLabel(addr.Over()), testpeers))
} else { } else {
handlefunc = makePssHandleForward(psss[id]) handlefunc = makePssHandleForward(psss[id])
} }
@ -965,7 +1203,7 @@ func newPssSimulationTester(t *testing.T, numnodes int, numfullnodes int, trigge
} }
for i, conf := range configs { for i, conf := range configs {
addr := NewPeerAddrFromNodeId(conf.Id) addr := NewPeerAddrFromNodeId(conf.Id)
psss[conf.Id] = makePss(addr.OverlayAddr()) psss[conf.Id] = makePss(addr.Over())
if i < numfullnodes { if i < numfullnodes {
tp := &pssTestPeer{ tp := &pssTestPeer{
Peer: &protocols.Peer{ Peer: &protocols.Peer{
@ -992,13 +1230,73 @@ func newPssSimulationTester(t *testing.T, numnodes int, numfullnodes int, trigge
} }
func makePss(addr []byte) *Pss { func makePss(addr []byte) *Pss {
kp := NewKadParams()
// set up storage
cachedir, err := ioutil.TempDir("", "pss-cache")
if err != nil {
log.Error("create pss cache tmpdir failed", "error", err)
os.Exit(1)
}
dpa, err := storage.NewLocalDPA(cachedir)
if err != nil {
log.Error("local dpa creation failed", "error", err)
os.Exit(1)
}
// cannot use pyramidchunker as it still lacks joinfunc TestPssRegisterHandler(t *testing.T) {
addr := RandomAddr()
ps := newTestPss(addr.UnderlayAddr())
from := RandomAddr()
payload := []byte("payload")
topic := NewTopic(protocolName, protocolVersion)
checkMsg := func(msg []byte, p *p2p.Peer, sender []byte) error {
if !bytes.Equal(from.OverlayAddr(), sender) {
return fmt.Errorf("sender mismatch. expected %x, got %x", from.OverlayAddr(), sender)
}
if !bytes.Equal(msg, payload) {
return fmt.Errorf("sender mismatch. expected %x, got %x", msg, payload)
}
if !bytes.Equal(from.UnderlayAddr(), p.ID()) {
return fmt.Errorf("sender mismatch. expected %x, got %x", from.UnderlayAddr(), p.ID())
}
}
deregister := ps.Register(topic, checkMsg)
pssmsg := &PssMsg{Data: NewPssEnvelope(from, topic, payload)}
err = ps.Process(pssmsg)
if err != nil {
t.Fatal(err)
}
var i int
err = ps.Process(&PssMsg{Data: NewPssEnvelope(from, []byte("topic"), payload)})
expErr := ""
if err == nil || err.Error() != expErr {
t.Fatalf("unhandled topic expected %v, got %v", expErr, err)
}
deregister2 := ps.Register(topic, func(msg []byte, p *p2p.Peer, sender []byte) error { i++; return nil })
ps.Process(pssmsg)
if err != nil {
t.Fatal(err)
}
if i != 1 {
t.Fatalf("second registerer handler did not run")
}
deregister()
deregister2()
err = ps.Process(&PssMsg{Data: NewPssEnvelope(from, topic, payload)})
expErr = ""
if err == nil || err.Error() != expErr {
t.Fatalf("reregister handler expected %v, got %v", expErr, err)
}
}
// dpa.Chunker = storage.NewPyramidChunker(storage.NewChunkerParams())
kp := network.NewKadParams()
kp.MinProxBinSize = 3 kp.MinProxBinSize = 3
pp := NewPssParams() pp := NewPssParams()
overlay := NewKademlia(addr, kp) overlay := network.NewKademlia(addr, kp)
ps := NewPss(overlay, pp) ps := NewPss(overlay, dpa, pp)
//overlay.Prune(time.Tick(time.Millisecond * 250)) //overlay.Prune(time.Tick(time.Millisecond * 250))
return ps return ps
} }
@ -1018,35 +1316,6 @@ func makeCustomProtocol(name string, version int, ct *protocols.CodeMap, testpee
return protocols.NewProtocol(name, uint(version), run, ct, nil, nil) return protocols.NewProtocol(name, uint(version), run, ct, nil, nil)
} }
func makeFakeMsg(ps *Pss, ct *protocols.CodeMap, topic PssTopic, senderaddr PeerAddr, content string) PssMsg {
data := pssTestPayload{}
code, found := ct.GetCode(&data)
if !found {
return PssMsg{}
}
data.Data = content
rlpbundle, err := makeMsg(code, data)
if err != nil {
return PssMsg{}
}
pssenv := pssEnvelope{
SenderOAddr: senderaddr.OverlayAddr(),
SenderUAddr: senderaddr.UnderlayAddr(),
Topic: topic,
TTL: DefaultTTL,
Payload: rlpbundle,
}
pssmsg := PssMsg{
Payload: pssenv,
}
pssmsg.SetRecipient(ps.Overlay.GetAddr().OverlayAddr())
return pssmsg
}
func makePssHandleForward(ps *Pss) func(msg interface{}) error { func makePssHandleForward(ps *Pss) func(msg interface{}) error {
// for the simple check it passes on the message if it's not for us // for the simple check it passes on the message if it's not for us
return func(msg interface{}) error { return func(msg interface{}) error {
@ -1110,3 +1379,4 @@ func (ptp *pssTestPeer) SimpleHandlePssPayload(msg interface{}) error {
return nil return nil
} }
*/

76
swarm/pss/pssapi.go Normal file
View file

@ -0,0 +1,76 @@
package pss
import (
"context"
"fmt"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc"
)
// PssAPI is the RPC API module for Pss
type PssAPI struct {
*Pss
}
// NewPssAPI constructs a PssAPI instance
func NewPssAPI(ps *Pss) *PssAPI {
return &PssAPI{Pss: ps}
}
// PssAPIMsg is the type for messages, it extends the rlp encoded protocol Msg
// with the Sender's overlay address
type PssAPIMsg struct {
Msg []byte
Addr []byte
}
// NewMsg API endpoint creates an RPC subscription
func (pssapi *PssAPI) NewMsg(ctx context.Context, topic PssTopic) (*rpc.Subscription, error) {
notifier, supported := rpc.NotifierFromContext(ctx)
if !supported {
return nil, fmt.Errorf("Subscribe not supported")
}
psssub := notifier.CreateSubscription()
handler := func(msg []byte, p *p2p.Peer, from []byte) error {
apimsg := &PssAPIMsg{
Msg: msg,
Addr: from,
}
if err := notifier.Notify(psssub.ID, apimsg); err != nil {
log.Warn(fmt.Sprintf("notification on pss sub topic %v rpc (sub %v) msg %v failed!", topic, psssub.ID, msg))
}
return nil
}
deregf := pssapi.Pss.Register(&topic, handler)
go func() {
defer deregf()
//defer psssub.Unsubscribe()
select {
case err := <-psssub.Err():
log.Warn(fmt.Sprintf("caught subscription error in pss sub topic: %v", topic, err))
case <-notifier.Closed():
log.Warn(fmt.Sprintf("rpc sub notifier closed"))
}
}()
return psssub, nil
}
// SendRaw sends the message (serialised into byte slice) to a peer with topic
func (pssapi *PssAPI) SendRaw(topic PssTopic, msg PssAPIMsg) error {
err := pssapi.Pss.Send(msg.Addr, topic, msg.Msg)
if err != nil {
return fmt.Errorf("send error: %v", err)
}
return fmt.Errorf("ok sent")
}
// BaseAddr gets our own overlayaddress
func (pssapi *PssAPI) BaseAddr() ([]byte, error) {
log.Warn("inside baseaddr")
return pssapi.Pss.Overlay.BaseAddr(), nil
}

View file

@ -32,13 +32,13 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/swarm/api" "github.com/ethereum/go-ethereum/swarm/api"
httpapi "github.com/ethereum/go-ethereum/swarm/api/http" httpapi "github.com/ethereum/go-ethereum/swarm/api/http"
"github.com/ethereum/go-ethereum/swarm/fuse" "github.com/ethereum/go-ethereum/swarm/fuse"
"github.com/ethereum/go-ethereum/swarm/network" "github.com/ethereum/go-ethereum/swarm/network"
"github.com/ethereum/go-ethereum/swarm/storage" "github.com/ethereum/go-ethereum/swarm/storage"
"github.com/ethereum/go-ethereum/swarm/pss"
) )
// the swarm stack // the swarm stack
@ -162,7 +162,7 @@ Start is called when the stack is started
* TODO: start subservices like sword, swear, swarmdns * TODO: start subservices like sword, swear, swarmdns
*/ */
// implements the node.Service interface // implements the node.Service interface
func (self *Swarm) Start(net p2p.Server) error { func (self *Swarm) Start(net *p2p.Server) error {
// set chequebook // set chequebook
if self.swapEnabled { if self.swapEnabled {
ctx := context.Background() // The initial setup has no deadline. ctx := context.Background() // The initial setup has no deadline.
@ -182,9 +182,10 @@ func (self *Swarm) Start(net p2p.Server) error {
func() <-chan time.Time { func() <-chan time.Time {
return time.NewTicker(time.Second).C return time.NewTicker(time.Second).C
}, },
nil,
) )
log.Info(fmt.Sprintf("Swarm network started on bzz address: %v", self.hive.GetAddr())) log.Info(fmt.Sprintf("Swarm network started on bzz address: %v", self.hive.BaseAddr()))
if self.pssEnabled { if self.pssEnabled {
pssparams := network.NewPssParams() pssparams := network.NewPssParams()
@ -238,48 +239,46 @@ func (self *Swarm) Stop() error {
// implements the node.Service interface // implements the node.Service interface
func (self *Swarm) Protocols() []p2p.Protocol { func (self *Swarm) Protocols() []p2p.Protocol {
ct := network.BzzCodeMap() ct := network.BzzCodeMap()
for _, m := range network.DiscoveryMsgs {
ct.Register(m)
}
if self.pssEnabled { if self.pssEnabled {
ct.Register(&network.PssMsg{}) ct.Register(1, &network.PssMsg{})
} }
ct.Register(2, network.DiscoveryMsgs...)
srv := func(p network.Peer) error { // srv := func(p network.Peer) error {
if self.pssEnabled { // if self.pssEnabled {
p.Register(&network.PssMsg{}, func(msg interface{}) error { // p.Register(&network.PssMsg{}, func(msg interface{}) error {
pssmsg := msg.(*network.PssMsg) // pssmsg := msg.(*network.PssMsg)
//
if self.pss.IsSelfRecipient(pssmsg) { // if self.pss.IsSelfRecipient(pssmsg) {
log.Trace("pss for us, yay! ... let's process!") // log.Trace("pss for us, yay! ... let's process!")
env := pssmsg.Payload // env := pssmsg.Payload
umsg := env.Payload // umsg := env.Payload
f := self.pss.GetHandler(env.Topic) // f := self.pss.GetHandler(env.Topic)
if f == nil { // if f == nil {
return fmt.Errorf("No registered handler for topic '%s'", env.Topic) // return fmt.Errorf("No registered handler for topic '%s'", env.Topic)
} // }
nid := adapters.NewNodeId(env.SenderUAddr) // nid := adapters.NewNodeId(env.SenderUAddr)
p := p2p.NewPeer(nid.NodeID, fmt.Sprintf("%x", common.ByteLabel(nid.Bytes())), []p2p.Cap{}) // p := p2p.NewPeer(nid.NodeID, fmt.Sprintf("%x", common.ByteLabel(nid.Bytes())), []p2p.Cap{})
return f(umsg, p, env.SenderOAddr) // return f(umsg, p, env.SenderOAddr)
} else { // } else {
log.Trace("pss was for someone else :'( ... forwarding") // log.Trace("pss was for someone else :'( ... forwarding")
return self.pss.Forward(pssmsg) // return self.pss.Forward(pssmsg)
} // }
return nil // return nil
}) // })
} // }
self.hive.Add(p) // self.hive.Add(p)
p.DisconnectHook(func(err error) { // p.DisconnectHook(func(err error) {
self.hive.Remove(p) // self.hive.Remove(p)
}) // })
return nil // return nil
} // }
proto := network.Bzz( proto := network.Bzz(
self.hive.Overlay.GetAddr().OverlayAddr(), self.hive.Overlay.GetAddr().Over(),
self.hive.Overlay.GetAddr().UnderlayAddr(), self.hive.Overlay.GetAddr().Under(),
ct, ct,
srv, nil,
nil, nil,
nil, nil,
) )

View file

@ -48,14 +48,16 @@ func main() {
shh := whisper.New() shh := whisper.New()
// Create an Ethereum peer to communicate through // Create an Ethereum peer to communicate through
server := p2p.NewServer(p2p.Config{ server := &p2p.Server{
PrivateKey: key, Config: p2p.Config{
MaxPeers: 10, PrivateKey: key,
Name: name, MaxPeers: 10,
Protocols: []p2p.Protocol{shh.Protocol()}, Name: name,
ListenAddr: ":30300", Protocols: []p2p.Protocol{shh.Protocol()},
NAT: nat.Any(), ListenAddr: ":30300",
}) NAT: nat.Any(),
},
}
fmt.Println("Starting Ethereum peer...") fmt.Println("Starting Ethereum peer...")
if err := server.Start(); err != nil { if err := server.Start(); err != nil {
fmt.Printf("Failed to start Ethereum peer: %v.\n", err) fmt.Printf("Failed to start Ethereum peer: %v.\n", err)

View file

@ -172,7 +172,7 @@ func (self *Whisper) Send(envelope *Envelope) error {
// Start implements node.Service, starting the background data propagation thread // Start implements node.Service, starting the background data propagation thread
// of the Whisper protocol. // of the Whisper protocol.
func (self *Whisper) Start(p2p.Server) error { func (self *Whisper) Start(*p2p.Server) error {
log.Info(fmt.Sprint("Whisper started")) log.Info(fmt.Sprint("Whisper started"))
go self.update() go self.update()
return nil return nil

View file

@ -78,7 +78,7 @@ type TestData struct {
type TestNode struct { type TestNode struct {
shh *Whisper shh *Whisper
id *ecdsa.PrivateKey id *ecdsa.PrivateKey
server p2p.Server server *p2p.Server
filerId string filerId string
} }
@ -140,17 +140,19 @@ func initialize(t *testing.T) {
peers = append(peers, peer) peers = append(peers, peer)
} }
node.server = p2p.NewServer(p2p.Config{ node.server = &p2p.Server{
PrivateKey: node.id, Config: p2p.Config{
MaxPeers: NumNodes/2 + 1, PrivateKey: node.id,
Name: name, MaxPeers: NumNodes/2 + 1,
Protocols: node.shh.Protocols(), Name: name,
ListenAddr: addr, Protocols: node.shh.Protocols(),
NAT: nat.Any(), ListenAddr: addr,
BootstrapNodes: peers, NAT: nat.Any(),
StaticNodes: peers, BootstrapNodes: peers,
TrustedNodes: peers, StaticNodes: peers,
}) TrustedNodes: peers,
},
}
err = node.server.Start() err = node.server.Start()
if err != nil { if err != nil {

View file

@ -396,7 +396,7 @@ func (w *Whisper) Send(envelope *Envelope) error {
// Start implements node.Service, starting the background data propagation thread // Start implements node.Service, starting the background data propagation thread
// of the Whisper protocol. // of the Whisper protocol.
func (w *Whisper) Start(p2p.Server) error { func (w *Whisper) Start(*p2p.Server) error {
log.Info("started whisper v." + ProtocolVersionStr) log.Info("started whisper v." + ProtocolVersionStr)
go w.update() go w.update()