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
}
func injectBootnodes(srv p2p.Server, nodes []string) {
func injectBootnodes(srv *p2p.Server, nodes []string) {
for _, url := range nodes {
n, err := discover.ParseNode(url)
if err != nil {

View file

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

View file

@ -94,7 +94,7 @@ func (r *ReleaseService) Protocols() []p2p.Protocol { return nil }
func (r *ReleaseService) APIs() []rpc.API { return nil }
// 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()
return nil
}

View file

@ -49,7 +49,7 @@ import (
)
type LesServer interface {
Start(srvr p2p.Server)
Start(srvr *p2p.Server)
Stop()
Protocols() []p2p.Protocol
}
@ -362,7 +362,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol {
// Start implements node.Service, starting all internal goroutines needed by the
// 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.protocolManager.Start()

View file

@ -52,7 +52,7 @@ const historyUpdateRange = 50
type Service struct {
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
les *les.LightEthereum // Light Ethereum service if monitoring a light node
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 }
// 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
go s.loop()

View file

@ -1434,12 +1434,12 @@ func (api *PrivateDebugAPI) SetHead(number hexutil.Uint64) {
// PublicNetAPI offers network related RPC methods
type PublicNetAPI struct {
net p2p.Server
net *p2p.Server
networkVersion uint64
}
// 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}
}

View file

@ -185,7 +185,7 @@ func (s *LightEthereum) Protocols() []p2p.Protocol {
// Start implements node.Service, starting all internal goroutines needed by the
// 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")
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.networkId)
s.protocolManager.Start(srvr)

View file

@ -61,10 +61,6 @@ const (
disableClientRemovePeer = false
)
type discV5Server interface {
DiscV5() *discv5.Network
}
// errIncompatibleConfig is returned if the requested protocols and configs are
// not compatible (low protocol version restrictions and high requirements).
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
if v, ok := srvr.(discV5Server); ok {
topicDisc = v.DiscV5()
if srvr != nil {
topicDisc = srvr.DiscV5
}
lesTopic := discv5.Topic("LES@" + common.Bytes2Hex(pm.blockchain.Genesis().Hash().Bytes()[0:8]))
if pm.lightSync {

View file

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

View file

@ -97,7 +97,7 @@ const (
type serverPool struct {
db ethdb.Database
dbKey []byte
server p2p.Server
server *p2p.Server
quit chan struct{}
wg *sync.WaitGroup
connWg sync.WaitGroup
@ -118,7 +118,7 @@ type serverPool struct {
}
// 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{
db: db,
dbKey: append(dbPrefix, []byte(topic)...),
@ -139,11 +139,11 @@ func newServerPool(db ethdb.Database, dbPrefix []byte, server p2p.Server, topic
pool.loadNodes()
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.discNodes = make(chan *discv5.Node, 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()

View file

@ -375,7 +375,7 @@ func NewPublicWeb3API(stack *Node) *PublicWeb3API {
// ClientVersion returns the node name
func (s *PublicWeb3API) ClientVersion() string {
return s.stack.serverConfig.Name
return s.stack.Server().Name
}
// 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
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)
services map[reflect.Type]Service // Currently running services
@ -165,6 +165,8 @@ func (n *Node) Start() error {
if n.serverConfig.NodeDatabase == "" {
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
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
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 errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] {
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
// only to inspect fields of the currently running server, life cycle management
// should be left to this Node entity.
func (n *Node) Server() p2p.Server {
func (n *Node) Server() *p2p.Server {
n.lock.RLock()
defer n.lock.RUnlock()

View file

@ -37,7 +37,7 @@ type SampleService struct{}
func (s *SampleService) Protocols() []p2p.Protocol { 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 ExampleService() {

View file

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

View file

@ -86,7 +86,7 @@ type Service interface {
// Start is called after all services have been constructed and the networking
// 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
// are all terminated.

View file

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

View file

@ -47,6 +47,19 @@ const (
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.
// it get's a chance to compute new tasks on every iteration
// of the main loop in server.run.
@ -84,7 +97,7 @@ type pastDial struct {
}
type task interface {
Do(*server)
Do(*Server)
}
// 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.resolve(srv) {
return
@ -288,7 +301,7 @@ func (t *dialTask) Do(srv *server) {
// Resolve operations are throttled with backoff to avoid flooding the
// discovery network with useless queries for nodes that don't exist.
// 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 {
log.Debug("Can't resolve node", "id", t.dest.ID, "err", "discovery is disabled")
return false
@ -317,15 +330,14 @@ func (t *dialTask) resolve(srv *server) bool {
}
// dial performs the actual connection attempt.
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("tcp", addr.String())
func (t *dialTask) dial(srv *Server, dest *discover.Node) bool {
fd, err := srv.Dialer.Dial(dest)
if err != nil {
log.Trace("Dial error", "task", t, "err", err)
return false
}
mfd := newMeteredConn(fd, false)
srv.setupConn(mfd, t.flags, dest)
srv.SetupConn(mfd, t.flags, dest)
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)
}
func (t *discoverTask) Do(srv *server) {
func (t *discoverTask) Do(srv *Server) {
// newTasks generates a lookup task whenever dynamic dials are
// necessary. Lookups need to take some time, otherwise the
// event loop spins too fast.
@ -355,7 +367,7 @@ func (t *discoverTask) String() string {
return s
}
func (t waitExpireTask) Do(*server) {
func (t waitExpireTask) Do(*Server) {
time.Sleep(t.Duration)
}
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.
config := Config{Dialer: &net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}}
srv := &server{ntab: table, Config: config}
config := Config{Dialer: TCPDialer{&net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}}}
srv := &Server{ntab: table, Config: config}
tasks[0].Do(srv)
if !reflect.DeepEqual(table.resolveCalls, []discover.NodeID{dest.ID}) {
t.Fatalf("wrong resolve calls, got %v", table.resolveCalls)

View file

@ -30,12 +30,13 @@ Standard protocol supports:
package protocols
import (
"context"
"fmt"
"reflect"
"sync"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
)
// error codes used by this protocol scheme
@ -45,23 +46,21 @@ const (
ErrWrite
ErrInvalidMsgCode
ErrInvalidMsgType
ErrLocalHandshake
ErrRemoteHandshake
ErrHandshake
ErrNoHandler
ErrHandler
)
// error description strings associated with the codes
var errorToString = map[int]string{
ErrMsgTooLong: "Message too long",
ErrDecode: "Invalid message (RLP error)",
ErrWrite: "Error sending message",
ErrInvalidMsgCode: "Invalid message code",
ErrInvalidMsgType: "Invalid message type",
ErrLocalHandshake: "Local handshake error",
ErrRemoteHandshake: "Remote handshake error",
ErrNoHandler: "No handler registered error",
ErrHandler: "Message handler error",
ErrMsgTooLong: "Message too long",
ErrDecode: "Invalid message (RLP error)",
ErrWrite: "Error sending message",
ErrInvalidMsgCode: "Invalid message code",
ErrInvalidMsgType: "Invalid message type",
ErrHandshake: "Handshake error",
ErrNoHandler: "No handler registered error",
ErrHandler: "Message handler error",
}
/*
@ -110,93 +109,74 @@ func errorf(code int, format string, params ...interface{}) *Error {
return self
}
// implements the code table spec
// listing the message codes and types etc
// and further metadata about the protocol
type CodeMap struct {
Name string // name of the protocol
Version uint // version
MaxMsgSize int // max length of message payload size
codes []reflect.Type // index of codes to msg types - to create zero values
messages map[reflect.Type]uint64 // index of types to codes, for sending by type
// Spec is a protocol specification including its name and version as well as
// the types of messages which are exchanged
type Spec struct {
// Name is the name of the protocol, often a three-letter word
Name string
// Version is the version number of the protocol
Version uint
// 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) {
if int(code) > len(self.codes)-1 {
func (s *Spec) init() {
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
}
typ := self.codes[code]
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
return reflect.New(typ).Interface(), true
}
// A Peer represents a remote peer or protocol instance that is running on a peer connection with
// a remote peer
type Peer struct {
ct *CodeMap // CodeMap for the protocol
*p2p.Peer // the p2p.Peer object representing the remote
rw p2p.MsgReadWriter // p2p.MsgReadWriter to send messages to and read messages from
handlers map[reflect.Type][]func(interface{}) error // message type -> message handler callback(s) map
*p2p.Peer // the p2p.Peer object representing the remote
rw p2p.MsgReadWriter // p2p.MsgReadWriter to send messages to and read messages from
spec *Spec
Errc chan error
wErrc chan error // write error channel
}
@ -205,55 +185,28 @@ type Peer struct {
// 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 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{
ct: ct,
Peer: p,
rw: rw,
Errc: make(chan error),
wErrc: make(chan error),
handlers: make(map[reflect.Type][]func(interface{}) error),
Peer: p,
rw: rw,
spec: spec,
Errc: make(chan error),
wErrc: make(chan 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
// called within the p2p.Protocol#Run function
func (self *Peer) Run() error {
func (self *Peer) Run(handler func(msg interface{}) error) error {
go func() {
for {
_, err := self.handleIncoming()
if err != nil {
if err := self.handleIncoming(handler); err != nil {
self.Errc <- err
return
}
}
}()
err := <-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
return <-self.Errc
}
// 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
// but often just used to forward and push messages to directly connected peers
func (self *Peer) Send(msg interface{}) error {
code, found := self.ct.GetCode(msg)
code, found := self.spec.GetCode(msg)
if !found {
return errorf(ErrInvalidMsgType, "%v", code)
}
log.Trace(fmt.Sprintf("=> msg #%d TO %v : %v", code, self.ID(), msg))
return p2p.Send(self.rw, uint64(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
})
return p2p.Send(self.rw, code, msg)
}
// 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
// checks message size, out-of-range message codes, handles decoding with reflection,
// 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()
if err != nil {
return nil, err
return err
}
log.Trace(fmt.Sprintf("<= %v", msg))
// make sure that the payload has been fully consumed
defer msg.Discard()
if msg.Size > uint32(self.ct.MaxMsgSize) {
return nil, errorf(ErrMsgTooLong, "%v > %v", msg.Size, self.ct.MaxMsgSize)
if msg.Size > self.spec.MaxMsgSize {
return errorf(ErrMsgTooLong, "%v > %v", msg.Size, self.spec.MaxMsgSize)
}
// check if the message code is correct
maxMsgCode := uint(len(self.ct.messages))
if msg.Code >= uint64(maxMsgCode) {
return nil, errorf(ErrInvalidMsgCode, "%v (>=%v)", msg.Code, maxMsgCode)
val, ok := self.spec.NewMsg(msg.Code)
if !ok {
return errorf(ErrInvalidMsgCode, "%v", msg.Code)
}
// it is safe to be unsafe here
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)
if err := msg.Decode(val); err != nil {
return 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
// a registered callback take the decoded message as argument as an interface
// 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
// chosen based on the proper type in the first place
handlers := self.handlers[typ]
if len(handlers) == 0 {
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)
}
}
if err := handle(val); err != nil {
return errorf(ErrHandler, "(msg code %v): %v", msg.Code, err)
}
return req.Interface(), nil
return nil
}
// Handshake initiates a handshake on the peer connection
// * the argument is the local handshake to be sent to the remote peer
// * expects a remote handshake back of the same type
// returns the remote hs and an error
func (self *Peer) Handshake(hs interface{}) (interface{}, error) {
typ := reflect.TypeOf(hs)
_, found := self.ct.messages[typ]
if !found {
return nil, errorf(ErrLocalHandshake, "unknown handshake message type: %v", typ)
func (self *Peer) Handshake(ctx context.Context, hs interface{}) (interface{}, error) {
if _, ok := self.spec.GetCode(hs); !ok {
return nil, errorf(ErrHandshake, "unknown handshake message type: %T", hs)
}
errc := make(chan error)
errc := make(chan error, 2)
go func() {
err := self.Send(hs)
if err != nil {
err = errorf(ErrLocalHandshake, "cannot send: %v", err)
if err := self.Send(hs); err != nil {
errc <- errorf(ErrHandshake, "cannot send: %v", err)
}
errc <- err
}()
// receiving and validating remote handshake, expect code
rhs, err := self.handleIncoming()
if err != nil {
return nil, errorf(ErrRemoteHandshake, "'%v': %v", self.ct.Name, err)
hsc := make(chan interface{})
go func() {
var rhs interface{}
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
import (
"context"
"errors"
"fmt"
"os"
"testing"
@ -13,7 +15,7 @@ import (
)
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
@ -55,28 +57,26 @@ const networkId = "420"
// newProtocol sets up a protocol
// the run function here demonstrates a typical protocol using peerPool, handshake
// and messages registered to handlers
func newProtocol(pp *p2ptest.TestPeerPool) adapters.RunProtocol {
ct := NewCodeMap("test", 42, 1024, &protoHandshake{}, &hs0{}, &kill{}, &drop{})
func newProtocol(pp *p2ptest.TestPeerPool) func(*p2p.Peer, p2p.MsgReadWriter) error {
spec := &Spec{
Name: "test",
Version: 42,
MaxMsgSize: 10 * 1024,
Messages: []interface{}{
protoHandshake{},
hs0{},
kill{},
drop{},
},
}
return func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
peer := NewPeer(p, ct, rw)
// 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")
})
peer := NewPeer(p, rw, spec)
// initiate one-off protohandshake and check validity
phs := &protoHandshake{ct.Version, networkId}
hs, err := peer.Handshake(phs)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
phs := &protoHandshake{42, networkId}
hs, err := peer.Handshake(ctx, phs)
if err != nil {
return err
}
@ -88,7 +88,7 @@ func newProtocol(pp *p2ptest.TestPeerPool) adapters.RunProtocol {
lhs := &hs0{42}
// 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 {
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)
}
peer.Register(lhs, func(msg interface{}) error {
rhs := msg.(*hs0)
if rhs.C > lhs.C {
return fmt.Errorf("handshake mismatch remote %v > local %v", rhs.C, lhs.C)
handle := func(msg interface{}) error {
switch msg := msg.(type) {
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))
pp.Add(peer)
defer pp.Remove(peer)
err = peer.Run()
err = peer.Run(handle)
log.Trace(fmt.Sprintf("peer %v protocol quitting: %v", peer, err))
return err

View file

@ -131,7 +131,7 @@ type Config struct {
// If Dialer is set to a non-nil value, the given Dialer
// 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.
NoDial bool `toml:",omitempty"`
@ -141,23 +141,8 @@ type Config struct {
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.
type server struct {
type Server struct {
// Config fields may not be modified while the server is running.
Config
@ -266,7 +251,7 @@ func (c *conn) is(f connFlag) bool {
}
// Peers returns all connected peers.
func (srv *server) Peers() []*Peer {
func (srv *Server) Peers() []*Peer {
var ps []*Peer
select {
// 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.
func (srv *server) PeerCount() int {
func (srv *Server) PeerCount() int {
var count int
select {
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
// server is shut down. If the connection fails for any reason, the server will
// attempt to reconnect the peer.
func (srv *server) AddPeer(node *discover.Node) {
func (srv *Server) AddPeer(node *discover.Node) {
select {
case srv.addstatic <- node:
case <-srv.quit:
@ -305,7 +290,7 @@ func (srv *server) AddPeer(node *discover.Node) {
}
// RemovePeer disconnects from the given node
func (srv *server) RemovePeer(node *discover.Node) {
func (srv *Server) RemovePeer(node *discover.Node) {
select {
case srv.removestatic <- node:
case <-srv.quit:
@ -313,12 +298,12 @@ func (srv *server) RemovePeer(node *discover.Node) {
}
// 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)
}
// Self returns the local node's endpoint information.
func (srv *server) Self() *discover.Node {
func (srv *Server) Self() *discover.Node {
srv.lock.Lock()
defer srv.lock.Unlock()
@ -328,7 +313,7 @@ func (srv *server) Self() *discover.Node {
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 node is running but discovery is off, manually assemble the node infos.
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.
// It blocks until all active connections have been closed.
func (srv *server) Stop() error {
func (srv *Server) Stop() error {
srv.lock.Lock()
defer srv.lock.Unlock()
if !srv.running {
@ -368,7 +353,7 @@ func (srv *server) Stop() error {
// Start starts running the server.
// Servers can not be re-used after stopping.
func (srv *server) Start() (err error) {
func (srv *Server) Start() (err error) {
srv.lock.Lock()
defer srv.lock.Unlock()
if srv.running {
@ -385,7 +370,7 @@ func (srv *server) Start() (err error) {
srv.newTransport = newRLPX
}
if srv.Dialer == nil {
srv.Dialer = &net.Dialer{Timeout: defaultDialTimeout}
srv.Dialer = TCPDialer{&net.Dialer{Timeout: defaultDialTimeout}}
}
srv.quit = make(chan struct{})
srv.addpeer = make(chan *conn)
@ -446,7 +431,7 @@ func (srv *server) Start() (err error) {
return nil
}
func (srv *server) startListening() error {
func (srv *Server) startListening() error {
// Launch the TCP listener.
listener, err := net.Listen("tcp", srv.ListenAddr)
if err != nil {
@ -475,7 +460,7 @@ type dialer interface {
removeStatic(*discover.Node)
}
func (srv *server) run(dialstate dialer) {
func (srv *Server) run(dialstate dialer) {
defer srv.loopWG.Done()
var (
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.
if len(srv.Protocols) > 0 && countMatchingProtocols(srv.Protocols, c.caps) == 0 {
return DiscUselessPeer
@ -626,7 +611,7 @@ func (srv *server) protoHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn
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 {
case !c.is(trustedConn|staticDialedConn) && len(peers) >= srv.MaxPeers:
return DiscTooManyPeers
@ -645,7 +630,7 @@ type tempError interface {
// listenLoop runs in its own goroutine and accepts
// inbound connections.
func (srv *server) listenLoop() {
func (srv *Server) listenLoop() {
defer srv.loopWG.Done()
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
// has been established.
go func() {
srv.setupConn(fd, inboundConn, nil)
srv.SetupConn(fd, inboundConn, nil)
slots <- struct{}{}
}()
}
@ -706,7 +691,7 @@ func (srv *server) listenLoop() {
// setupConn runs the handshakes and attempts to add the connection
// as a peer. It returns when the connection has been added as a peer
// 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.
srv.lock.Lock()
running := srv.running
@ -766,7 +751,7 @@ func truncateName(s string) string {
// checkpoint sends the conn to run, which performs the
// 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 {
case stage <- c:
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.
// it waits until the Peer logic returns and removes
// the peer.
func (srv *server) runPeer(p *Peer) {
func (srv *Server) runPeer(p *Peer) {
if srv.newPeerHook != nil {
srv.newPeerHook(p)
}
@ -824,7 +809,7 @@ type NodeInfo struct {
}
// 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()
// 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.
func (srv *server) PeersInfo() []*PeerInfo {
func (srv *Server) PeersInfo() []*PeerInfo {
// Gather all the generic and sub-protocol specific infos
infos := make([]*PeerInfo, 0, srv.PeerCount())
for _, peer := range srv.Peers() {
@ -872,6 +857,6 @@ func (srv *server) PeersInfo() []*PeerInfo {
return infos
}
func (srv *server) DiscV5() *discv5.Network {
func (srv *Server) DiscV5() *discv5.Network {
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",
PrivateKey: newkey(),
}
server := &server{
server := &Server{
Config: config,
newPeerHook: pf,
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
// because we're only interested in what run does.
srv := &server{
srv := &Server{
Config: Config{MaxPeers: 10},
quit: make(chan struct{}),
ntab: fakeTable{},
@ -246,7 +246,7 @@ func TestServerManyTasks(t *testing.T) {
}
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)
start, end = 0, 0
)
@ -317,7 +317,7 @@ func (t *testTask) Do(srv *Server) {
// at capacity. Trusted connections should still be accepted.
func TestServerAtCap(t *testing.T) {
trustedID := randomID()
srv := &server{
srv := &Server{
Config: Config{
PrivateKey: newkey(),
MaxPeers: 10,
@ -420,7 +420,7 @@ func TestServerSetupConn(t *testing.T) {
}
for i, test := range tests {
srv := &server{
srv := &Server{
Config: Config{
PrivateKey: srvkey,
MaxPeers: 10,
@ -435,7 +435,7 @@ func TestServerSetupConn(t *testing.T) {
}
}
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) {
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"
"path/filepath"
"runtime"
"strings"
"github.com/docker/docker/pkg/reexec"
"github.com/ethereum/go-ethereum/node"
@ -81,12 +82,16 @@ func (n *DockerNode) dockerCommand() *exec.Cmd {
return exec.Command(
"sh", "-c",
fmt.Sprintf(
`exec docker run --interactive --env _P2P_NODE_CONFIG="${_P2P_NODE_CONFIG}" %s p2p-node %s %s`,
dockerImage, n.Config.Node.Service, n.ID.String(),
`exec docker run --interactive --env _P2P_NODE_CONFIG="${_P2P_NODE_CONFIG}" --env _P2P_NODE_KEY="${_P2P_NODE_KEY}" %s p2p-node %s %s`,
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
const dockerImage = "p2p-node"

View file

@ -63,7 +63,7 @@ func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) {
Node: config,
}
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.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
// still communicate with it).
type ExecNode struct {
ID *NodeId
Dir string
Config *execNodeConfig
Cmd *exec.Cmd
Info *p2p.NodeInfo
ID *NodeId
Dir string
Config *execNodeConfig
Cmd *exec.Cmd
Info *p2p.NodeInfo
Services []string
client *rpc.Client
rpcMux *rpcMux
@ -164,13 +165,17 @@ func (n *ExecNode) Start(snapshot []byte) (err error) {
return nil
}
func (n *ExecNode) GetService(name string) node.Service {
return nil
}
// 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
// runs execP2PNode
func (n *ExecNode) execCommand() *exec.Cmd {
return &exec.Cmd{
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 {
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
if strings.HasPrefix(conf.Stack.P2P.ListenAddr, ":") {
@ -295,7 +300,7 @@ func execP2PNode() {
}
// start the devp2p stack
stack, err := startP2PNode(&conf.Stack, service)
stack, err := startP2PNode(&conf.Stack, services)
if err != nil {
log.Crit("error starting p2p node", "err", err)
}
@ -321,17 +326,20 @@ func execP2PNode() {
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)
if err != nil {
return nil, err
}
constructor := func(ctx *node.ServiceContext) (node.Service, error) {
return &snapshotService{service}, nil
}
if err := stack.Register(constructor); err != nil {
return nil, err
for _, svc := range services {
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.Start(); err != nil {
return nil, err
}

View file

@ -17,10 +17,11 @@
package adapters
import (
"context"
"errors"
"fmt"
"math"
"net"
"reflect"
"sync"
"github.com/ethereum/go-ethereum/event"
@ -36,7 +37,6 @@ import (
type SimAdapter struct {
mtx sync.RWMutex
nodes map[discover.NodeID]*SimNode
services map[string]ServiceFunc
}
// 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 {
return &SimAdapter{
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
func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
var nodeprotos []p2p.Protocol
s.mtx.Lock()
defer s.mtx.Unlock()
@ -66,6 +67,7 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
}
// check the service is valid and initialize it
/*
serviceFunc, exists := s.services[config.Service]
if !exists {
return nil, fmt.Errorf("unknown node service %q", config.Service)
@ -73,13 +75,60 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
node := &SimNode{
Id: id,
config: config,
adapter: s,
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
@ -90,14 +139,6 @@ func (s *SimAdapter) GetNode(id discover.NodeID) (*SimNode, bool) {
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
// in-memory p2p.MsgReadWriter pipe, running an underlying service protocol
// directly over that pipe.
@ -107,17 +148,13 @@ type MsgReadWriteCloser interface {
type SimNode struct {
lock sync.RWMutex
Id *NodeId
config *NodeConfig
adapter *SimAdapter
running node.Service
serviceFunc ServiceFunc
peers map[discover.NodeID]MsgReadWriteCloser
peerFeed event.Feed
serviceFunc ServiceFunc
node *node.Node
client *rpc.Client
rpcMux *rpcMux
// dropPeers is used to force peer disconnects when
// the node is stopped
dropPeers chan struct{}
running []node.Service
}
// Addr returns the node's discovery address
@ -127,7 +164,7 @@ func (self *SimNode) Addr() []byte {
// Node returns a discover.Node representing the SimNode
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
@ -154,82 +191,68 @@ func (self *SimNode) ServeRPC(conn net.Conn) error {
return nil
}
// Start initializes the service, starts the RPC handler and then starts
// the service
func (self *SimNode) Start(snapshot []byte) error {
service := self.serviceFunc(self.Id, snapshot)
// Snapshot creates a snapshot of the service state by calling the
// simulation_snapshot RPC method
func (self *SimNode) Snapshot() ([]byte, error) {
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
// multiple protocols on the same peer is extra effort, and we don't
// currently run any simulations which run multiple protocols)
if len(service.Protocols()) != 1 {
return errors.New("service must have a single protocol")
// Start starts the RPC handler and the underlying service
func (self *SimNode) Start(snapshot []byte) error {
self.lock.Lock()
defer self.lock.Unlock()
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{})
if err := self.startRPC(service); err != nil {
node, err := node.New(&node.Config{
P2P: p2p.Config{
PrivateKey: self.config.PrivateKey,
MaxPeers: math.MaxInt32,
NoDiscovery: true,
Dialer: self.adapter,
EnableMsgEvents: false,
},
NoUSB: true,
})
if err != nil {
return err
}
self.running = service
return service.Start(&simServer{self})
}
// simServer wraps a SimNode but modifies the Start method signature so that
// 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")
for _, service := range services {
log.Debug(fmt.Sprintf("service %v", service))
if err := node.Register(service); err != nil {
return err
}
}
// add SimAdminAPI so that the network can call the
// AddPeer, RemovePeer and PeerEvents RPC methods
apis := append(service.APIs(), []rpc.API{
{
Namespace: "admin",
Version: "1.0",
Service: &SimAdminAPI{self},
},
}...)
if err := node.Start(); err != nil {
return err
}
// start the RPC handler
handler := rpc.NewServer()
for _, api := range apis {
if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
return fmt.Errorf("error registering RPC: %s", err)
}
handler, err := node.RPCHandler()
if err != nil {
return err
}
// create an in-process RPC multiplexer
@ -240,197 +263,61 @@ func (self *SimNode) startRPC(service node.Service) error {
// create an in-process RPC client
self.client = self.rpcMux.Client()
self.node = node
return nil
}
// stopRPC closes the node's RPC client
func (self *SimNode) stopRPC() {
func (self *SimNode) Stop() error {
self.lock.Lock()
defer self.lock.Unlock()
if self.client != nil {
self.client.Close()
self.client = nil
self.rpcMux = nil
if self.node == nil {
return 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
// p2p.MsgReadWriter pipe and closing it (which will cause both the local
// and peer Protocol.Run functions to exit)
func (self *SimNode) RemovePeer(peer *discover.Node) {
// Service returns the underlying running node.Service matching the supplied servuce type
func (self *SimNode) Service(servicetype interface{}) node.Service {
self.lock.Lock()
defer self.lock.Unlock()
peerRW, exists := self.peers[peer.ID]
if !exists {
return
typ := reflect.TypeOf(servicetype)
for _, service := range self.running {
if reflect.TypeOf(service) == typ {
return service
}
}
peerRW.Close()
delete(self.peers, peer.ID)
log.Trace(fmt.Sprintf("dropped peer %v", peer.ID))
return nil
}
// AddPeer adds the given node as a peer by creating a p2p.MsgReadWriter pipe
// and running both the local and peer's Protocol.Run function over the pipe
func (self *SimNode) AddPeer(peer *discover.Node) {
func (self *SimNode) Server() *p2p.Server {
self.lock.Lock()
defer self.lock.Unlock()
if _, exists := self.peers[peer.ID]; exists {
return
if self.node == nil {
return nil
}
peerNode, exists := self.adapter.GetNode(peer.ID)
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)
return self.node.Server()
}
// SubscribeEvents subscribes the given channel to p2p peer events
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 {
self.lock.Lock()
defer self.lock.Unlock()
info := &p2p.NodeInfo{
ID: self.Id.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
server := self.Server()
if server == nil {
return &p2p.NodeInfo{
ID: self.Id.String(),
Enode: self.Node().String(),
}
}
return info
}
// 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
return server.NodeInfo()
}

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 string
// Service is the name of the service which should be run when starting
// the node (for SimNodes it should be the name of a service contained
// in SimAdapter.services, for other nodes it should be a service
// Service is the name of the services which should be run when starting
// the node (for SimNodes it should be the names of services contained
// in SimAdapter.services, for other nodes it should be services
// registered by calling the RegisterService function)
Service string
}
@ -140,7 +140,7 @@ type nodeConfigJSON struct {
Id string `json:"id"`
PrivateKey string `json:"private_key"`
Name string `json:"name"`
Service string `json:"service"`
Service string `json:"service"`
}
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
// it is mapped to strings representing TYPES of nodes
type Services map[string]ServiceFunc
// 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
// nodes

View file

@ -99,7 +99,7 @@ func (p *pingPongService) APIs() []rpc.API {
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")
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)
}
// 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
func (c *Client) CreateSnapshot(networkID string) (*Snapshot, error) {
snap := &Snapshot{}
@ -276,6 +291,9 @@ func NewServer(config *ServerConfig) *Server {
s.POST("/networks", s.CreateNetwork)
s.GET("/networks", s.GetNetworks)
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/snapshot", s.CreateSnapshot)
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)
}
// 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
func (s *Server) GetMocker(w http.ResponseWriter, req *http.Request) {
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
}

View file

@ -260,6 +260,30 @@ func (self *Conn) nodesUp() error {
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)
func (self *Network) Start(id *adapters.NodeId) error {
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()))
if err := node.Start(snapshot); err != nil {
log.Warn(fmt.Sprintf("start up failed: %v", err))
return err
}
node.Up = true

View file

@ -13,8 +13,7 @@ import (
)
type ProtocolSession struct {
*adapters.SimNode
Server *p2p.Server
Ids []*adapters.NodeId
adapter *adapters.SimAdapter
events chan *p2p.PeerEvent
@ -58,7 +57,7 @@ func (self *ProtocolSession) trigger(trig Trigger) error {
if !ok {
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 {
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 {
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 {
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 {
services := map[string]adapters.ServiceFunc{
"test": func(id *adapters.NodeId) node.Service {
return &testNode{run}
//func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester {
services := adapters.Services {
"test": func(id *adapters.NodeId, _ []byte) []node.Service {
return []node.Service{&testNode{run}}
},
"mock": func(id *adapters.NodeId) node.Service {
return newMockNode()
"mock": func(id *adapters.NodeId, _ []byte) []node.Service {
return []node.Service{newMockNode()}
},
}
adapters.RegisterServices(services)
adapter := adapters.NewSimAdapter(services)
net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{})
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)
node.SubscribeEvents(events)
ps := &ProtocolSession{
SimNode: node,
Server: node.Server(),
Ids: peerIDs,
adapter: adapter,
events: events,
@ -62,6 +64,10 @@ func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.P
return self
}
func (self *ProtocolTester) Stop() error {
return self.Server.Stop()
}
func (self *ProtocolTester) Connect(selfId *adapters.NodeId, peers ...*adapters.NodeConfig) {
for _, peer := range peers {
log.Trace(fmt.Sprintf("start node %v", peer.Id))
@ -86,14 +92,17 @@ type testNode struct {
}
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 {
return nil
}
func (t *testNode) Start(server p2p.Server) error {
func (t *testNode) Start(server *p2p.Server) error {
return nil
}

View file

@ -287,3 +287,62 @@ func (self *BoolAddress) PO(val PotVal, pos int) (po int, eq bool) {
}
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
}
type AnyVal interface{}
// Pot constructor. Requires value of type PotVal to pin
// and po to point to a span in the PotVal key
// 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 v' == v the pot is not changed
// 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()
defer t.lock.Unlock()
ba := NewBytesVal(val, nil)
var t0 *pot
t0, po, found, change = swap(t.pot, val, f)
t0, po, found, change = swap(t.pot, ba, f)
if change {
t.pot = t0
}

View file

@ -8,59 +8,62 @@ import (
// discovery bzz overlay extension doing peer relaying
// messages related to peer discovery
var DiscoveryMsgs = []interface{}{
&getPeersMsg{},
&peersMsg{},
&subPeersMsg{},
}
type discPeer struct {
Peer
*bzzPeer
overlay Overlay
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
}
// discovery peer contructor
// registers the handlers for discovery messages
func NewDiscovery(p Peer, o Overlay) *discPeer {
// NewDiscovery discovery peer contructor
func NewDiscovery(p *bzzPeer, o Overlay) *discPeer {
self := &discPeer{
overlay: o,
Peer: p,
bzzPeer: p,
peers: make(map[string]bool),
}
self.seen(self)
p.Register(&peersMsg{}, self.handlePeersMsg)
p.Register(&getPeersMsg{}, self.handleGetPeersMsg)
p.Register(&subPeersMsg{}, self.handleSubPeersMsg)
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.
// callback for overlay driver
func (self *discPeer) NotifyPeer(p Peer, po uint8) error {
log.Warn(fmt.Sprintf("peer %#v peers %v", p, self.peers))
if po < self.proxLimit || self.seen(p) {
func (self *discPeer) NotifyPeer(a OverlayAddr, po uint8) error {
if po < self.depth || self.seen(a) {
return nil
}
log.Warn(fmt.Sprintf("notification about %x", p.OverlayAddr()))
log.Warn(fmt.Sprintf("notification about %x", a.Address()))
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)
}
// 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
// or first empty row)
// callback for overlay driver
func (self *discPeer) NotifyProx(po uint8) error {
return self.Send(&subPeersMsg{ProxLimit: po})
func (self *discPeer) NotifyDepth(po uint8) error {
return self.Send(&subPeersMsg{Depth: po})
}
/*
@ -83,7 +86,7 @@ disconnected
// used for communicating about known peers
// relevant for bootstrapping connectivity and updating peersets
type peersMsg struct {
Peers []*peerAddr
Peers []*bzzAddr
}
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
type subPeersMsg struct {
ProxLimit uint8
Depth uint8
}
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 {
spm := msg.(*subPeersMsg)
self.proxLimit = spm.ProxLimit
func (self *discPeer) handleSubPeersMsg(msg *subPeersMsg) error {
self.depth = msg.Depth
if !self.sentPeers {
var peers []*peerAddr
self.overlay.EachLivePeer(self.OverlayAddr(), 255, func(p Peer, po int, isproxbin bool) bool {
if uint8(po) < self.proxLimit {
var peers []*bzzAddr
self.overlay.EachConn(self.Over(), 255, func(p OverlayConn, po int, isproxbin bool) bool {
if uint8(po) < self.depth {
return false
}
log.Warn(fmt.Sprintf("peer %#v proxlimit %v", p, self.proxLimit))
self.seen(p.(*discPeer).Peer)
peers = append(peers, &peerAddr{p.OverlayAddr(), p.UnderlayAddr()})
if !self.seen(p) {
peers = append(peers, ToAddr(p.Off()))
}
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 err := self.Send(&peersMsg{Peers: peers}); err != nil {
return err
@ -137,39 +139,41 @@ func (self *discPeer) handleSubPeersMsg(msg interface{}) error {
// 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
// Register interface method
func (self *discPeer) handlePeersMsg(msg interface{}) error {
func (self *discPeer) handlePeersMsg(msg *peersMsg) error {
// register all addresses
var nas []PeerAddr
for _, na := range msg.(*peersMsg).Peers {
addr := PeerAddr(na)
nas = append(nas, addr)
self.seen(addr)
}
if len(nas) == 0 {
if len(msg.Peers) == 0 {
log.Debug(fmt.Sprintf("whoops, no peers in incoming peersMsg from %v", self))
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
// peerset (for target address) request
// 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
func (self *discPeer) handleGetPeersMsg(msg interface{}) error {
var peers []*peerAddr
req := msg.(*getPeersMsg)
func (self *discPeer) handleGetPeersMsg(msg *getPeersMsg) error {
var peers []*bzzAddr
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++
// only send peers we have not sent before in this session
if self.seen(n) {
peers = append(peers, &peerAddr{n.OverlayAddr(), n.UnderlayAddr()})
a := ToAddr(p.Off())
if self.seen(a) {
peers = append(peers, a)
}
return len(peers) < int(req.Max)
return len(peers) < int(msg.Max)
})
if len(peers) == 0 {
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 err error
k.EachLivePeer(nil, 255, func(n Peer, po int, isproxbin bool) bool {
log.Trace(fmt.Sprintf("%T sent to %v", req, n))
if err := n.Send(req); err == nil {
k.EachConn(nil, 255, func(p OverlayConn, po int, isproxbin bool) bool {
if err := p.(Conn).Send(req); err == nil {
i++
if i >= broadcastSize {
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))
}
func (self *discPeer) seen(p PeerAddr) bool {
k := NodeId(p).NodeID.String()
func (self *discPeer) seen(p OverlayPeer) bool {
k := string(p.Address())
if self.peers[k] {
return true
}

View file

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

View file

@ -17,6 +17,7 @@
package network
import (
"encoding/json"
"fmt"
"sync"
"time"
@ -33,75 +34,89 @@ it uses an Overlay Topology driver (e.g., generic kademlia nodetable)
to find best peer list for any target
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
peer connections and disconnections are reported and registered
to keep the nodetable uptodate
*/
// Overlay is the interface to Jaak ahd ka)a
type Overlay interface {
Register(...PeerAddr) error
Register(chan OverlayAddr) error
On(Peer)
Off(Peer)
On(OverlayConn)
Off(OverlayConn)
EachLivePeer([]byte, int, func(Peer, int, bool) bool)
EachPeer([]byte, int, func(PeerAddr, int) bool)
EachConn([]byte, int, func(OverlayConn, int, bool) bool)
EachAddr([]byte, int, func(OverlayAddr, int) bool)
SuggestPeer() (PeerAddr, int, bool)
SuggestPeer() (OverlayAddr, int, bool)
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
type Hive struct {
*HiveParams // settings
Overlay // the overlay topology driver
lock sync.Mutex
quit chan bool
toggle chan bool
more chan bool
}
store StateStore
type HiveParams struct {
Discovery bool
PeersBroadcastSetSize uint8
MaxPeersPerRequest uint8
CallInterval uint
}
// bookkeeping
lock sync.Mutex
quit chan bool
more chan bool
func NewHiveParams() *HiveParams {
return &HiveParams{
Discovery: true,
PeersBroadcastSetSize: 2,
MaxPeersPerRequest: 5,
CallInterval: 1000,
}
tick <-chan time.Time
}
// Hive constructor embeds both arguments
// HiveParams config parameters
// Overlay Topology Driver Interface
func NewHive(params *HiveParams, overlay Overlay) *Hive {
// HiveParams: config parameters
// Overlay: Topology Driver Interface
func NewHive(params *HiveParams, overlay Overlay, store StateStore) *Hive {
return &Hive{
HiveParams: params,
Overlay: overlay,
store: store,
}
}
// 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
// af() returns an arbitrary ticker channel
func (self *Hive) Start(server p2p.Server, af func() <-chan time.Time) error {
self.toggle = make(chan bool)
// rw is a read writer for json configs
func (self *Hive) Start(server *p2p.Server) error {
if self.store != nil {
if err := self.loadPeers(); err != nil {
return err
}
}
self.more = make(chan bool, 1)
self.quit = make(chan bool)
log.Debug("hive started")
// this loop is doing bootstrapping and maintains a healthy table
go self.keepAlive(af)
go self.keepAlive()
go func() {
// each iteration, ask kademlia about most preferred peer
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)
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()
if addr != nil {
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 {
server.AddPeer(node)
server.AddPeer(under)
} else {
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
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 {
case self.toggle <- want:
log.Trace(fmt.Sprintf("keep hive alive: %v", want))
case <-self.quit:
return
default:
}
log.Info(fmt.Sprintf("%v", self))
}
}()
return nil
}
func (self *Hive) ticker() <-chan time.Time {
return time.NewTicker(time.Duration(self.CallInterval) * time.Millisecond).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 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
}
// Stop terminates the updateloop and saves the peers
func (self *Hive) Stop() {
if self.store != nil {
self.savePeers()
}
// closing toggle channel quits the updateloop
close(self.quit)
}
// Add is called at the end of a successful protocol handshake
// to register a connected (live) peer
func (self *Hive) Add(p Peer) error {
defer self.wake()
dp := NewDiscovery(p, self.Overlay)
func (self *Hive) Run(p *bzzPeer) error {
dp := NewDiscovery(p, self)
log.Debug(fmt.Sprintf("to add new bee %v", p))
self.On(dp)
self.String()
log.Debug(fmt.Sprintf("%v", self))
return nil
}
// Remove called after peer is disconnected
func (self *Hive) Remove(p Peer) {
self.wake()
defer self.wake()
log.Debug(fmt.Sprintf("remove bee %v", p))
self.Off(p)
defer self.Off(dp)
return p.Run(dp.HandleMsg)
}
// NodeInfo function is used by the p2p.server RPC interface to display
// protocol specific node information
func (self *Hive) NodeInfo() interface{} {
return interface{}(self.String())
return self.String()
}
// 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{} {
self.lock.Lock()
defer self.lock.Unlock()
addr := NewPeerAddrFromNodeId(adapters.NewNodeId(id[:]))
addr := NewAddrFromNodeId(adapters.NewNodeId(id[:]))
return interface{}(addr)
}
// Stop terminates the updateloop
func (self *Hive) Stop() {
// closing toggle channel quits the updateloop
close(self.quit)
}
func (self *Hive) Healthy() bool {
// TODO: determine if we have enough peers to consider the network
// to be healthy
return true
func (self *Hive) Register(peers chan OverlayAddr) error {
defer self.wake()
return self.Overlay.Register(peers)
}
// wake triggers
func (self *Hive) wake() {
select {
case self.more <- true:
@ -229,7 +208,100 @@ func (self *Hive) wake() {
}
}
// HexToBytes reads a hex string ontp
func HexToBytes(s string) []byte {
id := discover.MustHexID(s)
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
import (
"sync"
"testing"
"time"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
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) {
// setup
addr := RandomAddr() // tested peers peer address
to := NewTestOverlay(addr.OverlayAddr()) // overlay topology drive
pp := NewHive(params, to) // 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")
}
addr := RandomAddr() // tested peers peer address
to := NewKademlia(addr.OAddr, NewKadParams())
pp := NewHive(params, to, nil) // hive
return newBzzBaseTester(t, 1, addr, DiscoverySpec, pp.Run), pp
}
func TestRegisterAndConnect(t *testing.T) {
@ -70,30 +22,23 @@ func TestRegisterAndConnect(t *testing.T) {
defer s.Stop()
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
tc := &testConnect{
connectf: func(c string) error {
s.Connect(adapters.NewNodeIdFromHex(c))
return nil
},
ticker: make(chan time.Time),
}
pp.Start(s, tc.ping)
tick := make(chan time.Time)
pp.tick = tick
pp.Start(s.Server)
defer pp.Stop()
tc.ticker <- time.Now()
s.runHandshakes()
if pp.Overlay.(*testOverlay).posMap[string(raddr.OverlayAddr())] == nil {
t.Fatalf("Overlay#On not called on new peer")
}
tick <- time.Now()
// retrieve and broadcast
ord := order(raddr.OverlayAddr())
ord := raddr.Over()[0] / 32
o := 0
if ord == 0 {
o = 1

View file

@ -23,6 +23,8 @@ import (
"time"
"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"
)
@ -46,24 +48,20 @@ a guaranteed constant maximum limit on the number of hops needed to reach one
node from the other.
*/
type KadDiscovery interface {
NotifyPeer(Peer, uint8) error
NotifyProx(uint8) error
}
// KadParams holds the config params for Kademlia
type KadParams struct {
// adjustable parameters
MaxProxDisplay int
MinProxBinSize int
MinBinSize int
MaxBinSize int
RetryInterval int
RetryExponent int
MaxRetries int
PruneInterval int
MaxProxDisplay int // number of rows the table shows
MinProxBinSize int // nearest neighbour core minimum cardinality
MinBinSize int // minimum number of peers in a row
MaxBinSize int // maximum number of peers in a row before pruning
RetryInterval int // initial interval before a peer is first redialed
RetryExponent int // exponent to multiply retry intervals with
MaxRetries int // maximum number of redial attempts
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 {
return &KadParams{
MaxProxDisplay: 8,
@ -79,141 +77,103 @@ func NewKadParams() *KadParams {
// Kademlia is a table of live peers and a db of known peers
type Kademlia struct {
addr PeerAddr // immutable baseaddress of the table
// addr *pot.HashAddress // immutable baseaddress of the table
*KadParams // Kademlia configuration parameters
addrs, peers *pot.Pot // pots container for peers
lastProxLimit uint8 // stores the last calculated proxlimit
*KadParams // Kademlia configuration parameters
base []byte // immutable baseaddress of the table
addrs *pot.Pot // pots container for known peer addresses
conns *pot.Pot // pots container for live peer connections
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
// if params is nil, it uses default values
func NewKademlia(addr []byte, params *KadParams) *Kademlia {
if params == nil {
params = NewKadParams()
}
self := &Kademlia{
addr: &peerAddr{OAddr: addr},
return &Kademlia{
base: addr,
KadParams: params,
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
// 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 {
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))
}
}()
type Notifier interface {
NotifyPeer(OverlayAddr, uint8) error
NotifyDepth(uint8) error
}
// KadPeer represents a Kademlia Peer and extends
// * PeerAddr interface (overlay and underlay addresses)
// * Peer interface (id, last seen, drop)
type KadPeer struct {
PeerAddr
Peer Peer
// OverlayPeer interface captures the common aspect of view of a peer from the Overlay
// topology driver
type OverlayPeer interface {
Address() []byte
}
// 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
retries int
}
func (self *KadPeer) String() string {
if self == nil {
return "<nil>"
}
return fmt.Sprintf("%x", self.OverlayAddr())
}
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(),
// newEntry creates a kademlia peer from an OverlayPeer interface
func newEntry(p OverlayPeer) *entry {
return &entry{
PotVal: pot.NewBytesVal(p, nil),
OverlayPeer: p,
seenAt: time.Now(),
}
}
// retrieve the base address
// which is the overlayaddress used by peers to reach us
func (self *Kademlia) GetAddr() PeerAddr {
return self.addr
func (self *entry) addr() OverlayAddr {
a, _ := self.OverlayPeer.(OverlayAddr)
return a
}
// Register(nas) enters each PeerAddr as kademlia peers into the
// database of known peers
func (self *Kademlia) Register(nas ...PeerAddr) error {
label := fmt.Sprintf("%x", RandomAddr().OverlayAddr())
func (self *entry) conn() OverlayConn {
c, _ := self.OverlayPeer.(OverlayConn)
return c
}
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)
for _, na := range nas {
if bytes.Equal(na.OverlayAddr(), self.addr.OverlayAddr()) {
log.Warn(fmt.Sprintf("[%06s] add peers: %x is self.. skipped ", label, self.addr.OverlayAddr()))
continue
for p := range peers {
// error if self received, peer should know better
if bytes.Equal(p.Address(), self.base) {
return fmt.Errorf("add peers: %x is self", self.base)
}
p := NewKadPeer(na)
np, _, _ = pot.Add(np, pot.PotVal(p))
np, _, _ = pot.Add(np, newEntry(p))
}
oldpeers := pot.NewPot(nil, 0)
oldpeers.Merge(self.addrs)
self.addrs.Merge(np)
com := self.addrs.Merge(np)
log.Debug(fmt.Sprintf("merged %v peers, %v known, total: %v", np.Size(), com, self.addrs.Size()))
// log.Trace(fmt.Sprintf("merged %v peers, %v known", np.Size(), com))
// TODO: remove this check
m := make(map[string]bool)
self.addrs.Each(func(val pot.PotVal, i int) bool {
_, found := m[val.String()]
// TODO: remove this check
// log.Debug(fmt.Sprintf("-> %v %v", val, i))
if found {
panic("duplicate found")
}
@ -223,148 +183,34 @@ func (self *Kademlia) Register(nas ...PeerAddr) error {
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
// lowest bincount below proxLimit
// lowest bincount below depth
// 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
proxLimit := self.proxLimit()
depth := self.Depth()
// if there is a callable neighbour within the current proxBin, connect
// 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
self.addrs.EachNeighbour(self.addr, func(val pot.PotVal, po int) bool {
r := self.callable(val)
if r == nil {
return po >= proxLimit
}
p = r
ba := pot.NewBytesVal(self.base, nil)
self.addrs.EachNeighbour(ba, func(val pot.PotVal, po int) bool {
a = self.callable(val)
log.Trace(fmt.Sprintf("candidate prox peer at %x: %v (%v). a == nil is %v", val.(*entry).Address(), a, po, a == nil))
ppo = po
return false
return a == nil && po >= depth
})
if p != nil {
log.Trace(fmt.Sprintf("candidate prox peer found: %v (%v), %v", p, ppo, p))
return p, 0, false
if a != nil {
log.Trace(fmt.Sprintf("candidate prox peer found: %v (%v)", a, ppo))
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
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))
prev++
if po > prev {
@ -375,7 +221,7 @@ func (self *Kademlia) SuggestPeer() (p PeerAddr, o int, want bool) {
minsize = size
bpo = append(bpo, po)
}
return size > 0 && po < proxLimit
return size > 0 && po < depth
})
// all buckets are full
// minsize == self.MinBinSize
@ -387,46 +233,186 @@ func (self *Kademlia) SuggestPeer() (p PeerAddr, o int, want bool) {
// try to select a candidate peer
for i := len(bpo) - 1; i >= 0; i-- {
// 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
f(func(val pot.PotVal, i int) bool {
r := self.callable(val)
log.Trace(fmt.Sprintf("check PO%02d: ", po))
if r == nil {
return i < proxLimit
}
p = r
return false
log.Trace(fmt.Sprintf("check PO%02d: ", po))
f(func(val pot.PotVal, j int) bool {
a = self.callable(val)
return a == nil && po < depth
})
return false
})
// found a candidate
if p != nil {
if a != nil {
break
}
// cannot find a candidate, ask for more for this proximity bin specifically
o = bpo[i]
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 {
var rows []string
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("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("%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.conns.Size(), self.addrs.Size(), self.MinProxBinSize, self.MinBinSize, self.MaxBinSize))
liverows := make([]string, self.MaxProxDisplay)
peersrows := make([]string, self.MaxProxDisplay)
var proxLimit int
var depth int
prev := -1
var proxLimitSet bool
rest := self.peers.Size()
self.peers.EachBin(self.addr, 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool {
var depthSet bool
rest := self.conns.Size()
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
if po >= self.MaxProxDisplay {
po = self.MaxProxDisplay - 1
@ -434,13 +420,13 @@ func (self *Kademlia) String() string {
row := []string{fmt.Sprintf("%2d", size)}
rest -= size
f(func(val pot.PotVal, vpo int) bool {
row = append(row, val.(*KadPeer).String()[:6])
row = append(row, val.(*entry).String()[:6])
rowlen++
return rowlen < 4
})
if !proxLimitSet && (po > prev+1 || rest < self.MinProxBinSize) {
proxLimitSet = true
proxLimit = prev + 1
if !depthSet && (po > prev+1 || rest < self.MinProxBinSize) {
depthSet = true
depth = prev + 1
}
for ; rowlen <= 5; rowlen++ {
row = append(row, " ")
@ -450,7 +436,7 @@ func (self *Kademlia) String() string {
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
if po >= self.MaxProxDisplay {
po = self.MaxProxDisplay - 1
@ -461,8 +447,7 @@ func (self *Kademlia) String() string {
row := []string{fmt.Sprintf("%2d", size)}
// we are displaying live peers too
f(func(val pot.PotVal, vpo int) bool {
kp := val.(*KadPeer)
row = append(row, kp.String()[:6])
row = append(row, val.(*entry).String()[:6])
rowlen++
return rowlen < 4
})
@ -471,7 +456,7 @@ func (self *Kademlia) String() string {
})
for i := 0; i < self.MaxProxDisplay; i++ {
if i == proxLimit {
if i == depth {
rows = append(rows, fmt.Sprintf("============ PROX LIMIT: %d ==========================================", i))
}
left := liverows[i]
@ -487,3 +472,95 @@ func (self *Kademlia) String() string {
rows = append(rows, "=========================================================================")
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 (
"fmt"
"os"
"sync"
"testing"
"time"
@ -25,9 +26,15 @@ import (
"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()
return &peerAddr{OAddr: a, UAddr: a}
return &bzzAddr{OAddr: a, UAddr: a}
}
type testDropPeer struct {
@ -58,23 +65,24 @@ type dropError struct {
}
func (self *testDropPeer) Drop(err error) {
err2 := &dropError{err, overlayStr(self)}
err2 := &dropError{err, binStr(self)}
self.dropc <- err2
}
func (self *testDiscPeer) NotifyProx(po uint8) error {
key := overlayStr(self)
func (self *testDiscPeer) NotifyDepth(po uint8) error {
key := binStr(self)
self.lock.Lock()
defer self.lock.Unlock()
self.notifications[key] = po
return nil
}
func (self *testDiscPeer) NotifyPeer(p Peer, po uint8) error {
key := overlayStr(self)
key += overlayStr(p)
func (self *testDiscPeer) NotifyPeer(p OverlayAddr, po uint8) error {
key := binStr(self)
key += binStr(p)
self.lock.Lock()
defer self.lock.Unlock()
log.Trace(fmt.Sprintf("key %v=>%v", key, po))
self.notifications[key] = po
return nil
}
@ -102,40 +110,13 @@ func newTestKademlia(b string) *testKademlia {
}
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 {
return Peer(&testDiscPeer{dp, k.lock, k.notifications})
}
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 {
for _, s := range ons {
p := k.newTestKadPeer(s)
@ -146,25 +127,30 @@ func (k *testKademlia) On(ons ...string) *testKademlia {
func (k *testKademlia) Off(offs ...string) *testKademlia {
for _, s := range offs {
k.Kademlia.Off(k.newTestKadPeer(s))
k.Kademlia.Off(k.newTestKadPeer(s).(OverlayConn))
}
return k
}
func (k *testKademlia) Register(regs ...string) *testKademlia {
var ps []PeerAddr
for _, s := range regs {
ps = append(ps, PeerAddr(testKadPeerAddr(s)))
}
k.Kademlia.Register(ps...)
ch := make(chan OverlayAddr)
go func() {
defer close(ch)
for _, s := range regs {
ch <- testKadPeerAddr(s)
}
}()
err := k.Kademlia.Register(ch)
log.Trace(fmt.Sprintf("register %v addresses: %v", len(regs), err))
return k
}
func testSuggestPeer(t *testing.T, k *testKademlia, expAddr string, expPo int, expWant bool) error {
addr, o, want := k.SuggestPeer()
if overlayStr(addr) != expAddr {
return fmt.Errorf("incorrect peer address suggested. expected %v, got %v", expAddr, overlayStr(addr))
if binStr(addr) != expAddr {
return fmt.Errorf("incorrect peer address suggested. expected %v, got %v", expAddr, binStr(addr))
}
if o != expPo {
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
}
func binStr(a OverlayPeer) string {
if a == nil {
return "<nil>"
}
return pot.ToBin(a.Address())[:6]
}
func TestSuggestPeerFindPeers(t *testing.T) {
// 2 row gap, unsaturated proxbin, no callables -> want PO 0
k := newTestKademlia("000000").On("001000")
@ -222,7 +215,6 @@ func TestSuggestPeerFindPeers(t *testing.T) {
// second time disconnected peer not callable
// with reasonably set Interval
// err = testSuggestPeer(t, k, "010000", 2, true)
err = testSuggestPeer(t, k, "<nil>", 1, true)
if err != nil {
t.Fatal(err.Error())
@ -237,16 +229,16 @@ func TestSuggestPeerFindPeers(t *testing.T) {
}
k.On("010000")
k.Off("010000")
// PO1 disconnects
// new closer peer appears, it is immediately wanted
// k.Off("010000")
k.Register("000101")
err = testSuggestPeer(t, k, "000101", 0, false)
if err != nil {
t.Fatal(err.Error())
}
// PO1 disconnects
k.On("000101")
k.Off("010000")
// second time, gap filling
err = testSuggestPeer(t, k, "010000", 0, false)
if err != nil {
@ -265,6 +257,19 @@ func TestSuggestPeerFindPeers(t *testing.T) {
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("010001")
err = testSuggestPeer(t, k, "<nil>", 0, false)
@ -273,7 +278,18 @@ func TestSuggestPeerFindPeers(t *testing.T) {
}
k.MinBinSize = 3
k.Register("100010")
err = testSuggestPeer(t, k, "100010", 0, false)
if err != nil {
t.Fatal(err.Error())
}
k.On("100010")
err = testSuggestPeer(t, k, "<nil>", 1, true)
if err != nil {
t.Fatal(err.Error())
}
k.On("010010")
err = testSuggestPeer(t, k, "<nil>", 2, true)
if err != nil {
@ -281,8 +297,16 @@ func TestSuggestPeerFindPeers(t *testing.T) {
}
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)
if err != nil {
log.Trace("Kad:\n%v", k.String())
t.Fatal(err.Error())
}
@ -440,7 +464,7 @@ func TestNotifications(t *testing.T) {
k.Discovery = true
k.MinProxBinSize = 3
k.On("010000", "001000")
time.Sleep(100 * time.Millisecond)
time.Sleep(1000 * time.Millisecond)
err := k.checkNotifications(
[]*testPeerNotification{
&testPeerNotification{"010000", "001000", 1},

View file

@ -17,7 +17,11 @@
package network
import (
"context"
"errors"
"fmt"
"net"
"sync"
"time"
"github.com/ethereum/go-ethereum/crypto"
@ -26,83 +30,213 @@ import (
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/protocols"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/pot"
"github.com/ethereum/go-ethereum/rpc"
)
const (
ProtocolName = "bzz"
Version = 0
NetworkId = 322 // BZZ in l33t
ProtocolMaxMsgSize = 10 * 1024 * 1024
)
// bzz is the bzz protocol view of a protocols.Peer (itself an extension of p2p.Peer)
type bzzPeer struct {
*protocols.Peer
localAddr *peerAddr
*peerAddr // remote address
lastActive time.Time
var BzzHandshakeSpec = &protocols.Spec{
Name: "bzz",
Version: 1,
MaxMsgSize: 10 * 1024 * 1024,
Messages: []interface{}{
bzzHandshake{},
},
}
func (self *bzzPeer) LastActive() time.Time {
return self.lastActive
var DiscoverySpec = &protocols.Spec{
Name: "hive",
Version: 1,
MaxMsgSize: 10 * 1024 * 1024,
Messages: []interface{}{
peersMsg{},
getPeersMsg{},
subPeersMsg{},
},
}
// implemented by peerAddr
type PeerAddr interface {
OverlayAddr() []byte
UnderlayAddr() []byte
PO(pot.PotVal, int) (int, bool)
// the Addr interface that peerPool needs
type Addr interface {
OverlayPeer
Over() []byte
Under() []byte
String() string
Update(OverlayAddr) OverlayAddr
}
// the Peer interface that peerPool needs
// Peer interface represents an live peer connection
type Peer interface {
PeerAddr
// String() string // pretty printable the Node
ID() discover.NodeID // the key that uniquely identifies the Node for the peerPool
Send(interface{}) error // can send messages
Drop(error) // disconnect this peer
Register(interface{}, func(interface{}) error) uint64 // register message-handler callbacks
DisconnectHook(func(error))
Addr // the address of a peer
Conn // the live connection (protocols.Peer)
LastActive() time.Time // last time active
}
func BzzCodeMap(msgs ...interface{}) *protocols.CodeMap {
ct := protocols.NewCodeMap(ProtocolName, Version, ProtocolMaxMsgSize)
ct.Register(&bzzHandshake{})
ct.Register(msgs...)
return ct
// Conn interface represents an live peer connection
type Conn interface {
ID() discover.NodeID // the key that uniquely identifies the Node for the peerPool
Handshake(context.Context, interface{}) (interface{}, error) // can send messages
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
// returns p2p.Protocol that is to be offered by the node.Service
func Bzz(oAddr, uAddr []byte, ct *protocols.CodeMap, services func(Peer) error, peerInfo func(id discover.NodeID) interface{}, nodeInfo func() interface{}) *p2p.Protocol {
run := func(p *protocols.Peer) error {
bee := &bzzPeer{
Peer: p,
localAddr: &peerAddr{oAddr, uAddr},
}
// protocol handshake and its validation
// sets remote peer address
err := bee.bzzHandshake()
if err != nil {
log.Error(fmt.Sprintf("handshake error in peer %v: %v", bee.ID(), err))
// TODO: implement store for exec nodes
type StateStore interface {
Load(string) ([]byte, error)
Save(string, []byte) error
}
// BzzConfig captures the config params used by the hive
type BzzConfig struct {
OverlayAddr []byte
UnderlayAddr []byte
HiveParams *HiveParams
}
// 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
}
// mount external service models on the peer connection (swap, sync, hive)
if services != nil {
err = services(bee)
if err != nil {
log.Error(fmt.Sprintf("protocol service error for peer %v: %v", bee.ID(), err))
return err
}
// the handshake has succeeded so run the service
peer := &bzzPeer{
Peer: protocols.NewPeer(p, rw, spec),
localAddr: b.localAddr,
bzzAddr: handshake.peerAddr,
}
return bee.Run()
return run(peer)
}
}
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 {
Version 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 {
return fmt.Sprintf("Handshake: Version: %v, NetworkId: %v, Addr: %v", self.Version, self.NetworkId, self.Addr)
}
// peerAddr implements the PeerAddress interface
type peerAddr struct {
const bzzHandshakeTimeout = time.Second
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
UAddr []byte
}
func (self *peerAddr) OverlayAddr() []byte {
// implements OverlayPeer interface to be used in pot package
func (self *bzzAddr) Address() []byte {
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
}
func (self *peerAddr) PO(val pot.PotVal, pos int) (int, bool) {
kp := val.(PeerAddr)
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 *bzzAddr) Update(a OverlayAddr) OverlayAddr {
return &bzzAddr{self.OAddr, a.(Addr).Under()}
}
func (self *peerAddr) String() string {
return fmt.Sprintf("%x <%x>", 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
func (self *bzzAddr) String() string {
return fmt.Sprintf("%x <%s>", self.OAddr, self.UAddr)
}
// RandomAddr is a utility method generating an address from a public key
func RandomAddr() *peerAddr {
func RandomAddr() *bzzAddr {
key, err := crypto.GenerateKey()
if err != nil {
panic("unable to generate key")
@ -221,23 +334,28 @@ func RandomAddr() *peerAddr {
pubkey := crypto.FromECDSAPub(&key.PublicKey)
var id discover.NodeID
copy(id[:], pubkey[1:])
return &peerAddr{
return &bzzAddr{
OAddr: crypto.Keccak256(pubkey[1:]),
UAddr: id[:],
}
}
// NodeId transforms the underlay address to an adapters.NodeId
func NodeId(addr PeerAddr) *adapters.NodeId {
return adapters.NewNodeId(addr.UnderlayAddr())
// NewNodeIdFromAddr transforms the underlay address to an adapters.NodeId
func NewNodeIdFromAddr(addr Addr) *adapters.NodeId {
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
func NewPeerAddrFromNodeId(n *adapters.NodeId) *peerAddr {
func NewAddrFromNodeId(n *adapters.NodeId) Addr {
id := n.NodeID
return &peerAddr{
OAddr: crypto.Keccak256(id[:]),
UAddr: id[:],
return &bzzAddr{
OAddr: ToOverlayAddr(n.Bytes()),
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 (
"fmt"
"sync"
"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/simulations/adapters"
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 {
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 {
if ct == nil {
ct = BzzCodeMap()
}
func newBzzBaseTester(t *testing.T, n int, addr *bzzAddr, spec *protocols.Spec, run func(*bzzPeer) error) *bzzTester {
cs := make(map[string]chan bool)
srv := func(p Peer) error {
srv := func(p *bzzPeer) error {
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 {
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 {
*p2ptest.ProtocolTester
addr *peerAddr
addr *bzzAddr
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)
p.DisconnectHook(func(err error) {
pp.Remove(p)
})
if services != nil {
err := services(p)
if err != nil {
return err
}
defer pp.Remove(p)
if services == nil {
return nil
}
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
func (s *bzzTester) testHandshake(lhs, rhs *bzzHandshake, disconnects ...*p2ptest.Disconnect) {
var peers []*adapters.NodeId
id := NodeId(rhs.Addr)
id := NewNodeIdFromAddr(rhs.Addr)
if len(disconnects) > 0 {
for _, d := range disconnects {
peers = append(peers, d.Peer)
@ -106,14 +131,18 @@ func (s *bzzTester) runHandshakes(ids ...*adapters.NodeId) {
ids = s.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()]
}
}
func correctBzzHandshake(addr *peerAddr) *bzzHandshake {
return &bzzHandshake{0, 322, addr}
func correctBzzHandshake(addr *bzzAddr) *bzzHandshake {
return &bzzHandshake{
Version: 0,
NetworkId: 322,
Addr: addr,
}
}
func TestBzzHandshakeNetworkIdMismatch(t *testing.T) {
@ -125,7 +154,7 @@ func TestBzzHandshakeNetworkIdMismatch(t *testing.T) {
id := s.Ids[0]
s.testHandshake(
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)")},
)
}
@ -139,7 +168,7 @@ func TestBzzHandshakeVersionMismatch(t *testing.T) {
id := s.Ids[0]
s.testHandshake(
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)")},
)
}
@ -153,70 +182,6 @@ func TestBzzHandshakeSuccess(t *testing.T) {
id := s.Ids[0]
s.testHandshake(
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"
"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/simulations"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/swarm/network"
)
// serviceName is used with the exec adapter so the exec'd binary knows which
// service to execute
const serviceName = "discovery"
const testMinProxBinSize = 2
var services = adapters.Services{
serviceName: func(id *adapters.NodeId, snapshot []byte) p2pnode.Service {
return newNode(id)
},
serviceName: newService,
}
func init() {
@ -70,7 +68,7 @@ func testDiscoverySimulation(t *testing.T, adapter adapters.NodeAdapter) {
for i := 0; i < nodeCount; i++ {
node, err := net.NewNode()
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 {
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
}
nnmap := network.NewPeerPot(testMinProxBinSize, ids...)
check := func(ctx context.Context, id *adapters.NodeId) (bool, error) {
select {
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)
}
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 healthy, nil
@ -179,70 +178,26 @@ func triggerChecks(trigger chan *adapters.NodeId, net *simulations.Network, id *
return nil
}
type node struct {
*network.Hive
func newService(id *adapters.NodeId, snapshot []byte) node.Service {
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 {
addr := network.NewPeerAddrFromNodeId(id)
kademlia := newKademlia(addr.OverlayAddr())
hive := newHive(kademlia)
codeMap := network.BzzCodeMap(network.DiscoveryMsgs...)
node := &node{Hive: hive}
services := func(peer network.Peer) error {
discoveryPeer := network.NewDiscovery(peer, kademlia)
node.Add(discoveryPeer)
peer.DisconnectHook(func(err error) {
node.Remove(discoveryPeer)
})
return nil
hp := network.NewHiveParams()
hp.KeepAliveInterval = time.Second
config := &network.BzzConfig{
OverlayAddr: addr.Over(),
UnderlayAddr: addr.Under(),
HiveParams: hp,
}
node.protocol = network.Bzz(addr.OverlayAddr(), addr.UnderlayAddr(), codeMap, services, nil, nil)
return node
}
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)
return network.NewBzz(config, kad, nil)
}

View file

@ -11,83 +11,58 @@ import (
"net/http"
"os"
"runtime"
"sync"
"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/simulations"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/swarm/network"
)
// SimNode is the adapter used by Swarm simulations.
type SimNode struct {
hive *network.Hive
protocol *p2p.Protocol
type Simulation struct {
mtx sync.Mutex
stores map[discover.NodeID]*adapters.stateStore
}
func (s *SimNode) Protocols() []p2p.Protocol {
return []p2p.Protocol{*s.protocol}
func NewSimulation() *Simulation {
return &Simulation{
stores: make(map[discover.NodeID]*adapters.stateStore),
}
}
func (s *SimNode) APIs() []rpc.API {
return nil
}
func (s *Simulation) NewService(id *adapters.NodeId, snapshot []byte) node.Service {
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
func af() <-chan time.Time {
return time.NewTicker(1 * time.Second).C
}
addr := network.NewAddrFromNodeId(id)
// 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.MinProxBinSize = 2
kp.MaxBinSize = 3
kp.MinBinSize = 1
kp.MaxBinSize = 8
kp.MinBinSize = 2
kp.MaxRetries = 1000
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.CallInterval = 5000
pp := network.NewHive(hp, to) // hive
hp.KeepAliveInterval = 3 * time.Second
services := func(p network.Peer) error {
dp := network.NewDiscovery(p, to)
pp.Add(dp)
log.Trace(fmt.Sprintf("kademlia on %v", dp))
p.DisconnectHook(func(err error) {
pp.Remove(dp)
})
return nil
config := &network.BzzConfig{
OverlayAddr: addr.Over(),
UnderlayAddr: addr.Under(),
HiveParams: hp,
}
ct := network.BzzCodeMap(network.DiscoveryMsgs...) // bzz protocol code map
nodeInfo := func() interface{} { return pp.String() }
return &SimNode{
hive: pp,
protocol: network.Bzz(addr.OverlayAddr(), addr.UnderlayAddr(), ct, services, nil, nodeInfo),
}
return network.NewBzz(config, kad, store)
}
func createMockers() map[string]*simulations.MockerConfig {
@ -119,8 +94,9 @@ func setupMocker(net *simulations.Network) []*adapters.NodeId {
conf := net.Config()
conf.DefaultService = "overlay"
ids := make([]*adapters.NodeId, 10)
for i := 0; i < 10; i++ {
nodeCount := 50
ids := make([]*adapters.NodeId, nodeCount)
for i := 0; i < nodeCount; i++ {
node, err := net.NewNode()
if err != nil {
panic(err.Error())
@ -129,8 +105,6 @@ func setupMocker(net *simulations.Network) []*adapters.NodeId {
}
for _, id := range ids {
n := rand.Intn(1000)
time.Sleep(time.Duration(n) * time.Millisecond)
if err := net.Start(id); err != nil {
panic(err.Error())
}
@ -143,7 +117,12 @@ func setupMocker(net *simulations.Network) []*adapters.NodeId {
} else {
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())
}
}
@ -191,23 +170,23 @@ func randomMocker(net *simulations.Network) {
func startStopMocker(net *simulations.Network) {
ids := setupMocker(net)
for i, id := range ids {
n := 3000 + i*1000
go func(id *adapters.NodeId) {
for {
// n := rand.Intn(5000)
// n := 3000
time.Sleep(time.Duration(n) * time.Millisecond)
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
for range time.Tick(10 * time.Second) {
id := ids[rand.Intn(len(ids))]
go func() {
log.Error("stopping node", "id", id)
if err := net.Stop(id); err != nil {
log.Error("error stopping node", "id", id, "err", err)
return
}
}(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() {
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{
"overlay": NewSimNode,
"overlay": s.NewService,
}
adapters.RegisterServices(services)
@ -226,7 +206,7 @@ func main() {
config := &simulations.ServerConfig{
NewAdapter: func() adapters.NodeAdapter { return adapters.NewSimAdapter(services) },
DefaultMockerId: "start-stop",
DefaultMockerId: "bootNet",
Mockers: mockers,
}

View file

@ -1,182 +1,193 @@
package network
import (
"fmt"
"strings"
"sync"
"github.com/ethereum/go-ethereum/log"
)
const orders = 8
type testOverlay struct {
mu sync.Mutex
addr []byte
pos [][]*testPeerAddr
posMap map[string]*testPeerAddr
}
type testPeerAddr struct {
PeerAddr
Peer Peer
}
func (self *testOverlay) Register(nas ...PeerAddr) error {
self.mu.Lock()
defer self.mu.Unlock()
return self.register(nas...)
}
func (self *testOverlay) GetAddr() PeerAddr {
return &peerAddr{
OAddr: self.addr,
UAddr: []byte{},
}
}
func (self *testOverlay) register(nas ...PeerAddr) error {
for _, na := range nas {
tna := &testPeerAddr{PeerAddr: na}
addr := na.OverlayAddr()
if self.posMap[string(addr)] != nil {
continue
}
self.posMap[string(addr)] = tna
o := order(addr)
log.Trace(fmt.Sprintf("PO: %v, orders: %v", o, orders))
self.pos[o] = append(self.pos[o], tna)
}
return nil
}
func order(addr []byte) int {
return int(addr[0]) / 32
}
func (self *testOverlay) On(n Peer) {
self.mu.Lock()
defer self.mu.Unlock()
addr := n.OverlayAddr()
na := self.posMap[string(addr)]
if na == nil {
self.register(n)
na = self.posMap[string(addr)]
} else if na.Peer != nil {
return
}
log.Trace(fmt.Sprintf("Online: %x", addr[:4]))
na.Peer = n
return
}
func (self *testOverlay) Off(n Peer) {
self.mu.Lock()
defer self.mu.Unlock()
addr := n.OverlayAddr()
na := self.posMap[string(addr)]
if na == nil {
return
}
delete(self.posMap, string(addr))
na.Peer = nil
}
// caller must hold the lock
func (self *testOverlay) on(po []*testPeerAddr) (nodes []Peer) {
for _, na := range po {
if na.Peer != nil {
nodes = append(nodes, na.Peer)
}
}
return nodes
}
// caller must hold the lock
func (self *testOverlay) off(po []*testPeerAddr) (nas []PeerAddr) {
for _, na := range po {
if na.Peer == (*bzzPeer)(nil) {
nas = append(nas, PeerAddr(na))
}
}
return nas
}
func (self *testOverlay) EachLivePeer(base []byte, o int, f func(Peer, int, bool) bool) {
if base == nil {
base = self.addr
}
for i := o; i < len(self.pos); i++ {
for _, na := range self.pos[i] {
if na.Peer != nil {
if !f(na.Peer, o, false) {
return
}
}
}
}
}
func (self *testOverlay) EachPeer(base []byte, o int, f func(PeerAddr, int) bool) {
if base == nil {
base = self.addr
}
for i := o; i < len(self.pos); i++ {
for _, na := range self.pos[i] {
if !f(na, i) {
return
}
}
}
}
func (self *testOverlay) SuggestPeer() (PeerAddr, int, bool) {
self.mu.Lock()
defer self.mu.Unlock()
for i, po := range self.pos {
ons := self.on(po)
if len(ons) < 2 {
offs := self.off(po)
if len(offs) > 0 {
log.Trace(fmt.Sprintf("node %v is off", offs[0]))
return offs[0], i, true
}
}
}
return nil, 0, true
}
func (self *testOverlay) String() string {
self.mu.Lock()
defer self.mu.Unlock()
var t []string
var ons, offs int
var ns []Peer
var nas []PeerAddr
for o, po := range self.pos {
var row []string
ns = self.on(po)
nas = self.off(po)
ons = len(ns)
for _, n := range ns {
addr := n.OverlayAddr()
row = append(row, fmt.Sprintf("%x", addr[:4]))
}
row = append(row, "|")
offs = len(nas)
for _, na := range nas {
addr := na.OverlayAddr()
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),
}
}
//
// import (
// "fmt"
// "strings"
// "sync"
//
// "github.com/ethereum/go-ethereum/log"
// )
//
// const orders = 8
//
// type testOverlay struct {
// mu sync.Mutex
// addr []byte
// pos [][]OverlayAddr
// posMap map[string]OverlayAddr
// }
//
// type testPeerAddr struct {
// Addr
// Peer
// }
//
// func (self *testPeerAddr) Address() []byte {
// return nil
// }
//
// func (self *testPeerAddr) Update(a OverlayAddr) OverlayAddr {
// return self
// }
//
// func (self *testPeerAddr) On(p OverlayConn) OverlayConn {
// return self
// }
//
// func (self *testPeerAddr) Off() OverlayAddr {
// return self
// }
//
// func (self *testOverlay) Register(peers chan OverlayAddr) error {
// self.mu.Lock()
// defer self.mu.Unlock()
// var nas []OverlayAddr
// for a := range peers {
// nas = append(nas, a)
// }
// return self.register(nas...)
// }
//
// func (self *testOverlay) BaseAddr() []byte {
// return nil
// }
//
// func (self *testOverlay) register(nas ...OverlayAddr) error {
// for _, na := range nas {
// addr := na.Address()
// if self.posMap[string(addr)] != nil {
// continue
// }
// self.posMap[string(addr)] = na
// o := order(addr)
// log.Trace(fmt.Sprintf("PO: %v, orders: %v", o, orders))
// self.pos[o] = append(self.pos[o], na)
// }
// return nil
// }
//
// func order(addr []byte) int {
// return int(addr[0]) / 32
// }
//
// func (self *testOverlay) On(n OverlayConn) {
// self.mu.Lock()
// defer self.mu.Unlock()
// addr := n.Address()
// na := self.posMap[string(addr)]
// if na == nil {
// self.register(n)
// na = self.posMap[string(addr)]
// } else if na.Peer != nil {
// return
// }
// log.Trace(fmt.Sprintf("Online: %x", addr[:4]))
// na.Peer = n
// return
// }
//
// func (self *testOverlay) Off(n OverlayConn) {
// self.mu.Lock()
// defer self.mu.Unlock()
// addr := n.Over()
// na := self.posMap[string(addr)]
// if na == nil {
// return
// }
// delete(self.posMap, string(addr))
// na.Peer = nil
// }
//
// // caller must hold the lock
// func (self *testOverlay) on(po []*testPeerAddr) (nodes []OverlayConn) {
// for _, na := range po {
// if na.Peer != nil {
// nodes = append(nodes, na)
// }
// }
// return nodes
// }
//
// // caller must hold the lock
// 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) EachConn(base []byte, o int, f func(OverlayConn, int, bool) bool) {
// for i := o; i < len(self.pos); i++ {
// for _, na := range self.pos[i] {
// if na.Peer != nil {
// if !f(na, o, false) {
// return
// }
// }
// }
// }
// }
//
// func (self *testOverlay) EachAddr(base []byte, o int, f func(OverlayAddr, int) bool) {
// for i := o; i < len(self.pos); i++ {
// for _, na := range self.pos[i] {
// if !f(na, i) {
// return
// }
// }
// }
// }
//
// func (self *testOverlay) SuggestPeer() (OverlayAddr, int, bool) {
// self.mu.Lock()
// defer self.mu.Unlock()
// for i, po := range self.pos {
// ons := self.on(po)
// if len(ons) < 2 {
// offs := self.off(po)
// if len(offs) > 0 {
// log.Trace(fmt.Sprintf("node %v is off", offs[0]))
// return offs[0], i, true
// }
// }
// }
// return nil, 0, true
// }
//
// func (self *testOverlay) String() string {
// self.mu.Lock()
// defer self.mu.Unlock()
// var t []string
// var ons, offs int
// var ns []Peer
// var nas []Addr
// for o, po := range self.pos {
// var row []string
// ns = self.on(po)
// nas = self.off(po)
// ons = len(ns)
// for _, n := range ns {
// addr := n.Over()
// row = append(row, fmt.Sprintf("%x", addr[:4]))
// }
// row = append(row, "|")
// offs = len(nas)
// for _, na := range nas {
// addr := na.Over()
// 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 (
"bytes"
@ -9,13 +9,16 @@ import (
"time"
"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/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/protocols"
"github.com/ethereum/go-ethereum/pot"
"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"
)
@ -25,19 +28,20 @@ const (
TopicResolverLength = 8
PssPeerCapacity = 256
PssPeerTopicDefaultCapacity = 8
digestLength = 64
digestLength = 32
digestCapacity = 256
defaultDigestCacheTTL = time.Second
pingTopicName = "pss"
pingTopicVersion = 1
)
var (
errorNoForwarder = errors.New("no available forwarders in routing table")
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
type PssParams struct {
Cachettl time.Duration
@ -51,50 +55,14 @@ func NewPssParams() *PssParams {
}
// Encapsulates the message transported over pss.
//
// Warning: do not access the To-member directly. Use *PssMsg.GetRecipient() and *PssMsg.SetRecipient() instead.
type PssMsg struct {
// (we need the To-member exported for type inference)
To []byte
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
Payload *PssEnvelope
}
// String representation of PssMsg
func (self *PssMsg) String() string {
return fmt.Sprintf("PssMsg: Recipient: %x", common.ByteLabel(self.GetRecipient()))
}
// 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
return fmt.Sprintf("PssMsg: Recipient: %x", common.ByteLabel(self.To))
}
// 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()
type PssTopic [TopicLength]byte
// Pre-Whisper placeholder
type pssDigest uint32
func (self *PssTopic) String() string {
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.
//
// 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:
//
@ -120,51 +144,118 @@ type pssDigest uint32
// - a dispatcher lookup, mapping protocols to topics
// - a message cache to spot messages that previously have been forwarded
type Pss struct {
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
handlers map[PssTopic]func([]byte, *p2p.Peer, []byte) error // topic and version based pss payload handlers
events map[PssTopic]*event.Feed // subscriptions for each topic
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
network.Overlay // we can get the overlayaddress from this
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
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
lock sync.Mutex
dpa *storage.DPA
}
func (self *Pss) hashMsg(msg *PssMsg) pssDigest {
hasher := self.hasher("SHA3")()
hasher.Reset()
hasher.Write(msg.GetRecipient())
hasher.Write(msg.Payload.SenderUAddr)
hasher.Write(msg.Payload.SenderOAddr)
hasher.Write(msg.Payload.Topic[:])
hasher.Write(msg.Payload.Payload)
b := hasher.Sum([]byte{})
return pssDigest(binary.BigEndian.Uint32(b))
func (self *Pss) storeMsg(msg *PssMsg) (pssDigest, error) {
swg := &sync.WaitGroup{}
wwg := &sync.WaitGroup{}
buf := bytes.NewReader(msg.serialize())
key, err := self.dpa.Store(buf, int64(buf.Len()), swg, wwg)
if err != nil {
log.Warn("Could not store in swarm", "err", err)
return pssDigest{}, err
}
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
//
// TODO error check overlay integrity
func NewPss(k Overlay, params *PssParams) *Pss {
// TODO: error check overlay integrity
func NewPss(k network.Overlay, dpa *storage.DPA, params *PssParams) *Pss {
return &Pss{
Overlay: k,
//peerPool: make(map[pot.Address]map[PssTopic]*PssReadWriter, PssPeerCapacity),
peerPool: make(map[pot.Address]map[PssTopic]p2p.MsgReadWriter, PssPeerCapacity),
handlers: make(map[PssTopic]func([]byte, *p2p.Peer, []byte) error),
events: make(map[PssTopic]*event.Feed),
handlers: make(map[PssTopic]map[*pssHandler]bool),
fwdcache: make(map[pssDigest]pssCacheEntry),
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
//
// 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
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)
}
@ -210,35 +301,115 @@ func (self *Pss) checkFwdCache(addr []byte, digest pssDigest) bool {
return false
}
// Takes the generated PssTopic of a protocol, and links a handler function to it
// 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 {
func (self *Pss) getHandlers(topic PssTopic) map[*pssHandler]bool {
self.lock.Lock()
defer self.lock.Unlock()
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
//
// 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() {
err := run(p, rw)
log.Warn(fmt.Sprintf("pss vprotocol quit on addr %v topic %v: %v", addr, topic, err))
self.removePeerTopic(rw, topic)
}()
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 {
if self.peerPool[id][topic] == nil {
if self.peerPool[id] == nil {
self.peerPool[id] = make(map[PssTopic]p2p.MsgReadWriter, PssPeerTopicDefaultCapacity)
}
self.peerPool[id][topic] = rw
return nil
}
func (self *Pss) removePeerTopic(id pot.Address, topic PssTopic) {
self.peerPool[id][topic] = nil
return
func (self *Pss) removePeerTopic(rw p2p.MsgReadWriter, topic PssTopic) {
prw, ok := rw.(*PssReadWriter)
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 {
if 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
return self.peerPool[id][topic] != nil
}
// Convenience object that:
@ -374,36 +456,34 @@ func (self *Pss) Forward(msg *PssMsg) error {
// Implements p2p.MsgReadWriter
type PssReadWriter struct {
*Pss
RecipientOAddr pot.Address
LastActive time.Time
rw chan p2p.Msg
ct *protocols.CodeMap
topic *PssTopic
To pot.Address
LastActive time.Time
rw chan p2p.Msg
spec *protocols.Spec
topic *PssTopic
}
// Implements p2p.MsgReader
func (prw PssReadWriter) ReadMsg() (p2p.Msg, error) {
msg := <-prw.rw
log.Trace(fmt.Sprintf("pssrw readmsg: %v", msg))
return msg, nil
}
// Implements p2p.MsgWriter
func (prw PssReadWriter) WriteMsg(msg p2p.Msg) error {
log.Trace(fmt.Sprintf("pssrw writemsg: %v", msg))
ifc, found := prw.ct.GetInterface(msg.Code)
ifc, found := prw.spec.NewMsg(msg.Code)
if !found {
return fmt.Errorf("Writemsg couldn't find matching interface for code %d", msg.Code)
}
msg.Decode(ifc)
to := prw.RecipientOAddr.Bytes()
pmsg, _ := makeMsg(msg.Code, ifc)
return prw.Pss.Send(to, *prw.topic, pmsg)
pmsg, err := newProtocolMsg(msg.Code, ifc)
if err != nil {
return err
}
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
@ -416,50 +496,41 @@ func (prw PssReadWriter) injectMsg(msg p2p.Msg) error {
// Convenience object for passing messages in and out of the p2p layer
type PssProtocol struct {
*Pss
virtualProtocol *p2p.Protocol
proto *p2p.Protocol
topic *PssTopic
ct *protocols.CodeMap
spec *protocols.Spec
}
// 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{
Pss: pss,
virtualProtocol: targetprotocol,
proto: targetprotocol,
topic: topic,
ct: ct,
spec: spec,
}
return pp
}
// 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
pss.Register(topic, pp.handle)
//return pp
return nil
}
func (self *PssProtocol) handle(msg []byte, p *p2p.Peer, senderAddr []byte) error {
hashoaddr := pot.NewHashAddressFromBytes(senderAddr).Address
if !self.isActive(hashoaddr, *self.topic) {
rw := &PssReadWriter{
Pss: self.Pss,
RecipientOAddr: hashoaddr,
rw: make(chan p2p.Msg),
ct: self.ct,
topic: self.topic,
Pss: self.Pss,
To: hashoaddr,
rw: make(chan p2p.Msg),
spec: self.spec,
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{}
rlp.DecodeBytes(msg, payload)
pmsg := p2p.Msg{
Code: payload.Code,
Size: uint32(len(payload.Data)),
ReceivedAt: time.Now(),
Payload: bytes.NewBuffer(payload.Data),
pmsg, err := ToP2pMsg(msg)
if err != nil {
return fmt.Errorf("could not decode pssmsg")
}
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
}
func (self *Pss) IsSelfRecipient(msg *PssMsg) bool {
if bytes.Equal(msg.GetRecipient(), self.Overlay.GetAddr().OverlayAddr()) {
return true
}
return false
func (self *Pss) isSelfRecipient(msg *PssMsg) bool {
return bytes.Equal(msg.To, self.Overlay.BaseAddr())
}
func (self *Pss) GetPingHandler() func([]byte, *p2p.Peer, []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) {
func newProtocolMsg(code uint64, msg interface{}) ([]byte, error) {
rlpdata, err := rlp.EncodeToBytes(msg)
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
// therefore we use two separate []byte fields instead of peerAddr
// TODO verify that nested structs cannot be used in rlp
smsg := &pssPayload{
smsg := &PssProtocolMsg{
Code: code,
Size: uint32(len(rlpdata)),
Data: rlpdata,
Payload: rlpdata,
}
rlpbundle, err := rlp.EncodeToBytes(smsg)
if err != nil {
return nil, err
}
return rlpbundle, nil
return rlp.EncodeToBytes(smsg)
}
// 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
func MakeTopic(s string, v int) (PssTopic, error) {
t := [TopicLength]byte{}
if len(s)+4 <= TopicLength {
copy(t[4:len(s)+4], s)
} else {
return t, fmt.Errorf("topic '%t' too long", s)
}
binary.PutVarint(t[:4], int64(v))
return t, nil
func NewTopic(s string, v int) (topic PssTopic) {
h := sha3.NewKeccak256()
h.Write([]byte(s))
buf := make([]byte, TopicLength / 8)
binary.PutUvarint(buf, uint64(v))
h.Write(buf)
copy(topic[:], h.Sum(buf)[:])
return topic
}
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 (
"bytes"
"context"
"encoding/hex"
"fmt"
"math/rand"
"net"
"net/http"
"io/ioutil"
"os"
"strconv"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/protocols"
"github.com/ethereum/go-ethereum/p2p/simulations"
"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 (
protocolName = "foo"
protocolVersion = 42
pssServiceName = "pss"
bzzServiceName = "bzz"
)
var services = newServices()
func init() {
adapters.RegisterServices(services)
h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true)))
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
// message handlers are methods of this
// channels allow receipt reporting from p2p.Protocol message handler
@ -59,27 +439,19 @@ type pssTestNode struct {
apifunc func() []rpc.API
}
func (n *pssTestNode) Add(peer Peer) error {
func (n *pssTestNode) Add(peer *bzzPeer) error {
err := n.Hive.Add(peer)
time.Sleep(time.Millisecond * 250)
n.triggerCheck()
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() {
go func() { n.trigger <- n.id }()
}
func (n *pssTestNode) OverlayAddr() []byte {
return n.Pss.Overlay.GetAddr().OverlayAddr()
return n.Pss.Overlay.BaseAddr()
}
func (n *pssTestNode) UnderlayAddr() []byte {
@ -102,8 +474,9 @@ type pssTestService struct {
func newPssTestService(t *testing.T, handlefunc func(interface{}) error, testnode *pssTestNode) *pssTestService {
hp := NewHiveParams()
//hp.CallInterval = 250
testnode.Hive = NewHive(hp, testnode.Pss.Overlay)
hp.KeepAliveInterval = 300
bzz := NewBzz(testnode.OverlayAddr(), testnode.UnderlayAddr(), newTestStore())
testnode.Hive = NewHive(hp, testnode.Pss.Overlay, bzz)
return &pssTestService{
//nid := adapters.NewNodeId(addr.UnderlayAddr())
msgFunc: handlefunc,
@ -111,8 +484,8 @@ func newPssTestService(t *testing.T, handlefunc func(interface{}) error, testnod
}
}
func (self *pssTestService) Start(server p2p.Server) error {
return self.node.Hive.Start(server, self.node.hiveKeepAlive)
func (self *pssTestService) Start(server *p2p.Server) error {
return self.node.Hive.Start(server)
}
func (self *pssTestService) Stop() error {
@ -121,24 +494,13 @@ func (self *pssTestService) Stop() error {
}
func (self *pssTestService) Protocols() []p2p.Protocol {
ct := BzzCodeMap()
for _, m := range DiscoveryMsgs {
ct.Register(m)
}
ct.Register(&PssMsg{})
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}
bzz := NewBzz(self.node.OverlayAddr(), self.node.UnderlayAddr(), newTestStore())
return append(self.node.Hive.Protocols(), p2p.Protocol{
Name: PssProtocolName,
Version: PssProtocolVersion,
Length: PssProtocol.Length(),
Run: bzz.RunProtocol(PssProtocol, self.Run),
})
}
func (self *pssTestService) APIs() []rpc.API {
@ -153,142 +515,15 @@ func (self *pssTestService) APIs() []rpc.API {
return nil
}
func TestPssCache(t *testing.T) {
var err error
to, _ := hex.DecodeString("08090a0b0c0d0e0f1011121314150001020304050607161718191a1b1c1d1e1f")
oaddr, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
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 (self *pssTestService) Run(peer *bzzPeer) error {
self.node.Add(peer)
defer self.node.Remove(peer)
return peer.Run(self.msgFunc)
}
*/
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) {
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)
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)
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 {
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+1]]].Pss.GetAddr().OverlayAddr()),
common.ByteLabel(nodes[fullnodes[sends[i]]].Pss.BaseAddr()),
common.ByteLabel(nodes[fullnodes[sends[i+1]]].Pss.BaseAddr()),
common.ByteLabel(fullnodes[sends[i]].Bytes()),
common.ByteLabel(fullnodes[sends[i+1]].Bytes()))
}
@ -484,15 +721,15 @@ func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes in
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
}
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 {
logstring := fmt.Sprintf("Node oaddr %x kademlia: ", common.ByteLabel(node.Pss.Overlay.GetAddr().OverlayAddr()))
node.Pss.Overlay.EachLivePeer(nil, 256, func(p Peer, po int, isprox bool) bool {
logstring += fmt.Sprintf("%x ", common.ByteLabel(p.OverlayAddr()))
logstring := fmt.Sprintf("Node oaddr %x kademlia: ", common.ByteLabel(node.Pss.Overlay.BaseAddr()))
node.Pss.Overlay.EachConn(nil, 256, func(p Peer, po int, isprox bool) bool {
logstring += fmt.Sprintf("%x ", common.ByteLabel(p.Over()))
return true
})
t.Log(logstring)
@ -511,7 +748,8 @@ func TestPssFullLinearEcho(t *testing.T) {
var firstpssnode *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)
fullnodes := []*adapters.NodeId{}
@ -538,15 +776,15 @@ func TestPssFullLinearEcho(t *testing.T) {
return err
}
/*for i, id := range ids {
var peerId *adapters.NodeId
if i != 0 {
peerId = ids[i-1]
if err := net.Connect(id, peerId); err != nil {
return err
}
}
}*/
// for i, id := range ids {
// var peerId *adapters.NodeId
// if i != 0 {
// 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) {
@ -559,9 +797,8 @@ func TestPssFullLinearEcho(t *testing.T) {
node, ok := nodes[id]
if !ok {
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
}
@ -589,7 +826,7 @@ func TestPssFullLinearEcho(t *testing.T) {
// first find a node that we're connected to
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())))
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 {
if id.NodeID == p.ID() {
firstpssnode = id
@ -609,7 +846,7 @@ func TestPssFullLinearEcho(t *testing.T) {
// then find the node it's connected to
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())))
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 {
if id.NodeID == p.ID() && id.NodeID != fullnodes[0].NodeID {
secondpssnode = id
@ -692,7 +929,8 @@ func TestPssFullWS(t *testing.T) {
var firstpssnode, secondpssnode *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)
trigger := make(chan *adapters.NodeId)
@ -788,7 +1026,7 @@ func TestPssFullWS(t *testing.T) {
// then find the node it's connected to
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())))
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 {
if id.NodeID == p.ID() && id.NodeID != fullnodes[0].NodeID {
secondpssnode = id
@ -855,7 +1093,7 @@ func TestPssFullWS(t *testing.T) {
action = func(ctx context.Context) error {
go func() {
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
}()
return nil
@ -935,7 +1173,7 @@ func newPssSimulationTester(t *testing.T, numnodes int, numfullnodes int, trigge
if testpeers[id] != nil {
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 {
handlefunc = makePssHandleForward(psss[id])
}
@ -965,7 +1203,7 @@ func newPssSimulationTester(t *testing.T, numnodes int, numfullnodes int, trigge
}
for i, conf := range configs {
addr := NewPeerAddrFromNodeId(conf.Id)
psss[conf.Id] = makePss(addr.OverlayAddr())
psss[conf.Id] = makePss(addr.Over())
if i < numfullnodes {
tp := &pssTestPeer{
Peer: &protocols.Peer{
@ -992,13 +1230,73 @@ func newPssSimulationTester(t *testing.T, numnodes int, numfullnodes int, trigge
}
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
pp := NewPssParams()
overlay := NewKademlia(addr, kp)
ps := NewPss(overlay, pp)
overlay := network.NewKademlia(addr, kp)
ps := NewPss(overlay, dpa, pp)
//overlay.Prune(time.Tick(time.Millisecond * 250))
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)
}
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 {
// for the simple check it passes on the message if it's not for us
return func(msg interface{}) error {
@ -1110,3 +1379,4 @@ func (ptp *pssTestPeer) SimpleHandlePssPayload(msg interface{}) error {
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/node"
"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/swarm/api"
httpapi "github.com/ethereum/go-ethereum/swarm/api/http"
"github.com/ethereum/go-ethereum/swarm/fuse"
"github.com/ethereum/go-ethereum/swarm/network"
"github.com/ethereum/go-ethereum/swarm/storage"
"github.com/ethereum/go-ethereum/swarm/pss"
)
// the swarm stack
@ -162,7 +162,7 @@ Start is called when the stack is started
* TODO: start subservices like sword, swear, swarmdns
*/
// implements the node.Service interface
func (self *Swarm) Start(net p2p.Server) error {
func (self *Swarm) Start(net *p2p.Server) error {
// set chequebook
if self.swapEnabled {
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 {
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 {
pssparams := network.NewPssParams()
@ -238,48 +239,46 @@ func (self *Swarm) Stop() error {
// implements the node.Service interface
func (self *Swarm) Protocols() []p2p.Protocol {
ct := network.BzzCodeMap()
for _, m := range network.DiscoveryMsgs {
ct.Register(m)
}
if self.pssEnabled {
ct.Register(&network.PssMsg{})
ct.Register(1, &network.PssMsg{})
}
ct.Register(2, network.DiscoveryMsgs...)
srv := func(p network.Peer) error {
if self.pssEnabled {
p.Register(&network.PssMsg{}, func(msg interface{}) error {
pssmsg := msg.(*network.PssMsg)
if self.pss.IsSelfRecipient(pssmsg) {
log.Trace("pss for us, yay! ... let's process!")
env := pssmsg.Payload
umsg := env.Payload
f := self.pss.GetHandler(env.Topic)
if f == nil {
return fmt.Errorf("No registered handler for topic '%s'", env.Topic)
}
nid := adapters.NewNodeId(env.SenderUAddr)
p := p2p.NewPeer(nid.NodeID, fmt.Sprintf("%x", common.ByteLabel(nid.Bytes())), []p2p.Cap{})
return f(umsg, p, env.SenderOAddr)
} else {
log.Trace("pss was for someone else :'( ... forwarding")
return self.pss.Forward(pssmsg)
}
return nil
})
}
self.hive.Add(p)
p.DisconnectHook(func(err error) {
self.hive.Remove(p)
})
return nil
}
// srv := func(p network.Peer) error {
// if self.pssEnabled {
// p.Register(&network.PssMsg{}, func(msg interface{}) error {
// pssmsg := msg.(*network.PssMsg)
//
// if self.pss.IsSelfRecipient(pssmsg) {
// log.Trace("pss for us, yay! ... let's process!")
// env := pssmsg.Payload
// umsg := env.Payload
// f := self.pss.GetHandler(env.Topic)
// if f == nil {
// return fmt.Errorf("No registered handler for topic '%s'", env.Topic)
// }
// nid := adapters.NewNodeId(env.SenderUAddr)
// p := p2p.NewPeer(nid.NodeID, fmt.Sprintf("%x", common.ByteLabel(nid.Bytes())), []p2p.Cap{})
// return f(umsg, p, env.SenderOAddr)
// } else {
// log.Trace("pss was for someone else :'( ... forwarding")
// return self.pss.Forward(pssmsg)
// }
// return nil
// })
// }
// self.hive.Add(p)
// p.DisconnectHook(func(err error) {
// self.hive.Remove(p)
// })
// return nil
// }
proto := network.Bzz(
self.hive.Overlay.GetAddr().OverlayAddr(),
self.hive.Overlay.GetAddr().UnderlayAddr(),
self.hive.Overlay.GetAddr().Over(),
self.hive.Overlay.GetAddr().Under(),
ct,
srv,
nil,
nil,
nil,
)

View file

@ -48,14 +48,16 @@ func main() {
shh := whisper.New()
// Create an Ethereum peer to communicate through
server := p2p.NewServer(p2p.Config{
PrivateKey: key,
MaxPeers: 10,
Name: name,
Protocols: []p2p.Protocol{shh.Protocol()},
ListenAddr: ":30300",
NAT: nat.Any(),
})
server := &p2p.Server{
Config: p2p.Config{
PrivateKey: key,
MaxPeers: 10,
Name: name,
Protocols: []p2p.Protocol{shh.Protocol()},
ListenAddr: ":30300",
NAT: nat.Any(),
},
}
fmt.Println("Starting Ethereum peer...")
if err := server.Start(); err != nil {
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
// of the Whisper protocol.
func (self *Whisper) Start(p2p.Server) error {
func (self *Whisper) Start(*p2p.Server) error {
log.Info(fmt.Sprint("Whisper started"))
go self.update()
return nil

View file

@ -78,7 +78,7 @@ type TestData struct {
type TestNode struct {
shh *Whisper
id *ecdsa.PrivateKey
server p2p.Server
server *p2p.Server
filerId string
}
@ -140,17 +140,19 @@ func initialize(t *testing.T) {
peers = append(peers, peer)
}
node.server = p2p.NewServer(p2p.Config{
PrivateKey: node.id,
MaxPeers: NumNodes/2 + 1,
Name: name,
Protocols: node.shh.Protocols(),
ListenAddr: addr,
NAT: nat.Any(),
BootstrapNodes: peers,
StaticNodes: peers,
TrustedNodes: peers,
})
node.server = &p2p.Server{
Config: p2p.Config{
PrivateKey: node.id,
MaxPeers: NumNodes/2 + 1,
Name: name,
Protocols: node.shh.Protocols(),
ListenAddr: addr,
NAT: nat.Any(),
BootstrapNodes: peers,
StaticNodes: peers,
TrustedNodes: peers,
},
}
err = node.server.Start()
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
// of the Whisper protocol.
func (w *Whisper) Start(p2p.Server) error {
func (w *Whisper) Start(*p2p.Server) error {
log.Info("started whisper v." + ProtocolVersionStr)
go w.update()