mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 23:26:44 +00:00
Merge pull request #92 from ethersphere/swarm-pss-rpcclient
swarm/pss: rpcclient + pss test fixes
This commit is contained in:
commit
ebd6199dfc
58 changed files with 3421 additions and 2374 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
10
node/node.go
10
node/node.go
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
30
p2p/dial.go
30
p2p/dial.go
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
20
p2p/simulations/adapters/state.go
Normal file
20
p2p/simulations/adapters/state.go
Normal 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),
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
228
swarm/pss/client.go
Normal 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
173
swarm/pss/client_test.go
Normal 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
125
swarm/pss/common.go
Normal 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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
76
swarm/pss/pssapi.go
Normal 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
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue