mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-10 22:13:47 +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
|
return password
|
||||||
}
|
}
|
||||||
|
|
||||||
func injectBootnodes(srv p2p.Server, nodes []string) {
|
func injectBootnodes(srv *p2p.Server, nodes []string) {
|
||||||
for _, url := range nodes {
|
for _, url := range nodes {
|
||||||
n, err := discover.ParseNode(url)
|
n, err := discover.ParseNode(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ const quitCommand = "~Q"
|
||||||
|
|
||||||
// singletons
|
// singletons
|
||||||
var (
|
var (
|
||||||
server p2p.Server
|
server *p2p.Server
|
||||||
shh *whisper.Whisper
|
shh *whisper.Whisper
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
mailServer mailserver.WMailServer
|
mailServer mailserver.WMailServer
|
||||||
|
|
@ -253,17 +253,19 @@ func initialize() {
|
||||||
maxPeers = 800
|
maxPeers = 800
|
||||||
}
|
}
|
||||||
|
|
||||||
server = p2p.NewServer(p2p.Config{
|
server = &p2p.Server{
|
||||||
PrivateKey: nodeid,
|
Config: p2p.Config{
|
||||||
MaxPeers: maxPeers,
|
PrivateKey: nodeid,
|
||||||
Name: common.MakeName("wnode", "5.0"),
|
MaxPeers: maxPeers,
|
||||||
Protocols: shh.Protocols(),
|
Name: common.MakeName("wnode", "5.0"),
|
||||||
ListenAddr: *argIP,
|
Protocols: shh.Protocols(),
|
||||||
NAT: nat.Any(),
|
ListenAddr: *argIP,
|
||||||
BootstrapNodes: peers,
|
NAT: nat.Any(),
|
||||||
StaticNodes: peers,
|
BootstrapNodes: peers,
|
||||||
TrustedNodes: peers,
|
StaticNodes: peers,
|
||||||
})
|
TrustedNodes: peers,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func startServer() {
|
func startServer() {
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ func (r *ReleaseService) Protocols() []p2p.Protocol { return nil }
|
||||||
func (r *ReleaseService) APIs() []rpc.API { return nil }
|
func (r *ReleaseService) APIs() []rpc.API { return nil }
|
||||||
|
|
||||||
// Start spawns the periodic version checker goroutine
|
// Start spawns the periodic version checker goroutine
|
||||||
func (r *ReleaseService) Start(server p2p.Server) error {
|
func (r *ReleaseService) Start(server *p2p.Server) error {
|
||||||
go r.checker()
|
go r.checker()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type LesServer interface {
|
type LesServer interface {
|
||||||
Start(srvr p2p.Server)
|
Start(srvr *p2p.Server)
|
||||||
Stop()
|
Stop()
|
||||||
Protocols() []p2p.Protocol
|
Protocols() []p2p.Protocol
|
||||||
}
|
}
|
||||||
|
|
@ -362,7 +362,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol {
|
||||||
|
|
||||||
// Start implements node.Service, starting all internal goroutines needed by the
|
// Start implements node.Service, starting all internal goroutines needed by the
|
||||||
// Ethereum protocol implementation.
|
// Ethereum protocol implementation.
|
||||||
func (s *Ethereum) Start(srvr p2p.Server) error {
|
func (s *Ethereum) Start(srvr *p2p.Server) error {
|
||||||
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion())
|
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion())
|
||||||
|
|
||||||
s.protocolManager.Start()
|
s.protocolManager.Start()
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ const historyUpdateRange = 50
|
||||||
type Service struct {
|
type Service struct {
|
||||||
stack *node.Node // Temporary workaround, remove when API finalized
|
stack *node.Node // Temporary workaround, remove when API finalized
|
||||||
|
|
||||||
server p2p.Server // Peer-to-peer server to retrieve networking infos
|
server *p2p.Server // Peer-to-peer server to retrieve networking infos
|
||||||
eth *eth.Ethereum // Full Ethereum service if monitoring a full node
|
eth *eth.Ethereum // Full Ethereum service if monitoring a full node
|
||||||
les *les.LightEthereum // Light Ethereum service if monitoring a light node
|
les *les.LightEthereum // Light Ethereum service if monitoring a light node
|
||||||
engine consensus.Engine // Consensus engine to retrieve variadic block fields
|
engine consensus.Engine // Consensus engine to retrieve variadic block fields
|
||||||
|
|
@ -101,7 +101,7 @@ func (s *Service) Protocols() []p2p.Protocol { return nil }
|
||||||
func (s *Service) APIs() []rpc.API { return nil }
|
func (s *Service) APIs() []rpc.API { return nil }
|
||||||
|
|
||||||
// Start implements node.Service, starting up the monitoring and reporting daemon.
|
// Start implements node.Service, starting up the monitoring and reporting daemon.
|
||||||
func (s *Service) Start(server p2p.Server) error {
|
func (s *Service) Start(server *p2p.Server) error {
|
||||||
s.server = server
|
s.server = server
|
||||||
go s.loop()
|
go s.loop()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1434,12 +1434,12 @@ func (api *PrivateDebugAPI) SetHead(number hexutil.Uint64) {
|
||||||
|
|
||||||
// PublicNetAPI offers network related RPC methods
|
// PublicNetAPI offers network related RPC methods
|
||||||
type PublicNetAPI struct {
|
type PublicNetAPI struct {
|
||||||
net p2p.Server
|
net *p2p.Server
|
||||||
networkVersion uint64
|
networkVersion uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPublicNetAPI creates a new net API instance.
|
// NewPublicNetAPI creates a new net API instance.
|
||||||
func NewPublicNetAPI(net p2p.Server, networkVersion uint64) *PublicNetAPI {
|
func NewPublicNetAPI(net *p2p.Server, networkVersion uint64) *PublicNetAPI {
|
||||||
return &PublicNetAPI{net, networkVersion}
|
return &PublicNetAPI{net, networkVersion}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ func (s *LightEthereum) Protocols() []p2p.Protocol {
|
||||||
|
|
||||||
// Start implements node.Service, starting all internal goroutines needed by the
|
// Start implements node.Service, starting all internal goroutines needed by the
|
||||||
// Ethereum protocol implementation.
|
// Ethereum protocol implementation.
|
||||||
func (s *LightEthereum) Start(srvr p2p.Server) error {
|
func (s *LightEthereum) Start(srvr *p2p.Server) error {
|
||||||
log.Warn("Light client mode is an experimental feature")
|
log.Warn("Light client mode is an experimental feature")
|
||||||
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.networkId)
|
s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.networkId)
|
||||||
s.protocolManager.Start(srvr)
|
s.protocolManager.Start(srvr)
|
||||||
|
|
|
||||||
|
|
@ -61,10 +61,6 @@ const (
|
||||||
disableClientRemovePeer = false
|
disableClientRemovePeer = false
|
||||||
)
|
)
|
||||||
|
|
||||||
type discV5Server interface {
|
|
||||||
DiscV5() *discv5.Network
|
|
||||||
}
|
|
||||||
|
|
||||||
// errIncompatibleConfig is returned if the requested protocols and configs are
|
// errIncompatibleConfig is returned if the requested protocols and configs are
|
||||||
// not compatible (low protocol version restrictions and high requirements).
|
// not compatible (low protocol version restrictions and high requirements).
|
||||||
var errIncompatibleConfig = errors.New("incompatible configuration")
|
var errIncompatibleConfig = errors.New("incompatible configuration")
|
||||||
|
|
@ -260,10 +256,10 @@ func (pm *ProtocolManager) removePeer(id string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *ProtocolManager) Start(srvr p2p.Server) {
|
func (pm *ProtocolManager) Start(srvr *p2p.Server) {
|
||||||
var topicDisc *discv5.Network
|
var topicDisc *discv5.Network
|
||||||
if v, ok := srvr.(discV5Server); ok {
|
if srvr != nil {
|
||||||
topicDisc = v.DiscV5()
|
topicDisc = srvr.DiscV5
|
||||||
}
|
}
|
||||||
lesTopic := discv5.Topic("LES@" + common.Bytes2Hex(pm.blockchain.Genesis().Hash().Bytes()[0:8]))
|
lesTopic := discv5.Topic("LES@" + common.Bytes2Hex(pm.blockchain.Genesis().Hash().Bytes()[0:8]))
|
||||||
if pm.lightSync {
|
if pm.lightSync {
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ func (s *LesServer) Protocols() []p2p.Protocol {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts the LES server
|
// Start starts the LES server
|
||||||
func (s *LesServer) Start(srvr p2p.Server) {
|
func (s *LesServer) Start(srvr *p2p.Server) {
|
||||||
s.protocolManager.Start(srvr)
|
s.protocolManager.Start(srvr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ const (
|
||||||
type serverPool struct {
|
type serverPool struct {
|
||||||
db ethdb.Database
|
db ethdb.Database
|
||||||
dbKey []byte
|
dbKey []byte
|
||||||
server p2p.Server
|
server *p2p.Server
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
wg *sync.WaitGroup
|
wg *sync.WaitGroup
|
||||||
connWg sync.WaitGroup
|
connWg sync.WaitGroup
|
||||||
|
|
@ -118,7 +118,7 @@ type serverPool struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newServerPool creates a new serverPool instance
|
// newServerPool creates a new serverPool instance
|
||||||
func newServerPool(db ethdb.Database, dbPrefix []byte, server p2p.Server, topic discv5.Topic, quit chan struct{}, wg *sync.WaitGroup) *serverPool {
|
func newServerPool(db ethdb.Database, dbPrefix []byte, server *p2p.Server, topic discv5.Topic, quit chan struct{}, wg *sync.WaitGroup) *serverPool {
|
||||||
pool := &serverPool{
|
pool := &serverPool{
|
||||||
db: db,
|
db: db,
|
||||||
dbKey: append(dbPrefix, []byte(topic)...),
|
dbKey: append(dbPrefix, []byte(topic)...),
|
||||||
|
|
@ -139,11 +139,11 @@ func newServerPool(db ethdb.Database, dbPrefix []byte, server p2p.Server, topic
|
||||||
pool.loadNodes()
|
pool.loadNodes()
|
||||||
pool.checkDial()
|
pool.checkDial()
|
||||||
|
|
||||||
if srv, ok := pool.server.(discV5Server); ok && srv.DiscV5() != nil {
|
if pool.server.DiscV5 != nil {
|
||||||
pool.discSetPeriod = make(chan time.Duration, 1)
|
pool.discSetPeriod = make(chan time.Duration, 1)
|
||||||
pool.discNodes = make(chan *discv5.Node, 100)
|
pool.discNodes = make(chan *discv5.Node, 100)
|
||||||
pool.discLookups = make(chan bool, 100)
|
pool.discLookups = make(chan bool, 100)
|
||||||
go srv.DiscV5().SearchTopic(topic, pool.discSetPeriod, pool.discNodes, pool.discLookups)
|
go pool.server.DiscV5.SearchTopic(topic, pool.discSetPeriod, pool.discNodes, pool.discLookups)
|
||||||
}
|
}
|
||||||
|
|
||||||
go pool.eventLoop()
|
go pool.eventLoop()
|
||||||
|
|
|
||||||
|
|
@ -375,7 +375,7 @@ func NewPublicWeb3API(stack *Node) *PublicWeb3API {
|
||||||
|
|
||||||
// ClientVersion returns the node name
|
// ClientVersion returns the node name
|
||||||
func (s *PublicWeb3API) ClientVersion() string {
|
func (s *PublicWeb3API) ClientVersion() string {
|
||||||
return s.stack.serverConfig.Name
|
return s.stack.Server().Name
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sha3 applies the ethereum sha3 implementation on the input.
|
// Sha3 applies the ethereum sha3 implementation on the input.
|
||||||
|
|
|
||||||
10
node/node.go
10
node/node.go
|
|
@ -56,7 +56,7 @@ type Node struct {
|
||||||
instanceDirLock storage.Storage // prevents concurrent use of instance directory
|
instanceDirLock storage.Storage // prevents concurrent use of instance directory
|
||||||
|
|
||||||
serverConfig p2p.Config
|
serverConfig p2p.Config
|
||||||
server p2p.Server // Currently running P2P networking layer
|
server *p2p.Server // Currently running P2P networking layer
|
||||||
|
|
||||||
serviceFuncs []ServiceConstructor // Service constructors (in dependency order)
|
serviceFuncs []ServiceConstructor // Service constructors (in dependency order)
|
||||||
services map[reflect.Type]Service // Currently running services
|
services map[reflect.Type]Service // Currently running services
|
||||||
|
|
@ -165,6 +165,8 @@ func (n *Node) Start() error {
|
||||||
if n.serverConfig.NodeDatabase == "" {
|
if n.serverConfig.NodeDatabase == "" {
|
||||||
n.serverConfig.NodeDatabase = n.config.NodeDB()
|
n.serverConfig.NodeDatabase = n.config.NodeDB()
|
||||||
}
|
}
|
||||||
|
running := &p2p.Server{Config: n.serverConfig}
|
||||||
|
log.Info("Starting peer-to-peer node", "instance", n.serverConfig.Name)
|
||||||
|
|
||||||
// Otherwise copy and specialize the P2P configuration
|
// Otherwise copy and specialize the P2P configuration
|
||||||
services := make(map[reflect.Type]Service)
|
services := make(map[reflect.Type]Service)
|
||||||
|
|
@ -192,10 +194,8 @@ func (n *Node) Start() error {
|
||||||
}
|
}
|
||||||
// Gather the protocols and start the freshly assembled P2P server
|
// Gather the protocols and start the freshly assembled P2P server
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
n.serverConfig.Protocols = append(n.serverConfig.Protocols, service.Protocols()...)
|
running.Protocols = append(running.Protocols, service.Protocols()...)
|
||||||
}
|
}
|
||||||
running := p2p.NewServer(n.serverConfig)
|
|
||||||
log.Info("Starting peer-to-peer node", "instance", n.serverConfig.Name)
|
|
||||||
if err := running.Start(); err != nil {
|
if err := running.Start(); err != nil {
|
||||||
if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] {
|
if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] {
|
||||||
return ErrDatadirUsed
|
return ErrDatadirUsed
|
||||||
|
|
@ -582,7 +582,7 @@ func (n *Node) RPCHandler() (*rpc.Server, error) {
|
||||||
// Server retrieves the currently running P2P network layer. This method is meant
|
// Server retrieves the currently running P2P network layer. This method is meant
|
||||||
// only to inspect fields of the currently running server, life cycle management
|
// only to inspect fields of the currently running server, life cycle management
|
||||||
// should be left to this Node entity.
|
// should be left to this Node entity.
|
||||||
func (n *Node) Server() p2p.Server {
|
func (n *Node) Server() *p2p.Server {
|
||||||
n.lock.RLock()
|
n.lock.RLock()
|
||||||
defer n.lock.RUnlock()
|
defer n.lock.RUnlock()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ type SampleService struct{}
|
||||||
|
|
||||||
func (s *SampleService) Protocols() []p2p.Protocol { return nil }
|
func (s *SampleService) Protocols() []p2p.Protocol { return nil }
|
||||||
func (s *SampleService) APIs() []rpc.API { return nil }
|
func (s *SampleService) APIs() []rpc.API { return nil }
|
||||||
func (s *SampleService) Start(p2p.Server) error { fmt.Println("Service starting..."); return nil }
|
func (s *SampleService) Start(*p2p.Server) error { fmt.Println("Service starting..."); return nil }
|
||||||
func (s *SampleService) Stop() error { fmt.Println("Service stopping..."); return nil }
|
func (s *SampleService) Stop() error { fmt.Println("Service stopping..."); return nil }
|
||||||
|
|
||||||
func ExampleService() {
|
func ExampleService() {
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ func TestServiceLifeCycle(t *testing.T) {
|
||||||
id := id // Closure for the constructor
|
id := id // Closure for the constructor
|
||||||
constructor := func(*ServiceContext) (Service, error) {
|
constructor := func(*ServiceContext) (Service, error) {
|
||||||
return &InstrumentedService{
|
return &InstrumentedService{
|
||||||
startHook: func(p2p.Server) { started[id] = true },
|
startHook: func(*p2p.Server) { started[id] = true },
|
||||||
stopHook: func() { stopped[id] = true },
|
stopHook: func() { stopped[id] = true },
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -200,7 +200,7 @@ func TestServiceRestarts(t *testing.T) {
|
||||||
running = false
|
running = false
|
||||||
|
|
||||||
return &InstrumentedService{
|
return &InstrumentedService{
|
||||||
startHook: func(p2p.Server) {
|
startHook: func(*p2p.Server) {
|
||||||
if running {
|
if running {
|
||||||
panic("already running")
|
panic("already running")
|
||||||
}
|
}
|
||||||
|
|
@ -250,7 +250,7 @@ func TestServiceConstructionAbortion(t *testing.T) {
|
||||||
id := id // Closure for the constructor
|
id := id // Closure for the constructor
|
||||||
constructor := func(*ServiceContext) (Service, error) {
|
constructor := func(*ServiceContext) (Service, error) {
|
||||||
return &InstrumentedService{
|
return &InstrumentedService{
|
||||||
startHook: func(p2p.Server) { started[id] = true },
|
startHook: func(*p2p.Server) { started[id] = true },
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
if err := stack.Register(maker(constructor)); err != nil {
|
if err := stack.Register(maker(constructor)); err != nil {
|
||||||
|
|
@ -299,7 +299,7 @@ func TestServiceStartupAbortion(t *testing.T) {
|
||||||
id := id // Closure for the constructor
|
id := id // Closure for the constructor
|
||||||
constructor := func(*ServiceContext) (Service, error) {
|
constructor := func(*ServiceContext) (Service, error) {
|
||||||
return &InstrumentedService{
|
return &InstrumentedService{
|
||||||
startHook: func(p2p.Server) { started[id] = true },
|
startHook: func(*p2p.Server) { started[id] = true },
|
||||||
stopHook: func() { stopped[id] = true },
|
stopHook: func() { stopped[id] = true },
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -352,7 +352,7 @@ func TestServiceTerminationGuarantee(t *testing.T) {
|
||||||
id := id // Closure for the constructor
|
id := id // Closure for the constructor
|
||||||
constructor := func(*ServiceContext) (Service, error) {
|
constructor := func(*ServiceContext) (Service, error) {
|
||||||
return &InstrumentedService{
|
return &InstrumentedService{
|
||||||
startHook: func(p2p.Server) { started[id] = true },
|
startHook: func(*p2p.Server) { started[id] = true },
|
||||||
stopHook: func() { stopped[id] = true },
|
stopHook: func() { stopped[id] = true },
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ type Service interface {
|
||||||
|
|
||||||
// Start is called after all services have been constructed and the networking
|
// Start is called after all services have been constructed and the networking
|
||||||
// layer was also initialized to spawn any goroutines required by the service.
|
// layer was also initialized to spawn any goroutines required by the service.
|
||||||
Start(server p2p.Server) error
|
Start(server *p2p.Server) error
|
||||||
|
|
||||||
// Stop terminates all goroutines belonging to the service, blocking until they
|
// Stop terminates all goroutines belonging to the service, blocking until they
|
||||||
// are all terminated.
|
// are all terminated.
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ type NoopService struct{}
|
||||||
|
|
||||||
func (s *NoopService) Protocols() []p2p.Protocol { return nil }
|
func (s *NoopService) Protocols() []p2p.Protocol { return nil }
|
||||||
func (s *NoopService) APIs() []rpc.API { return nil }
|
func (s *NoopService) APIs() []rpc.API { return nil }
|
||||||
func (s *NoopService) Start(p2p.Server) error { return nil }
|
func (s *NoopService) Start(*p2p.Server) error { return nil }
|
||||||
func (s *NoopService) Stop() error { return nil }
|
func (s *NoopService) Stop() error { return nil }
|
||||||
|
|
||||||
func NewNoopService(*ServiceContext) (Service, error) { return new(NoopService), nil }
|
func NewNoopService(*ServiceContext) (Service, error) { return new(NoopService), nil }
|
||||||
|
|
@ -57,7 +57,7 @@ type InstrumentedService struct {
|
||||||
stop error
|
stop error
|
||||||
|
|
||||||
protocolsHook func()
|
protocolsHook func()
|
||||||
startHook func(p2p.Server)
|
startHook func(*p2p.Server)
|
||||||
stopHook func()
|
stopHook func()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,7 +74,7 @@ func (s *InstrumentedService) APIs() []rpc.API {
|
||||||
return s.apis
|
return s.apis
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InstrumentedService) Start(server p2p.Server) error {
|
func (s *InstrumentedService) Start(server *p2p.Server) error {
|
||||||
if s.startHook != nil {
|
if s.startHook != nil {
|
||||||
s.startHook(server)
|
s.startHook(server)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
30
p2p/dial.go
30
p2p/dial.go
|
|
@ -47,6 +47,19 @@ const (
|
||||||
maxResolveDelay = time.Hour
|
maxResolveDelay = time.Hour
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type NodeDialer interface {
|
||||||
|
Dial(*discover.Node) (net.Conn, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TCPDialer struct {
|
||||||
|
*net.Dialer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TCPDialer) Dial(dest *discover.Node) (net.Conn, error) {
|
||||||
|
addr := &net.TCPAddr{IP: dest.IP, Port: int(dest.TCP)}
|
||||||
|
return t.Dialer.Dial("tcp", addr.String())
|
||||||
|
}
|
||||||
|
|
||||||
// dialstate schedules dials and discovery lookups.
|
// dialstate schedules dials and discovery lookups.
|
||||||
// it get's a chance to compute new tasks on every iteration
|
// it get's a chance to compute new tasks on every iteration
|
||||||
// of the main loop in server.run.
|
// of the main loop in server.run.
|
||||||
|
|
@ -84,7 +97,7 @@ type pastDial struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type task interface {
|
type task interface {
|
||||||
Do(*server)
|
Do(*Server)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A dialTask is generated for each node that is dialed. Its
|
// A dialTask is generated for each node that is dialed. Its
|
||||||
|
|
@ -267,7 +280,7 @@ func (s *dialstate) taskDone(t task, now time.Time) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *dialTask) Do(srv *server) {
|
func (t *dialTask) Do(srv *Server) {
|
||||||
if t.dest.Incomplete() {
|
if t.dest.Incomplete() {
|
||||||
if !t.resolve(srv) {
|
if !t.resolve(srv) {
|
||||||
return
|
return
|
||||||
|
|
@ -288,7 +301,7 @@ func (t *dialTask) Do(srv *server) {
|
||||||
// Resolve operations are throttled with backoff to avoid flooding the
|
// Resolve operations are throttled with backoff to avoid flooding the
|
||||||
// discovery network with useless queries for nodes that don't exist.
|
// discovery network with useless queries for nodes that don't exist.
|
||||||
// The backoff delay resets when the node is found.
|
// The backoff delay resets when the node is found.
|
||||||
func (t *dialTask) resolve(srv *server) bool {
|
func (t *dialTask) resolve(srv *Server) bool {
|
||||||
if srv.ntab == nil {
|
if srv.ntab == nil {
|
||||||
log.Debug("Can't resolve node", "id", t.dest.ID, "err", "discovery is disabled")
|
log.Debug("Can't resolve node", "id", t.dest.ID, "err", "discovery is disabled")
|
||||||
return false
|
return false
|
||||||
|
|
@ -317,15 +330,14 @@ func (t *dialTask) resolve(srv *server) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// dial performs the actual connection attempt.
|
// dial performs the actual connection attempt.
|
||||||
func (t *dialTask) dial(srv *server, dest *discover.Node) bool {
|
func (t *dialTask) dial(srv *Server, dest *discover.Node) bool {
|
||||||
addr := &net.TCPAddr{IP: dest.IP, Port: int(dest.TCP)}
|
fd, err := srv.Dialer.Dial(dest)
|
||||||
fd, err := srv.Dialer.Dial("tcp", addr.String())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Trace("Dial error", "task", t, "err", err)
|
log.Trace("Dial error", "task", t, "err", err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
mfd := newMeteredConn(fd, false)
|
mfd := newMeteredConn(fd, false)
|
||||||
srv.setupConn(mfd, t.flags, dest)
|
srv.SetupConn(mfd, t.flags, dest)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -333,7 +345,7 @@ func (t *dialTask) String() string {
|
||||||
return fmt.Sprintf("%v %x %v:%d", t.flags, t.dest.ID[:8], t.dest.IP, t.dest.TCP)
|
return fmt.Sprintf("%v %x %v:%d", t.flags, t.dest.ID[:8], t.dest.IP, t.dest.TCP)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *discoverTask) Do(srv *server) {
|
func (t *discoverTask) Do(srv *Server) {
|
||||||
// newTasks generates a lookup task whenever dynamic dials are
|
// newTasks generates a lookup task whenever dynamic dials are
|
||||||
// necessary. Lookups need to take some time, otherwise the
|
// necessary. Lookups need to take some time, otherwise the
|
||||||
// event loop spins too fast.
|
// event loop spins too fast.
|
||||||
|
|
@ -355,7 +367,7 @@ func (t *discoverTask) String() string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t waitExpireTask) Do(*server) {
|
func (t waitExpireTask) Do(*Server) {
|
||||||
time.Sleep(t.Duration)
|
time.Sleep(t.Duration)
|
||||||
}
|
}
|
||||||
func (t waitExpireTask) String() string {
|
func (t waitExpireTask) String() string {
|
||||||
|
|
|
||||||
|
|
@ -597,8 +597,8 @@ func TestDialResolve(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now run the task, it should resolve the ID once.
|
// Now run the task, it should resolve the ID once.
|
||||||
config := Config{Dialer: &net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}}
|
config := Config{Dialer: TCPDialer{&net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}}}
|
||||||
srv := &server{ntab: table, Config: config}
|
srv := &Server{ntab: table, Config: config}
|
||||||
tasks[0].Do(srv)
|
tasks[0].Do(srv)
|
||||||
if !reflect.DeepEqual(table.resolveCalls, []discover.NodeID{dest.ID}) {
|
if !reflect.DeepEqual(table.resolveCalls, []discover.NodeID{dest.ID}) {
|
||||||
t.Fatalf("wrong resolve calls, got %v", table.resolveCalls)
|
t.Fatalf("wrong resolve calls, got %v", table.resolveCalls)
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,13 @@ Standard protocol supports:
|
||||||
package protocols
|
package protocols
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// error codes used by this protocol scheme
|
// error codes used by this protocol scheme
|
||||||
|
|
@ -45,23 +46,21 @@ const (
|
||||||
ErrWrite
|
ErrWrite
|
||||||
ErrInvalidMsgCode
|
ErrInvalidMsgCode
|
||||||
ErrInvalidMsgType
|
ErrInvalidMsgType
|
||||||
ErrLocalHandshake
|
ErrHandshake
|
||||||
ErrRemoteHandshake
|
|
||||||
ErrNoHandler
|
ErrNoHandler
|
||||||
ErrHandler
|
ErrHandler
|
||||||
)
|
)
|
||||||
|
|
||||||
// error description strings associated with the codes
|
// error description strings associated with the codes
|
||||||
var errorToString = map[int]string{
|
var errorToString = map[int]string{
|
||||||
ErrMsgTooLong: "Message too long",
|
ErrMsgTooLong: "Message too long",
|
||||||
ErrDecode: "Invalid message (RLP error)",
|
ErrDecode: "Invalid message (RLP error)",
|
||||||
ErrWrite: "Error sending message",
|
ErrWrite: "Error sending message",
|
||||||
ErrInvalidMsgCode: "Invalid message code",
|
ErrInvalidMsgCode: "Invalid message code",
|
||||||
ErrInvalidMsgType: "Invalid message type",
|
ErrInvalidMsgType: "Invalid message type",
|
||||||
ErrLocalHandshake: "Local handshake error",
|
ErrHandshake: "Handshake error",
|
||||||
ErrRemoteHandshake: "Remote handshake error",
|
ErrNoHandler: "No handler registered error",
|
||||||
ErrNoHandler: "No handler registered error",
|
ErrHandler: "Message handler error",
|
||||||
ErrHandler: "Message handler error",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -110,93 +109,74 @@ func errorf(code int, format string, params ...interface{}) *Error {
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
// implements the code table spec
|
// Spec is a protocol specification including its name and version as well as
|
||||||
// listing the message codes and types etc
|
// the types of messages which are exchanged
|
||||||
// and further metadata about the protocol
|
type Spec struct {
|
||||||
type CodeMap struct {
|
// Name is the name of the protocol, often a three-letter word
|
||||||
Name string // name of the protocol
|
Name string
|
||||||
Version uint // version
|
|
||||||
MaxMsgSize int // max length of message payload size
|
// Version is the version number of the protocol
|
||||||
codes []reflect.Type // index of codes to msg types - to create zero values
|
Version uint
|
||||||
messages map[reflect.Type]uint64 // index of types to codes, for sending by type
|
|
||||||
|
// MaxMsgSize is the maximum accepted length of the message payload
|
||||||
|
MaxMsgSize uint32
|
||||||
|
|
||||||
|
// Messages is a list of message types which this protocol uses, with
|
||||||
|
// each message type being sent with its array index as the code (so
|
||||||
|
// [&foo{}, &bar{}, &baz{}] would send foo, bar and baz with codes
|
||||||
|
// 0, 1 and 2 respectively)
|
||||||
|
Messages []interface{}
|
||||||
|
|
||||||
|
initOnce sync.Once
|
||||||
|
codes map[reflect.Type]uint64
|
||||||
|
types map[uint64]reflect.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CodeMap) GetInterface(code uint64) (interface{}, bool) {
|
func (s *Spec) init() {
|
||||||
if int(code) > len(self.codes)-1 {
|
s.initOnce.Do(func() {
|
||||||
|
s.codes = make(map[reflect.Type]uint64, len(s.Messages))
|
||||||
|
s.types = make(map[uint64]reflect.Type, len(s.Messages))
|
||||||
|
for i, msg := range s.Messages {
|
||||||
|
code := uint64(i)
|
||||||
|
typ := reflect.TypeOf(msg)
|
||||||
|
if typ.Kind() == reflect.Ptr {
|
||||||
|
typ = typ.Elem()
|
||||||
|
}
|
||||||
|
s.codes[typ] = code
|
||||||
|
s.types[code] = typ
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Spec) Length() uint64 {
|
||||||
|
return uint64(len(s.Messages))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Spec) GetCode(msg interface{}) (uint64, bool) {
|
||||||
|
s.init()
|
||||||
|
typ := reflect.TypeOf(msg)
|
||||||
|
if typ.Kind() == reflect.Ptr {
|
||||||
|
typ = typ.Elem()
|
||||||
|
}
|
||||||
|
code, ok := s.codes[typ]
|
||||||
|
return code, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Spec) NewMsg(code uint64) (interface{}, bool) {
|
||||||
|
s.init()
|
||||||
|
typ, ok := s.types[code]
|
||||||
|
if !ok {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
typ := self.codes[code]
|
return reflect.New(typ).Interface(), true
|
||||||
val := reflect.New(typ)
|
|
||||||
return val.Interface(), true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *CodeMap) GetCode(msg interface{}) (uint64, bool) {
|
|
||||||
code, found := self.messages[reflect.TypeOf(msg)]
|
|
||||||
return code, found
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCodeMap(name string, version uint, maxMsgSize int, msgs ...interface{}) *CodeMap {
|
|
||||||
self := &CodeMap{
|
|
||||||
Name: name,
|
|
||||||
Version: version,
|
|
||||||
MaxMsgSize: maxMsgSize,
|
|
||||||
messages: make(map[reflect.Type]uint64),
|
|
||||||
}
|
|
||||||
self.Register(msgs...)
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *CodeMap) Length() uint64 {
|
|
||||||
return uint64(len(self.codes))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *CodeMap) Register(msgs ...interface{}) {
|
|
||||||
code := uint64(len(self.codes))
|
|
||||||
for _, msg := range msgs {
|
|
||||||
typ := reflect.TypeOf(msg)
|
|
||||||
_, found := self.messages[typ]
|
|
||||||
if found {
|
|
||||||
// ignore duplicates
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// next code assigned to message type typ
|
|
||||||
self.messages[typ] = code
|
|
||||||
self.codes = append(self.codes, typ)
|
|
||||||
code++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProtocol(protocolname string, protocolversion uint, run func(*Peer) error, ct *CodeMap, peerInfo func(id discover.NodeID) interface{}, nodeInfo func() interface{}) *p2p.Protocol {
|
|
||||||
|
|
||||||
// PeerInfo is an optional helper method to retrieve protocol specific metadata
|
|
||||||
// about a certain peer in the network. If an info retrieval function is set,
|
|
||||||
// but returns nil, it is assumed that the protocol handshake is still running.
|
|
||||||
r := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
|
||||||
return run(NewPeer(p, ct, rw))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return &p2p.Protocol{
|
|
||||||
Name: protocolname,
|
|
||||||
Version: protocolversion,
|
|
||||||
Length: ct.Length(),
|
|
||||||
Run: r,
|
|
||||||
PeerInfo: peerInfo,
|
|
||||||
NodeInfo: nodeInfo,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Disconnect struct {
|
|
||||||
err error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Peer represents a remote peer or protocol instance that is running on a peer connection with
|
// A Peer represents a remote peer or protocol instance that is running on a peer connection with
|
||||||
// a remote peer
|
// a remote peer
|
||||||
type Peer struct {
|
type Peer struct {
|
||||||
ct *CodeMap // CodeMap for the protocol
|
*p2p.Peer // the p2p.Peer object representing the remote
|
||||||
*p2p.Peer // the p2p.Peer object representing the remote
|
rw p2p.MsgReadWriter // p2p.MsgReadWriter to send messages to and read messages from
|
||||||
rw p2p.MsgReadWriter // p2p.MsgReadWriter to send messages to and read messages from
|
spec *Spec
|
||||||
handlers map[reflect.Type][]func(interface{}) error // message type -> message handler callback(s) map
|
|
||||||
Errc chan error
|
Errc chan error
|
||||||
wErrc chan error // write error channel
|
wErrc chan error // write error channel
|
||||||
}
|
}
|
||||||
|
|
@ -205,55 +185,28 @@ type Peer struct {
|
||||||
// this constructor is called by the p2p.Protocol#Run function
|
// this constructor is called by the p2p.Protocol#Run function
|
||||||
// the first two arguments are comming the arguments passed to p2p.Protocol.Run function
|
// the first two arguments are comming the arguments passed to p2p.Protocol.Run function
|
||||||
// the third argument is the CodeMap describing the protocol messages and options
|
// the third argument is the CodeMap describing the protocol messages and options
|
||||||
func NewPeer(p *p2p.Peer, ct *CodeMap, rw p2p.MsgReadWriter) *Peer {
|
func NewPeer(p *p2p.Peer, rw p2p.MsgReadWriter, spec *Spec) *Peer {
|
||||||
return &Peer{
|
return &Peer{
|
||||||
ct: ct,
|
Peer: p,
|
||||||
Peer: p,
|
rw: rw,
|
||||||
rw: rw,
|
spec: spec,
|
||||||
Errc: make(chan error),
|
Errc: make(chan error),
|
||||||
wErrc: make(chan error),
|
wErrc: make(chan error),
|
||||||
handlers: make(map[reflect.Type][]func(interface{}) error),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register is called on the peer typically within the constructor of service instances running on peer connections
|
|
||||||
// These constructors are called by the p2p.Protocol#Run function
|
|
||||||
// It ties handler callbackss for specific message types
|
|
||||||
// A message type can have several handlers registered by the same or different protocol services
|
|
||||||
// Register is meant to be called once, deregistering is not currently supported therefore
|
|
||||||
// handlers are assumed to be static across handshake renegotiations
|
|
||||||
// i.e., a service instance either handles a message or not (irrespective of the handshake)
|
|
||||||
// it panics if the message type is not defined in the CodeMap
|
|
||||||
func (self *Peer) Register(msg interface{}, handler func(interface{}) error) uint64 {
|
|
||||||
typ := reflect.TypeOf(msg)
|
|
||||||
code, found := self.ct.messages[typ]
|
|
||||||
if !found {
|
|
||||||
panic(fmt.Sprintf("message type '%v' unknown ", typ))
|
|
||||||
}
|
|
||||||
log.Trace(fmt.Sprintf("register handle for %v", typ))
|
|
||||||
self.handlers[typ] = append(self.handlers[typ], handler)
|
|
||||||
return code
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run starts the forever loop that handles incoming messages
|
// Run starts the forever loop that handles incoming messages
|
||||||
// called within the p2p.Protocol#Run function
|
// called within the p2p.Protocol#Run function
|
||||||
func (self *Peer) Run() error {
|
func (self *Peer) Run(handler func(msg interface{}) error) error {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
_, err := self.handleIncoming()
|
if err := self.handleIncoming(handler); err != nil {
|
||||||
if err != nil {
|
|
||||||
self.Errc <- err
|
self.Errc <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
err := <-self.Errc
|
return <-self.Errc
|
||||||
d := &Disconnect{err}
|
|
||||||
for _, f := range self.handlers[reflect.TypeOf(d)] {
|
|
||||||
log.Trace(fmt.Sprintf("disconnect hook for %v", d))
|
|
||||||
f(err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop disconnects a peer.
|
// Drop disconnects a peer.
|
||||||
|
|
@ -271,20 +224,12 @@ func (self *Peer) Drop(err error) {
|
||||||
// this low level call will be wrapped by libraries providing routed or broadcast sends
|
// this low level call will be wrapped by libraries providing routed or broadcast sends
|
||||||
// but often just used to forward and push messages to directly connected peers
|
// but often just used to forward and push messages to directly connected peers
|
||||||
func (self *Peer) Send(msg interface{}) error {
|
func (self *Peer) Send(msg interface{}) error {
|
||||||
code, found := self.ct.GetCode(msg)
|
code, found := self.spec.GetCode(msg)
|
||||||
if !found {
|
if !found {
|
||||||
return errorf(ErrInvalidMsgType, "%v", code)
|
return errorf(ErrInvalidMsgType, "%v", code)
|
||||||
}
|
}
|
||||||
log.Trace(fmt.Sprintf("=> msg #%d TO %v : %v", code, self.ID(), msg))
|
log.Trace(fmt.Sprintf("=> msg #%d TO %v : %v", code, self.ID(), msg))
|
||||||
return p2p.Send(self.rw, uint64(code), msg)
|
return p2p.Send(self.rw, code, msg)
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Peer) DisconnectHook(f func(error)) {
|
|
||||||
typ := reflect.TypeOf(&Disconnect{})
|
|
||||||
self.handlers[typ] = append(self.handlers[typ], func(e interface{}) error {
|
|
||||||
f(e.(error))
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleIncoming(code)
|
// handleIncoming(code)
|
||||||
|
|
@ -292,79 +237,72 @@ func (self *Peer) DisconnectHook(f func(error)) {
|
||||||
// if this returns an error the loop returns and the peer is disconnected with the error
|
// if this returns an error the loop returns and the peer is disconnected with the error
|
||||||
// checks message size, out-of-range message codes, handles decoding with reflection,
|
// checks message size, out-of-range message codes, handles decoding with reflection,
|
||||||
// call handlers as callback onside
|
// call handlers as callback onside
|
||||||
func (self *Peer) handleIncoming() (interface{}, error) {
|
func (self *Peer) handleIncoming(handle func(msg interface{}) error) error {
|
||||||
msg, err := self.rw.ReadMsg()
|
msg, err := self.rw.ReadMsg()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
log.Trace(fmt.Sprintf("<= %v", msg))
|
log.Trace(fmt.Sprintf("<= %v", msg))
|
||||||
// make sure that the payload has been fully consumed
|
// make sure that the payload has been fully consumed
|
||||||
defer msg.Discard()
|
defer msg.Discard()
|
||||||
|
|
||||||
if msg.Size > uint32(self.ct.MaxMsgSize) {
|
if msg.Size > self.spec.MaxMsgSize {
|
||||||
return nil, errorf(ErrMsgTooLong, "%v > %v", msg.Size, self.ct.MaxMsgSize)
|
return errorf(ErrMsgTooLong, "%v > %v", msg.Size, self.spec.MaxMsgSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the message code is correct
|
val, ok := self.spec.NewMsg(msg.Code)
|
||||||
maxMsgCode := uint(len(self.ct.messages))
|
if !ok {
|
||||||
if msg.Code >= uint64(maxMsgCode) {
|
return errorf(ErrInvalidMsgCode, "%v", msg.Code)
|
||||||
return nil, errorf(ErrInvalidMsgCode, "%v (>=%v)", msg.Code, maxMsgCode)
|
|
||||||
}
|
}
|
||||||
|
if err := msg.Decode(val); err != nil {
|
||||||
// it is safe to be unsafe here
|
return errorf(ErrDecode, "<= %v: %v", msg, err)
|
||||||
typ := self.ct.codes[msg.Code]
|
|
||||||
val := reflect.New(typ)
|
|
||||||
req := val.Elem()
|
|
||||||
req.Set(reflect.Zero(typ))
|
|
||||||
if err := msg.Decode(val.Interface()); err != nil {
|
|
||||||
return nil, errorf(ErrDecode, "<= %v: %v", msg, err)
|
|
||||||
}
|
}
|
||||||
log.Trace(fmt.Sprintf("<= %v FROM %v %v %v", msg, self.ID(), req, typ))
|
log.Trace(fmt.Sprintf("<= %v FROM %v %T %v", msg, self.ID(), val, val))
|
||||||
|
|
||||||
// call the registered handler callbacks
|
// call the registered handler callbacks
|
||||||
// a registered callback take the decoded message as argument as an interface
|
// a registered callback take the decoded message as argument as an interface
|
||||||
// which the handler is supposed to cast to the appropriate type
|
// which the handler is supposed to cast to the appropriate type
|
||||||
// it is entirely safe not to check the cast in the handler since the handler is
|
// it is entirely safe not to check the cast in the handler since the handler is
|
||||||
// chosen based on the proper type in the first place
|
// chosen based on the proper type in the first place
|
||||||
handlers := self.handlers[typ]
|
if err := handle(val); err != nil {
|
||||||
if len(handlers) == 0 {
|
return errorf(ErrHandler, "(msg code %v): %v", msg.Code, err)
|
||||||
log.Trace(fmt.Sprintf("no handler (msg code %v)", msg.Code))
|
|
||||||
// return nil, errorf(ErrNoHandler, "(msg code %v)", msg.Code)
|
|
||||||
} else {
|
|
||||||
for i, f := range handlers {
|
|
||||||
log.Trace(fmt.Sprintf("handler %v for %v", i, typ))
|
|
||||||
err = f(req.Interface())
|
|
||||||
if err != nil {
|
|
||||||
return nil, errorf(ErrHandler, "(msg code %v): %v", msg.Code, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return req.Interface(), nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handshake initiates a handshake on the peer connection
|
// Handshake initiates a handshake on the peer connection
|
||||||
// * the argument is the local handshake to be sent to the remote peer
|
// * the argument is the local handshake to be sent to the remote peer
|
||||||
// * expects a remote handshake back of the same type
|
// * expects a remote handshake back of the same type
|
||||||
// returns the remote hs and an error
|
// returns the remote hs and an error
|
||||||
func (self *Peer) Handshake(hs interface{}) (interface{}, error) {
|
func (self *Peer) Handshake(ctx context.Context, hs interface{}) (interface{}, error) {
|
||||||
typ := reflect.TypeOf(hs)
|
if _, ok := self.spec.GetCode(hs); !ok {
|
||||||
_, found := self.ct.messages[typ]
|
return nil, errorf(ErrHandshake, "unknown handshake message type: %T", hs)
|
||||||
if !found {
|
|
||||||
return nil, errorf(ErrLocalHandshake, "unknown handshake message type: %v", typ)
|
|
||||||
}
|
}
|
||||||
errc := make(chan error)
|
errc := make(chan error, 2)
|
||||||
go func() {
|
go func() {
|
||||||
err := self.Send(hs)
|
if err := self.Send(hs); err != nil {
|
||||||
if err != nil {
|
errc <- errorf(ErrHandshake, "cannot send: %v", err)
|
||||||
err = errorf(ErrLocalHandshake, "cannot send: %v", err)
|
|
||||||
}
|
}
|
||||||
errc <- err
|
|
||||||
}()
|
}()
|
||||||
// receiving and validating remote handshake, expect code
|
hsc := make(chan interface{})
|
||||||
rhs, err := self.handleIncoming()
|
go func() {
|
||||||
if err != nil {
|
var rhs interface{}
|
||||||
return nil, errorf(ErrRemoteHandshake, "'%v': %v", self.ct.Name, err)
|
err := self.handleIncoming(func(msg interface{}) error {
|
||||||
|
rhs = msg
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
errc <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hsc <- rhs
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case rhs := <-hsc:
|
||||||
|
return rhs, nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case err := <-errc:
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
err = <-errc
|
|
||||||
return rhs, err
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package protocols
|
package protocols
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -13,7 +15,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
log.Root().SetHandler(log.LvlFilterHandler(log.LvlError, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||||
}
|
}
|
||||||
|
|
||||||
// handshake message type
|
// handshake message type
|
||||||
|
|
@ -55,28 +57,26 @@ const networkId = "420"
|
||||||
// newProtocol sets up a protocol
|
// newProtocol sets up a protocol
|
||||||
// the run function here demonstrates a typical protocol using peerPool, handshake
|
// the run function here demonstrates a typical protocol using peerPool, handshake
|
||||||
// and messages registered to handlers
|
// and messages registered to handlers
|
||||||
func newProtocol(pp *p2ptest.TestPeerPool) adapters.RunProtocol {
|
func newProtocol(pp *p2ptest.TestPeerPool) func(*p2p.Peer, p2p.MsgReadWriter) error {
|
||||||
ct := NewCodeMap("test", 42, 1024, &protoHandshake{}, &hs0{}, &kill{}, &drop{})
|
spec := &Spec{
|
||||||
|
Name: "test",
|
||||||
|
Version: 42,
|
||||||
|
MaxMsgSize: 10 * 1024,
|
||||||
|
Messages: []interface{}{
|
||||||
|
protoHandshake{},
|
||||||
|
hs0{},
|
||||||
|
kill{},
|
||||||
|
drop{},
|
||||||
|
},
|
||||||
|
}
|
||||||
return func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
return func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||||
peer := NewPeer(p, ct, rw)
|
peer := NewPeer(p, rw, spec)
|
||||||
|
|
||||||
// demonstrates use of peerPool, killing another peer connection as a response to a message
|
|
||||||
peer.Register(&kill{}, func(msg interface{}) error {
|
|
||||||
id := msg.(*kill).C
|
|
||||||
pp.Get(id).Drop(fmt.Errorf("killed"))
|
|
||||||
log.Trace(fmt.Sprintf("id %v killed", id))
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// for testing we can trigger self induced disconnect upon receiving drop message
|
|
||||||
peer.Register(&drop{}, func(msg interface{}) error {
|
|
||||||
log.Trace("dropped")
|
|
||||||
return fmt.Errorf("dropped")
|
|
||||||
})
|
|
||||||
|
|
||||||
// initiate one-off protohandshake and check validity
|
// initiate one-off protohandshake and check validity
|
||||||
phs := &protoHandshake{ct.Version, networkId}
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||||
hs, err := peer.Handshake(phs)
|
defer cancel()
|
||||||
|
phs := &protoHandshake{42, networkId}
|
||||||
|
hs, err := peer.Handshake(ctx, phs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +88,7 @@ func newProtocol(pp *p2ptest.TestPeerPool) adapters.RunProtocol {
|
||||||
|
|
||||||
lhs := &hs0{42}
|
lhs := &hs0{42}
|
||||||
// module handshake demonstrating a simple repeatable exchange of same-type message
|
// module handshake demonstrating a simple repeatable exchange of same-type message
|
||||||
hs, err = peer.Handshake(lhs)
|
hs, err = peer.Handshake(ctx, lhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -97,19 +97,40 @@ func newProtocol(pp *p2ptest.TestPeerPool) adapters.RunProtocol {
|
||||||
return fmt.Errorf("handshake mismatch remote %v > local %v", rmhs.C, lhs.C)
|
return fmt.Errorf("handshake mismatch remote %v > local %v", rmhs.C, lhs.C)
|
||||||
}
|
}
|
||||||
|
|
||||||
peer.Register(lhs, func(msg interface{}) error {
|
handle := func(msg interface{}) error {
|
||||||
rhs := msg.(*hs0)
|
switch msg := msg.(type) {
|
||||||
if rhs.C > lhs.C {
|
|
||||||
return fmt.Errorf("handshake mismatch remote %v > local %v", rhs.C, lhs.C)
|
case *protoHandshake:
|
||||||
|
return errors.New("duplicate handshake")
|
||||||
|
|
||||||
|
case *hs0:
|
||||||
|
rhs := msg
|
||||||
|
if rhs.C > lhs.C {
|
||||||
|
return fmt.Errorf("handshake mismatch remote %v > local %v", rhs.C, lhs.C)
|
||||||
|
}
|
||||||
|
lhs.C += rhs.C
|
||||||
|
return peer.Send(lhs)
|
||||||
|
|
||||||
|
case *kill:
|
||||||
|
// demonstrates use of peerPool, killing another peer connection as a response to a message
|
||||||
|
id := msg.C
|
||||||
|
pp.Get(id).Drop(errors.New("killed"))
|
||||||
|
log.Trace(fmt.Sprintf("id %v killed", id))
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *drop:
|
||||||
|
// for testing we can trigger self induced disconnect upon receiving drop message
|
||||||
|
return errors.New("dropped")
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown message type: %T", msg)
|
||||||
}
|
}
|
||||||
lhs.C += rhs.C
|
}
|
||||||
return peer.Send(lhs)
|
|
||||||
})
|
|
||||||
|
|
||||||
log.Trace(fmt.Sprintf("adding peer %v", peer))
|
log.Trace(fmt.Sprintf("adding peer %v", peer))
|
||||||
pp.Add(peer)
|
pp.Add(peer)
|
||||||
defer pp.Remove(peer)
|
defer pp.Remove(peer)
|
||||||
err = peer.Run()
|
err = peer.Run(handle)
|
||||||
log.Trace(fmt.Sprintf("peer %v protocol quitting: %v", peer, err))
|
log.Trace(fmt.Sprintf("peer %v protocol quitting: %v", peer, err))
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ type Config struct {
|
||||||
|
|
||||||
// If Dialer is set to a non-nil value, the given Dialer
|
// If Dialer is set to a non-nil value, the given Dialer
|
||||||
// is used to dial outbound peer connections.
|
// is used to dial outbound peer connections.
|
||||||
Dialer *net.Dialer `toml:"-"`
|
Dialer NodeDialer `toml:"-"`
|
||||||
|
|
||||||
// If NoDial is true, the server will not dial any peers.
|
// If NoDial is true, the server will not dial any peers.
|
||||||
NoDial bool `toml:",omitempty"`
|
NoDial bool `toml:",omitempty"`
|
||||||
|
|
@ -141,23 +141,8 @@ type Config struct {
|
||||||
EnableMsgEvents bool
|
EnableMsgEvents bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Server interface {
|
|
||||||
Start() error
|
|
||||||
Stop() error
|
|
||||||
AddPeer(node *discover.Node)
|
|
||||||
RemovePeer(node *discover.Node)
|
|
||||||
SubscribeEvents(ch chan *PeerEvent) event.Subscription
|
|
||||||
PeerCount() int
|
|
||||||
NodeInfo() *NodeInfo
|
|
||||||
PeersInfo() []*PeerInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewServer(conf Config) Server {
|
|
||||||
return &server{Config: conf}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server manages all peer connections.
|
// Server manages all peer connections.
|
||||||
type server struct {
|
type Server struct {
|
||||||
// Config fields may not be modified while the server is running.
|
// Config fields may not be modified while the server is running.
|
||||||
Config
|
Config
|
||||||
|
|
||||||
|
|
@ -266,7 +251,7 @@ func (c *conn) is(f connFlag) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Peers returns all connected peers.
|
// Peers returns all connected peers.
|
||||||
func (srv *server) Peers() []*Peer {
|
func (srv *Server) Peers() []*Peer {
|
||||||
var ps []*Peer
|
var ps []*Peer
|
||||||
select {
|
select {
|
||||||
// Note: We'd love to put this function into a variable but
|
// Note: We'd love to put this function into a variable but
|
||||||
|
|
@ -284,7 +269,7 @@ func (srv *server) Peers() []*Peer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeerCount returns the number of connected peers.
|
// PeerCount returns the number of connected peers.
|
||||||
func (srv *server) PeerCount() int {
|
func (srv *Server) PeerCount() int {
|
||||||
var count int
|
var count int
|
||||||
select {
|
select {
|
||||||
case srv.peerOp <- func(ps map[discover.NodeID]*Peer) { count = len(ps) }:
|
case srv.peerOp <- func(ps map[discover.NodeID]*Peer) { count = len(ps) }:
|
||||||
|
|
@ -297,7 +282,7 @@ func (srv *server) PeerCount() int {
|
||||||
// AddPeer connects to the given node and maintains the connection until the
|
// AddPeer connects to the given node and maintains the connection until the
|
||||||
// server is shut down. If the connection fails for any reason, the server will
|
// server is shut down. If the connection fails for any reason, the server will
|
||||||
// attempt to reconnect the peer.
|
// attempt to reconnect the peer.
|
||||||
func (srv *server) AddPeer(node *discover.Node) {
|
func (srv *Server) AddPeer(node *discover.Node) {
|
||||||
select {
|
select {
|
||||||
case srv.addstatic <- node:
|
case srv.addstatic <- node:
|
||||||
case <-srv.quit:
|
case <-srv.quit:
|
||||||
|
|
@ -305,7 +290,7 @@ func (srv *server) AddPeer(node *discover.Node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemovePeer disconnects from the given node
|
// RemovePeer disconnects from the given node
|
||||||
func (srv *server) RemovePeer(node *discover.Node) {
|
func (srv *Server) RemovePeer(node *discover.Node) {
|
||||||
select {
|
select {
|
||||||
case srv.removestatic <- node:
|
case srv.removestatic <- node:
|
||||||
case <-srv.quit:
|
case <-srv.quit:
|
||||||
|
|
@ -313,12 +298,12 @@ func (srv *server) RemovePeer(node *discover.Node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubscribePeers subscribes the given channel to peer events
|
// SubscribePeers subscribes the given channel to peer events
|
||||||
func (srv *server) SubscribeEvents(ch chan *PeerEvent) event.Subscription {
|
func (srv *Server) SubscribeEvents(ch chan *PeerEvent) event.Subscription {
|
||||||
return srv.peerFeed.Subscribe(ch)
|
return srv.peerFeed.Subscribe(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Self returns the local node's endpoint information.
|
// Self returns the local node's endpoint information.
|
||||||
func (srv *server) Self() *discover.Node {
|
func (srv *Server) Self() *discover.Node {
|
||||||
srv.lock.Lock()
|
srv.lock.Lock()
|
||||||
defer srv.lock.Unlock()
|
defer srv.lock.Unlock()
|
||||||
|
|
||||||
|
|
@ -328,7 +313,7 @@ func (srv *server) Self() *discover.Node {
|
||||||
return srv.makeSelf(srv.listener, srv.ntab)
|
return srv.makeSelf(srv.listener, srv.ntab)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *server) makeSelf(listener net.Listener, ntab discoverTable) *discover.Node {
|
func (srv *Server) makeSelf(listener net.Listener, ntab discoverTable) *discover.Node {
|
||||||
// If the server's not running, return an empty node.
|
// If the server's not running, return an empty node.
|
||||||
// If the node is running but discovery is off, manually assemble the node infos.
|
// If the node is running but discovery is off, manually assemble the node infos.
|
||||||
if ntab == nil {
|
if ntab == nil {
|
||||||
|
|
@ -350,7 +335,7 @@ func (srv *server) makeSelf(listener net.Listener, ntab discoverTable) *discover
|
||||||
|
|
||||||
// Stop terminates the server and all active peer connections.
|
// Stop terminates the server and all active peer connections.
|
||||||
// It blocks until all active connections have been closed.
|
// It blocks until all active connections have been closed.
|
||||||
func (srv *server) Stop() error {
|
func (srv *Server) Stop() error {
|
||||||
srv.lock.Lock()
|
srv.lock.Lock()
|
||||||
defer srv.lock.Unlock()
|
defer srv.lock.Unlock()
|
||||||
if !srv.running {
|
if !srv.running {
|
||||||
|
|
@ -368,7 +353,7 @@ func (srv *server) Stop() error {
|
||||||
|
|
||||||
// Start starts running the server.
|
// Start starts running the server.
|
||||||
// Servers can not be re-used after stopping.
|
// Servers can not be re-used after stopping.
|
||||||
func (srv *server) Start() (err error) {
|
func (srv *Server) Start() (err error) {
|
||||||
srv.lock.Lock()
|
srv.lock.Lock()
|
||||||
defer srv.lock.Unlock()
|
defer srv.lock.Unlock()
|
||||||
if srv.running {
|
if srv.running {
|
||||||
|
|
@ -385,7 +370,7 @@ func (srv *server) Start() (err error) {
|
||||||
srv.newTransport = newRLPX
|
srv.newTransport = newRLPX
|
||||||
}
|
}
|
||||||
if srv.Dialer == nil {
|
if srv.Dialer == nil {
|
||||||
srv.Dialer = &net.Dialer{Timeout: defaultDialTimeout}
|
srv.Dialer = TCPDialer{&net.Dialer{Timeout: defaultDialTimeout}}
|
||||||
}
|
}
|
||||||
srv.quit = make(chan struct{})
|
srv.quit = make(chan struct{})
|
||||||
srv.addpeer = make(chan *conn)
|
srv.addpeer = make(chan *conn)
|
||||||
|
|
@ -446,7 +431,7 @@ func (srv *server) Start() (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *server) startListening() error {
|
func (srv *Server) startListening() error {
|
||||||
// Launch the TCP listener.
|
// Launch the TCP listener.
|
||||||
listener, err := net.Listen("tcp", srv.ListenAddr)
|
listener, err := net.Listen("tcp", srv.ListenAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -475,7 +460,7 @@ type dialer interface {
|
||||||
removeStatic(*discover.Node)
|
removeStatic(*discover.Node)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *server) run(dialstate dialer) {
|
func (srv *Server) run(dialstate dialer) {
|
||||||
defer srv.loopWG.Done()
|
defer srv.loopWG.Done()
|
||||||
var (
|
var (
|
||||||
peers = make(map[discover.NodeID]*Peer)
|
peers = make(map[discover.NodeID]*Peer)
|
||||||
|
|
@ -616,7 +601,7 @@ running:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *server) protoHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn) error {
|
func (srv *Server) protoHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn) error {
|
||||||
// Drop connections with no matching protocols.
|
// Drop connections with no matching protocols.
|
||||||
if len(srv.Protocols) > 0 && countMatchingProtocols(srv.Protocols, c.caps) == 0 {
|
if len(srv.Protocols) > 0 && countMatchingProtocols(srv.Protocols, c.caps) == 0 {
|
||||||
return DiscUselessPeer
|
return DiscUselessPeer
|
||||||
|
|
@ -626,7 +611,7 @@ func (srv *server) protoHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn
|
||||||
return srv.encHandshakeChecks(peers, c)
|
return srv.encHandshakeChecks(peers, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *server) encHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn) error {
|
func (srv *Server) encHandshakeChecks(peers map[discover.NodeID]*Peer, c *conn) error {
|
||||||
switch {
|
switch {
|
||||||
case !c.is(trustedConn|staticDialedConn) && len(peers) >= srv.MaxPeers:
|
case !c.is(trustedConn|staticDialedConn) && len(peers) >= srv.MaxPeers:
|
||||||
return DiscTooManyPeers
|
return DiscTooManyPeers
|
||||||
|
|
@ -645,7 +630,7 @@ type tempError interface {
|
||||||
|
|
||||||
// listenLoop runs in its own goroutine and accepts
|
// listenLoop runs in its own goroutine and accepts
|
||||||
// inbound connections.
|
// inbound connections.
|
||||||
func (srv *server) listenLoop() {
|
func (srv *Server) listenLoop() {
|
||||||
defer srv.loopWG.Done()
|
defer srv.loopWG.Done()
|
||||||
log.Info("RLPx listener up", "self", srv.makeSelf(srv.listener, srv.ntab))
|
log.Info("RLPx listener up", "self", srv.makeSelf(srv.listener, srv.ntab))
|
||||||
|
|
||||||
|
|
@ -697,7 +682,7 @@ func (srv *server) listenLoop() {
|
||||||
// Spawn the handler. It will give the slot back when the connection
|
// Spawn the handler. It will give the slot back when the connection
|
||||||
// has been established.
|
// has been established.
|
||||||
go func() {
|
go func() {
|
||||||
srv.setupConn(fd, inboundConn, nil)
|
srv.SetupConn(fd, inboundConn, nil)
|
||||||
slots <- struct{}{}
|
slots <- struct{}{}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
@ -706,7 +691,7 @@ func (srv *server) listenLoop() {
|
||||||
// setupConn runs the handshakes and attempts to add the connection
|
// setupConn runs the handshakes and attempts to add the connection
|
||||||
// as a peer. It returns when the connection has been added as a peer
|
// as a peer. It returns when the connection has been added as a peer
|
||||||
// or the handshakes have failed.
|
// or the handshakes have failed.
|
||||||
func (srv *server) setupConn(fd net.Conn, flags connFlag, dialDest *discover.Node) {
|
func (srv *Server) SetupConn(fd net.Conn, flags connFlag, dialDest *discover.Node) {
|
||||||
// Prevent leftover pending conns from entering the handshake.
|
// Prevent leftover pending conns from entering the handshake.
|
||||||
srv.lock.Lock()
|
srv.lock.Lock()
|
||||||
running := srv.running
|
running := srv.running
|
||||||
|
|
@ -766,7 +751,7 @@ func truncateName(s string) string {
|
||||||
|
|
||||||
// checkpoint sends the conn to run, which performs the
|
// checkpoint sends the conn to run, which performs the
|
||||||
// post-handshake checks for the stage (posthandshake, addpeer).
|
// post-handshake checks for the stage (posthandshake, addpeer).
|
||||||
func (srv *server) checkpoint(c *conn, stage chan<- *conn) error {
|
func (srv *Server) checkpoint(c *conn, stage chan<- *conn) error {
|
||||||
select {
|
select {
|
||||||
case stage <- c:
|
case stage <- c:
|
||||||
case <-srv.quit:
|
case <-srv.quit:
|
||||||
|
|
@ -783,7 +768,7 @@ func (srv *server) checkpoint(c *conn, stage chan<- *conn) error {
|
||||||
// runPeer runs in its own goroutine for each peer.
|
// runPeer runs in its own goroutine for each peer.
|
||||||
// it waits until the Peer logic returns and removes
|
// it waits until the Peer logic returns and removes
|
||||||
// the peer.
|
// the peer.
|
||||||
func (srv *server) runPeer(p *Peer) {
|
func (srv *Server) runPeer(p *Peer) {
|
||||||
if srv.newPeerHook != nil {
|
if srv.newPeerHook != nil {
|
||||||
srv.newPeerHook(p)
|
srv.newPeerHook(p)
|
||||||
}
|
}
|
||||||
|
|
@ -824,7 +809,7 @@ type NodeInfo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeInfo gathers and returns a collection of metadata known about the host.
|
// NodeInfo gathers and returns a collection of metadata known about the host.
|
||||||
func (srv *server) NodeInfo() *NodeInfo {
|
func (srv *Server) NodeInfo() *NodeInfo {
|
||||||
node := srv.Self()
|
node := srv.Self()
|
||||||
|
|
||||||
// Gather and assemble the generic node infos
|
// Gather and assemble the generic node infos
|
||||||
|
|
@ -853,7 +838,7 @@ func (srv *server) NodeInfo() *NodeInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeersInfo returns an array of metadata objects describing connected peers.
|
// PeersInfo returns an array of metadata objects describing connected peers.
|
||||||
func (srv *server) PeersInfo() []*PeerInfo {
|
func (srv *Server) PeersInfo() []*PeerInfo {
|
||||||
// Gather all the generic and sub-protocol specific infos
|
// Gather all the generic and sub-protocol specific infos
|
||||||
infos := make([]*PeerInfo, 0, srv.PeerCount())
|
infos := make([]*PeerInfo, 0, srv.PeerCount())
|
||||||
for _, peer := range srv.Peers() {
|
for _, peer := range srv.Peers() {
|
||||||
|
|
@ -872,6 +857,6 @@ func (srv *server) PeersInfo() []*PeerInfo {
|
||||||
return infos
|
return infos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *server) DiscV5() *discv5.Network {
|
func (srv *Server) DiscV5() *discv5.Network {
|
||||||
return srv.discV5
|
return srv.discV5
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ func startTestServer(t *testing.T, id discover.NodeID, pf func(*Peer)) *Server {
|
||||||
ListenAddr: "127.0.0.1:0",
|
ListenAddr: "127.0.0.1:0",
|
||||||
PrivateKey: newkey(),
|
PrivateKey: newkey(),
|
||||||
}
|
}
|
||||||
server := &server{
|
server := &Server{
|
||||||
Config: config,
|
Config: config,
|
||||||
newPeerHook: pf,
|
newPeerHook: pf,
|
||||||
newTransport: func(fd net.Conn) transport { return newTestTransport(id, fd) },
|
newTransport: func(fd net.Conn) transport { return newTestTransport(id, fd) },
|
||||||
|
|
@ -201,7 +201,7 @@ func TestServerTaskScheduling(t *testing.T) {
|
||||||
|
|
||||||
// The Server in this test isn't actually running
|
// The Server in this test isn't actually running
|
||||||
// because we're only interested in what run does.
|
// because we're only interested in what run does.
|
||||||
srv := &server{
|
srv := &Server{
|
||||||
Config: Config{MaxPeers: 10},
|
Config: Config{MaxPeers: 10},
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
ntab: fakeTable{},
|
ntab: fakeTable{},
|
||||||
|
|
@ -246,7 +246,7 @@ func TestServerManyTasks(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
srv = &server{quit: make(chan struct{}), ntab: fakeTable{}, running: true}
|
srv = &Server{quit: make(chan struct{}), ntab: fakeTable{}, running: true}
|
||||||
done = make(chan *testTask)
|
done = make(chan *testTask)
|
||||||
start, end = 0, 0
|
start, end = 0, 0
|
||||||
)
|
)
|
||||||
|
|
@ -317,7 +317,7 @@ func (t *testTask) Do(srv *Server) {
|
||||||
// at capacity. Trusted connections should still be accepted.
|
// at capacity. Trusted connections should still be accepted.
|
||||||
func TestServerAtCap(t *testing.T) {
|
func TestServerAtCap(t *testing.T) {
|
||||||
trustedID := randomID()
|
trustedID := randomID()
|
||||||
srv := &server{
|
srv := &Server{
|
||||||
Config: Config{
|
Config: Config{
|
||||||
PrivateKey: newkey(),
|
PrivateKey: newkey(),
|
||||||
MaxPeers: 10,
|
MaxPeers: 10,
|
||||||
|
|
@ -420,7 +420,7 @@ func TestServerSetupConn(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
srv := &server{
|
srv := &Server{
|
||||||
Config: Config{
|
Config: Config{
|
||||||
PrivateKey: srvkey,
|
PrivateKey: srvkey,
|
||||||
MaxPeers: 10,
|
MaxPeers: 10,
|
||||||
|
|
@ -435,7 +435,7 @@ func TestServerSetupConn(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p1, _ := net.Pipe()
|
p1, _ := net.Pipe()
|
||||||
srv.setupConn(p1, test.flags, test.dialDest)
|
srv.SetupConn(p1, test.flags, test.dialDest)
|
||||||
if !reflect.DeepEqual(test.tt.closeErr, test.wantCloseErr) {
|
if !reflect.DeepEqual(test.tt.closeErr, test.wantCloseErr) {
|
||||||
t.Errorf("test %d: close error mismatch: got %q, want %q", i, test.tt.closeErr, test.wantCloseErr)
|
t.Errorf("test %d: close error mismatch: got %q, want %q", i, test.tt.closeErr, test.wantCloseErr)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/reexec"
|
"github.com/docker/docker/pkg/reexec"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
|
@ -81,12 +82,16 @@ func (n *DockerNode) dockerCommand() *exec.Cmd {
|
||||||
return exec.Command(
|
return exec.Command(
|
||||||
"sh", "-c",
|
"sh", "-c",
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
`exec docker run --interactive --env _P2P_NODE_CONFIG="${_P2P_NODE_CONFIG}" %s p2p-node %s %s`,
|
`exec docker run --interactive --env _P2P_NODE_CONFIG="${_P2P_NODE_CONFIG}" --env _P2P_NODE_KEY="${_P2P_NODE_KEY}" %s p2p-node %s %s`,
|
||||||
dockerImage, n.Config.Node.Service, n.ID.String(),
|
dockerImage, strings.Join(n.Services, " "), n.ID.String(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *DockerNode) GetService(name string) node.Service {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// dockerImage is the name of the docker image
|
// dockerImage is the name of the docker image
|
||||||
const dockerImage = "p2p-node"
|
const dockerImage = "p2p-node"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) {
|
||||||
Node: config,
|
Node: config,
|
||||||
}
|
}
|
||||||
conf.Stack.DataDir = filepath.Join(dir, "data")
|
conf.Stack.DataDir = filepath.Join(dir, "data")
|
||||||
conf.Stack.P2P.EnableMsgEvents = true
|
conf.Stack.P2P.EnableMsgEvents = false
|
||||||
conf.Stack.P2P.NoDiscovery = true
|
conf.Stack.P2P.NoDiscovery = true
|
||||||
conf.Stack.P2P.NAT = nil
|
conf.Stack.P2P.NAT = nil
|
||||||
|
|
||||||
|
|
@ -88,11 +88,12 @@ func (e *ExecAdapter) NewNode(config *NodeConfig) (Node, error) {
|
||||||
// (so for example we can run the node in a remote Docker container and
|
// (so for example we can run the node in a remote Docker container and
|
||||||
// still communicate with it).
|
// still communicate with it).
|
||||||
type ExecNode struct {
|
type ExecNode struct {
|
||||||
ID *NodeId
|
ID *NodeId
|
||||||
Dir string
|
Dir string
|
||||||
Config *execNodeConfig
|
Config *execNodeConfig
|
||||||
Cmd *exec.Cmd
|
Cmd *exec.Cmd
|
||||||
Info *p2p.NodeInfo
|
Info *p2p.NodeInfo
|
||||||
|
Services []string
|
||||||
|
|
||||||
client *rpc.Client
|
client *rpc.Client
|
||||||
rpcMux *rpcMux
|
rpcMux *rpcMux
|
||||||
|
|
@ -164,13 +165,17 @@ func (n *ExecNode) Start(snapshot []byte) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *ExecNode) GetService(name string) node.Service {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// execCommand returns a command which runs the node locally by exec'ing
|
// execCommand returns a command which runs the node locally by exec'ing
|
||||||
// the current binary but setting argv[0] to "p2p-node" so that the child
|
// the current binary but setting argv[0] to "p2p-node" so that the child
|
||||||
// runs execP2PNode
|
// runs execP2PNode
|
||||||
func (n *ExecNode) execCommand() *exec.Cmd {
|
func (n *ExecNode) execCommand() *exec.Cmd {
|
||||||
return &exec.Cmd{
|
return &exec.Cmd{
|
||||||
Path: reexec.Self(),
|
Path: reexec.Self(),
|
||||||
Args: []string{"p2p-node", n.Config.Node.Service, n.ID.String()},
|
Args: []string{"p2p-node", n.Services[0], n.ID.String()},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -278,7 +283,7 @@ func execP2PNode() {
|
||||||
if !exists {
|
if !exists {
|
||||||
log.Crit(fmt.Sprintf("unknown node service %q", serviceName))
|
log.Crit(fmt.Sprintf("unknown node service %q", serviceName))
|
||||||
}
|
}
|
||||||
service := serviceFunc(id, conf.Snapshot)
|
services := serviceFunc(id, conf.Snapshot)
|
||||||
|
|
||||||
// use explicit IP address in ListenAddr so that Enode URL is usable
|
// use explicit IP address in ListenAddr so that Enode URL is usable
|
||||||
if strings.HasPrefix(conf.Stack.P2P.ListenAddr, ":") {
|
if strings.HasPrefix(conf.Stack.P2P.ListenAddr, ":") {
|
||||||
|
|
@ -295,7 +300,7 @@ func execP2PNode() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// start the devp2p stack
|
// start the devp2p stack
|
||||||
stack, err := startP2PNode(&conf.Stack, service)
|
stack, err := startP2PNode(&conf.Stack, services)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Crit("error starting p2p node", "err", err)
|
log.Crit("error starting p2p node", "err", err)
|
||||||
}
|
}
|
||||||
|
|
@ -321,17 +326,20 @@ func execP2PNode() {
|
||||||
stack.Wait()
|
stack.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func startP2PNode(conf *node.Config, service node.Service) (*node.Node, error) {
|
func startP2PNode(conf *node.Config, services []node.Service) (*node.Node, error) {
|
||||||
stack, err := node.New(conf)
|
stack, err := node.New(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
constructor := func(ctx *node.ServiceContext) (node.Service, error) {
|
for _, svc := range services {
|
||||||
return &snapshotService{service}, nil
|
constructor := func(ctx *node.ServiceContext) (node.Service, error) {
|
||||||
}
|
return &snapshotService{svc}, nil
|
||||||
if err := stack.Register(constructor); err != nil {
|
}
|
||||||
return nil, err
|
if err := stack.Register(constructor); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := stack.Start(); err != nil {
|
if err := stack.Start(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,11 @@
|
||||||
package adapters
|
package adapters
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"net"
|
"net"
|
||||||
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
|
@ -36,7 +37,6 @@ import (
|
||||||
type SimAdapter struct {
|
type SimAdapter struct {
|
||||||
mtx sync.RWMutex
|
mtx sync.RWMutex
|
||||||
nodes map[discover.NodeID]*SimNode
|
nodes map[discover.NodeID]*SimNode
|
||||||
services map[string]ServiceFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSimAdapter creates a SimAdapter which is capable of running in-memory
|
// NewSimAdapter creates a SimAdapter which is capable of running in-memory
|
||||||
|
|
@ -45,7 +45,6 @@ type SimAdapter struct {
|
||||||
func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter {
|
func NewSimAdapter(services map[string]ServiceFunc) *SimAdapter {
|
||||||
return &SimAdapter{
|
return &SimAdapter{
|
||||||
nodes: make(map[discover.NodeID]*SimNode),
|
nodes: make(map[discover.NodeID]*SimNode),
|
||||||
services: services,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,6 +55,8 @@ func (s *SimAdapter) Name() string {
|
||||||
|
|
||||||
// NewNode returns a new SimNode using the given config
|
// NewNode returns a new SimNode using the given config
|
||||||
func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
|
func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
|
||||||
|
var nodeprotos []p2p.Protocol
|
||||||
|
|
||||||
s.mtx.Lock()
|
s.mtx.Lock()
|
||||||
defer s.mtx.Unlock()
|
defer s.mtx.Unlock()
|
||||||
|
|
||||||
|
|
@ -66,6 +67,7 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the service is valid and initialize it
|
// check the service is valid and initialize it
|
||||||
|
/*
|
||||||
serviceFunc, exists := s.services[config.Service]
|
serviceFunc, exists := s.services[config.Service]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, fmt.Errorf("unknown node service %q", config.Service)
|
return nil, fmt.Errorf("unknown node service %q", config.Service)
|
||||||
|
|
@ -73,13 +75,60 @@ func (s *SimAdapter) NewNode(config *NodeConfig) (Node, error) {
|
||||||
|
|
||||||
node := &SimNode{
|
node := &SimNode{
|
||||||
Id: id,
|
Id: id,
|
||||||
|
config: config,
|
||||||
adapter: s,
|
adapter: s,
|
||||||
serviceFunc: serviceFunc,
|
serviceFunc: serviceFunc,
|
||||||
peers: make(map[discover.NodeID]MsgReadWriteCloser),
|
*/
|
||||||
dropPeers: make(chan struct{}),
|
//serviceFunc, exists := s.services[config.Service]
|
||||||
|
|
||||||
|
//if !exists {
|
||||||
|
// return nil, fmt.Errorf("unknown node service %q", config.Service)
|
||||||
|
//}
|
||||||
|
//service := serviceFunc(id)
|
||||||
|
|
||||||
|
_, err := node.New(&node.Config{
|
||||||
|
P2P: p2p.Config{
|
||||||
|
PrivateKey: config.PrivateKey,
|
||||||
|
MaxPeers: math.MaxInt32,
|
||||||
|
NoDiscovery: true,
|
||||||
|
Protocols: nodeprotos,
|
||||||
|
Dialer: s,
|
||||||
|
EnableMsgEvents: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
s.nodes[id.NodeID] = node
|
|
||||||
return node, nil
|
for _, service := range serviceFuncs[config.Service](id, nil) {
|
||||||
|
for _, proto := range service.Protocols() {
|
||||||
|
nodeprotos = append(nodeprotos, proto)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
simnode := &SimNode{
|
||||||
|
Id: id,
|
||||||
|
serviceFunc: serviceFuncs[config.Service],
|
||||||
|
adapter: s,
|
||||||
|
config: config,
|
||||||
|
running: []node.Service{},
|
||||||
|
}
|
||||||
|
s.nodes[id.NodeID] = simnode
|
||||||
|
return simnode, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SimAdapter) Dial(dest *discover.Node) (conn net.Conn, err error) {
|
||||||
|
node, ok := s.GetNode(dest.ID)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unknown node: %s", dest.ID)
|
||||||
|
}
|
||||||
|
srv := node.Server()
|
||||||
|
if srv == nil {
|
||||||
|
return nil, fmt.Errorf("node not running: %s", dest.ID)
|
||||||
|
}
|
||||||
|
pipe1, pipe2 := net.Pipe()
|
||||||
|
go srv.SetupConn(pipe1, 0, nil)
|
||||||
|
return pipe2, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNode returns the node with the given ID if it exists
|
// GetNode returns the node with the given ID if it exists
|
||||||
|
|
@ -90,14 +139,6 @@ func (s *SimAdapter) GetNode(id discover.NodeID) (*SimNode, bool) {
|
||||||
return node, ok
|
return node, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// MsgReadWriteCloser wraps a MsgReadWriter with the addition of a Close method
|
|
||||||
// so we can simulate the closing of a p2p connection (which usually happens by
|
|
||||||
/// closing the underlying TCP connection)
|
|
||||||
type MsgReadWriteCloser interface {
|
|
||||||
p2p.MsgReadWriter
|
|
||||||
Close() error
|
|
||||||
}
|
|
||||||
|
|
||||||
// SimNode is an in-memory node which connects to other SimNodes using an
|
// SimNode is an in-memory node which connects to other SimNodes using an
|
||||||
// in-memory p2p.MsgReadWriter pipe, running an underlying service protocol
|
// in-memory p2p.MsgReadWriter pipe, running an underlying service protocol
|
||||||
// directly over that pipe.
|
// directly over that pipe.
|
||||||
|
|
@ -107,17 +148,13 @@ type MsgReadWriteCloser interface {
|
||||||
type SimNode struct {
|
type SimNode struct {
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
Id *NodeId
|
Id *NodeId
|
||||||
|
config *NodeConfig
|
||||||
adapter *SimAdapter
|
adapter *SimAdapter
|
||||||
running node.Service
|
serviceFunc ServiceFunc
|
||||||
serviceFunc ServiceFunc
|
node *node.Node
|
||||||
peers map[discover.NodeID]MsgReadWriteCloser
|
|
||||||
peerFeed event.Feed
|
|
||||||
client *rpc.Client
|
client *rpc.Client
|
||||||
rpcMux *rpcMux
|
rpcMux *rpcMux
|
||||||
|
running []node.Service
|
||||||
// dropPeers is used to force peer disconnects when
|
|
||||||
// the node is stopped
|
|
||||||
dropPeers chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Addr returns the node's discovery address
|
// Addr returns the node's discovery address
|
||||||
|
|
@ -127,7 +164,7 @@ func (self *SimNode) Addr() []byte {
|
||||||
|
|
||||||
// Node returns a discover.Node representing the SimNode
|
// Node returns a discover.Node representing the SimNode
|
||||||
func (self *SimNode) Node() *discover.Node {
|
func (self *SimNode) Node() *discover.Node {
|
||||||
return discover.NewNode(self.Id.NodeID, nil, 0, 0)
|
return discover.NewNode(self.Id.NodeID, net.IP{127, 0, 0, 1}, 30303, 30303)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client returns an rpc.Client which can be used to communicate with the
|
// Client returns an rpc.Client which can be used to communicate with the
|
||||||
|
|
@ -154,82 +191,68 @@ func (self *SimNode) ServeRPC(conn net.Conn) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start initializes the service, starts the RPC handler and then starts
|
// Snapshot creates a snapshot of the service state by calling the
|
||||||
// the service
|
// simulation_snapshot RPC method
|
||||||
func (self *SimNode) Start(snapshot []byte) error {
|
func (self *SimNode) Snapshot() ([]byte, error) {
|
||||||
service := self.serviceFunc(self.Id, snapshot)
|
self.lock.Lock()
|
||||||
|
defer self.lock.Unlock()
|
||||||
|
if self.client == nil {
|
||||||
|
return nil, errors.New("RPC not started")
|
||||||
|
}
|
||||||
|
var snapshot []byte
|
||||||
|
return snapshot, self.client.Call(&snapshot, "simulation_snapshot")
|
||||||
|
}
|
||||||
|
|
||||||
// for simplicity, only support single protocol services (simulating
|
// Start starts the RPC handler and the underlying service
|
||||||
// multiple protocols on the same peer is extra effort, and we don't
|
func (self *SimNode) Start(snapshot []byte) error {
|
||||||
// currently run any simulations which run multiple protocols)
|
self.lock.Lock()
|
||||||
if len(service.Protocols()) != 1 {
|
defer self.lock.Unlock()
|
||||||
return errors.New("service must have a single protocol")
|
if self.node != nil {
|
||||||
|
return errors.New("node already started")
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dropPeers = make(chan struct{})
|
services := []node.ServiceConstructor{}
|
||||||
if err := self.startRPC(service); err != nil {
|
|
||||||
|
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])
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
return err
|
||||||
}
|
}
|
||||||
self.running = service
|
|
||||||
return service.Start(&simServer{self})
|
|
||||||
}
|
|
||||||
|
|
||||||
// simServer wraps a SimNode but modifies the Start method signature so that
|
for _, service := range services {
|
||||||
// it implements the p2p.Server interface (the Start method is never actually
|
log.Debug(fmt.Sprintf("service %v", service))
|
||||||
// called when using the SimAdapter)
|
if err := node.Register(service); err != nil {
|
||||||
type simServer struct {
|
return err
|
||||||
*SimNode
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (s *simServer) Start() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop stops the RPC handler, stops the underlying service and disconnects
|
|
||||||
// any currently connected peers
|
|
||||||
func (self *SimNode) Stop() error {
|
|
||||||
self.stopRPC()
|
|
||||||
close(self.dropPeers)
|
|
||||||
return self.running.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Running returns whether or not the service is running
|
|
||||||
func (self *SimNode) Running() bool {
|
|
||||||
self.lock.Lock()
|
|
||||||
defer self.lock.Unlock()
|
|
||||||
return self.running != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Service returns the running node.Service
|
|
||||||
func (self *SimNode) Service() node.Service {
|
|
||||||
return self.running
|
|
||||||
}
|
|
||||||
|
|
||||||
// startRPC starts an RPC server and connects to it using an in-process RPC
|
|
||||||
// client
|
|
||||||
func (self *SimNode) startRPC(service node.Service) error {
|
|
||||||
self.lock.Lock()
|
|
||||||
defer self.lock.Unlock()
|
|
||||||
if self.client != nil {
|
|
||||||
return errors.New("RPC already started")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add SimAdminAPI so that the network can call the
|
if err := node.Start(); err != nil {
|
||||||
// AddPeer, RemovePeer and PeerEvents RPC methods
|
return err
|
||||||
apis := append(service.APIs(), []rpc.API{
|
}
|
||||||
{
|
|
||||||
Namespace: "admin",
|
|
||||||
Version: "1.0",
|
|
||||||
Service: &SimAdminAPI{self},
|
|
||||||
},
|
|
||||||
}...)
|
|
||||||
|
|
||||||
// start the RPC handler
|
handler, err := node.RPCHandler()
|
||||||
handler := rpc.NewServer()
|
if err != nil {
|
||||||
for _, api := range apis {
|
return err
|
||||||
if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
|
|
||||||
return fmt.Errorf("error registering RPC: %s", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// create an in-process RPC multiplexer
|
// create an in-process RPC multiplexer
|
||||||
|
|
@ -240,197 +263,61 @@ func (self *SimNode) startRPC(service node.Service) error {
|
||||||
// create an in-process RPC client
|
// create an in-process RPC client
|
||||||
self.client = self.rpcMux.Client()
|
self.client = self.rpcMux.Client()
|
||||||
|
|
||||||
|
self.node = node
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// stopRPC closes the node's RPC client
|
func (self *SimNode) Stop() error {
|
||||||
func (self *SimNode) stopRPC() {
|
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
if self.client != nil {
|
if self.node == nil {
|
||||||
self.client.Close()
|
return nil
|
||||||
self.client = nil
|
|
||||||
self.rpcMux = nil
|
|
||||||
}
|
}
|
||||||
|
if err := self.node.Stop(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
self.node = nil
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemovePeer removes the given node as a peer by looking up the corresponding
|
// Service returns the underlying running node.Service matching the supplied servuce type
|
||||||
// p2p.MsgReadWriter pipe and closing it (which will cause both the local
|
func (self *SimNode) Service(servicetype interface{}) node.Service {
|
||||||
// and peer Protocol.Run functions to exit)
|
|
||||||
func (self *SimNode) RemovePeer(peer *discover.Node) {
|
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
peerRW, exists := self.peers[peer.ID]
|
typ := reflect.TypeOf(servicetype)
|
||||||
if !exists {
|
for _, service := range self.running {
|
||||||
return
|
if reflect.TypeOf(service) == typ {
|
||||||
|
return service
|
||||||
|
}
|
||||||
}
|
}
|
||||||
peerRW.Close()
|
return nil
|
||||||
delete(self.peers, peer.ID)
|
|
||||||
log.Trace(fmt.Sprintf("dropped peer %v", peer.ID))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPeer adds the given node as a peer by creating a p2p.MsgReadWriter pipe
|
func (self *SimNode) Server() *p2p.Server {
|
||||||
// and running both the local and peer's Protocol.Run function over the pipe
|
|
||||||
func (self *SimNode) AddPeer(peer *discover.Node) {
|
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
if _, exists := self.peers[peer.ID]; exists {
|
if self.node == nil {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
peerNode, exists := self.adapter.GetNode(peer.ID)
|
return self.node.Server()
|
||||||
if !exists {
|
|
||||||
panic(fmt.Sprintf("unknown peer: %s", peer.ID))
|
|
||||||
}
|
|
||||||
if !peerNode.Running() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p1, p2 := p2p.MsgPipe()
|
|
||||||
localRW := p2p.NewMsgEventer(p1, &self.peerFeed, peer.ID)
|
|
||||||
peerRW := p2p.NewMsgEventer(p2, &self.peerFeed, self.Id.NodeID)
|
|
||||||
self.peers[peer.ID] = peerRW
|
|
||||||
peerNode.RunProtocol(self, peerRW)
|
|
||||||
self.RunProtocol(peerNode, localRW)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubscribeEvents subscribes the given channel to p2p peer events
|
|
||||||
func (self *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription {
|
func (self *SimNode) SubscribeEvents(ch chan *p2p.PeerEvent) event.Subscription {
|
||||||
return self.peerFeed.Subscribe(ch)
|
srv := self.Server()
|
||||||
|
if srv == nil {
|
||||||
|
panic("node not running")
|
||||||
|
}
|
||||||
|
return srv.SubscribeEvents(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeerCount returns the number of currently connected peers
|
|
||||||
func (self *SimNode) PeerCount() int {
|
|
||||||
self.lock.Lock()
|
|
||||||
defer self.lock.Unlock()
|
|
||||||
return len(self.peers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NodeInfo returns information about the node
|
|
||||||
func (self *SimNode) NodeInfo() *p2p.NodeInfo {
|
func (self *SimNode) NodeInfo() *p2p.NodeInfo {
|
||||||
self.lock.Lock()
|
server := self.Server()
|
||||||
defer self.lock.Unlock()
|
if server == nil {
|
||||||
info := &p2p.NodeInfo{
|
return &p2p.NodeInfo{
|
||||||
ID: self.Id.String(),
|
ID: self.Id.String(),
|
||||||
Enode: self.Node().String(),
|
Enode: self.Node().String(),
|
||||||
Protocols: make(map[string]interface{}),
|
|
||||||
}
|
|
||||||
if self.running != nil {
|
|
||||||
for _, proto := range self.running.Protocols() {
|
|
||||||
nodeInfo := interface{}("unknown")
|
|
||||||
if query := proto.NodeInfo; query != nil {
|
|
||||||
nodeInfo = proto.NodeInfo()
|
|
||||||
}
|
|
||||||
info.Protocols[proto.Name] = nodeInfo
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return info
|
return server.NodeInfo()
|
||||||
}
|
|
||||||
|
|
||||||
// PeersInfo is a stub so that SimNode implements p2p.Server
|
|
||||||
func (self *SimNode) PeersInfo() (info []*p2p.PeerInfo) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Snapshot creates a snapshot of the running service
|
|
||||||
func (self *SimNode) Snapshot() ([]byte, error) {
|
|
||||||
self.lock.Lock()
|
|
||||||
service := self.running
|
|
||||||
self.lock.Unlock()
|
|
||||||
if service == nil {
|
|
||||||
return nil, errors.New("service not running")
|
|
||||||
}
|
|
||||||
return SnapshotAPI{service}.Snapshot()
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunProtocol runs the underlying service's protocol with the peer using the
|
|
||||||
// given MsgReadWriteCloser, emitting peer add / drop events for peer event
|
|
||||||
// subscribers
|
|
||||||
func (self *SimNode) RunProtocol(peer *SimNode, rw MsgReadWriteCloser) {
|
|
||||||
// close the rw if the node is stopped to disconnect the peer
|
|
||||||
go func() {
|
|
||||||
<-self.dropPeers
|
|
||||||
log.Trace("dropping peer", "self.id", self.Id, "peer.id", peer.Id)
|
|
||||||
rw.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
id := peer.Id
|
|
||||||
log.Trace(fmt.Sprintf("protocol starting on peer %v (connection with %v)", self.Id, id))
|
|
||||||
protocol := self.running.Protocols()[0]
|
|
||||||
p := p2p.NewPeer(id.NodeID, id.Label(), []p2p.Cap{})
|
|
||||||
go func() {
|
|
||||||
// emit peer add event
|
|
||||||
self.peerFeed.Send(&p2p.PeerEvent{
|
|
||||||
Type: p2p.PeerEventTypeAdd,
|
|
||||||
Peer: id.NodeID,
|
|
||||||
})
|
|
||||||
|
|
||||||
// run the protocol
|
|
||||||
err := protocol.Run(p, rw)
|
|
||||||
|
|
||||||
// remove the peer
|
|
||||||
self.RemovePeer(peer.Node())
|
|
||||||
log.Trace(fmt.Sprintf("protocol quit on peer %v (connection with %v broken: %v)", self.Id, id, err))
|
|
||||||
|
|
||||||
// emit peer drop event
|
|
||||||
self.peerFeed.Send(&p2p.PeerEvent{
|
|
||||||
Type: p2p.PeerEventTypeDrop,
|
|
||||||
Peer: id.NodeID,
|
|
||||||
Error: err.Error(),
|
|
||||||
})
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SimAdminAPI implements the AddPeer and RemovePeer RPC methods (API
|
|
||||||
// compatible with node.PrivateAdminAPI)
|
|
||||||
type SimAdminAPI struct {
|
|
||||||
*SimNode
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *SimAdminAPI) AddPeer(url string) (bool, error) {
|
|
||||||
node, err := discover.ParseNode(url)
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("invalid enode: %v", err)
|
|
||||||
}
|
|
||||||
api.SimNode.AddPeer(node)
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *SimAdminAPI) RemovePeer(url string) (bool, error) {
|
|
||||||
node, err := discover.ParseNode(url)
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("invalid enode: %v", err)
|
|
||||||
}
|
|
||||||
api.SimNode.RemovePeer(node)
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PeerEvents creates an RPC subscription which receives peer events from the
|
|
||||||
// underlying p2p.Server
|
|
||||||
func (api *SimAdminAPI) PeerEvents(ctx context.Context) (*rpc.Subscription, error) {
|
|
||||||
notifier, supported := rpc.NotifierFromContext(ctx)
|
|
||||||
if !supported {
|
|
||||||
return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
rpcSub := notifier.CreateSubscription()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
events := make(chan *p2p.PeerEvent)
|
|
||||||
sub := api.SubscribeEvents(events)
|
|
||||||
defer sub.Unsubscribe()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case event := <-events:
|
|
||||||
notifier.Notify(rpcSub.ID, event)
|
|
||||||
case <-sub.Err():
|
|
||||||
return
|
|
||||||
case <-rpcSub.Err():
|
|
||||||
return
|
|
||||||
case <-notifier.Closed():
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return rpcSub, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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 is a human friendly name for the node like "node01"
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
// Service is the name of the service which should be run when starting
|
// Service is the name of the services which should be run when starting
|
||||||
// the node (for SimNodes it should be the name of a service contained
|
// the node (for SimNodes it should be the names of services contained
|
||||||
// in SimAdapter.services, for other nodes it should be a service
|
// in SimAdapter.services, for other nodes it should be services
|
||||||
// registered by calling the RegisterService function)
|
// registered by calling the RegisterService function)
|
||||||
Service string
|
Service string
|
||||||
}
|
}
|
||||||
|
|
@ -140,7 +140,7 @@ type nodeConfigJSON struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
PrivateKey string `json:"private_key"`
|
PrivateKey string `json:"private_key"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Service string `json:"service"`
|
Service string `json:"service"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NodeConfig) MarshalJSON() ([]byte, error) {
|
func (n *NodeConfig) MarshalJSON() ([]byte, error) {
|
||||||
|
|
@ -202,10 +202,11 @@ func RandomNodeConfig() *NodeConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Services is a collection of services which can be run in a simulation
|
// Services is a collection of services which can be run in a simulation
|
||||||
|
// it is mapped to strings representing TYPES of nodes
|
||||||
type Services map[string]ServiceFunc
|
type Services map[string]ServiceFunc
|
||||||
|
|
||||||
// ServiceFunc returns a node.Service which can be used to boot devp2p nodes
|
// ServiceFunc returns a node.Service which can be used to boot devp2p nodes
|
||||||
type ServiceFunc func(id *NodeId, snapshot []byte) node.Service
|
type ServiceFunc func(id *NodeId, snapshot []byte) []node.Service
|
||||||
|
|
||||||
// serviceFuncs is a map of registered services which are used to boot devp2p
|
// serviceFuncs is a map of registered services which are used to boot devp2p
|
||||||
// nodes
|
// nodes
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ func (p *pingPongService) APIs() []rpc.API {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pingPongService) Start(server p2p.Server) error {
|
func (p *pingPongService) Start(server *p2p.Server) error {
|
||||||
p.log.Info("ping-pong service starting")
|
p.log.Info("ping-pong service starting")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,21 @@ func (c *Client) GetNetwork(networkID string) (*Network, error) {
|
||||||
return network, c.Get(fmt.Sprintf("/networks/%s", networkID), network)
|
return network, c.Get(fmt.Sprintf("/networks/%s", networkID), network)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartNetwork starts all existing nodes in a simulation network
|
||||||
|
func (c *Client) StartNetwork(networkID string) error {
|
||||||
|
return c.Post(fmt.Sprintf("/networks/%s/start", networkID), nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopNetwork stops all existing nodes in a simulation network
|
||||||
|
func (c *Client) StopNetwork(networkID string) error {
|
||||||
|
return c.Post(fmt.Sprintf("/networks/%s/stop", networkID), nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteNetwork stops and deletes a simulation network
|
||||||
|
func (c *Client) DeleteNetwork(networkID string) error {
|
||||||
|
return c.Delete(fmt.Sprintf("/networks/%s", networkID))
|
||||||
|
}
|
||||||
|
|
||||||
// CreateSnapshot creates a network snapshot
|
// CreateSnapshot creates a network snapshot
|
||||||
func (c *Client) CreateSnapshot(networkID string) (*Snapshot, error) {
|
func (c *Client) CreateSnapshot(networkID string) (*Snapshot, error) {
|
||||||
snap := &Snapshot{}
|
snap := &Snapshot{}
|
||||||
|
|
@ -276,6 +291,9 @@ func NewServer(config *ServerConfig) *Server {
|
||||||
s.POST("/networks", s.CreateNetwork)
|
s.POST("/networks", s.CreateNetwork)
|
||||||
s.GET("/networks", s.GetNetworks)
|
s.GET("/networks", s.GetNetworks)
|
||||||
s.GET("/networks/:netid", s.GetNetwork)
|
s.GET("/networks/:netid", s.GetNetwork)
|
||||||
|
s.POST("/networks/:netid/start", s.StartNetwork)
|
||||||
|
s.POST("/networks/:netid/stop", s.StopNetwork)
|
||||||
|
s.DELETE("/networks/:netid", s.DeleteNetwork)
|
||||||
s.GET("/networks/:netid/events", s.StreamNetworkEvents)
|
s.GET("/networks/:netid/events", s.StreamNetworkEvents)
|
||||||
s.GET("/networks/:netid/snapshot", s.CreateSnapshot)
|
s.GET("/networks/:netid/snapshot", s.CreateSnapshot)
|
||||||
s.POST("/networks/:netid/snapshot", s.LoadSnapshot)
|
s.POST("/networks/:netid/snapshot", s.LoadSnapshot)
|
||||||
|
|
@ -341,6 +359,46 @@ func (s *Server) GetNetwork(w http.ResponseWriter, req *http.Request) {
|
||||||
s.JSON(w, http.StatusOK, network)
|
s.JSON(w, http.StatusOK, network)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartNetwork starts all nodes in a network
|
||||||
|
func (s *Server) StartNetwork(w http.ResponseWriter, req *http.Request) {
|
||||||
|
network := req.Context().Value("network").(*Network)
|
||||||
|
|
||||||
|
if err := network.StartAll(); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopNetwork stops all nodes in a network
|
||||||
|
func (s *Server) StopNetwork(w http.ResponseWriter, req *http.Request) {
|
||||||
|
network := req.Context().Value("network").(*Network)
|
||||||
|
|
||||||
|
if err := network.StopAll(); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteNetwork stops all nodes in a network and deletes it
|
||||||
|
func (s *Server) DeleteNetwork(w http.ResponseWriter, req *http.Request) {
|
||||||
|
network := req.Context().Value("network").(*Network)
|
||||||
|
|
||||||
|
if err := network.StopAll(); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mtx.Lock()
|
||||||
|
delete(s.networks, network.Id)
|
||||||
|
s.mtx.Unlock()
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
//Get the info for a particular mocker
|
//Get the info for a particular mocker
|
||||||
func (s *Server) GetMocker(w http.ResponseWriter, req *http.Request) {
|
func (s *Server) GetMocker(w http.ResponseWriter, req *http.Request) {
|
||||||
m := make(map[string]string)
|
m := make(map[string]string)
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ func (t *testService) APIs() []rpc.API {
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testService) Start(server p2p.Server) error {
|
func (t *testService) Start(server *p2p.Server) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -260,6 +260,30 @@ func (self *Conn) nodesUp() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Network) StartAll() error {
|
||||||
|
for _, node := range self.Nodes {
|
||||||
|
if node.Up {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := self.Start(node.ID()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Network) StopAll() error {
|
||||||
|
for _, node := range self.Nodes {
|
||||||
|
if !node.Up {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := self.Stop(node.ID()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Start(id) starts up the node (relevant only for instance with own p2p or remote)
|
// Start(id) starts up the node (relevant only for instance with own p2p or remote)
|
||||||
func (self *Network) Start(id *adapters.NodeId) error {
|
func (self *Network) Start(id *adapters.NodeId) error {
|
||||||
return self.startWithSnapshot(id, nil)
|
return self.startWithSnapshot(id, nil)
|
||||||
|
|
@ -275,6 +299,7 @@ func (self *Network) startWithSnapshot(id *adapters.NodeId, snapshot []byte) err
|
||||||
}
|
}
|
||||||
log.Trace(fmt.Sprintf("starting node %v: %v using %v", id, node.Up, self.nodeAdapter.Name()))
|
log.Trace(fmt.Sprintf("starting node %v: %v using %v", id, node.Up, self.nodeAdapter.Name()))
|
||||||
if err := node.Start(snapshot); err != nil {
|
if err := node.Start(snapshot); err != nil {
|
||||||
|
log.Warn(fmt.Sprintf("start up failed: %v", err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
node.Up = true
|
node.Up = true
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProtocolSession struct {
|
type ProtocolSession struct {
|
||||||
*adapters.SimNode
|
Server *p2p.Server
|
||||||
|
|
||||||
Ids []*adapters.NodeId
|
Ids []*adapters.NodeId
|
||||||
adapter *adapters.SimAdapter
|
adapter *adapters.SimAdapter
|
||||||
events chan *p2p.PeerEvent
|
events chan *p2p.PeerEvent
|
||||||
|
|
@ -58,7 +57,7 @@ func (self *ProtocolSession) trigger(trig Trigger) error {
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(self.Ids))
|
return fmt.Errorf("trigger: peer %v does not exist (1- %v)", trig.Peer, len(self.Ids))
|
||||||
}
|
}
|
||||||
mockNode, ok := simNode.Service().(*mockNode)
|
mockNode, ok := simNode.Service(&mockNode{}).(*mockNode)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("trigger: peer %v is not a mock", trig.Peer)
|
return fmt.Errorf("trigger: peer %v is not a mock", trig.Peer)
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +92,7 @@ func (self *ProtocolSession) expect(exp Expect) error {
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("trigger: peer %v does not exist (1- %v)", exp.Peer, len(self.Ids))
|
return fmt.Errorf("trigger: peer %v does not exist (1- %v)", exp.Peer, len(self.Ids))
|
||||||
}
|
}
|
||||||
mockNode, ok := simNode.Service().(*mockNode)
|
mockNode, ok := simNode.Service(&mockNode{}).(*mockNode)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("trigger: peer %v is not a mock", exp.Peer)
|
return fmt.Errorf("trigger: peer %v is not a mock", exp.Peer)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,16 @@ type ProtocolTester struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester {
|
func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester {
|
||||||
services := map[string]adapters.ServiceFunc{
|
//func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester {
|
||||||
"test": func(id *adapters.NodeId) node.Service {
|
services := adapters.Services {
|
||||||
return &testNode{run}
|
"test": func(id *adapters.NodeId, _ []byte) []node.Service {
|
||||||
|
return []node.Service{&testNode{run}}
|
||||||
},
|
},
|
||||||
"mock": func(id *adapters.NodeId) node.Service {
|
"mock": func(id *adapters.NodeId, _ []byte) []node.Service {
|
||||||
return newMockNode()
|
return []node.Service{newMockNode()}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
adapters.RegisterServices(services)
|
||||||
adapter := adapters.NewSimAdapter(services)
|
adapter := adapters.NewSimAdapter(services)
|
||||||
net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{})
|
net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{})
|
||||||
if _, err := net.NewNodeWithConfig(&adapters.NodeConfig{Id: id, Service: "test"}); err != nil {
|
if _, err := net.NewNodeWithConfig(&adapters.NodeConfig{Id: id, Service: "test"}); err != nil {
|
||||||
|
|
@ -47,7 +49,7 @@ func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.P
|
||||||
events := make(chan *p2p.PeerEvent, 1000)
|
events := make(chan *p2p.PeerEvent, 1000)
|
||||||
node.SubscribeEvents(events)
|
node.SubscribeEvents(events)
|
||||||
ps := &ProtocolSession{
|
ps := &ProtocolSession{
|
||||||
SimNode: node,
|
Server: node.Server(),
|
||||||
Ids: peerIDs,
|
Ids: peerIDs,
|
||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
events: events,
|
events: events,
|
||||||
|
|
@ -62,6 +64,10 @@ func NewProtocolTester(t *testing.T, id *adapters.NodeId, n int, run func(*p2p.P
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *ProtocolTester) Stop() error {
|
||||||
|
return self.Server.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
func (self *ProtocolTester) Connect(selfId *adapters.NodeId, peers ...*adapters.NodeConfig) {
|
func (self *ProtocolTester) Connect(selfId *adapters.NodeId, peers ...*adapters.NodeConfig) {
|
||||||
for _, peer := range peers {
|
for _, peer := range peers {
|
||||||
log.Trace(fmt.Sprintf("start node %v", peer.Id))
|
log.Trace(fmt.Sprintf("start node %v", peer.Id))
|
||||||
|
|
@ -86,14 +92,17 @@ type testNode struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testNode) Protocols() []p2p.Protocol {
|
func (t *testNode) Protocols() []p2p.Protocol {
|
||||||
return []p2p.Protocol{{Run: t.run}}
|
return []p2p.Protocol{{
|
||||||
|
Length: 100,
|
||||||
|
Run: t.run,
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testNode) APIs() []rpc.API {
|
func (t *testNode) APIs() []rpc.API {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testNode) Start(server p2p.Server) error {
|
func (t *testNode) Start(server *p2p.Server) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -287,3 +287,62 @@ func (self *BoolAddress) PO(val PotVal, pos int) (po int, eq bool) {
|
||||||
}
|
}
|
||||||
return po, true
|
return po, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BytesAddress interface {
|
||||||
|
Address() []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type bytesAddress struct {
|
||||||
|
bytes []byte
|
||||||
|
toBytes func(v AnyVal) []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBytesVal(v AnyVal, f func(v AnyVal) []byte) *bytesAddress {
|
||||||
|
if f == nil {
|
||||||
|
f = ToBytes
|
||||||
|
}
|
||||||
|
b := f(v)
|
||||||
|
return &bytesAddress{b, f}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToBytes(v AnyVal) []byte {
|
||||||
|
b, ok := v.([]byte)
|
||||||
|
if !ok {
|
||||||
|
ba, ok := v.(BytesAddress)
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("unsupported value type %T", v))
|
||||||
|
}
|
||||||
|
b = ba.Address()
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *bytesAddress) String() string {
|
||||||
|
return fmt.Sprintf("%08b", a.bytes)
|
||||||
|
}
|
||||||
|
func (a *bytesAddress) Address() []byte {
|
||||||
|
return a.bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *bytesAddress) PO(val PotVal, i int) (int, bool) {
|
||||||
|
return proximityOrder(a.bytes, a.toBytes(val), i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func proximityOrder(one, other []byte, pos int) (int, bool) {
|
||||||
|
for i := pos / 8; i < len(one); i++ {
|
||||||
|
if one[i] == other[i] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
oxo := one[i] ^ other[i]
|
||||||
|
start := 0
|
||||||
|
if i == pos/8 {
|
||||||
|
start = pos % 8
|
||||||
|
}
|
||||||
|
for j := start; j < 8; j++ {
|
||||||
|
if (uint8(oxo)>>uint8(7-j))&0x01 != 0 {
|
||||||
|
return i*8 + j, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(one) * 8, true
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ type PotVal interface {
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AnyVal interface{}
|
||||||
|
|
||||||
// Pot constructor. Requires value of type PotVal to pin
|
// Pot constructor. Requires value of type PotVal to pin
|
||||||
// and po to point to a span in the PotVal key
|
// and po to point to a span in the PotVal key
|
||||||
// The pinned item counts towards the size
|
// The pinned item counts towards the size
|
||||||
|
|
@ -245,11 +247,12 @@ func remove(t *pot, val PotVal) (r *pot, po int, found bool) {
|
||||||
// if f returns v' <> v then v' is inserted into the Pot
|
// if f returns v' <> v then v' is inserted into the Pot
|
||||||
// if v' == v the pot is not changed
|
// if v' == v the pot is not changed
|
||||||
// it panics if v'.PO(k, 0) says v and k are not equal
|
// it panics if v'.PO(k, 0) says v and k are not equal
|
||||||
func (t *Pot) Swap(val PotVal, f func(v PotVal) PotVal) (po int, found bool, change bool) {
|
func (t *Pot) Swap(val AnyVal, f func(v PotVal) PotVal) (po int, found bool, change bool) {
|
||||||
t.lock.Lock()
|
t.lock.Lock()
|
||||||
defer t.lock.Unlock()
|
defer t.lock.Unlock()
|
||||||
|
ba := NewBytesVal(val, nil)
|
||||||
var t0 *pot
|
var t0 *pot
|
||||||
t0, po, found, change = swap(t.pot, val, f)
|
t0, po, found, change = swap(t.pot, ba, f)
|
||||||
if change {
|
if change {
|
||||||
t.pot = t0
|
t.pot = t0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,59 +8,62 @@ import (
|
||||||
|
|
||||||
// discovery bzz overlay extension doing peer relaying
|
// discovery bzz overlay extension doing peer relaying
|
||||||
|
|
||||||
// messages related to peer discovery
|
|
||||||
var DiscoveryMsgs = []interface{}{
|
|
||||||
&getPeersMsg{},
|
|
||||||
&peersMsg{},
|
|
||||||
&subPeersMsg{},
|
|
||||||
}
|
|
||||||
|
|
||||||
type discPeer struct {
|
type discPeer struct {
|
||||||
Peer
|
*bzzPeer
|
||||||
overlay Overlay
|
overlay Overlay
|
||||||
peers map[string]bool
|
peers map[string]bool
|
||||||
proxLimit uint8 // the proximity radius advertised by remote to subscribe to peers
|
depth uint8 // the proximity radius advertised by remote to subscribe to peers
|
||||||
sentPeers bool // set to true when the peer is first notifed of peers close to them
|
sentPeers bool // set to true when the peer is first notifed of peers close to them
|
||||||
}
|
}
|
||||||
|
|
||||||
// discovery peer contructor
|
// NewDiscovery discovery peer contructor
|
||||||
// registers the handlers for discovery messages
|
func NewDiscovery(p *bzzPeer, o Overlay) *discPeer {
|
||||||
func NewDiscovery(p Peer, o Overlay) *discPeer {
|
|
||||||
self := &discPeer{
|
self := &discPeer{
|
||||||
overlay: o,
|
overlay: o,
|
||||||
Peer: p,
|
bzzPeer: p,
|
||||||
peers: make(map[string]bool),
|
peers: make(map[string]bool),
|
||||||
}
|
}
|
||||||
self.seen(self)
|
self.seen(self)
|
||||||
|
|
||||||
p.Register(&peersMsg{}, self.handlePeersMsg)
|
|
||||||
p.Register(&getPeersMsg{}, self.handleGetPeersMsg)
|
|
||||||
p.Register(&subPeersMsg{}, self.handleSubPeersMsg)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *discPeer) HandleMsg(msg interface{}) error {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
|
||||||
|
case *peersMsg:
|
||||||
|
return self.handlePeersMsg(msg)
|
||||||
|
|
||||||
|
case *getPeersMsg:
|
||||||
|
return self.handleGetPeersMsg(msg)
|
||||||
|
|
||||||
|
case *subPeersMsg:
|
||||||
|
return self.handleSubPeersMsg(msg)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown message type: %T", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NotifyPeer notifies the receiver remote end of a peer p or PO po.
|
// NotifyPeer notifies the receiver remote end of a peer p or PO po.
|
||||||
// callback for overlay driver
|
// callback for overlay driver
|
||||||
func (self *discPeer) NotifyPeer(p Peer, po uint8) error {
|
func (self *discPeer) NotifyPeer(a OverlayAddr, po uint8) error {
|
||||||
log.Warn(fmt.Sprintf("peer %#v peers %v", p, self.peers))
|
if po < self.depth || self.seen(a) {
|
||||||
if po < self.proxLimit || self.seen(p) {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
log.Warn(fmt.Sprintf("notification about %x", p.OverlayAddr()))
|
log.Warn(fmt.Sprintf("notification about %x", a.Address()))
|
||||||
|
|
||||||
resp := &peersMsg{
|
resp := &peersMsg{
|
||||||
Peers: []*peerAddr{&peerAddr{OAddr: p.OverlayAddr(), UAddr: p.UnderlayAddr()}}, // perhaps the PeerAddr interface is unnecessary generalization
|
Peers: []*bzzAddr{ToAddr(a)}, // perhaps the PeerAddr interface is unnecessary generalization
|
||||||
}
|
}
|
||||||
return self.Send(resp)
|
return self.Send(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifyProx sends a subPeers Msg to the receiver notifying them about
|
// NotifyDepth sends a subPeers Msg to the receiver notifying them about
|
||||||
// a change in the prox limit (radius of the set including the nearest X peers
|
// a change in the prox limit (radius of the set including the nearest X peers
|
||||||
// or first empty row)
|
// or first empty row)
|
||||||
// callback for overlay driver
|
// callback for overlay driver
|
||||||
func (self *discPeer) NotifyProx(po uint8) error {
|
func (self *discPeer) NotifyDepth(po uint8) error {
|
||||||
return self.Send(&subPeersMsg{ProxLimit: po})
|
return self.Send(&subPeersMsg{Depth: po})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -83,7 +86,7 @@ disconnected
|
||||||
// used for communicating about known peers
|
// used for communicating about known peers
|
||||||
// relevant for bootstrapping connectivity and updating peersets
|
// relevant for bootstrapping connectivity and updating peersets
|
||||||
type peersMsg struct {
|
type peersMsg struct {
|
||||||
Peers []*peerAddr
|
Peers []*bzzAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self peersMsg) String() string {
|
func (self peersMsg) String() string {
|
||||||
|
|
@ -102,28 +105,27 @@ func (self getPeersMsg) String() string {
|
||||||
|
|
||||||
// subPeers msg is communicating the depth/sharpness/focus of the overlay table of a peer
|
// subPeers msg is communicating the depth/sharpness/focus of the overlay table of a peer
|
||||||
type subPeersMsg struct {
|
type subPeersMsg struct {
|
||||||
ProxLimit uint8
|
Depth uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self subPeersMsg) String() string {
|
func (self subPeersMsg) String() string {
|
||||||
return fmt.Sprintf("%T: request peers > PO%02d. ", self, self.ProxLimit)
|
return fmt.Sprintf("%T: request peers > PO%02d. ", self, self.Depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *discPeer) handleSubPeersMsg(msg interface{}) error {
|
func (self *discPeer) handleSubPeersMsg(msg *subPeersMsg) error {
|
||||||
spm := msg.(*subPeersMsg)
|
self.depth = msg.Depth
|
||||||
self.proxLimit = spm.ProxLimit
|
|
||||||
if !self.sentPeers {
|
if !self.sentPeers {
|
||||||
var peers []*peerAddr
|
var peers []*bzzAddr
|
||||||
self.overlay.EachLivePeer(self.OverlayAddr(), 255, func(p Peer, po int, isproxbin bool) bool {
|
self.overlay.EachConn(self.Over(), 255, func(p OverlayConn, po int, isproxbin bool) bool {
|
||||||
if uint8(po) < self.proxLimit {
|
if uint8(po) < self.depth {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
log.Warn(fmt.Sprintf("peer %#v proxlimit %v", p, self.proxLimit))
|
if !self.seen(p) {
|
||||||
self.seen(p.(*discPeer).Peer)
|
peers = append(peers, ToAddr(p.Off()))
|
||||||
peers = append(peers, &peerAddr{p.OverlayAddr(), p.UnderlayAddr()})
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
log.Warn(fmt.Sprintf("found initial %v peers not farther than %v", len(peers), self.proxLimit))
|
log.Warn(fmt.Sprintf("found initial %v peers not farther than %v", len(peers), self.depth))
|
||||||
if len(peers) > 0 {
|
if len(peers) > 0 {
|
||||||
if err := self.Send(&peersMsg{Peers: peers}); err != nil {
|
if err := self.Send(&peersMsg{Peers: peers}); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -137,39 +139,41 @@ func (self *discPeer) handleSubPeersMsg(msg interface{}) error {
|
||||||
// handlePeersMsg called by the protocol when receiving peerset (for target address)
|
// handlePeersMsg called by the protocol when receiving peerset (for target address)
|
||||||
// list of nodes ([]PeerAddr in peersMsg) is added to the overlay db using the
|
// list of nodes ([]PeerAddr in peersMsg) is added to the overlay db using the
|
||||||
// Register interface method
|
// Register interface method
|
||||||
func (self *discPeer) handlePeersMsg(msg interface{}) error {
|
func (self *discPeer) handlePeersMsg(msg *peersMsg) error {
|
||||||
// register all addresses
|
// register all addresses
|
||||||
var nas []PeerAddr
|
if len(msg.Peers) == 0 {
|
||||||
for _, na := range msg.(*peersMsg).Peers {
|
|
||||||
addr := PeerAddr(na)
|
|
||||||
nas = append(nas, addr)
|
|
||||||
self.seen(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(nas) == 0 {
|
|
||||||
log.Debug(fmt.Sprintf("whoops, no peers in incoming peersMsg from %v", self))
|
log.Debug(fmt.Sprintf("whoops, no peers in incoming peersMsg from %v", self))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
log.Debug(fmt.Sprintf("got peer addresses from %x, %v (%v)", self.OverlayAddr(), nas, len(nas)))
|
|
||||||
return self.overlay.Register(nas...)
|
c := make(chan OverlayAddr)
|
||||||
|
go func() {
|
||||||
|
defer close(c)
|
||||||
|
for _, a := range msg.Peers {
|
||||||
|
self.seen(a)
|
||||||
|
c <- a
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
log.Info("discovery overlay register")
|
||||||
|
return self.overlay.Register(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleGetPeersMsg is called by the protocol when receiving a
|
// handleGetPeersMsg is called by the protocol when receiving a
|
||||||
// peerset (for target address) request
|
// peerset (for target address) request
|
||||||
// peers suggestions are retrieved from the overlay topology driver
|
// peers suggestions are retrieved from the overlay topology driver
|
||||||
// using the EachLivePeer interface iterator method
|
// using the EachConn interface iterator method
|
||||||
// peers sent are remembered throughout a session and not sent twice
|
// peers sent are remembered throughout a session and not sent twice
|
||||||
func (self *discPeer) handleGetPeersMsg(msg interface{}) error {
|
func (self *discPeer) handleGetPeersMsg(msg *getPeersMsg) error {
|
||||||
var peers []*peerAddr
|
var peers []*bzzAddr
|
||||||
req := msg.(*getPeersMsg)
|
|
||||||
i := 0
|
i := 0
|
||||||
self.overlay.EachLivePeer(self.OverlayAddr(), int(req.Order), func(n Peer, po int, isproxbin bool) bool {
|
self.overlay.EachConn(self.Over(), int(msg.Order), func(p OverlayConn, po int, isproxbin bool) bool {
|
||||||
i++
|
i++
|
||||||
// only send peers we have not sent before in this session
|
// only send peers we have not sent before in this session
|
||||||
if self.seen(n) {
|
a := ToAddr(p.Off())
|
||||||
peers = append(peers, &peerAddr{n.OverlayAddr(), n.UnderlayAddr()})
|
if self.seen(a) {
|
||||||
|
peers = append(peers, a)
|
||||||
}
|
}
|
||||||
return len(peers) < int(req.Max)
|
return len(peers) < int(msg.Max)
|
||||||
})
|
})
|
||||||
if len(peers) == 0 {
|
if len(peers) == 0 {
|
||||||
log.Debug(fmt.Sprintf("no peers found for %v", self))
|
log.Debug(fmt.Sprintf("no peers found for %v", self))
|
||||||
|
|
@ -189,9 +193,8 @@ func RequestOrder(k Overlay, order, broadcastSize, maxPeers uint8) {
|
||||||
}
|
}
|
||||||
var i uint8
|
var i uint8
|
||||||
//var err error
|
//var err error
|
||||||
k.EachLivePeer(nil, 255, func(n Peer, po int, isproxbin bool) bool {
|
k.EachConn(nil, 255, func(p OverlayConn, po int, isproxbin bool) bool {
|
||||||
log.Trace(fmt.Sprintf("%T sent to %v", req, n))
|
if err := p.(Conn).Send(req); err == nil {
|
||||||
if err := n.Send(req); err == nil {
|
|
||||||
i++
|
i++
|
||||||
if i >= broadcastSize {
|
if i >= broadcastSize {
|
||||||
return false
|
return false
|
||||||
|
|
@ -202,8 +205,8 @@ func RequestOrder(k Overlay, order, broadcastSize, maxPeers uint8) {
|
||||||
log.Info(fmt.Sprintf("requesting bees of PO%03d from %v/%v (each max %v)", order, i, broadcastSize, maxPeers))
|
log.Info(fmt.Sprintf("requesting bees of PO%03d from %v/%v (each max %v)", order, i, broadcastSize, maxPeers))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *discPeer) seen(p PeerAddr) bool {
|
func (self *discPeer) seen(p OverlayPeer) bool {
|
||||||
k := NodeId(p).NodeID.String()
|
k := string(p.Address())
|
||||||
if self.peers[k] {
|
if self.peers[k] {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,29 +16,24 @@ import (
|
||||||
func TestDiscovery(t *testing.T) {
|
func TestDiscovery(t *testing.T) {
|
||||||
addr := RandomAddr()
|
addr := RandomAddr()
|
||||||
to := NewKademlia(addr.OAddr, NewKadParams())
|
to := NewKademlia(addr.OAddr, NewKadParams())
|
||||||
ct := BzzCodeMap(DiscoveryMsgs...)
|
|
||||||
|
|
||||||
services := func(p Peer) error {
|
run := func(p *bzzPeer) error {
|
||||||
dp := NewDiscovery(p, to)
|
dp := NewDiscovery(p, to)
|
||||||
to.On(dp)
|
to.On(p)
|
||||||
|
defer to.Off(p)
|
||||||
log.Trace(fmt.Sprintf("kademlia on %v", p))
|
log.Trace(fmt.Sprintf("kademlia on %v", p))
|
||||||
p.DisconnectHook(func(err error) {
|
return p.Run(dp.HandleMsg)
|
||||||
to.Off(p)
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s := newBzzBaseTester(t, 1, addr, ct, services)
|
s := newBzzBaseTester(t, 1, addr, DiscoverySpec, run)
|
||||||
defer s.Stop()
|
defer s.Stop()
|
||||||
|
|
||||||
s.runHandshakes()
|
|
||||||
// o := 0
|
|
||||||
s.TestExchanges(p2ptest.Exchange{
|
s.TestExchanges(p2ptest.Exchange{
|
||||||
Label: "outgoing SubPeersMsg",
|
Label: "outgoing SubPeersMsg",
|
||||||
Expects: []p2ptest.Expect{
|
Expects: []p2ptest.Expect{
|
||||||
p2ptest.Expect{
|
p2ptest.Expect{
|
||||||
Code: 3,
|
Code: 3,
|
||||||
Msg: &subPeersMsg{ProxLimit: 0},
|
Msg: &subPeersMsg{Depth: 0},
|
||||||
Peer: s.ProtocolTester.Ids[0],
|
Peer: s.ProtocolTester.Ids[0],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package network
|
package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -33,75 +34,89 @@ it uses an Overlay Topology driver (e.g., generic kademlia nodetable)
|
||||||
to find best peer list for any target
|
to find best peer list for any target
|
||||||
this is used by the netstore to search for content in the swarm
|
this is used by the netstore to search for content in the swarm
|
||||||
|
|
||||||
It handles the bzz protocol getPeersMsg peersMsg exchange
|
It handles the hive protocol getPeersMsg peersMsg exchange
|
||||||
and relay the peer request process to the Overlay module
|
and relay the peer request process to the Overlay module
|
||||||
|
|
||||||
peer connections and disconnections are reported and registered
|
peer connections and disconnections are reported and registered
|
||||||
to keep the nodetable uptodate
|
to keep the nodetable uptodate
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Overlay is the interface to Jaak ahd ka)a
|
||||||
type Overlay interface {
|
type Overlay interface {
|
||||||
Register(...PeerAddr) error
|
Register(chan OverlayAddr) error
|
||||||
|
|
||||||
On(Peer)
|
On(OverlayConn)
|
||||||
Off(Peer)
|
Off(OverlayConn)
|
||||||
|
|
||||||
EachLivePeer([]byte, int, func(Peer, int, bool) bool)
|
EachConn([]byte, int, func(OverlayConn, int, bool) bool)
|
||||||
EachPeer([]byte, int, func(PeerAddr, int) bool)
|
EachAddr([]byte, int, func(OverlayAddr, int) bool)
|
||||||
|
|
||||||
SuggestPeer() (PeerAddr, int, bool)
|
SuggestPeer() (OverlayAddr, int, bool)
|
||||||
|
|
||||||
String() string
|
String() string
|
||||||
GetAddr() PeerAddr
|
BaseAddr() []byte
|
||||||
|
Healthy([][]byte) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// HiveParams holds the config options to hive
|
||||||
|
type HiveParams struct {
|
||||||
|
Discovery bool // if want discovery of not
|
||||||
|
PeersBroadcastSetSize uint8 // how many peers to use when relaying
|
||||||
|
MaxPeersPerRequest uint8 // max size for peer address batches
|
||||||
|
KeepAliveInterval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHiveParams returns hive config with only the
|
||||||
|
func NewHiveParams() *HiveParams {
|
||||||
|
return &HiveParams{
|
||||||
|
Discovery: true,
|
||||||
|
PeersBroadcastSetSize: 2,
|
||||||
|
MaxPeersPerRequest: 5,
|
||||||
|
KeepAliveInterval: time.Second,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hive implements the PeerPool interface
|
// Hive implements the PeerPool interface
|
||||||
type Hive struct {
|
type Hive struct {
|
||||||
*HiveParams // settings
|
*HiveParams // settings
|
||||||
Overlay // the overlay topology driver
|
Overlay // the overlay topology driver
|
||||||
lock sync.Mutex
|
store StateStore
|
||||||
quit chan bool
|
|
||||||
toggle chan bool
|
|
||||||
more chan bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type HiveParams struct {
|
// bookkeeping
|
||||||
Discovery bool
|
lock sync.Mutex
|
||||||
PeersBroadcastSetSize uint8
|
quit chan bool
|
||||||
MaxPeersPerRequest uint8
|
more chan bool
|
||||||
CallInterval uint
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHiveParams() *HiveParams {
|
tick <-chan time.Time
|
||||||
return &HiveParams{
|
|
||||||
Discovery: true,
|
|
||||||
PeersBroadcastSetSize: 2,
|
|
||||||
MaxPeersPerRequest: 5,
|
|
||||||
CallInterval: 1000,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hive constructor embeds both arguments
|
// Hive constructor embeds both arguments
|
||||||
// HiveParams config parameters
|
// HiveParams: config parameters
|
||||||
// Overlay Topology Driver Interface
|
// Overlay: Topology Driver Interface
|
||||||
func NewHive(params *HiveParams, overlay Overlay) *Hive {
|
func NewHive(params *HiveParams, overlay Overlay, store StateStore) *Hive {
|
||||||
return &Hive{
|
return &Hive{
|
||||||
HiveParams: params,
|
HiveParams: params,
|
||||||
Overlay: overlay,
|
Overlay: overlay,
|
||||||
|
store: store,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start receives network info only at startup
|
// Start receives network info only at startup
|
||||||
// connectPeer is a function to connect to a peer based on its NodeID or enode URL
|
// server is used to connect to a peer based on its NodeID or enode URL
|
||||||
// these are called on the p2p.Server which runs on the node
|
// these are called on the p2p.Server which runs on the node
|
||||||
// af() returns an arbitrary ticker channel
|
// af() returns an arbitrary ticker channel
|
||||||
func (self *Hive) Start(server p2p.Server, af func() <-chan time.Time) error {
|
// rw is a read writer for json configs
|
||||||
|
func (self *Hive) Start(server *p2p.Server) error {
|
||||||
self.toggle = make(chan bool)
|
if self.store != nil {
|
||||||
|
if err := self.loadPeers(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
self.more = make(chan bool, 1)
|
self.more = make(chan bool, 1)
|
||||||
self.quit = make(chan bool)
|
self.quit = make(chan bool)
|
||||||
log.Debug("hive started")
|
log.Debug("hive started")
|
||||||
// this loop is doing bootstrapping and maintains a healthy table
|
// this loop is doing bootstrapping and maintains a healthy table
|
||||||
go self.keepAlive(af)
|
go self.keepAlive()
|
||||||
go func() {
|
go func() {
|
||||||
// each iteration, ask kademlia about most preferred peer
|
// each iteration, ask kademlia about most preferred peer
|
||||||
for more := range self.more {
|
for more := range self.more {
|
||||||
|
|
@ -110,14 +125,15 @@ func (self *Hive) Start(server p2p.Server, af func() <-chan time.Time) error {
|
||||||
// to attempt to write to more (remove Peer when shutting down)
|
// to attempt to write to more (remove Peer when shutting down)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Trace("hive delegate to overlay driver: suggest addr to connect to")
|
log.Debug("hive delegate to overlay driver: suggest addr to connect to")
|
||||||
|
// log.Trace("hive delegate to overlay driver: suggest addr to connect to")
|
||||||
addr, order, want := self.SuggestPeer()
|
addr, order, want := self.SuggestPeer()
|
||||||
|
|
||||||
if addr != nil {
|
if addr != nil {
|
||||||
log.Info(fmt.Sprintf("========> connect to bee %v", addr))
|
log.Info(fmt.Sprintf("========> connect to bee %v", addr))
|
||||||
node, err := discover.ParseNode(NodeId(addr).NodeID.String())
|
under, err := discover.ParseNode(string(addr.(Addr).Under()))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
server.AddPeer(node)
|
server.AddPeer(under)
|
||||||
} else {
|
} else {
|
||||||
log.Error(fmt.Sprintf("===X====> connect to bee %v failed: invalid node URL: %v", addr, err))
|
log.Error(fmt.Sprintf("===X====> connect to bee %v failed: invalid node URL: %v", addr, err))
|
||||||
}
|
}
|
||||||
|
|
@ -127,75 +143,44 @@ func (self *Hive) Start(server p2p.Server, af func() <-chan time.Time) error {
|
||||||
|
|
||||||
want = want && self.Discovery
|
want = want && self.Discovery
|
||||||
if want {
|
if want {
|
||||||
go RequestOrder(self.Overlay, uint8(order), self.PeersBroadcastSetSize, self.MaxPeersPerRequest)
|
log.Debug(fmt.Sprintf("========> request peers nearest %v", addr))
|
||||||
|
RequestOrder(self.Overlay, uint8(order), self.PeersBroadcastSetSize, self.MaxPeersPerRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Info(fmt.Sprintf("%v", self))
|
||||||
select {
|
select {
|
||||||
case self.toggle <- want:
|
|
||||||
log.Trace(fmt.Sprintf("keep hive alive: %v", want))
|
|
||||||
case <-self.quit:
|
case <-self.quit:
|
||||||
return
|
return
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
log.Info(fmt.Sprintf("%v", self))
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Hive) ticker() <-chan time.Time {
|
// Stop terminates the updateloop and saves the peers
|
||||||
return time.NewTicker(time.Duration(self.CallInterval) * time.Millisecond).C
|
func (self *Hive) Stop() {
|
||||||
}
|
if self.store != nil {
|
||||||
|
self.savePeers()
|
||||||
// keepAlive is a forever loop
|
|
||||||
// in its awake state it periodically triggers connection attempts
|
|
||||||
// by writing to self.more until Kademlia Table is saturated
|
|
||||||
// wake state is toggled by writing to self.toggle
|
|
||||||
// it restarts if the table becomes non-full again due to disconnections
|
|
||||||
func (self *Hive) keepAlive(af func() <-chan time.Time) {
|
|
||||||
log.Trace("keep alive loop started")
|
|
||||||
alarm := af()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-alarm:
|
|
||||||
log.Trace("wake up: make hive alive")
|
|
||||||
self.wake()
|
|
||||||
case need := <-self.toggle:
|
|
||||||
if alarm == nil && need {
|
|
||||||
alarm = af()
|
|
||||||
}
|
|
||||||
// if hive saturated, no more peers asked
|
|
||||||
if alarm != nil && !need {
|
|
||||||
alarm = nil
|
|
||||||
}
|
|
||||||
case <-self.quit:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// closing toggle channel quits the updateloop
|
||||||
|
close(self.quit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add is called at the end of a successful protocol handshake
|
func (self *Hive) Run(p *bzzPeer) error {
|
||||||
// to register a connected (live) peer
|
dp := NewDiscovery(p, self)
|
||||||
func (self *Hive) Add(p Peer) error {
|
|
||||||
defer self.wake()
|
|
||||||
dp := NewDiscovery(p, self.Overlay)
|
|
||||||
log.Debug(fmt.Sprintf("to add new bee %v", p))
|
log.Debug(fmt.Sprintf("to add new bee %v", p))
|
||||||
self.On(dp)
|
self.On(dp)
|
||||||
self.String()
|
self.wake()
|
||||||
log.Debug(fmt.Sprintf("%v", self))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove called after peer is disconnected
|
|
||||||
func (self *Hive) Remove(p Peer) {
|
|
||||||
defer self.wake()
|
defer self.wake()
|
||||||
log.Debug(fmt.Sprintf("remove bee %v", p))
|
defer self.Off(dp)
|
||||||
self.Off(p)
|
return p.Run(dp.HandleMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeInfo function is used by the p2p.server RPC interface to display
|
// NodeInfo function is used by the p2p.server RPC interface to display
|
||||||
// protocol specific node information
|
// protocol specific node information
|
||||||
func (self *Hive) NodeInfo() interface{} {
|
func (self *Hive) NodeInfo() interface{} {
|
||||||
return interface{}(self.String())
|
return self.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeerInfo function is used by the p2p.server RPC interface to display
|
// PeerInfo function is used by the p2p.server RPC interface to display
|
||||||
|
|
@ -203,22 +188,16 @@ func (self *Hive) NodeInfo() interface{} {
|
||||||
func (self *Hive) PeerInfo(id discover.NodeID) interface{} {
|
func (self *Hive) PeerInfo(id discover.NodeID) interface{} {
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
addr := NewPeerAddrFromNodeId(adapters.NewNodeId(id[:]))
|
addr := NewAddrFromNodeId(adapters.NewNodeId(id[:]))
|
||||||
return interface{}(addr)
|
return interface{}(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop terminates the updateloop
|
func (self *Hive) Register(peers chan OverlayAddr) error {
|
||||||
func (self *Hive) Stop() {
|
defer self.wake()
|
||||||
// closing toggle channel quits the updateloop
|
return self.Overlay.Register(peers)
|
||||||
close(self.quit)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Hive) Healthy() bool {
|
|
||||||
// TODO: determine if we have enough peers to consider the network
|
|
||||||
// to be healthy
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wake triggers
|
||||||
func (self *Hive) wake() {
|
func (self *Hive) wake() {
|
||||||
select {
|
select {
|
||||||
case self.more <- true:
|
case self.more <- true:
|
||||||
|
|
@ -229,7 +208,100 @@ func (self *Hive) wake() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HexToBytes reads a hex string ontp
|
||||||
func HexToBytes(s string) []byte {
|
func HexToBytes(s string) []byte {
|
||||||
id := discover.MustHexID(s)
|
id := discover.MustHexID(s)
|
||||||
return id[:]
|
return id[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToAddr returns the serialisable version of u
|
||||||
|
func ToAddr(pa OverlayPeer) *bzzAddr {
|
||||||
|
if addr, ok := pa.(*bzzAddr); ok {
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
if p, ok := pa.(*discPeer); ok {
|
||||||
|
return p.bzzAddr
|
||||||
|
}
|
||||||
|
return pa.(*bzzPeer).bzzAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
type hiveTicker interface {
|
||||||
|
Ch() <-chan time.Time
|
||||||
|
Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
type timeTicker struct {
|
||||||
|
*time.Ticker
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *timeTicker) Ch() <-chan time.Time {
|
||||||
|
return t.C
|
||||||
|
}
|
||||||
|
|
||||||
|
// keepAlive is a forever loop
|
||||||
|
// in its awake state it periodically triggers connection attempts
|
||||||
|
// by writing to self.more until Kademlia Table is saturated
|
||||||
|
// wake state is toggled by writing to self.toggle
|
||||||
|
// it goes to sleep mode if table is saturated
|
||||||
|
// it restarts if the table becomes non-full again due to disconnections
|
||||||
|
func (self *Hive) keepAlive() {
|
||||||
|
if self.tick == nil {
|
||||||
|
ticker := time.NewTicker(self.KeepAliveInterval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
self.tick = ticker.C
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-self.tick:
|
||||||
|
log.Debug("wake up: make hive alive")
|
||||||
|
self.wake()
|
||||||
|
case <-self.quit:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadPeers, savePeer implement persistence callback/
|
||||||
|
func (self *Hive) loadPeers() error {
|
||||||
|
data, err := self.store.Load("peers")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if data == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var as []*bzzAddr
|
||||||
|
if err := json.Unmarshal(data, &as); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c := make(chan OverlayAddr)
|
||||||
|
go func() {
|
||||||
|
defer close(c)
|
||||||
|
for _, a := range as {
|
||||||
|
c <- a
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return self.Overlay.Register(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// savePeers, savePeer implement persistence callback/
|
||||||
|
func (self *Hive) savePeers() error {
|
||||||
|
var peers []*bzzAddr
|
||||||
|
self.Overlay.EachAddr(nil, 256, func(pa OverlayAddr, i int) bool {
|
||||||
|
if pa == nil {
|
||||||
|
log.Warn(fmt.Sprintf("empty addr: %v", i))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
peers = append(peers, ToAddr(pa))
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
data, err := json.Marshal(peers)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not encode peers: %v", err)
|
||||||
|
}
|
||||||
|
if err := self.store.Save("peers", data); err != nil {
|
||||||
|
return fmt.Errorf("could not save peers: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,67 +1,19 @@
|
||||||
package network
|
package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
|
||||||
p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
|
p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testConnect struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
conns []string
|
|
||||||
connectf func(c string) error
|
|
||||||
ticker chan time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *testConnect) ping() <-chan time.Time {
|
|
||||||
return self.ticker
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *testConnect) connect(na string) error {
|
|
||||||
self.mu.Lock()
|
|
||||||
defer self.mu.Unlock()
|
|
||||||
self.conns = append(self.conns, na)
|
|
||||||
self.connectf(na)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHiveTester(t *testing.T, params *HiveParams) (*bzzTester, *Hive) {
|
func newHiveTester(t *testing.T, params *HiveParams) (*bzzTester, *Hive) {
|
||||||
// setup
|
// setup
|
||||||
addr := RandomAddr() // tested peers peer address
|
addr := RandomAddr() // tested peers peer address
|
||||||
to := NewTestOverlay(addr.OverlayAddr()) // overlay topology drive
|
to := NewKademlia(addr.OAddr, NewKadParams())
|
||||||
pp := NewHive(params, to) // hive
|
pp := NewHive(params, to, nil) // hive
|
||||||
|
|
||||||
ct := BzzCodeMap(DiscoveryMsgs...) // bzz protocol code map
|
|
||||||
services := func(p Peer) error {
|
|
||||||
pp.Add(p)
|
|
||||||
p.DisconnectHook(func(err error) {
|
|
||||||
pp.Remove(p)
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return newBzzBaseTester(t, 1, addr, ct, services), pp
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOverlayRegistration(t *testing.T) {
|
|
||||||
params := NewHiveParams()
|
|
||||||
params.Discovery = false
|
|
||||||
s, pp := newHiveTester(t, params)
|
|
||||||
defer s.Stop()
|
|
||||||
|
|
||||||
id := s.Ids[0]
|
|
||||||
raddr := NewPeerAddrFromNodeId(id)
|
|
||||||
|
|
||||||
s.runHandshakes()
|
|
||||||
|
|
||||||
// hive should have called the overlay
|
|
||||||
if pp.Overlay.(*testOverlay).posMap[string(raddr.OverlayAddr())] == nil {
|
|
||||||
t.Fatalf("Overlay#On not called on new peer")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return newBzzBaseTester(t, 1, addr, DiscoverySpec, pp.Run), pp
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRegisterAndConnect(t *testing.T) {
|
func TestRegisterAndConnect(t *testing.T) {
|
||||||
|
|
@ -70,30 +22,23 @@ func TestRegisterAndConnect(t *testing.T) {
|
||||||
defer s.Stop()
|
defer s.Stop()
|
||||||
|
|
||||||
id := s.Ids[0]
|
id := s.Ids[0]
|
||||||
raddr := NewPeerAddrFromNodeId(id)
|
raddr := NewAddrFromNodeId(id)
|
||||||
|
|
||||||
pp.Register(raddr)
|
ch := make(chan OverlayAddr)
|
||||||
|
go func() {
|
||||||
|
ch <- raddr
|
||||||
|
close(ch)
|
||||||
|
}()
|
||||||
|
pp.Register(ch)
|
||||||
|
|
||||||
// start the hive and wait for the connection
|
// start the hive and wait for the connection
|
||||||
tc := &testConnect{
|
tick := make(chan time.Time)
|
||||||
connectf: func(c string) error {
|
pp.tick = tick
|
||||||
s.Connect(adapters.NewNodeIdFromHex(c))
|
pp.Start(s.Server)
|
||||||
return nil
|
|
||||||
},
|
|
||||||
ticker: make(chan time.Time),
|
|
||||||
}
|
|
||||||
pp.Start(s, tc.ping)
|
|
||||||
defer pp.Stop()
|
defer pp.Stop()
|
||||||
tc.ticker <- time.Now()
|
tick <- time.Now()
|
||||||
|
|
||||||
s.runHandshakes()
|
|
||||||
|
|
||||||
if pp.Overlay.(*testOverlay).posMap[string(raddr.OverlayAddr())] == nil {
|
|
||||||
t.Fatalf("Overlay#On not called on new peer")
|
|
||||||
}
|
|
||||||
|
|
||||||
// retrieve and broadcast
|
// retrieve and broadcast
|
||||||
ord := order(raddr.OverlayAddr())
|
ord := raddr.Over()[0] / 32
|
||||||
o := 0
|
o := 0
|
||||||
if ord == 0 {
|
if ord == 0 {
|
||||||
o = 1
|
o = 1
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||||
"github.com/ethereum/go-ethereum/pot"
|
"github.com/ethereum/go-ethereum/pot"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -46,24 +48,20 @@ a guaranteed constant maximum limit on the number of hops needed to reach one
|
||||||
node from the other.
|
node from the other.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type KadDiscovery interface {
|
// KadParams holds the config params for Kademlia
|
||||||
NotifyPeer(Peer, uint8) error
|
|
||||||
NotifyProx(uint8) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type KadParams struct {
|
type KadParams struct {
|
||||||
// adjustable parameters
|
// adjustable parameters
|
||||||
MaxProxDisplay int
|
MaxProxDisplay int // number of rows the table shows
|
||||||
MinProxBinSize int
|
MinProxBinSize int // nearest neighbour core minimum cardinality
|
||||||
MinBinSize int
|
MinBinSize int // minimum number of peers in a row
|
||||||
MaxBinSize int
|
MaxBinSize int // maximum number of peers in a row before pruning
|
||||||
RetryInterval int
|
RetryInterval int // initial interval before a peer is first redialed
|
||||||
RetryExponent int
|
RetryExponent int // exponent to multiply retry intervals with
|
||||||
MaxRetries int
|
MaxRetries int // maximum number of redial attempts
|
||||||
PruneInterval int
|
PruneInterval int // interval between peer pruning cycles
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewKadParams() returns a params struct with default values
|
// NewKadParams returns a params struct with default values
|
||||||
func NewKadParams() *KadParams {
|
func NewKadParams() *KadParams {
|
||||||
return &KadParams{
|
return &KadParams{
|
||||||
MaxProxDisplay: 8,
|
MaxProxDisplay: 8,
|
||||||
|
|
@ -79,141 +77,103 @@ func NewKadParams() *KadParams {
|
||||||
|
|
||||||
// Kademlia is a table of live peers and a db of known peers
|
// Kademlia is a table of live peers and a db of known peers
|
||||||
type Kademlia struct {
|
type Kademlia struct {
|
||||||
addr PeerAddr // immutable baseaddress of the table
|
*KadParams // Kademlia configuration parameters
|
||||||
// addr *pot.HashAddress // immutable baseaddress of the table
|
base []byte // immutable baseaddress of the table
|
||||||
*KadParams // Kademlia configuration parameters
|
addrs *pot.Pot // pots container for known peer addresses
|
||||||
addrs, peers *pot.Pot // pots container for peers
|
conns *pot.Pot // pots container for live peer connections
|
||||||
lastProxLimit uint8 // stores the last calculated proxlimit
|
depth uint8 // stores the last calculated depth
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewKademlia(addr, params) creates a Kademlia table for base address addr
|
// NewKademlia creates a Kademlia table for base address addr
|
||||||
// with parameters as in params
|
// with parameters as in params
|
||||||
// if params is nil, it uses default values
|
// if params is nil, it uses default values
|
||||||
func NewKademlia(addr []byte, params *KadParams) *Kademlia {
|
func NewKademlia(addr []byte, params *KadParams) *Kademlia {
|
||||||
if params == nil {
|
if params == nil {
|
||||||
params = NewKadParams()
|
params = NewKadParams()
|
||||||
}
|
}
|
||||||
self := &Kademlia{
|
return &Kademlia{
|
||||||
addr: &peerAddr{OAddr: addr},
|
base: addr,
|
||||||
KadParams: params,
|
KadParams: params,
|
||||||
addrs: pot.NewPot(nil, 0),
|
addrs: pot.NewPot(nil, 0),
|
||||||
peers: pot.NewPot(nil, 0),
|
conns: pot.NewPot(nil, 0),
|
||||||
}
|
}
|
||||||
return self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prune implements a forever loop reacting to a ticker time channel given
|
type Notifier interface {
|
||||||
// as the first argument
|
NotifyPeer(OverlayAddr, uint8) error
|
||||||
// the loop quits if the channel is closed
|
NotifyDepth(uint8) error
|
||||||
// it checks each kademlia bin and if the peer count is higher than
|
|
||||||
// the MaxBinSize parameter it drops the oldest n peers such that
|
|
||||||
// the bin is reduced to MinBinSize peers thus leaving slots to newly
|
|
||||||
// connecting peers
|
|
||||||
func (self *Kademlia) Prune(c <-chan time.Time) {
|
|
||||||
go func() {
|
|
||||||
for _ = range c {
|
|
||||||
log.Debug("pruning...")
|
|
||||||
total := 0
|
|
||||||
self.peers.EachBin(self.addr, 0, func(po, size int, f func(func(pot.PotVal, int) bool) bool) bool {
|
|
||||||
extra := size - self.MinBinSize
|
|
||||||
if size > self.MaxBinSize {
|
|
||||||
n := 0
|
|
||||||
f(func(v pot.PotVal, po int) bool {
|
|
||||||
p := v.(*KadPeer).Peer
|
|
||||||
if p != nil {
|
|
||||||
p.Drop(fmt.Errorf("bucket full"))
|
|
||||||
}
|
|
||||||
n++
|
|
||||||
return n < extra
|
|
||||||
})
|
|
||||||
total += extra
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
log.Debug(fmt.Sprintf("pruned %v peers", total))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// KadPeer represents a Kademlia Peer and extends
|
// OverlayPeer interface captures the common aspect of view of a peer from the Overlay
|
||||||
// * PeerAddr interface (overlay and underlay addresses)
|
// topology driver
|
||||||
// * Peer interface (id, last seen, drop)
|
type OverlayPeer interface {
|
||||||
type KadPeer struct {
|
Address() []byte
|
||||||
PeerAddr
|
}
|
||||||
Peer Peer
|
|
||||||
|
// OverlayConn represents a connected peer
|
||||||
|
type OverlayConn interface {
|
||||||
|
OverlayPeer
|
||||||
|
Drop(error) // call to indicate a peer should be expunged
|
||||||
|
Off() OverlayAddr // call to return a persitent OverlayAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
type OverlayAddr interface {
|
||||||
|
OverlayPeer
|
||||||
|
Update(OverlayAddr) OverlayAddr // returns the updated version of the original
|
||||||
|
}
|
||||||
|
|
||||||
|
// entry represents a Kademlia table entry (an extension of OverlayPeer)
|
||||||
|
// implements the pot.PotVal interface via BytesAddress, so entry can be
|
||||||
|
// used directly as a pot element
|
||||||
|
type entry struct {
|
||||||
|
pot.PotVal
|
||||||
|
OverlayPeer
|
||||||
seenAt time.Time
|
seenAt time.Time
|
||||||
retries int
|
retries int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *KadPeer) String() string {
|
// newEntry creates a kademlia peer from an OverlayPeer interface
|
||||||
if self == nil {
|
func newEntry(p OverlayPeer) *entry {
|
||||||
return "<nil>"
|
return &entry{
|
||||||
}
|
PotVal: pot.NewBytesVal(p, nil),
|
||||||
return fmt.Sprintf("%x", self.OverlayAddr())
|
OverlayPeer: p,
|
||||||
}
|
seenAt: time.Now(),
|
||||||
|
|
||||||
func (self *Kademlia) callable(val pot.PotVal) *KadPeer {
|
|
||||||
kp := val.(*KadPeer)
|
|
||||||
// not callable if peer is live or exceeded maxRetries
|
|
||||||
if kp.Peer != nil || kp.retries > self.MaxRetries {
|
|
||||||
log.Trace(fmt.Sprintf("peer %v (%T) not callable", kp, kp.Peer))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// calculate the allowed number of retries based on time lapsed since last seen
|
|
||||||
timeAgo := time.Since(kp.seenAt)
|
|
||||||
var retries int
|
|
||||||
for delta := int(timeAgo) / self.RetryInterval; delta > 0; delta /= self.RetryExponent {
|
|
||||||
log.Trace(fmt.Sprintf("delta: %v", delta))
|
|
||||||
retries++
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is never called concurrently, so safe to increment
|
|
||||||
// peer can be retried again
|
|
||||||
if retries < kp.retries {
|
|
||||||
log.Trace(fmt.Sprintf("log time needed before retry %v, wait only warrants %v", kp.retries, retries))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
kp.retries++
|
|
||||||
log.Trace(fmt.Sprintf("peer %v is callable", kp))
|
|
||||||
|
|
||||||
return kp
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewKadPeer creates a kademlia peer from a PeerAddr interface
|
|
||||||
func NewKadPeer(na PeerAddr) *KadPeer {
|
|
||||||
return &KadPeer{
|
|
||||||
PeerAddr: na,
|
|
||||||
seenAt: time.Now(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// retrieve the base address
|
func (self *entry) addr() OverlayAddr {
|
||||||
// which is the overlayaddress used by peers to reach us
|
a, _ := self.OverlayPeer.(OverlayAddr)
|
||||||
func (self *Kademlia) GetAddr() PeerAddr {
|
return a
|
||||||
return self.addr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register(nas) enters each PeerAddr as kademlia peers into the
|
func (self *entry) conn() OverlayConn {
|
||||||
// database of known peers
|
c, _ := self.OverlayPeer.(OverlayConn)
|
||||||
func (self *Kademlia) Register(nas ...PeerAddr) error {
|
return c
|
||||||
label := fmt.Sprintf("%x", RandomAddr().OverlayAddr())
|
}
|
||||||
|
|
||||||
|
func (self *entry) String() string {
|
||||||
|
return fmt.Sprintf("%x", self.OverlayPeer.Address())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register enters each OverlayAddr as kademlia peer record into the
|
||||||
|
// database of known peer addresses
|
||||||
|
func (self *Kademlia) Register(peers chan OverlayAddr) error {
|
||||||
np := pot.NewPot(nil, 0)
|
np := pot.NewPot(nil, 0)
|
||||||
for _, na := range nas {
|
for p := range peers {
|
||||||
if bytes.Equal(na.OverlayAddr(), self.addr.OverlayAddr()) {
|
// error if self received, peer should know better
|
||||||
log.Warn(fmt.Sprintf("[%06s] add peers: %x is self.. skipped ", label, self.addr.OverlayAddr()))
|
if bytes.Equal(p.Address(), self.base) {
|
||||||
continue
|
return fmt.Errorf("add peers: %x is self", self.base)
|
||||||
}
|
}
|
||||||
p := NewKadPeer(na)
|
np, _, _ = pot.Add(np, newEntry(p))
|
||||||
np, _, _ = pot.Add(np, pot.PotVal(p))
|
|
||||||
}
|
}
|
||||||
oldpeers := pot.NewPot(nil, 0)
|
com := self.addrs.Merge(np)
|
||||||
oldpeers.Merge(self.addrs)
|
log.Debug(fmt.Sprintf("merged %v peers, %v known, total: %v", np.Size(), com, self.addrs.Size()))
|
||||||
self.addrs.Merge(np)
|
// log.Trace(fmt.Sprintf("merged %v peers, %v known", np.Size(), com))
|
||||||
|
|
||||||
|
// TODO: remove this check
|
||||||
m := make(map[string]bool)
|
m := make(map[string]bool)
|
||||||
self.addrs.Each(func(val pot.PotVal, i int) bool {
|
self.addrs.Each(func(val pot.PotVal, i int) bool {
|
||||||
_, found := m[val.String()]
|
_, found := m[val.String()]
|
||||||
// TODO: remove this check
|
|
||||||
// log.Debug(fmt.Sprintf("-> %v %v", val, i))
|
|
||||||
if found {
|
if found {
|
||||||
panic("duplicate found")
|
panic("duplicate found")
|
||||||
}
|
}
|
||||||
|
|
@ -223,148 +183,34 @@ func (self *Kademlia) Register(nas ...PeerAddr) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// On(p) inserts the peer as a kademlia peer into the live peers
|
|
||||||
func (self *Kademlia) On(p Peer) {
|
|
||||||
kp := NewKadPeer(p)
|
|
||||||
kp.Peer = p
|
|
||||||
self.peers.Swap(kp, func(v pot.PotVal) pot.PotVal {
|
|
||||||
// if not found live
|
|
||||||
if v == nil {
|
|
||||||
// switch the offline peer
|
|
||||||
self.addrs.Swap(kp, func(v pot.PotVal) pot.PotVal {
|
|
||||||
return pot.PotVal(kp)
|
|
||||||
})
|
|
||||||
// insert new peer
|
|
||||||
return pot.PotVal(kp)
|
|
||||||
}
|
|
||||||
// found among live peers, do nothing
|
|
||||||
return v
|
|
||||||
})
|
|
||||||
prox := self.proxLimit()
|
|
||||||
|
|
||||||
vp, ok := kp.Peer.(KadDiscovery)
|
|
||||||
if !ok {
|
|
||||||
// log.Trace(fmt.Sprintf("not discovery peer %T", kp))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go vp.NotifyProx(uint8(prox))
|
|
||||||
f := func(val pot.PotVal, po int) {
|
|
||||||
dp := val.(*KadPeer).Peer.(KadDiscovery)
|
|
||||||
log.Debug(fmt.Sprintf("peer %v notified of %v (%v)", dp, kp, po))
|
|
||||||
dp.NotifyPeer(kp.Peer, uint8(po))
|
|
||||||
if uint8(prox) != self.lastProxLimit {
|
|
||||||
self.lastProxLimit = uint8(prox)
|
|
||||||
dp.NotifyProx(uint8(prox))
|
|
||||||
}
|
|
||||||
log.Debug("peer notified")
|
|
||||||
}
|
|
||||||
self.peers.EachNeighbourAsync(kp, 1024, 255, f, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Off removes a peer from among live peers
|
|
||||||
func (self *Kademlia) Off(p Peer) {
|
|
||||||
kp := NewKadPeer(p)
|
|
||||||
self.addrs.Swap(kp, func(v pot.PotVal) pot.PotVal {
|
|
||||||
if v != nil {
|
|
||||||
self.peers.Swap(kp, func(v pot.PotVal) pot.PotVal {
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type ByteAddr struct {
|
|
||||||
key []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachLivePeer(base, po, f) is an iterator applying f to each live peer
|
|
||||||
// that has proximity order po or less as measured from the base
|
|
||||||
// if base is nil, kademlia base address is used
|
|
||||||
func (self *Kademlia) EachLivePeer(base []byte, o int, f func(Peer, int, bool) bool) {
|
|
||||||
var p pot.PotVal
|
|
||||||
if base == nil {
|
|
||||||
p = pot.PotVal(self.addr)
|
|
||||||
} else {
|
|
||||||
p = pot.PotVal(&peerAddr{OAddr: base})
|
|
||||||
}
|
|
||||||
self.peers.EachNeighbour(p, func(val pot.PotVal, po int) bool {
|
|
||||||
if po > o {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
isproxbin := false
|
|
||||||
if l, _ := p.PO(val, 0); l >= self.proxLimit() {
|
|
||||||
isproxbin = true
|
|
||||||
}
|
|
||||||
return f(val.(*KadPeer).Peer, po, isproxbin)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachPeer(base, po, f) is an iterator applying f to each known peer
|
|
||||||
// that has proximity order po or less as measured from the base
|
|
||||||
// if base is nil, kademlia base address is used
|
|
||||||
func (self *Kademlia) EachPeer(base []byte, o int, f func(PeerAddr, int) bool) {
|
|
||||||
var p pot.PotVal
|
|
||||||
if base == nil {
|
|
||||||
p = pot.PotVal(self.addr)
|
|
||||||
} else {
|
|
||||||
p = pot.NewHashAddressFromBytes(base)
|
|
||||||
}
|
|
||||||
self.addrs.EachNeighbour(p, func(val pot.PotVal, po int) bool {
|
|
||||||
if po > o {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return f(val.(*KadPeer).Peer, po)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// proxLimit() returns the proximity order that defines the distance of
|
|
||||||
// the nearest neighbour set with cardinality >= MinProxBinSize
|
|
||||||
// if there is altogether less than MinProxBinSize peers it returns 0
|
|
||||||
func (self *Kademlia) proxLimit() int {
|
|
||||||
if self.peers.Size() < self.MinProxBinSize {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var proxLimit int
|
|
||||||
var size int
|
|
||||||
f := func(v pot.PotVal, i int) bool {
|
|
||||||
size++
|
|
||||||
proxLimit = i
|
|
||||||
return size < self.MinProxBinSize
|
|
||||||
}
|
|
||||||
self.peers.EachNeighbour(pot.PotVal(self.addr), f)
|
|
||||||
return proxLimit
|
|
||||||
}
|
|
||||||
|
|
||||||
// SuggestPeer returns a known peer for the lowest proximity bin for the
|
// SuggestPeer returns a known peer for the lowest proximity bin for the
|
||||||
// lowest bincount below proxLimit
|
// lowest bincount below depth
|
||||||
// naturally if there is an empty row it returns a peer for that
|
// naturally if there is an empty row it returns a peer for that
|
||||||
//
|
//
|
||||||
func (self *Kademlia) SuggestPeer() (p PeerAddr, o int, want bool) {
|
func (self *Kademlia) SuggestPeer() (a OverlayAddr, o int, want bool) {
|
||||||
minsize := self.MinBinSize
|
minsize := self.MinBinSize
|
||||||
proxLimit := self.proxLimit()
|
depth := self.Depth()
|
||||||
// if there is a callable neighbour within the current proxBin, connect
|
// if there is a callable neighbour within the current proxBin, connect
|
||||||
// this makes sure nearest neighbour set is fully connected
|
// this makes sure nearest neighbour set is fully connected
|
||||||
log.Trace(fmt.Sprintf("candidate prox peer checking above PO %v", proxLimit))
|
log.Debug(fmt.Sprintf("candidate prox peer checking above PO %v", depth))
|
||||||
|
// log.Trace(fmt.Sprintf("candidate prox peer checking above PO %v", depth))
|
||||||
var ppo int
|
var ppo int
|
||||||
self.addrs.EachNeighbour(self.addr, func(val pot.PotVal, po int) bool {
|
ba := pot.NewBytesVal(self.base, nil)
|
||||||
r := self.callable(val)
|
self.addrs.EachNeighbour(ba, func(val pot.PotVal, po int) bool {
|
||||||
if r == nil {
|
a = self.callable(val)
|
||||||
return po >= proxLimit
|
log.Trace(fmt.Sprintf("candidate prox peer at %x: %v (%v). a == nil is %v", val.(*entry).Address(), a, po, a == nil))
|
||||||
}
|
|
||||||
p = r
|
|
||||||
ppo = po
|
ppo = po
|
||||||
return false
|
return a == nil && po >= depth
|
||||||
})
|
})
|
||||||
if p != nil {
|
if a != nil {
|
||||||
log.Trace(fmt.Sprintf("candidate prox peer found: %v (%v), %v", p, ppo, p))
|
log.Trace(fmt.Sprintf("candidate prox peer found: %v (%v)", a, ppo))
|
||||||
return p, 0, false
|
return a, 0, false
|
||||||
}
|
}
|
||||||
log.Trace(fmt.Sprintf("no candidate prox peers to connect to (ProxLimit: %v, minProxSize: %v)", proxLimit, self.MinProxBinSize))
|
log.Trace(fmt.Sprintf("no candidate prox peers to connect to (Depth: %v, minProxSize: %v) %#v", depth, self.MinProxBinSize, a))
|
||||||
|
|
||||||
var bpo []int
|
var bpo []int
|
||||||
prev := -1
|
prev := -1
|
||||||
self.peers.EachBin(self.addr, 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool {
|
self.conns.EachBin(pot.NewBytesVal(self.base, nil), 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool {
|
||||||
log.Trace(fmt.Sprintf("check PO%02d: ", po))
|
log.Trace(fmt.Sprintf("check PO%02d: ", po))
|
||||||
prev++
|
prev++
|
||||||
if po > prev {
|
if po > prev {
|
||||||
|
|
@ -375,7 +221,7 @@ func (self *Kademlia) SuggestPeer() (p PeerAddr, o int, want bool) {
|
||||||
minsize = size
|
minsize = size
|
||||||
bpo = append(bpo, po)
|
bpo = append(bpo, po)
|
||||||
}
|
}
|
||||||
return size > 0 && po < proxLimit
|
return size > 0 && po < depth
|
||||||
})
|
})
|
||||||
// all buckets are full
|
// all buckets are full
|
||||||
// minsize == self.MinBinSize
|
// minsize == self.MinBinSize
|
||||||
|
|
@ -387,46 +233,186 @@ func (self *Kademlia) SuggestPeer() (p PeerAddr, o int, want bool) {
|
||||||
// try to select a candidate peer
|
// try to select a candidate peer
|
||||||
for i := len(bpo) - 1; i >= 0; i-- {
|
for i := len(bpo) - 1; i >= 0; i-- {
|
||||||
// find the first callable peer
|
// find the first callable peer
|
||||||
self.addrs.EachBin(self.addr, bpo[i], func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool {
|
self.addrs.EachBin(ba, bpo[i], func(po, size int, f func(func(pot.PotVal, int) bool) bool) bool {
|
||||||
// for each bin we find callable candidate peers
|
// for each bin we find callable candidate peers
|
||||||
f(func(val pot.PotVal, i int) bool {
|
log.Trace(fmt.Sprintf("check PO%02d: ", po))
|
||||||
r := self.callable(val)
|
f(func(val pot.PotVal, j int) bool {
|
||||||
log.Trace(fmt.Sprintf("check PO%02d: ", po))
|
a = self.callable(val)
|
||||||
if r == nil {
|
return a == nil && po < depth
|
||||||
return i < proxLimit
|
|
||||||
}
|
|
||||||
p = r
|
|
||||||
return false
|
|
||||||
})
|
})
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
// found a candidate
|
// found a candidate
|
||||||
if p != nil {
|
if a != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// cannot find a candidate, ask for more for this proximity bin specifically
|
// cannot find a candidate, ask for more for this proximity bin specifically
|
||||||
o = bpo[i]
|
o = bpo[i]
|
||||||
want = true
|
want = true
|
||||||
}
|
}
|
||||||
return p, o, want
|
return a, o, want
|
||||||
}
|
}
|
||||||
|
|
||||||
// kademlia table + kaddb table displayed with ascii
|
// On inserts the peer as a kademlia peer into the live peers
|
||||||
|
func (self *Kademlia) On(p OverlayConn) {
|
||||||
|
e := newEntry(p)
|
||||||
|
self.conns.Swap(p, func(v pot.PotVal) pot.PotVal {
|
||||||
|
// if not found live
|
||||||
|
if v == nil {
|
||||||
|
// insert new online peer into addrs
|
||||||
|
self.addrs.Swap(p, func(v pot.PotVal) pot.PotVal {
|
||||||
|
return e
|
||||||
|
})
|
||||||
|
// insert new online peer into conns
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
// found among live peers, do nothing
|
||||||
|
return v
|
||||||
|
})
|
||||||
|
|
||||||
|
np, ok := p.(Notifier)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
depth := uint8(self.Depth())
|
||||||
|
if depth != self.depth {
|
||||||
|
self.depth = depth
|
||||||
|
} else {
|
||||||
|
depth = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
go np.NotifyDepth(depth)
|
||||||
|
f := func(val pot.PotVal, po int) {
|
||||||
|
dp := val.(*entry).OverlayPeer.(Notifier)
|
||||||
|
dp.NotifyPeer(p.Off(), uint8(po))
|
||||||
|
// log.Trace(fmt.Sprintf("peer %v notified of %v (%v)", dp, p, po))
|
||||||
|
log.Debug(fmt.Sprintf("peer %v notified of %v (%v)", dp, p, po))
|
||||||
|
if depth > 0 {
|
||||||
|
dp.NotifyDepth(depth)
|
||||||
|
log.Debug(fmt.Sprintf("peer %v notified of new depth %v", dp, depth))
|
||||||
|
// log.Trace(fmt.Sprintf("peer %v notified of new depth %v", dp, depth))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.conns.EachNeighbourAsync(e, 1024, 255, f, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Off removes a peer from among live peers
|
||||||
|
func (self *Kademlia) Off(p OverlayConn) {
|
||||||
|
self.addrs.Swap(p, func(v pot.PotVal) pot.PotVal {
|
||||||
|
// v cannot be nil, must check otherwise we overwrite entry
|
||||||
|
if v == nil {
|
||||||
|
panic(fmt.Sprintf("connected peer not found %v", p))
|
||||||
|
}
|
||||||
|
self.conns.Swap(p, func(v pot.PotVal) pot.PotVal {
|
||||||
|
// v cannot be nil, but no need to check
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return newEntry(p.Off())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// EachConn is an iterator with args (base, po, f) applies f to each live peer
|
||||||
|
// that has proximity order po or less as measured from the base
|
||||||
|
// if base is nil, kademlia base address is used
|
||||||
|
func (self *Kademlia) EachConn(base []byte, o int, f func(OverlayConn, int, bool) bool) {
|
||||||
|
if len(base) == 0 {
|
||||||
|
base = self.base
|
||||||
|
}
|
||||||
|
p := pot.NewBytesVal(base, nil)
|
||||||
|
self.conns.EachNeighbour(p, func(val pot.PotVal, po int) bool {
|
||||||
|
if po > o {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
isproxbin := false
|
||||||
|
if l, _ := p.PO(val, 0); l >= self.Depth() {
|
||||||
|
isproxbin = true
|
||||||
|
}
|
||||||
|
return f(val.(*entry).conn(), po, isproxbin)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// EachAddr(base, po, f) is an iterator applying f to each known peer
|
||||||
|
// that has proximity order po or less as measured from the base
|
||||||
|
// if base is nil, kademlia base address is used
|
||||||
|
func (self *Kademlia) EachAddr(base []byte, o int, f func(OverlayAddr, int) bool) {
|
||||||
|
if len(base) == 0 {
|
||||||
|
base = self.base
|
||||||
|
}
|
||||||
|
p := pot.NewBytesVal(base, nil)
|
||||||
|
self.addrs.EachNeighbour(p, func(val pot.PotVal, po int) bool {
|
||||||
|
if po > o {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return f(val.(*entry).addr(), po)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depth returns the proximity order that defines the distance of
|
||||||
|
// the nearest neighbour set with cardinality >= MinProxBinSize
|
||||||
|
// if there is altogether less than MinProxBinSize peers it returns 0
|
||||||
|
func (self *Kademlia) Depth() (depth int) {
|
||||||
|
if self.conns.Size() < self.MinProxBinSize {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var size int
|
||||||
|
f := func(v pot.PotVal, i int) bool {
|
||||||
|
size++
|
||||||
|
depth = i
|
||||||
|
return size < self.MinProxBinSize
|
||||||
|
}
|
||||||
|
self.conns.EachNeighbour(pot.NewBytesVal(self.base, nil), f)
|
||||||
|
return depth
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Kademlia) callable(val pot.PotVal) OverlayAddr {
|
||||||
|
e := val.(*entry)
|
||||||
|
// not callable if peer is live or exceeded maxRetries
|
||||||
|
if e.conn() != nil || e.retries > self.MaxRetries {
|
||||||
|
log.Trace(fmt.Sprintf("peer %v (%T) not callable", e, e.OverlayPeer))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// calculate the allowed number of retries based on time lapsed since last seen
|
||||||
|
timeAgo := time.Since(e.seenAt)
|
||||||
|
var retries int
|
||||||
|
for delta := int(timeAgo) / self.RetryInterval; delta > 0; delta /= self.RetryExponent {
|
||||||
|
log.Trace(fmt.Sprintf("delta: %v", delta))
|
||||||
|
retries++
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is never called concurrently, so safe to increment
|
||||||
|
// peer can be retried again
|
||||||
|
if retries < e.retries {
|
||||||
|
log.Trace(fmt.Sprintf("long time since last try (at %v) needed before retry %v, wait only warrants %v", timeAgo, e.retries, retries))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
e.retries++
|
||||||
|
log.Trace(fmt.Sprintf("peer %v is callable", e))
|
||||||
|
|
||||||
|
return e.addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// BaseAddr return the kademlia base addres
|
||||||
|
func (self *Kademlia) BaseAddr() []byte {
|
||||||
|
return self.base
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns kademlia table + kaddb table displayed with ascii
|
||||||
func (self *Kademlia) String() string {
|
func (self *Kademlia) String() string {
|
||||||
|
|
||||||
var rows []string
|
var rows []string
|
||||||
|
|
||||||
rows = append(rows, "=========================================================================")
|
rows = append(rows, "=========================================================================")
|
||||||
rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %v", time.Now().UTC().Format(time.UnixDate), fmt.Sprintf("%x", self.addr.OverlayAddr()[:3])))
|
rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %x", time.Now().UTC().Format(time.UnixDate), self.BaseAddr()[:3]))
|
||||||
rows = append(rows, fmt.Sprintf("population: %d (%d), MinProxBinSize: %d, MinBinSize: %d, MaxBinSize: %d", self.peers.Size(), self.addrs.Size(), self.MinProxBinSize, self.MinBinSize, self.MaxBinSize))
|
rows = append(rows, fmt.Sprintf("population: %d (%d), MinProxBinSize: %d, MinBinSize: %d, MaxBinSize: %d", self.conns.Size(), self.addrs.Size(), self.MinProxBinSize, self.MinBinSize, self.MaxBinSize))
|
||||||
|
|
||||||
liverows := make([]string, self.MaxProxDisplay)
|
liverows := make([]string, self.MaxProxDisplay)
|
||||||
peersrows := make([]string, self.MaxProxDisplay)
|
peersrows := make([]string, self.MaxProxDisplay)
|
||||||
var proxLimit int
|
var depth int
|
||||||
prev := -1
|
prev := -1
|
||||||
var proxLimitSet bool
|
var depthSet bool
|
||||||
rest := self.peers.Size()
|
rest := self.conns.Size()
|
||||||
self.peers.EachBin(self.addr, 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool {
|
self.conns.EachBin(pot.NewBytesVal(self.base, nil), 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool {
|
||||||
var rowlen int
|
var rowlen int
|
||||||
if po >= self.MaxProxDisplay {
|
if po >= self.MaxProxDisplay {
|
||||||
po = self.MaxProxDisplay - 1
|
po = self.MaxProxDisplay - 1
|
||||||
|
|
@ -434,13 +420,13 @@ func (self *Kademlia) String() string {
|
||||||
row := []string{fmt.Sprintf("%2d", size)}
|
row := []string{fmt.Sprintf("%2d", size)}
|
||||||
rest -= size
|
rest -= size
|
||||||
f(func(val pot.PotVal, vpo int) bool {
|
f(func(val pot.PotVal, vpo int) bool {
|
||||||
row = append(row, val.(*KadPeer).String()[:6])
|
row = append(row, val.(*entry).String()[:6])
|
||||||
rowlen++
|
rowlen++
|
||||||
return rowlen < 4
|
return rowlen < 4
|
||||||
})
|
})
|
||||||
if !proxLimitSet && (po > prev+1 || rest < self.MinProxBinSize) {
|
if !depthSet && (po > prev+1 || rest < self.MinProxBinSize) {
|
||||||
proxLimitSet = true
|
depthSet = true
|
||||||
proxLimit = prev + 1
|
depth = prev + 1
|
||||||
}
|
}
|
||||||
for ; rowlen <= 5; rowlen++ {
|
for ; rowlen <= 5; rowlen++ {
|
||||||
row = append(row, " ")
|
row = append(row, " ")
|
||||||
|
|
@ -450,7 +436,7 @@ func (self *Kademlia) String() string {
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
self.addrs.EachBin(self.addr, 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool {
|
self.addrs.EachBin(pot.NewBytesVal(self.base, nil), 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool {
|
||||||
var rowlen int
|
var rowlen int
|
||||||
if po >= self.MaxProxDisplay {
|
if po >= self.MaxProxDisplay {
|
||||||
po = self.MaxProxDisplay - 1
|
po = self.MaxProxDisplay - 1
|
||||||
|
|
@ -461,8 +447,7 @@ func (self *Kademlia) String() string {
|
||||||
row := []string{fmt.Sprintf("%2d", size)}
|
row := []string{fmt.Sprintf("%2d", size)}
|
||||||
// we are displaying live peers too
|
// we are displaying live peers too
|
||||||
f(func(val pot.PotVal, vpo int) bool {
|
f(func(val pot.PotVal, vpo int) bool {
|
||||||
kp := val.(*KadPeer)
|
row = append(row, val.(*entry).String()[:6])
|
||||||
row = append(row, kp.String()[:6])
|
|
||||||
rowlen++
|
rowlen++
|
||||||
return rowlen < 4
|
return rowlen < 4
|
||||||
})
|
})
|
||||||
|
|
@ -471,7 +456,7 @@ func (self *Kademlia) String() string {
|
||||||
})
|
})
|
||||||
|
|
||||||
for i := 0; i < self.MaxProxDisplay; i++ {
|
for i := 0; i < self.MaxProxDisplay; i++ {
|
||||||
if i == proxLimit {
|
if i == depth {
|
||||||
rows = append(rows, fmt.Sprintf("============ PROX LIMIT: %d ==========================================", i))
|
rows = append(rows, fmt.Sprintf("============ PROX LIMIT: %d ==========================================", i))
|
||||||
}
|
}
|
||||||
left := liverows[i]
|
left := liverows[i]
|
||||||
|
|
@ -487,3 +472,95 @@ func (self *Kademlia) String() string {
|
||||||
rows = append(rows, "=========================================================================")
|
rows = append(rows, "=========================================================================")
|
||||||
return "\n" + strings.Join(rows, "\n")
|
return "\n" + strings.Join(rows, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prune implements a forever loop reacting to a ticker time channel given
|
||||||
|
// as the first argument
|
||||||
|
// the loop quits if the channel is closed
|
||||||
|
// it checks each kademlia bin and if the peer count is higher than
|
||||||
|
// the MaxBinSize parameter it drops the oldest n peers such that
|
||||||
|
// the bin is reduced to MinBinSize peers thus leaving slots to newly
|
||||||
|
// connecting peers
|
||||||
|
func (self *Kademlia) Prune(c <-chan time.Time) {
|
||||||
|
go func() {
|
||||||
|
for range c {
|
||||||
|
total := 0
|
||||||
|
self.conns.EachBin(pot.NewBytesVal(self.base, nil), 0, func(po, size int, f func(func(pot.PotVal, int) bool) bool) bool {
|
||||||
|
extra := size - self.MinBinSize
|
||||||
|
if size > self.MaxBinSize {
|
||||||
|
n := 0
|
||||||
|
f(func(v pot.PotVal, po int) bool {
|
||||||
|
v.(*entry).conn().Drop(fmt.Errorf("bucket full"))
|
||||||
|
n++
|
||||||
|
return n < extra
|
||||||
|
})
|
||||||
|
total += extra
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
log.Debug(fmt.Sprintf("pruned %v peers", total))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPeerPot(kadMinProxSize int, ids ...*adapters.NodeId) map[discover.NodeID][][]byte {
|
||||||
|
// create a table of all nodes for health check
|
||||||
|
np := pot.NewPot(nil, 0)
|
||||||
|
for _, id := range ids {
|
||||||
|
o := ToOverlayAddr(id.Bytes())
|
||||||
|
np, _, _ = pot.Add(np, pot.NewBytesVal(o, nil))
|
||||||
|
}
|
||||||
|
nnmap := make(map[discover.NodeID][][]byte)
|
||||||
|
|
||||||
|
for _, id := range ids {
|
||||||
|
pl := 0
|
||||||
|
var nns [][]byte
|
||||||
|
np.EachNeighbour(pot.NewBytesVal(id.Bytes(), nil), func(val pot.PotVal, po int) bool {
|
||||||
|
a := val.(pot.BytesAddress).Address()
|
||||||
|
nns = append(nns, a)
|
||||||
|
if len(nns) >= kadMinProxSize {
|
||||||
|
pl = po
|
||||||
|
}
|
||||||
|
return pl == 0 || pl == po
|
||||||
|
})
|
||||||
|
nnmap[id.NodeID] = nns
|
||||||
|
}
|
||||||
|
return nnmap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Kademlia) FirstEmptyBin() (i int) {
|
||||||
|
i = -1
|
||||||
|
self.conns.EachBin(pot.NewBytesVal(self.base, nil), 0, func(po, size int, f func(func(val pot.PotVal, i int) bool) bool) bool {
|
||||||
|
if po > i+1 {
|
||||||
|
i = po
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
i = po
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Kademlia) Full() bool {
|
||||||
|
return self.FirstEmptyBin() >= self.Depth()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Healthy reports the health state of the kademlia connectivity
|
||||||
|
//
|
||||||
|
func (self *Kademlia) Healthy(peers [][]byte) bool {
|
||||||
|
return self.gotNearestNeighbours(peers) && self.Full()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Kademlia) gotNearestNeighbours(peers [][]byte) (got bool) {
|
||||||
|
pm := make(map[string]bool)
|
||||||
|
for _, p := range peers {
|
||||||
|
pm[string(p)] = true
|
||||||
|
}
|
||||||
|
self.EachConn(nil, 255, func(p OverlayConn, po int, nn bool) bool {
|
||||||
|
if !nn {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, got = pm[string(p.Address())]
|
||||||
|
return got
|
||||||
|
})
|
||||||
|
return got
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -25,9 +26,15 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/pot"
|
"github.com/ethereum/go-ethereum/pot"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testKadPeerAddr(s string) *peerAddr {
|
func init() {
|
||||||
|
h := log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))
|
||||||
|
// h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true)))
|
||||||
|
log.Root().SetHandler(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testKadPeerAddr(s string) *bzzAddr {
|
||||||
a := pot.NewHashAddress(s).Bytes()
|
a := pot.NewHashAddress(s).Bytes()
|
||||||
return &peerAddr{OAddr: a, UAddr: a}
|
return &bzzAddr{OAddr: a, UAddr: a}
|
||||||
}
|
}
|
||||||
|
|
||||||
type testDropPeer struct {
|
type testDropPeer struct {
|
||||||
|
|
@ -58,23 +65,24 @@ type dropError struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *testDropPeer) Drop(err error) {
|
func (self *testDropPeer) Drop(err error) {
|
||||||
err2 := &dropError{err, overlayStr(self)}
|
err2 := &dropError{err, binStr(self)}
|
||||||
self.dropc <- err2
|
self.dropc <- err2
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *testDiscPeer) NotifyProx(po uint8) error {
|
func (self *testDiscPeer) NotifyDepth(po uint8) error {
|
||||||
key := overlayStr(self)
|
key := binStr(self)
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
self.notifications[key] = po
|
self.notifications[key] = po
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *testDiscPeer) NotifyPeer(p Peer, po uint8) error {
|
func (self *testDiscPeer) NotifyPeer(p OverlayAddr, po uint8) error {
|
||||||
key := overlayStr(self)
|
key := binStr(self)
|
||||||
key += overlayStr(p)
|
key += binStr(p)
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
|
log.Trace(fmt.Sprintf("key %v=>%v", key, po))
|
||||||
self.notifications[key] = po
|
self.notifications[key] = po
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -102,40 +110,13 @@ func newTestKademlia(b string) *testKademlia {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *testKademlia) newTestKadPeer(s string) Peer {
|
func (k *testKademlia) newTestKadPeer(s string) Peer {
|
||||||
dp := &testDropPeer{&bzzPeer{peerAddr: testKadPeerAddr(s)}, k.dropc}
|
dp := &testDropPeer{&bzzPeer{bzzAddr: testKadPeerAddr(s)}, k.dropc}
|
||||||
if k.Discovery {
|
if k.Discovery {
|
||||||
return Peer(&testDiscPeer{dp, k.lock, k.notifications})
|
return Peer(&testDiscPeer{dp, k.lock, k.notifications})
|
||||||
}
|
}
|
||||||
return Peer(dp)
|
return Peer(dp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func overlayStr(a PeerAddr) string {
|
|
||||||
log.Error(fmt.Sprintf("PeerAddr: %v (%T)", a, a))
|
|
||||||
// if a == (*KadPeer)(nil) || a == (*testDiscPeer)(nil) || a == (*bzzPeer)(nil) || a == nil {
|
|
||||||
// return "<nil>"
|
|
||||||
// }
|
|
||||||
// var p Peer
|
|
||||||
// s, ok := a.(*KadPeer)
|
|
||||||
// if ok {
|
|
||||||
// p = s.Peer
|
|
||||||
// } else {
|
|
||||||
// p = a.(*testDiscPeer).Peer
|
|
||||||
// }
|
|
||||||
// log.Error(fmt.Sprintf("PeerAddr: %v (%T)", p, p))
|
|
||||||
// if p == (Peer)(nil) || p == (*testDiscPeer)(nil) || p == (*bzzPeer)(nil) {
|
|
||||||
// return "<nil>"
|
|
||||||
// }
|
|
||||||
// return pot.NewHashAddressFromBytes(p.OverlayAddr()).Bin()[:6]
|
|
||||||
if a == nil {
|
|
||||||
return "<nil>"
|
|
||||||
}
|
|
||||||
k, ok := a.(*KadPeer)
|
|
||||||
if ok && k.Peer != nil {
|
|
||||||
return pot.ToBin(a.(*KadPeer).Peer.OverlayAddr())[:6]
|
|
||||||
}
|
|
||||||
return pot.ToBin(a.OverlayAddr())[:6]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *testKademlia) On(ons ...string) *testKademlia {
|
func (k *testKademlia) On(ons ...string) *testKademlia {
|
||||||
for _, s := range ons {
|
for _, s := range ons {
|
||||||
p := k.newTestKadPeer(s)
|
p := k.newTestKadPeer(s)
|
||||||
|
|
@ -146,25 +127,30 @@ func (k *testKademlia) On(ons ...string) *testKademlia {
|
||||||
|
|
||||||
func (k *testKademlia) Off(offs ...string) *testKademlia {
|
func (k *testKademlia) Off(offs ...string) *testKademlia {
|
||||||
for _, s := range offs {
|
for _, s := range offs {
|
||||||
k.Kademlia.Off(k.newTestKadPeer(s))
|
k.Kademlia.Off(k.newTestKadPeer(s).(OverlayConn))
|
||||||
}
|
}
|
||||||
|
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *testKademlia) Register(regs ...string) *testKademlia {
|
func (k *testKademlia) Register(regs ...string) *testKademlia {
|
||||||
var ps []PeerAddr
|
ch := make(chan OverlayAddr)
|
||||||
for _, s := range regs {
|
go func() {
|
||||||
ps = append(ps, PeerAddr(testKadPeerAddr(s)))
|
defer close(ch)
|
||||||
}
|
for _, s := range regs {
|
||||||
k.Kademlia.Register(ps...)
|
ch <- testKadPeerAddr(s)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
err := k.Kademlia.Register(ch)
|
||||||
|
log.Trace(fmt.Sprintf("register %v addresses: %v", len(regs), err))
|
||||||
|
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSuggestPeer(t *testing.T, k *testKademlia, expAddr string, expPo int, expWant bool) error {
|
func testSuggestPeer(t *testing.T, k *testKademlia, expAddr string, expPo int, expWant bool) error {
|
||||||
addr, o, want := k.SuggestPeer()
|
addr, o, want := k.SuggestPeer()
|
||||||
if overlayStr(addr) != expAddr {
|
if binStr(addr) != expAddr {
|
||||||
return fmt.Errorf("incorrect peer address suggested. expected %v, got %v", expAddr, overlayStr(addr))
|
return fmt.Errorf("incorrect peer address suggested. expected %v, got %v", expAddr, binStr(addr))
|
||||||
}
|
}
|
||||||
if o != expPo {
|
if o != expPo {
|
||||||
return fmt.Errorf("incorrect prox order suggested. expected %v, got %v", expPo, o)
|
return fmt.Errorf("incorrect prox order suggested. expected %v, got %v", expPo, o)
|
||||||
|
|
@ -175,6 +161,13 @@ func testSuggestPeer(t *testing.T, k *testKademlia, expAddr string, expPo int, e
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func binStr(a OverlayPeer) string {
|
||||||
|
if a == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
return pot.ToBin(a.Address())[:6]
|
||||||
|
}
|
||||||
|
|
||||||
func TestSuggestPeerFindPeers(t *testing.T) {
|
func TestSuggestPeerFindPeers(t *testing.T) {
|
||||||
// 2 row gap, unsaturated proxbin, no callables -> want PO 0
|
// 2 row gap, unsaturated proxbin, no callables -> want PO 0
|
||||||
k := newTestKademlia("000000").On("001000")
|
k := newTestKademlia("000000").On("001000")
|
||||||
|
|
@ -222,7 +215,6 @@ func TestSuggestPeerFindPeers(t *testing.T) {
|
||||||
|
|
||||||
// second time disconnected peer not callable
|
// second time disconnected peer not callable
|
||||||
// with reasonably set Interval
|
// with reasonably set Interval
|
||||||
// err = testSuggestPeer(t, k, "010000", 2, true)
|
|
||||||
err = testSuggestPeer(t, k, "<nil>", 1, true)
|
err = testSuggestPeer(t, k, "<nil>", 1, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
|
|
@ -237,16 +229,16 @@ func TestSuggestPeerFindPeers(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
k.On("010000")
|
k.On("010000")
|
||||||
k.Off("010000")
|
|
||||||
// PO1 disconnects
|
|
||||||
// new closer peer appears, it is immediately wanted
|
// new closer peer appears, it is immediately wanted
|
||||||
// k.Off("010000")
|
|
||||||
k.Register("000101")
|
k.Register("000101")
|
||||||
err = testSuggestPeer(t, k, "000101", 0, false)
|
err = testSuggestPeer(t, k, "000101", 0, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PO1 disconnects
|
||||||
|
k.On("000101")
|
||||||
|
k.Off("010000")
|
||||||
// second time, gap filling
|
// second time, gap filling
|
||||||
err = testSuggestPeer(t, k, "010000", 0, false)
|
err = testSuggestPeer(t, k, "010000", 0, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -265,6 +257,19 @@ func TestSuggestPeerFindPeers(t *testing.T) {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
k.Register("010001")
|
||||||
|
err = testSuggestPeer(t, k, "<nil>", 0, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
k.On("100001")
|
||||||
|
log.Trace("Kad:\n%v", k.String())
|
||||||
|
err = testSuggestPeer(t, k, "010001", 0, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
k.On("100001")
|
k.On("100001")
|
||||||
k.On("010001")
|
k.On("010001")
|
||||||
err = testSuggestPeer(t, k, "<nil>", 0, false)
|
err = testSuggestPeer(t, k, "<nil>", 0, false)
|
||||||
|
|
@ -273,7 +278,18 @@ func TestSuggestPeerFindPeers(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
k.MinBinSize = 3
|
k.MinBinSize = 3
|
||||||
|
k.Register("100010")
|
||||||
|
err = testSuggestPeer(t, k, "100010", 0, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
k.On("100010")
|
k.On("100010")
|
||||||
|
err = testSuggestPeer(t, k, "<nil>", 1, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
k.On("010010")
|
k.On("010010")
|
||||||
err = testSuggestPeer(t, k, "<nil>", 2, true)
|
err = testSuggestPeer(t, k, "<nil>", 2, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -281,8 +297,16 @@ func TestSuggestPeerFindPeers(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
k.On("001010")
|
k.On("001010")
|
||||||
|
err = testSuggestPeer(t, k, "<nil>", 3, true)
|
||||||
|
if err != nil {
|
||||||
|
log.Trace("Kad:\n%v", k.String())
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
k.On("000110")
|
||||||
err = testSuggestPeer(t, k, "<nil>", 0, false)
|
err = testSuggestPeer(t, k, "<nil>", 0, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Trace("Kad:\n%v", k.String())
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -440,7 +464,7 @@ func TestNotifications(t *testing.T) {
|
||||||
k.Discovery = true
|
k.Discovery = true
|
||||||
k.MinProxBinSize = 3
|
k.MinProxBinSize = 3
|
||||||
k.On("010000", "001000")
|
k.On("010000", "001000")
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(1000 * time.Millisecond)
|
||||||
err := k.checkNotifications(
|
err := k.checkNotifications(
|
||||||
[]*testPeerNotification{
|
[]*testPeerNotification{
|
||||||
&testPeerNotification{"010000", "001000", 1},
|
&testPeerNotification{"010000", "001000", 1},
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,11 @@
|
||||||
package network
|
package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
@ -26,83 +30,213 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
"github.com/ethereum/go-ethereum/p2p/protocols"
|
"github.com/ethereum/go-ethereum/p2p/protocols"
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||||
"github.com/ethereum/go-ethereum/pot"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ProtocolName = "bzz"
|
|
||||||
Version = 0
|
|
||||||
NetworkId = 322 // BZZ in l33t
|
NetworkId = 322 // BZZ in l33t
|
||||||
ProtocolMaxMsgSize = 10 * 1024 * 1024
|
ProtocolMaxMsgSize = 10 * 1024 * 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
// bzz is the bzz protocol view of a protocols.Peer (itself an extension of p2p.Peer)
|
var BzzHandshakeSpec = &protocols.Spec{
|
||||||
type bzzPeer struct {
|
Name: "bzz",
|
||||||
*protocols.Peer
|
Version: 1,
|
||||||
localAddr *peerAddr
|
MaxMsgSize: 10 * 1024 * 1024,
|
||||||
*peerAddr // remote address
|
Messages: []interface{}{
|
||||||
lastActive time.Time
|
bzzHandshake{},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *bzzPeer) LastActive() time.Time {
|
var DiscoverySpec = &protocols.Spec{
|
||||||
return self.lastActive
|
Name: "hive",
|
||||||
|
Version: 1,
|
||||||
|
MaxMsgSize: 10 * 1024 * 1024,
|
||||||
|
Messages: []interface{}{
|
||||||
|
peersMsg{},
|
||||||
|
getPeersMsg{},
|
||||||
|
subPeersMsg{},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// implemented by peerAddr
|
// the Addr interface that peerPool needs
|
||||||
type PeerAddr interface {
|
type Addr interface {
|
||||||
OverlayAddr() []byte
|
OverlayPeer
|
||||||
UnderlayAddr() []byte
|
Over() []byte
|
||||||
PO(pot.PotVal, int) (int, bool)
|
Under() []byte
|
||||||
String() string
|
String() string
|
||||||
|
Update(OverlayAddr) OverlayAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
// the Peer interface that peerPool needs
|
// Peer interface represents an live peer connection
|
||||||
type Peer interface {
|
type Peer interface {
|
||||||
PeerAddr
|
Addr // the address of a peer
|
||||||
// String() string // pretty printable the Node
|
Conn // the live connection (protocols.Peer)
|
||||||
ID() discover.NodeID // the key that uniquely identifies the Node for the peerPool
|
LastActive() time.Time // last time active
|
||||||
Send(interface{}) error // can send messages
|
|
||||||
Drop(error) // disconnect this peer
|
|
||||||
Register(interface{}, func(interface{}) error) uint64 // register message-handler callbacks
|
|
||||||
DisconnectHook(func(error))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func BzzCodeMap(msgs ...interface{}) *protocols.CodeMap {
|
// Conn interface represents an live peer connection
|
||||||
ct := protocols.NewCodeMap(ProtocolName, Version, ProtocolMaxMsgSize)
|
type Conn interface {
|
||||||
ct.Register(&bzzHandshake{})
|
ID() discover.NodeID // the key that uniquely identifies the Node for the peerPool
|
||||||
ct.Register(msgs...)
|
Handshake(context.Context, interface{}) (interface{}, error) // can send messages
|
||||||
return ct
|
Send(interface{}) error // can send messages
|
||||||
|
Drop(error) // disconnect this peer
|
||||||
|
Run(func(interface{}) error) error // the run function to run a protocol
|
||||||
|
Off() OverlayAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bzz is the protocol constructor
|
// TODO: implement store for exec nodes
|
||||||
// returns p2p.Protocol that is to be offered by the node.Service
|
type StateStore interface {
|
||||||
func Bzz(oAddr, uAddr []byte, ct *protocols.CodeMap, services func(Peer) error, peerInfo func(id discover.NodeID) interface{}, nodeInfo func() interface{}) *p2p.Protocol {
|
Load(string) ([]byte, error)
|
||||||
run := func(p *protocols.Peer) error {
|
Save(string, []byte) error
|
||||||
bee := &bzzPeer{
|
}
|
||||||
Peer: p,
|
|
||||||
localAddr: &peerAddr{oAddr, uAddr},
|
// BzzConfig captures the config params used by the hive
|
||||||
}
|
type BzzConfig struct {
|
||||||
// protocol handshake and its validation
|
OverlayAddr []byte
|
||||||
// sets remote peer address
|
UnderlayAddr []byte
|
||||||
err := bee.bzzHandshake()
|
HiveParams *HiveParams
|
||||||
if err != nil {
|
}
|
||||||
log.Error(fmt.Sprintf("handshake error in peer %v: %v", bee.ID(), err))
|
|
||||||
|
// Bzz is the swarm protocol bundle
|
||||||
|
type Bzz struct {
|
||||||
|
Hive *Hive
|
||||||
|
localAddr *bzzAddr
|
||||||
|
mtx sync.Mutex
|
||||||
|
handshakes map[discover.NodeID]*bzzHandshake
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBzz is the swarm protocol constructor
|
||||||
|
func NewBzz(config *BzzConfig, kad Overlay, store StateStore) *Bzz {
|
||||||
|
return &Bzz{
|
||||||
|
Hive: NewHive(config.HiveParams, kad, store),
|
||||||
|
localAddr: &bzzAddr{config.OverlayAddr, config.UnderlayAddr},
|
||||||
|
handshakes: make(map[discover.NodeID]*bzzHandshake),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bzz implements the node.Service interface, offers Protocols
|
||||||
|
// * handshake/hive
|
||||||
|
// * discovery
|
||||||
|
func (b *Bzz) Protocols() []p2p.Protocol {
|
||||||
|
return []p2p.Protocol{
|
||||||
|
{
|
||||||
|
Name: BzzHandshakeSpec.Name,
|
||||||
|
Version: BzzHandshakeSpec.Version,
|
||||||
|
Length: BzzHandshakeSpec.Length(),
|
||||||
|
Run: b.runHandshake,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: DiscoverySpec.Name,
|
||||||
|
Version: DiscoverySpec.Version,
|
||||||
|
Length: DiscoverySpec.Length(),
|
||||||
|
Run: b.runProtocol(DiscoverySpec, b.Hive.Run),
|
||||||
|
NodeInfo: b.Hive.NodeInfo,
|
||||||
|
PeerInfo: b.Hive.PeerInfo,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bzz implements the node.Service interface, offers APIs:
|
||||||
|
// * hive
|
||||||
|
func (b *Bzz) APIs() []rpc.API {
|
||||||
|
return []rpc.API{{
|
||||||
|
Namespace: "hive",
|
||||||
|
Version: "1.0",
|
||||||
|
Service: b.Hive,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bzz) Start(server *p2p.Server) error {
|
||||||
|
return b.Hive.Start(server)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bzz) Stop() error {
|
||||||
|
b.Hive.Stop()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bzz) runHandshake(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||||
|
handshake := b.getHandshake(p.ID())
|
||||||
|
defer b.removeHandshake(p.ID())
|
||||||
|
|
||||||
|
if err := handshake.Perform(p, rw); err != nil {
|
||||||
|
log.Error("handshake failed", "peer", p.ID(), "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// fail if we get another handshake
|
||||||
|
msg, err := rw.ReadMsg()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.Discard()
|
||||||
|
return errors.New("received multiple handshakes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bzz) runProtocol(spec *protocols.Spec, run func(*bzzPeer) error) func(*p2p.Peer, p2p.MsgReadWriter) error {
|
||||||
|
return func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||||
|
// wait for the bzz protocol to perform the handshake
|
||||||
|
handshake := b.getHandshake(p.ID())
|
||||||
|
if err := handshake.Wait(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// mount external service models on the peer connection (swap, sync, hive)
|
// the handshake has succeeded so run the service
|
||||||
if services != nil {
|
peer := &bzzPeer{
|
||||||
err = services(bee)
|
Peer: protocols.NewPeer(p, rw, spec),
|
||||||
if err != nil {
|
localAddr: b.localAddr,
|
||||||
log.Error(fmt.Sprintf("protocol service error for peer %v: %v", bee.ID(), err))
|
bzzAddr: handshake.peerAddr,
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return run(peer)
|
||||||
return bee.Run()
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return protocols.NewProtocol(ProtocolName, Version, run, ct, peerInfo, nodeInfo)
|
func (b *Bzz) removeHandshake(peerID discover.NodeID) {
|
||||||
|
b.mtx.Lock()
|
||||||
|
defer b.mtx.Unlock()
|
||||||
|
delete(b.handshakes, peerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bzz) getHandshake(peerID discover.NodeID) *bzzHandshake {
|
||||||
|
b.mtx.Lock()
|
||||||
|
defer b.mtx.Unlock()
|
||||||
|
handshake, ok := b.handshakes[peerID]
|
||||||
|
if !ok {
|
||||||
|
handshake = &bzzHandshake{
|
||||||
|
Version: uint64(BzzHandshakeSpec.Version),
|
||||||
|
NetworkId: uint64(NetworkId),
|
||||||
|
Addr: b.localAddr,
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
b.handshakes[peerID] = handshake
|
||||||
|
}
|
||||||
|
return handshake
|
||||||
|
}
|
||||||
|
|
||||||
|
// bzzPeer is the bzz protocol view of a protocols.Peer (itself an extension of p2p.Peer)
|
||||||
|
// implements the Peer interface and all interfaces Peer implements: Addr, OverlayPeer
|
||||||
|
type bzzPeer struct {
|
||||||
|
*protocols.Peer // represents the connection for online peers
|
||||||
|
localAddr *bzzAddr // local Peers address
|
||||||
|
*bzzAddr // remote address -> implements Addr interface = protocols.Peer
|
||||||
|
lastActive time.Time // time is updated whenever mutexes are releasing
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBzzPeer(p *protocols.Peer, over, under []byte) *bzzPeer {
|
||||||
|
return &bzzPeer{
|
||||||
|
Peer: p,
|
||||||
|
localAddr: &bzzAddr{over, under},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Off returns the overlay peer record for offline persistance
|
||||||
|
func (self *bzzPeer) Off() OverlayAddr {
|
||||||
|
return self.bzzAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// LastActive returns the time the peer was last active
|
||||||
|
func (self *bzzPeer) LastActive() time.Time {
|
||||||
|
return self.lastActive
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -115,105 +249,84 @@ func Bzz(oAddr, uAddr []byte, ct *protocols.CodeMap, services func(Peer) error,
|
||||||
type bzzHandshake struct {
|
type bzzHandshake struct {
|
||||||
Version uint64
|
Version uint64
|
||||||
NetworkId uint64
|
NetworkId uint64
|
||||||
Addr *peerAddr
|
Addr *bzzAddr
|
||||||
|
|
||||||
|
// peerAddr is the address received in the peer handshake
|
||||||
|
peerAddr *bzzAddr
|
||||||
|
|
||||||
|
done chan struct{}
|
||||||
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *bzzHandshake) String() string {
|
func (self *bzzHandshake) String() string {
|
||||||
return fmt.Sprintf("Handshake: Version: %v, NetworkId: %v, Addr: %v", self.Version, self.NetworkId, self.Addr)
|
return fmt.Sprintf("Handshake: Version: %v, NetworkId: %v, Addr: %v", self.Version, self.NetworkId, self.Addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// peerAddr implements the PeerAddress interface
|
const bzzHandshakeTimeout = time.Second
|
||||||
type peerAddr struct {
|
|
||||||
|
func (self *bzzHandshake) Perform(p *p2p.Peer, rw p2p.MsgReadWriter) (err error) {
|
||||||
|
defer func() {
|
||||||
|
self.err = err
|
||||||
|
close(self.done)
|
||||||
|
}()
|
||||||
|
peer := protocols.NewPeer(p, rw, BzzHandshakeSpec)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), bzzHandshakeTimeout)
|
||||||
|
defer cancel()
|
||||||
|
hs, err := peer.Handshake(ctx, self)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rhs := hs.(*bzzHandshake)
|
||||||
|
if rhs.NetworkId != self.NetworkId {
|
||||||
|
return fmt.Errorf("network id mismatch %d (!= %d)", rhs.NetworkId, self.NetworkId)
|
||||||
|
}
|
||||||
|
if rhs.Version != self.Version {
|
||||||
|
return fmt.Errorf("version mismatch %d (!= %d)", rhs.Version, self.Version)
|
||||||
|
}
|
||||||
|
self.peerAddr = rhs.Addr
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *bzzHandshake) Wait() error {
|
||||||
|
select {
|
||||||
|
case <-self.done:
|
||||||
|
return self.err
|
||||||
|
case <-time.After(bzzHandshakeTimeout):
|
||||||
|
return errors.New("timed out waiting for bzz handshake")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bzzAddr implements the PeerAddr interface
|
||||||
|
type bzzAddr struct {
|
||||||
OAddr []byte
|
OAddr []byte
|
||||||
UAddr []byte
|
UAddr []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *peerAddr) OverlayAddr() []byte {
|
// implements OverlayPeer interface to be used in pot package
|
||||||
|
func (self *bzzAddr) Address() []byte {
|
||||||
return self.OAddr
|
return self.OAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *peerAddr) UnderlayAddr() []byte {
|
// Over returns the overlay address
|
||||||
|
func (self *bzzAddr) Over() []byte {
|
||||||
|
return self.OAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Under retrun the underlay address
|
||||||
|
func (self *bzzAddr) Under() []byte {
|
||||||
return self.UAddr
|
return self.UAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *peerAddr) PO(val pot.PotVal, pos int) (int, bool) {
|
func (self *bzzAddr) Update(a OverlayAddr) OverlayAddr {
|
||||||
kp := val.(PeerAddr)
|
return &bzzAddr{self.OAddr, a.(Addr).Under()}
|
||||||
one := kp.OverlayAddr()
|
|
||||||
other := self.OAddr
|
|
||||||
for i := pos / 8; i < len(one); i++ {
|
|
||||||
if one[i] == other[i] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
oxo := one[i] ^ other[i]
|
|
||||||
start := 0
|
|
||||||
if i == pos/8 {
|
|
||||||
start = pos % 8
|
|
||||||
}
|
|
||||||
for j := start; j < 8; j++ {
|
|
||||||
if (uint8(oxo)>>uint8(7-j))&0x01 != 0 {
|
|
||||||
return i*8 + j, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return len(one) * 8, true
|
|
||||||
// var ha *pot.HashAddress
|
|
||||||
// var left, right string
|
|
||||||
// if ok {
|
|
||||||
// ha = kp.HashAddress
|
|
||||||
// } else {
|
|
||||||
// ha = val.(*pot.HashAddress)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *peerAddr) String() string {
|
func (self *bzzAddr) String() string {
|
||||||
return fmt.Sprintf("%x <%x>", self.OAddr, self.UAddr)
|
return fmt.Sprintf("%x <%s>", self.OAddr, self.UAddr)
|
||||||
}
|
|
||||||
|
|
||||||
// bzzHandshake negotiates the bzz master handshake
|
|
||||||
// and validates the response, returns error when
|
|
||||||
// mismatch/incompatibility is evident
|
|
||||||
func (self *bzzPeer) bzzHandshake() error {
|
|
||||||
|
|
||||||
lhs := &bzzHandshake{
|
|
||||||
Version: uint64(Version),
|
|
||||||
NetworkId: uint64(NetworkId),
|
|
||||||
Addr: self.localAddr,
|
|
||||||
}
|
|
||||||
|
|
||||||
hs, err := self.Handshake(lhs)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(fmt.Sprintf("handshake failed: %v", err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
rhs := hs.(*bzzHandshake)
|
|
||||||
self.peerAddr = rhs.Addr
|
|
||||||
err = checkBzzHandshake(rhs)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(fmt.Sprintf("handshake between %v and %v failed: %v", self.localAddr, self.peerAddr, err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkBzzHandshake checks for the validity and compatibility of the remote handshake
|
|
||||||
func checkBzzHandshake(rhs *bzzHandshake) error {
|
|
||||||
|
|
||||||
if NetworkId != rhs.NetworkId {
|
|
||||||
return fmt.Errorf("network id mismatch %d (!= %d)", rhs.NetworkId, NetworkId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if Version != rhs.Version {
|
|
||||||
return fmt.Errorf("version mismatch %d (!= %d)", rhs.Version, Version)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RandomAddr is a utility method generating an address from a public key
|
// RandomAddr is a utility method generating an address from a public key
|
||||||
func RandomAddr() *peerAddr {
|
func RandomAddr() *bzzAddr {
|
||||||
key, err := crypto.GenerateKey()
|
key, err := crypto.GenerateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("unable to generate key")
|
panic("unable to generate key")
|
||||||
|
|
@ -221,23 +334,28 @@ func RandomAddr() *peerAddr {
|
||||||
pubkey := crypto.FromECDSAPub(&key.PublicKey)
|
pubkey := crypto.FromECDSAPub(&key.PublicKey)
|
||||||
var id discover.NodeID
|
var id discover.NodeID
|
||||||
copy(id[:], pubkey[1:])
|
copy(id[:], pubkey[1:])
|
||||||
return &peerAddr{
|
return &bzzAddr{
|
||||||
OAddr: crypto.Keccak256(pubkey[1:]),
|
OAddr: crypto.Keccak256(pubkey[1:]),
|
||||||
UAddr: id[:],
|
UAddr: id[:],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeId transforms the underlay address to an adapters.NodeId
|
// NewNodeIdFromAddr transforms the underlay address to an adapters.NodeId
|
||||||
func NodeId(addr PeerAddr) *adapters.NodeId {
|
func NewNodeIdFromAddr(addr Addr) *adapters.NodeId {
|
||||||
return adapters.NewNodeId(addr.UnderlayAddr())
|
return adapters.NewNodeId(addr.Under())
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPeerAddrFromNodeId constucts a peerAddr from an adapters.NodeId
|
// NewAddrFromNodeId constucts a bzzAddr from an adapters.NodeId
|
||||||
// the overlay address is derived as the hash of the nodeId
|
// the overlay address is derived as the hash of the nodeId
|
||||||
func NewPeerAddrFromNodeId(n *adapters.NodeId) *peerAddr {
|
func NewAddrFromNodeId(n *adapters.NodeId) Addr {
|
||||||
id := n.NodeID
|
id := n.NodeID
|
||||||
return &peerAddr{
|
return &bzzAddr{
|
||||||
OAddr: crypto.Keccak256(id[:]),
|
OAddr: ToOverlayAddr(n.Bytes()),
|
||||||
UAddr: id[:],
|
UAddr: []byte(discover.NewNode(id, net.IP{127, 0, 0, 1}, 30303, 30303).String()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToOverlayAddr creates an overlayaddress from NodeID
|
||||||
|
func ToOverlayAddr(id []byte) []byte {
|
||||||
|
return crypto.Keccak256(id)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,42 @@ package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/protocols"
|
"github.com/ethereum/go-ethereum/p2p/protocols"
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||||
p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
|
p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type testStore struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
values map[string][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestStore() *testStore {
|
||||||
|
return &testStore{values: make(map[string][]byte)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testStore) Load(key string) ([]byte, error) {
|
||||||
|
t.Lock()
|
||||||
|
defer t.Unlock()
|
||||||
|
v, ok := t.values[key]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("key not found: %s", key)
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testStore) Save(key string, v []byte) error {
|
||||||
|
t.Lock()
|
||||||
|
defer t.Unlock()
|
||||||
|
t.values[key] = v
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func bzzHandshakeExchange(lhs, rhs *bzzHandshake, id *adapters.NodeId) []p2ptest.Exchange {
|
func bzzHandshakeExchange(lhs, rhs *bzzHandshake, id *adapters.NodeId) []p2ptest.Exchange {
|
||||||
|
|
||||||
return []p2ptest.Exchange{
|
return []p2ptest.Exchange{
|
||||||
|
|
@ -34,21 +62,23 @@ func bzzHandshakeExchange(lhs, rhs *bzzHandshake, id *adapters.NodeId) []p2ptest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBzzBaseTester(t *testing.T, n int, addr *peerAddr, ct *protocols.CodeMap, services func(Peer) error) *bzzTester {
|
func newBzzBaseTester(t *testing.T, n int, addr *bzzAddr, spec *protocols.Spec, run func(*bzzPeer) error) *bzzTester {
|
||||||
if ct == nil {
|
|
||||||
ct = BzzCodeMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
cs := make(map[string]chan bool)
|
cs := make(map[string]chan bool)
|
||||||
|
|
||||||
srv := func(p Peer) error {
|
srv := func(p *bzzPeer) error {
|
||||||
defer close(cs[p.ID().String()])
|
defer close(cs[p.ID().String()])
|
||||||
return services(p)
|
return run(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
protocall := Bzz(addr.OverlayAddr(), addr.UnderlayAddr(), ct, srv, nil, nil).Run
|
protocall := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||||
|
return srv(&bzzPeer{
|
||||||
|
Peer: protocols.NewPeer(p, rw, spec),
|
||||||
|
localAddr: addr,
|
||||||
|
bzzAddr: NewAddrFromNodeId(&adapters.NodeId{NodeID: p.ID()}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
s := p2ptest.NewProtocolTester(t, NodeId(addr), n, protocall)
|
s := p2ptest.NewProtocolTester(t, NewNodeIdFromAddr(addr), n, protocall)
|
||||||
|
|
||||||
for _, id := range s.Ids {
|
for _, id := range s.Ids {
|
||||||
cs[id.NodeID.String()] = make(chan bool)
|
cs[id.NodeID.String()] = make(chan bool)
|
||||||
|
|
@ -63,32 +93,27 @@ func newBzzBaseTester(t *testing.T, n int, addr *peerAddr, ct *protocols.CodeMap
|
||||||
|
|
||||||
type bzzTester struct {
|
type bzzTester struct {
|
||||||
*p2ptest.ProtocolTester
|
*p2ptest.ProtocolTester
|
||||||
addr *peerAddr
|
addr *bzzAddr
|
||||||
cs map[string]chan bool
|
cs map[string]chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBzzTester(t *testing.T, n int, addr *peerAddr, pp *p2ptest.TestPeerPool, ct *protocols.CodeMap, services func(Peer) error) *bzzTester {
|
func newBzzTester(t *testing.T, n int, addr *bzzAddr, pp *p2ptest.TestPeerPool, spec *protocols.Spec, services func(Peer) error) *bzzTester {
|
||||||
|
|
||||||
extraservices := func(p Peer) error {
|
extraservices := func(p *bzzPeer) error {
|
||||||
pp.Add(p)
|
pp.Add(p)
|
||||||
p.DisconnectHook(func(err error) {
|
defer pp.Remove(p)
|
||||||
pp.Remove(p)
|
if services == nil {
|
||||||
})
|
return nil
|
||||||
if services != nil {
|
|
||||||
err := services(p)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return services(p)
|
||||||
}
|
}
|
||||||
return newBzzBaseTester(t, n, addr, ct, extraservices)
|
return newBzzBaseTester(t, n, addr, spec, extraservices)
|
||||||
}
|
}
|
||||||
|
|
||||||
// should test handshakes in one exchange? parallelisation
|
// should test handshakes in one exchange? parallelisation
|
||||||
func (s *bzzTester) testHandshake(lhs, rhs *bzzHandshake, disconnects ...*p2ptest.Disconnect) {
|
func (s *bzzTester) testHandshake(lhs, rhs *bzzHandshake, disconnects ...*p2ptest.Disconnect) {
|
||||||
var peers []*adapters.NodeId
|
var peers []*adapters.NodeId
|
||||||
id := NodeId(rhs.Addr)
|
id := NewNodeIdFromAddr(rhs.Addr)
|
||||||
if len(disconnects) > 0 {
|
if len(disconnects) > 0 {
|
||||||
for _, d := range disconnects {
|
for _, d := range disconnects {
|
||||||
peers = append(peers, d.Peer)
|
peers = append(peers, d.Peer)
|
||||||
|
|
@ -106,14 +131,18 @@ func (s *bzzTester) runHandshakes(ids ...*adapters.NodeId) {
|
||||||
ids = s.Ids
|
ids = s.Ids
|
||||||
}
|
}
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
s.testHandshake(correctBzzHandshake(s.addr), correctBzzHandshake(NewPeerAddrFromNodeId(id)))
|
s.testHandshake(correctBzzHandshake(s.addr), correctBzzHandshake(NewAddrFromNodeId(id)))
|
||||||
<-s.cs[id.NodeID.String()]
|
<-s.cs[id.NodeID.String()]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func correctBzzHandshake(addr *peerAddr) *bzzHandshake {
|
func correctBzzHandshake(addr *bzzAddr) *bzzHandshake {
|
||||||
return &bzzHandshake{0, 322, addr}
|
return &bzzHandshake{
|
||||||
|
Version: 0,
|
||||||
|
NetworkId: 322,
|
||||||
|
Addr: addr,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBzzHandshakeNetworkIdMismatch(t *testing.T) {
|
func TestBzzHandshakeNetworkIdMismatch(t *testing.T) {
|
||||||
|
|
@ -125,7 +154,7 @@ func TestBzzHandshakeNetworkIdMismatch(t *testing.T) {
|
||||||
id := s.Ids[0]
|
id := s.Ids[0]
|
||||||
s.testHandshake(
|
s.testHandshake(
|
||||||
correctBzzHandshake(addr),
|
correctBzzHandshake(addr),
|
||||||
&bzzHandshake{0, 321, NewPeerAddrFromNodeId(id)},
|
&bzzHandshake{Version: 0, NetworkId: 321, Addr: NewAddrFromNodeId(id)},
|
||||||
&p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("network id mismatch 321 (!= 322)")},
|
&p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("network id mismatch 321 (!= 322)")},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +168,7 @@ func TestBzzHandshakeVersionMismatch(t *testing.T) {
|
||||||
id := s.Ids[0]
|
id := s.Ids[0]
|
||||||
s.testHandshake(
|
s.testHandshake(
|
||||||
correctBzzHandshake(addr),
|
correctBzzHandshake(addr),
|
||||||
&bzzHandshake{1, 322, NewPeerAddrFromNodeId(id)},
|
&bzzHandshake{Version: 1, NetworkId: 322, Addr: NewAddrFromNodeId(id)},
|
||||||
&p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("version mismatch 1 (!= 0)")},
|
&p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("version mismatch 1 (!= 0)")},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -153,70 +182,6 @@ func TestBzzHandshakeSuccess(t *testing.T) {
|
||||||
id := s.Ids[0]
|
id := s.Ids[0]
|
||||||
s.testHandshake(
|
s.testHandshake(
|
||||||
correctBzzHandshake(addr),
|
correctBzzHandshake(addr),
|
||||||
&bzzHandshake{0, 322, NewPeerAddrFromNodeId(id)},
|
&bzzHandshake{Version: 0, NetworkId: 322, Addr: NewAddrFromNodeId(id)},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBzzPeerPoolAdd(t *testing.T) {
|
|
||||||
pp := p2ptest.NewTestPeerPool()
|
|
||||||
addr := RandomAddr()
|
|
||||||
s := newBzzTester(t, 1, addr, pp, nil, nil)
|
|
||||||
defer s.Stop()
|
|
||||||
|
|
||||||
id := s.Ids[0]
|
|
||||||
log.Trace(fmt.Sprintf("handshake with %v", id))
|
|
||||||
s.runHandshakes()
|
|
||||||
|
|
||||||
if !pp.Has(id) {
|
|
||||||
t.Fatalf("peer '%v' not added: %v", id, pp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBzzPeerPoolRemove(t *testing.T) {
|
|
||||||
addr := RandomAddr()
|
|
||||||
pp := p2ptest.NewTestPeerPool()
|
|
||||||
s := newBzzTester(t, 1, addr, pp, nil, nil)
|
|
||||||
defer s.Stop()
|
|
||||||
|
|
||||||
s.runHandshakes()
|
|
||||||
|
|
||||||
id := s.Ids[0]
|
|
||||||
pp.Get(id).Drop(fmt.Errorf("p2p: read or write on closed message pipe"))
|
|
||||||
s.TestDisconnected(&p2ptest.Disconnect{id, fmt.Errorf("p2p: read or write on closed message pipe")})
|
|
||||||
if pp.Has(id) {
|
|
||||||
t.Fatalf("peer '%v' not removed: %v", id, pp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBzzPeerPoolBothAddRemove(t *testing.T) {
|
|
||||||
addr := RandomAddr()
|
|
||||||
pp := p2ptest.NewTestPeerPool()
|
|
||||||
s := newBzzTester(t, 1, addr, pp, nil, nil)
|
|
||||||
defer s.Stop()
|
|
||||||
|
|
||||||
s.runHandshakes()
|
|
||||||
|
|
||||||
id := s.Ids[0]
|
|
||||||
if !pp.Has(id) {
|
|
||||||
t.Fatalf("peer '%v' not added: %v", id, pp)
|
|
||||||
}
|
|
||||||
|
|
||||||
pp.Get(id).Drop(fmt.Errorf("p2p: read or write on closed message pipe"))
|
|
||||||
s.TestDisconnected(&p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("p2p: read or write on closed message pipe")})
|
|
||||||
if pp.Has(id) {
|
|
||||||
t.Fatalf("peer '%v' not removed: %v", id, pp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBzzPeerPoolNotAdd(t *testing.T) {
|
|
||||||
addr := RandomAddr()
|
|
||||||
pp := p2ptest.NewTestPeerPool()
|
|
||||||
s := newBzzTester(t, 1, addr, pp, nil, nil)
|
|
||||||
defer s.Stop()
|
|
||||||
|
|
||||||
id := s.Ids[0]
|
|
||||||
s.testHandshake(correctBzzHandshake(addr), &bzzHandshake{0, 321, NewPeerAddrFromNodeId(id)}, &p2ptest.Disconnect{Peer: id, Error: fmt.Errorf("network id mismatch 321 (!= 322)")})
|
|
||||||
if pp.Has(id) {
|
|
||||||
t.Fatalf("peer %v incorrectly added: %v", id, pp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
p2pnode "github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
"github.com/ethereum/go-ethereum/swarm/network"
|
"github.com/ethereum/go-ethereum/swarm/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
// serviceName is used with the exec adapter so the exec'd binary knows which
|
// serviceName is used with the exec adapter so the exec'd binary knows which
|
||||||
// service to execute
|
// service to execute
|
||||||
const serviceName = "discovery"
|
const serviceName = "discovery"
|
||||||
|
const testMinProxBinSize = 2
|
||||||
|
|
||||||
var services = adapters.Services{
|
var services = adapters.Services{
|
||||||
serviceName: func(id *adapters.NodeId, snapshot []byte) p2pnode.Service {
|
serviceName: newService,
|
||||||
return newNode(id)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -70,7 +68,7 @@ func testDiscoverySimulation(t *testing.T, adapter adapters.NodeAdapter) {
|
||||||
for i := 0; i < nodeCount; i++ {
|
for i := 0; i < nodeCount; i++ {
|
||||||
node, err := net.NewNode()
|
node, err := net.NewNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error starting node %s: %s", node.ID().Label(), err)
|
t.Fatalf("error starting node: %s", err)
|
||||||
}
|
}
|
||||||
if err := net.Start(node.ID()); err != nil {
|
if err := net.Start(node.ID()); err != nil {
|
||||||
t.Fatalf("error starting node %s: %s", node.ID().Label(), err)
|
t.Fatalf("error starting node %s: %s", node.ID().Label(), err)
|
||||||
|
|
@ -97,6 +95,7 @@ func testDiscoverySimulation(t *testing.T, adapter adapters.NodeAdapter) {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
nnmap := network.NewPeerPot(testMinProxBinSize, ids...)
|
||||||
check := func(ctx context.Context, id *adapters.NodeId) (bool, error) {
|
check := func(ctx context.Context, id *adapters.NodeId) (bool, error) {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
|
@ -113,7 +112,7 @@ func testDiscoverySimulation(t *testing.T, adapter adapters.NodeAdapter) {
|
||||||
return false, fmt.Errorf("error getting node client: %s", err)
|
return false, fmt.Errorf("error getting node client: %s", err)
|
||||||
}
|
}
|
||||||
var healthy bool
|
var healthy bool
|
||||||
if err := client.Call(&healthy, "hive_healthy", nil); err != nil {
|
if err := client.Call(&healthy, "hive_healthy", nnmap[id.NodeID]); err != nil {
|
||||||
return false, fmt.Errorf("error getting node health: %s", err)
|
return false, fmt.Errorf("error getting node health: %s", err)
|
||||||
}
|
}
|
||||||
return healthy, nil
|
return healthy, nil
|
||||||
|
|
@ -179,70 +178,26 @@ func triggerChecks(trigger chan *adapters.NodeId, net *simulations.Network, id *
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type node struct {
|
func newService(id *adapters.NodeId, snapshot []byte) node.Service {
|
||||||
*network.Hive
|
addr := network.NewAddrFromNodeId(id)
|
||||||
|
|
||||||
protocol *p2p.Protocol
|
kp := network.NewKadParams()
|
||||||
}
|
kp.MinProxBinSize = testMinProxBinSize
|
||||||
|
kp.MaxBinSize = 3
|
||||||
|
kp.MinBinSize = 1
|
||||||
|
kp.MaxRetries = 1000
|
||||||
|
kp.RetryExponent = 2
|
||||||
|
kp.RetryInterval = 1000000
|
||||||
|
kad := network.NewKademlia(addr.Over(), kp)
|
||||||
|
|
||||||
func newNode(id *adapters.NodeId) *node {
|
hp := network.NewHiveParams()
|
||||||
addr := network.NewPeerAddrFromNodeId(id)
|
hp.KeepAliveInterval = time.Second
|
||||||
kademlia := newKademlia(addr.OverlayAddr())
|
|
||||||
hive := newHive(kademlia)
|
config := &network.BzzConfig{
|
||||||
codeMap := network.BzzCodeMap(network.DiscoveryMsgs...)
|
OverlayAddr: addr.Over(),
|
||||||
node := &node{Hive: hive}
|
UnderlayAddr: addr.Under(),
|
||||||
services := func(peer network.Peer) error {
|
HiveParams: hp,
|
||||||
discoveryPeer := network.NewDiscovery(peer, kademlia)
|
|
||||||
node.Add(discoveryPeer)
|
|
||||||
peer.DisconnectHook(func(err error) {
|
|
||||||
node.Remove(discoveryPeer)
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
node.protocol = network.Bzz(addr.OverlayAddr(), addr.UnderlayAddr(), codeMap, services, nil, nil)
|
|
||||||
return node
|
return network.NewBzz(config, kad, nil)
|
||||||
}
|
|
||||||
|
|
||||||
func newKademlia(overlayAddr []byte) *network.Kademlia {
|
|
||||||
params := network.NewKadParams()
|
|
||||||
params.MinProxBinSize = 2
|
|
||||||
params.MaxBinSize = 3
|
|
||||||
params.MinBinSize = 1
|
|
||||||
params.MaxRetries = 1000
|
|
||||||
params.RetryExponent = 2
|
|
||||||
params.RetryInterval = 1000000
|
|
||||||
|
|
||||||
return network.NewKademlia(overlayAddr, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHive(kademlia *network.Kademlia) *network.Hive {
|
|
||||||
params := network.NewHiveParams()
|
|
||||||
params.CallInterval = 5000
|
|
||||||
|
|
||||||
return network.NewHive(params, kademlia)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *node) Protocols() []p2p.Protocol {
|
|
||||||
return []p2p.Protocol{*n.protocol}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *node) APIs() []rpc.API {
|
|
||||||
return []rpc.API{{
|
|
||||||
Namespace: "hive",
|
|
||||||
Version: "1.0",
|
|
||||||
Service: n.Hive,
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *node) Start(server p2p.Server) error {
|
|
||||||
return n.Hive.Start(server, n.hiveKeepAlive)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *node) Stop() error {
|
|
||||||
n.Hive.Stop()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *node) hiveKeepAlive() <-chan time.Time {
|
|
||||||
return time.Tick(time.Second)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,83 +11,58 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
"github.com/ethereum/go-ethereum/swarm/network"
|
"github.com/ethereum/go-ethereum/swarm/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SimNode is the adapter used by Swarm simulations.
|
type Simulation struct {
|
||||||
type SimNode struct {
|
mtx sync.Mutex
|
||||||
hive *network.Hive
|
stores map[discover.NodeID]*adapters.stateStore
|
||||||
protocol *p2p.Protocol
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SimNode) Protocols() []p2p.Protocol {
|
func NewSimulation() *Simulation {
|
||||||
return []p2p.Protocol{*s.protocol}
|
return &Simulation{
|
||||||
|
stores: make(map[discover.NodeID]*adapters.stateStore),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SimNode) APIs() []rpc.API {
|
func (s *Simulation) NewService(id *adapters.NodeId, snapshot []byte) node.Service {
|
||||||
return nil
|
s.mtx.Lock()
|
||||||
}
|
store, ok := s.stores[id.NodeID]
|
||||||
|
if !ok {
|
||||||
|
store = NewSimStore()
|
||||||
|
s.stores[id.NodeID] = store
|
||||||
|
}
|
||||||
|
s.mtx.Unlock()
|
||||||
|
|
||||||
// the hive update ticker for hive
|
addr := network.NewAddrFromNodeId(id)
|
||||||
func af() <-chan time.Time {
|
|
||||||
return time.NewTicker(1 * time.Second).C
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start() starts up the hive
|
|
||||||
// makes SimNode implement node.Service
|
|
||||||
func (self *SimNode) Start(server p2p.Server) error {
|
|
||||||
return self.hive.Start(server, af)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop() shuts down the hive
|
|
||||||
// makes SimNode implement node.Service
|
|
||||||
func (self *SimNode) Stop() error {
|
|
||||||
self.hive.Stop()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSimNode creates adapters for nodes in the simulation.
|
|
||||||
func NewSimNode(id *adapters.NodeId, snapshot []byte) node.Service {
|
|
||||||
addr := network.NewPeerAddrFromNodeId(id)
|
|
||||||
kp := network.NewKadParams()
|
kp := network.NewKadParams()
|
||||||
|
|
||||||
kp.MinProxBinSize = 2
|
kp.MinProxBinSize = 2
|
||||||
kp.MaxBinSize = 3
|
kp.MaxBinSize = 8
|
||||||
kp.MinBinSize = 1
|
kp.MinBinSize = 2
|
||||||
kp.MaxRetries = 1000
|
kp.MaxRetries = 1000
|
||||||
kp.RetryExponent = 2
|
kp.RetryExponent = 2
|
||||||
kp.RetryInterval = 1000000
|
kp.RetryInterval = 1000
|
||||||
|
kad := network.NewKademlia(addr.Over(), kp)
|
||||||
|
|
||||||
to := network.NewKademlia(addr.OverlayAddr(), kp) // overlay topology driver
|
|
||||||
hp := network.NewHiveParams()
|
hp := network.NewHiveParams()
|
||||||
hp.CallInterval = 5000
|
hp.KeepAliveInterval = 3 * time.Second
|
||||||
pp := network.NewHive(hp, to) // hive
|
|
||||||
|
|
||||||
services := func(p network.Peer) error {
|
config := &network.BzzConfig{
|
||||||
dp := network.NewDiscovery(p, to)
|
OverlayAddr: addr.Over(),
|
||||||
pp.Add(dp)
|
UnderlayAddr: addr.Under(),
|
||||||
log.Trace(fmt.Sprintf("kademlia on %v", dp))
|
HiveParams: hp,
|
||||||
p.DisconnectHook(func(err error) {
|
|
||||||
pp.Remove(dp)
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ct := network.BzzCodeMap(network.DiscoveryMsgs...) // bzz protocol code map
|
return network.NewBzz(config, kad, store)
|
||||||
nodeInfo := func() interface{} { return pp.String() }
|
|
||||||
|
|
||||||
return &SimNode{
|
|
||||||
hive: pp,
|
|
||||||
protocol: network.Bzz(addr.OverlayAddr(), addr.UnderlayAddr(), ct, services, nil, nodeInfo),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createMockers() map[string]*simulations.MockerConfig {
|
func createMockers() map[string]*simulations.MockerConfig {
|
||||||
|
|
@ -119,8 +94,9 @@ func setupMocker(net *simulations.Network) []*adapters.NodeId {
|
||||||
conf := net.Config()
|
conf := net.Config()
|
||||||
conf.DefaultService = "overlay"
|
conf.DefaultService = "overlay"
|
||||||
|
|
||||||
ids := make([]*adapters.NodeId, 10)
|
nodeCount := 50
|
||||||
for i := 0; i < 10; i++ {
|
ids := make([]*adapters.NodeId, nodeCount)
|
||||||
|
for i := 0; i < nodeCount; i++ {
|
||||||
node, err := net.NewNode()
|
node, err := net.NewNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
|
|
@ -129,8 +105,6 @@ func setupMocker(net *simulations.Network) []*adapters.NodeId {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
n := rand.Intn(1000)
|
|
||||||
time.Sleep(time.Duration(n) * time.Millisecond)
|
|
||||||
if err := net.Start(id); err != nil {
|
if err := net.Start(id); err != nil {
|
||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -143,7 +117,12 @@ func setupMocker(net *simulations.Network) []*adapters.NodeId {
|
||||||
} else {
|
} else {
|
||||||
peerId = ids[i-1]
|
peerId = ids[i-1]
|
||||||
}
|
}
|
||||||
if err := net.Connect(id, peerId); err != nil {
|
ch := make(chan network.OverlayAddr)
|
||||||
|
go func() {
|
||||||
|
defer close(ch)
|
||||||
|
ch <- network.NewAddrFromNodeId(peerId)
|
||||||
|
}()
|
||||||
|
if err := net.GetNode(id).Node.(*adapters.SimNode).Service().(*network.Bzz).Hive.Register(ch); err != nil {
|
||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -191,23 +170,23 @@ func randomMocker(net *simulations.Network) {
|
||||||
func startStopMocker(net *simulations.Network) {
|
func startStopMocker(net *simulations.Network) {
|
||||||
ids := setupMocker(net)
|
ids := setupMocker(net)
|
||||||
|
|
||||||
for i, id := range ids {
|
for range time.Tick(10 * time.Second) {
|
||||||
n := 3000 + i*1000
|
id := ids[rand.Intn(len(ids))]
|
||||||
go func(id *adapters.NodeId) {
|
go func() {
|
||||||
for {
|
log.Error("stopping node", "id", id)
|
||||||
// n := rand.Intn(5000)
|
if err := net.Stop(id); err != nil {
|
||||||
// n := 3000
|
log.Error("error stopping node", "id", id, "err", err)
|
||||||
time.Sleep(time.Duration(n) * time.Millisecond)
|
return
|
||||||
log.Debug(fmt.Sprintf("node %v shutting down", id))
|
|
||||||
net.Stop(id)
|
|
||||||
// n = rand.Intn(5000)
|
|
||||||
n = 2000
|
|
||||||
time.Sleep(time.Duration(n) * time.Millisecond)
|
|
||||||
log.Debug(fmt.Sprintf("node %v starting up", id))
|
|
||||||
net.Start(id)
|
|
||||||
n = 5000
|
|
||||||
}
|
}
|
||||||
}(id)
|
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
|
||||||
|
log.Error("starting node", "id", id)
|
||||||
|
if err := net.Start(id); err != nil {
|
||||||
|
log.Error("error starting node", "id", id, "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,10 +194,11 @@ func startStopMocker(net *simulations.Network) {
|
||||||
func main() {
|
func main() {
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|
||||||
log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
log.Root().SetHandler(log.LvlFilterHandler(log.LvlDebug, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||||
|
|
||||||
|
s := NewSimulation()
|
||||||
services := adapters.Services{
|
services := adapters.Services{
|
||||||
"overlay": NewSimNode,
|
"overlay": s.NewService,
|
||||||
}
|
}
|
||||||
adapters.RegisterServices(services)
|
adapters.RegisterServices(services)
|
||||||
|
|
||||||
|
|
@ -226,7 +206,7 @@ func main() {
|
||||||
|
|
||||||
config := &simulations.ServerConfig{
|
config := &simulations.ServerConfig{
|
||||||
NewAdapter: func() adapters.NodeAdapter { return adapters.NewSimAdapter(services) },
|
NewAdapter: func() adapters.NodeAdapter { return adapters.NewSimAdapter(services) },
|
||||||
DefaultMockerId: "start-stop",
|
DefaultMockerId: "bootNet",
|
||||||
Mockers: mockers,
|
Mockers: mockers,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,182 +1,193 @@
|
||||||
package network
|
package network
|
||||||
|
|
||||||
import (
|
//
|
||||||
"fmt"
|
// import (
|
||||||
"strings"
|
// "fmt"
|
||||||
"sync"
|
// "strings"
|
||||||
|
// "sync"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
//
|
||||||
)
|
// "github.com/ethereum/go-ethereum/log"
|
||||||
|
// )
|
||||||
const orders = 8
|
//
|
||||||
|
// const orders = 8
|
||||||
type testOverlay struct {
|
//
|
||||||
mu sync.Mutex
|
// type testOverlay struct {
|
||||||
addr []byte
|
// mu sync.Mutex
|
||||||
pos [][]*testPeerAddr
|
// addr []byte
|
||||||
posMap map[string]*testPeerAddr
|
// pos [][]OverlayAddr
|
||||||
}
|
// posMap map[string]OverlayAddr
|
||||||
|
// }
|
||||||
type testPeerAddr struct {
|
//
|
||||||
PeerAddr
|
// type testPeerAddr struct {
|
||||||
Peer Peer
|
// Addr
|
||||||
}
|
// Peer
|
||||||
|
// }
|
||||||
func (self *testOverlay) Register(nas ...PeerAddr) error {
|
//
|
||||||
self.mu.Lock()
|
// func (self *testPeerAddr) Address() []byte {
|
||||||
defer self.mu.Unlock()
|
// return nil
|
||||||
return self.register(nas...)
|
// }
|
||||||
}
|
//
|
||||||
|
// func (self *testPeerAddr) Update(a OverlayAddr) OverlayAddr {
|
||||||
func (self *testOverlay) GetAddr() PeerAddr {
|
// return self
|
||||||
return &peerAddr{
|
// }
|
||||||
OAddr: self.addr,
|
//
|
||||||
UAddr: []byte{},
|
// func (self *testPeerAddr) On(p OverlayConn) OverlayConn {
|
||||||
}
|
// return self
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
func (self *testOverlay) register(nas ...PeerAddr) error {
|
// func (self *testPeerAddr) Off() OverlayAddr {
|
||||||
for _, na := range nas {
|
// return self
|
||||||
tna := &testPeerAddr{PeerAddr: na}
|
// }
|
||||||
addr := na.OverlayAddr()
|
//
|
||||||
if self.posMap[string(addr)] != nil {
|
// func (self *testOverlay) Register(peers chan OverlayAddr) error {
|
||||||
continue
|
// self.mu.Lock()
|
||||||
}
|
// defer self.mu.Unlock()
|
||||||
self.posMap[string(addr)] = tna
|
// var nas []OverlayAddr
|
||||||
o := order(addr)
|
// for a := range peers {
|
||||||
log.Trace(fmt.Sprintf("PO: %v, orders: %v", o, orders))
|
// nas = append(nas, a)
|
||||||
self.pos[o] = append(self.pos[o], tna)
|
// }
|
||||||
}
|
// return self.register(nas...)
|
||||||
return nil
|
// }
|
||||||
}
|
//
|
||||||
|
// func (self *testOverlay) BaseAddr() []byte {
|
||||||
func order(addr []byte) int {
|
// return nil
|
||||||
return int(addr[0]) / 32
|
// }
|
||||||
}
|
//
|
||||||
|
// func (self *testOverlay) register(nas ...OverlayAddr) error {
|
||||||
func (self *testOverlay) On(n Peer) {
|
// for _, na := range nas {
|
||||||
self.mu.Lock()
|
// addr := na.Address()
|
||||||
defer self.mu.Unlock()
|
// if self.posMap[string(addr)] != nil {
|
||||||
addr := n.OverlayAddr()
|
// continue
|
||||||
na := self.posMap[string(addr)]
|
// }
|
||||||
if na == nil {
|
// self.posMap[string(addr)] = na
|
||||||
self.register(n)
|
// o := order(addr)
|
||||||
na = self.posMap[string(addr)]
|
// log.Trace(fmt.Sprintf("PO: %v, orders: %v", o, orders))
|
||||||
} else if na.Peer != nil {
|
// self.pos[o] = append(self.pos[o], na)
|
||||||
return
|
// }
|
||||||
}
|
// return nil
|
||||||
log.Trace(fmt.Sprintf("Online: %x", addr[:4]))
|
// }
|
||||||
na.Peer = n
|
//
|
||||||
return
|
// func order(addr []byte) int {
|
||||||
}
|
// return int(addr[0]) / 32
|
||||||
|
// }
|
||||||
func (self *testOverlay) Off(n Peer) {
|
//
|
||||||
self.mu.Lock()
|
// func (self *testOverlay) On(n OverlayConn) {
|
||||||
defer self.mu.Unlock()
|
// self.mu.Lock()
|
||||||
addr := n.OverlayAddr()
|
// defer self.mu.Unlock()
|
||||||
na := self.posMap[string(addr)]
|
// addr := n.Address()
|
||||||
if na == nil {
|
// na := self.posMap[string(addr)]
|
||||||
return
|
// if na == nil {
|
||||||
}
|
// self.register(n)
|
||||||
delete(self.posMap, string(addr))
|
// na = self.posMap[string(addr)]
|
||||||
na.Peer = nil
|
// } else if na.Peer != nil {
|
||||||
}
|
// return
|
||||||
|
// }
|
||||||
// caller must hold the lock
|
// log.Trace(fmt.Sprintf("Online: %x", addr[:4]))
|
||||||
func (self *testOverlay) on(po []*testPeerAddr) (nodes []Peer) {
|
// na.Peer = n
|
||||||
for _, na := range po {
|
// return
|
||||||
if na.Peer != nil {
|
// }
|
||||||
nodes = append(nodes, na.Peer)
|
//
|
||||||
}
|
// func (self *testOverlay) Off(n OverlayConn) {
|
||||||
}
|
// self.mu.Lock()
|
||||||
return nodes
|
// defer self.mu.Unlock()
|
||||||
}
|
// addr := n.Over()
|
||||||
|
// na := self.posMap[string(addr)]
|
||||||
// caller must hold the lock
|
// if na == nil {
|
||||||
func (self *testOverlay) off(po []*testPeerAddr) (nas []PeerAddr) {
|
// return
|
||||||
for _, na := range po {
|
// }
|
||||||
if na.Peer == (*bzzPeer)(nil) {
|
// delete(self.posMap, string(addr))
|
||||||
nas = append(nas, PeerAddr(na))
|
// na.Peer = nil
|
||||||
}
|
// }
|
||||||
}
|
//
|
||||||
return nas
|
// // caller must hold the lock
|
||||||
}
|
// func (self *testOverlay) on(po []*testPeerAddr) (nodes []OverlayConn) {
|
||||||
|
// for _, na := range po {
|
||||||
func (self *testOverlay) EachLivePeer(base []byte, o int, f func(Peer, int, bool) bool) {
|
// if na.Peer != nil {
|
||||||
if base == nil {
|
// nodes = append(nodes, na)
|
||||||
base = self.addr
|
// }
|
||||||
}
|
// }
|
||||||
for i := o; i < len(self.pos); i++ {
|
// return nodes
|
||||||
for _, na := range self.pos[i] {
|
// }
|
||||||
if na.Peer != nil {
|
//
|
||||||
if !f(na.Peer, o, false) {
|
// // caller must hold the lock
|
||||||
return
|
// func (self *testOverlay) off(po []*testPeerAddr) (nas []OverlayAddr) {
|
||||||
}
|
// for _, na := range po {
|
||||||
}
|
// if na.Peer == (*bzzPeer)(nil) {
|
||||||
}
|
// nas = append(nas, Addr(na))
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
// return nas
|
||||||
func (self *testOverlay) EachPeer(base []byte, o int, f func(PeerAddr, int) bool) {
|
// }
|
||||||
if base == nil {
|
//
|
||||||
base = self.addr
|
// func (self *testOverlay) EachConn(base []byte, o int, f func(OverlayConn, int, bool) bool) {
|
||||||
}
|
// for i := o; i < len(self.pos); i++ {
|
||||||
for i := o; i < len(self.pos); i++ {
|
// for _, na := range self.pos[i] {
|
||||||
for _, na := range self.pos[i] {
|
// if na.Peer != nil {
|
||||||
if !f(na, i) {
|
// if !f(na, o, false) {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
func (self *testOverlay) SuggestPeer() (PeerAddr, int, bool) {
|
//
|
||||||
self.mu.Lock()
|
// func (self *testOverlay) EachAddr(base []byte, o int, f func(OverlayAddr, int) bool) {
|
||||||
defer self.mu.Unlock()
|
// for i := o; i < len(self.pos); i++ {
|
||||||
for i, po := range self.pos {
|
// for _, na := range self.pos[i] {
|
||||||
ons := self.on(po)
|
// if !f(na, i) {
|
||||||
if len(ons) < 2 {
|
// return
|
||||||
offs := self.off(po)
|
// }
|
||||||
if len(offs) > 0 {
|
// }
|
||||||
log.Trace(fmt.Sprintf("node %v is off", offs[0]))
|
// }
|
||||||
return offs[0], i, true
|
// }
|
||||||
}
|
//
|
||||||
}
|
// func (self *testOverlay) SuggestPeer() (OverlayAddr, int, bool) {
|
||||||
}
|
// self.mu.Lock()
|
||||||
return nil, 0, true
|
// defer self.mu.Unlock()
|
||||||
}
|
// for i, po := range self.pos {
|
||||||
|
// ons := self.on(po)
|
||||||
func (self *testOverlay) String() string {
|
// if len(ons) < 2 {
|
||||||
self.mu.Lock()
|
// offs := self.off(po)
|
||||||
defer self.mu.Unlock()
|
// if len(offs) > 0 {
|
||||||
var t []string
|
// log.Trace(fmt.Sprintf("node %v is off", offs[0]))
|
||||||
var ons, offs int
|
// return offs[0], i, true
|
||||||
var ns []Peer
|
// }
|
||||||
var nas []PeerAddr
|
// }
|
||||||
for o, po := range self.pos {
|
// }
|
||||||
var row []string
|
// return nil, 0, true
|
||||||
ns = self.on(po)
|
// }
|
||||||
nas = self.off(po)
|
//
|
||||||
ons = len(ns)
|
// func (self *testOverlay) String() string {
|
||||||
for _, n := range ns {
|
// self.mu.Lock()
|
||||||
addr := n.OverlayAddr()
|
// defer self.mu.Unlock()
|
||||||
row = append(row, fmt.Sprintf("%x", addr[:4]))
|
// var t []string
|
||||||
}
|
// var ons, offs int
|
||||||
row = append(row, "|")
|
// var ns []Peer
|
||||||
offs = len(nas)
|
// var nas []Addr
|
||||||
for _, na := range nas {
|
// for o, po := range self.pos {
|
||||||
addr := na.OverlayAddr()
|
// var row []string
|
||||||
row = append(row, fmt.Sprintf("%x", addr[:4]))
|
// ns = self.on(po)
|
||||||
}
|
// nas = self.off(po)
|
||||||
t = append(t, fmt.Sprintf("%v: (%v/%v) %v", o, ons, offs, strings.Join(row, " ")))
|
// ons = len(ns)
|
||||||
}
|
// for _, n := range ns {
|
||||||
return strings.Join(t, "\n")
|
// addr := n.Over()
|
||||||
}
|
// row = append(row, fmt.Sprintf("%x", addr[:4]))
|
||||||
|
// }
|
||||||
func NewTestOverlay(addr []byte) *testOverlay {
|
// row = append(row, "|")
|
||||||
return &testOverlay{
|
// offs = len(nas)
|
||||||
addr: addr,
|
// for _, na := range nas {
|
||||||
posMap: make(map[string]*testPeerAddr),
|
// addr := na.Over()
|
||||||
pos: make([][]*testPeerAddr, orders),
|
// row = append(row, fmt.Sprintf("%x", addr[:4]))
|
||||||
}
|
// }
|
||||||
}
|
// t = append(t, fmt.Sprintf("%v: (%v/%v) %v", o, ons, offs, strings.Join(row, " ")))
|
||||||
|
// }
|
||||||
|
// return strings.Join(t, "\n")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func NewTestOverlay(addr []byte) *testOverlay {
|
||||||
|
// return &testOverlay{
|
||||||
|
// addr: addr,
|
||||||
|
// posMap: make(map[string]*testPeerAddr),
|
||||||
|
// pos: make([][]*testPeerAddr, orders),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
|
||||||
228
swarm/pss/client.go
Normal file
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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
@ -9,13 +9,16 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/protocols"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/protocols"
|
||||||
"github.com/ethereum/go-ethereum/pot"
|
"github.com/ethereum/go-ethereum/pot"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/network"
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -25,19 +28,20 @@ const (
|
||||||
TopicResolverLength = 8
|
TopicResolverLength = 8
|
||||||
PssPeerCapacity = 256
|
PssPeerCapacity = 256
|
||||||
PssPeerTopicDefaultCapacity = 8
|
PssPeerTopicDefaultCapacity = 8
|
||||||
digestLength = 64
|
digestLength = 32
|
||||||
digestCapacity = 256
|
digestCapacity = 256
|
||||||
defaultDigestCacheTTL = time.Second
|
defaultDigestCacheTTL = time.Second
|
||||||
pingTopicName = "pss"
|
|
||||||
pingTopicVersion = 1
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errorNoForwarder = errors.New("no available forwarders in routing table")
|
|
||||||
errorForwardToSelf = errors.New("forward to self")
|
errorForwardToSelf = errors.New("forward to self")
|
||||||
errorBlockByCache = errors.New("message found in blocking cache")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type senderPeer interface {
|
||||||
|
Address() []byte
|
||||||
|
Send(interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
// Defines params for Pss
|
// Defines params for Pss
|
||||||
type PssParams struct {
|
type PssParams struct {
|
||||||
Cachettl time.Duration
|
Cachettl time.Duration
|
||||||
|
|
@ -51,50 +55,14 @@ func NewPssParams() *PssParams {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encapsulates the message transported over pss.
|
// Encapsulates the message transported over pss.
|
||||||
//
|
|
||||||
// Warning: do not access the To-member directly. Use *PssMsg.GetRecipient() and *PssMsg.SetRecipient() instead.
|
|
||||||
type PssMsg struct {
|
type PssMsg struct {
|
||||||
// (we need the To-member exported for type inference)
|
|
||||||
To []byte
|
To []byte
|
||||||
Payload pssEnvelope
|
Payload *PssEnvelope
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the remote peer receipient address of the message
|
|
||||||
func (self *PssMsg) GetRecipient() []byte {
|
|
||||||
return self.To
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the remote peer recipient address of the message
|
|
||||||
func (self *PssMsg) SetRecipient(to []byte) {
|
|
||||||
self.To = to
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// String representation of PssMsg
|
// String representation of PssMsg
|
||||||
func (self *PssMsg) String() string {
|
func (self *PssMsg) String() string {
|
||||||
return fmt.Sprintf("PssMsg: Recipient: %x", common.ByteLabel(self.GetRecipient()))
|
return fmt.Sprintf("PssMsg: Recipient: %x", common.ByteLabel(self.To))
|
||||||
}
|
|
||||||
|
|
||||||
// Pre-Whisper placeholder
|
|
||||||
type pssEnvelope struct {
|
|
||||||
Topic PssTopic
|
|
||||||
TTL uint16
|
|
||||||
Payload []byte
|
|
||||||
SenderOAddr []byte
|
|
||||||
SenderUAddr []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pre-Whisper placeholder
|
|
||||||
type pssPayload struct {
|
|
||||||
Code uint64
|
|
||||||
Size uint32
|
|
||||||
Data []byte
|
|
||||||
ReceivedAt time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pre-Whisper placeholder
|
|
||||||
type pssCacheEntry struct {
|
|
||||||
expiresAt time.Time
|
|
||||||
receivedFrom []byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Topic defines the context of a message being transported over pss
|
// Topic defines the context of a message being transported over pss
|
||||||
|
|
@ -102,14 +70,70 @@ type pssCacheEntry struct {
|
||||||
// Typically, one can map protocol handlers for the message payloads by mapping topic to them; see *Pss.Register()
|
// Typically, one can map protocol handlers for the message payloads by mapping topic to them; see *Pss.Register()
|
||||||
type PssTopic [TopicLength]byte
|
type PssTopic [TopicLength]byte
|
||||||
|
|
||||||
// Pre-Whisper placeholder
|
func (self *PssTopic) String() string {
|
||||||
type pssDigest uint32
|
return fmt.Sprintf("%x", self)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-Whisper placeholder, payload of PssMsg
|
||||||
|
type PssEnvelope struct {
|
||||||
|
Topic PssTopic
|
||||||
|
TTL uint16
|
||||||
|
Payload []byte
|
||||||
|
From []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// creates Pss envelope from sender address, topic and raw payload
|
||||||
|
func NewPssEnvelope(addr []byte, topic PssTopic, payload []byte) *PssEnvelope {
|
||||||
|
return &PssEnvelope{
|
||||||
|
From: addr,
|
||||||
|
Topic: topic,
|
||||||
|
TTL: DefaultTTL,
|
||||||
|
Payload: payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (msg *PssMsg) serialize() []byte {
|
||||||
|
rlpdata, _ := rlp.EncodeToBytes(msg)
|
||||||
|
/*buf := bytes.NewBuffer(nil)
|
||||||
|
buf.Write(self.PssEnvelope.Topic[:])
|
||||||
|
buf.Write(self.PssEnvelope.Payload)
|
||||||
|
buf.Write(self.PssEnvelope.From)
|
||||||
|
return buf.Bytes()*/
|
||||||
|
return rlpdata
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var pssSpec = &protocols.Spec{
|
||||||
|
Name: "pss",
|
||||||
|
Version: 1,
|
||||||
|
MaxMsgSize: 10 * 1024 * 1024,
|
||||||
|
Messages: []interface{}{
|
||||||
|
PssMsg{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// encapsulates a protocol msg as PssEnvelope data
|
||||||
|
type PssProtocolMsg struct {
|
||||||
|
Code uint64
|
||||||
|
Size uint32
|
||||||
|
Payload []byte
|
||||||
|
ReceivedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type pssCacheEntry struct {
|
||||||
|
expiresAt time.Time
|
||||||
|
receivedFrom []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type pssDigest [digestLength]byte
|
||||||
|
|
||||||
|
// Message handler func for a topic
|
||||||
|
type pssHandler func(msg []byte, p *p2p.Peer, from []byte) error
|
||||||
|
|
||||||
// pss provides sending messages to nodes without having to be directly connected to them.
|
// pss provides sending messages to nodes without having to be directly connected to them.
|
||||||
//
|
//
|
||||||
// The messages are wrapped in a PssMsg structure and routed using the swarm kademlia routing.
|
// The messages are wrapped in a PssMsg structure and routed using the swarm kademlia routing.
|
||||||
// The structure is used by normal incoming message handlers on the nodes to determine which action to take, forward or process.
|
|
||||||
// Thus it is up to the implementer to write a handler, and link the PssMsg to this appropriate handler.
|
|
||||||
//
|
//
|
||||||
// The top-level Pss object provides:
|
// The top-level Pss object provides:
|
||||||
//
|
//
|
||||||
|
|
@ -120,51 +144,118 @@ type pssDigest uint32
|
||||||
// - a dispatcher lookup, mapping protocols to topics
|
// - a dispatcher lookup, mapping protocols to topics
|
||||||
// - a message cache to spot messages that previously have been forwarded
|
// - a message cache to spot messages that previously have been forwarded
|
||||||
type Pss struct {
|
type Pss struct {
|
||||||
Overlay // we can get the overlayaddress from this
|
network.Overlay // we can get the overlayaddress from this
|
||||||
//peerPool map[pot.Address]map[PssTopic]*PssReadWriter // keep track of all virtual p2p.Peers we are currently speaking to
|
peerPool map[pot.Address]map[PssTopic]p2p.MsgReadWriter // keep track of all virtual p2p.Peers we are currently speaking to
|
||||||
peerPool map[pot.Address]map[PssTopic]p2p.MsgReadWriter // keep track of all virtual p2p.Peers we are currently speaking to
|
handlers map[PssTopic]map[*pssHandler]bool // topic and version based pss payload handlers
|
||||||
handlers map[PssTopic]func([]byte, *p2p.Peer, []byte) error // topic and version based pss payload handlers
|
fwdcache map[pssDigest]pssCacheEntry // checksum of unique fields from pssmsg mapped to expiry, cache to determine whether to drop msg
|
||||||
events map[PssTopic]*event.Feed // subscriptions for each topic
|
cachettl time.Duration // how long to keep messages in fwdcache
|
||||||
fwdcache map[pssDigest]pssCacheEntry // checksum of unique fields from pssmsg mapped to expiry, cache to determine whether to drop msg
|
|
||||||
cachettl time.Duration // how long to keep messages in fwdcache
|
|
||||||
hasher func(string) storage.Hasher // hasher to digest message to cache
|
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
|
dpa *storage.DPA
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Pss) hashMsg(msg *PssMsg) pssDigest {
|
func (self *Pss) storeMsg(msg *PssMsg) (pssDigest, error) {
|
||||||
hasher := self.hasher("SHA3")()
|
swg := &sync.WaitGroup{}
|
||||||
hasher.Reset()
|
wwg := &sync.WaitGroup{}
|
||||||
hasher.Write(msg.GetRecipient())
|
buf := bytes.NewReader(msg.serialize())
|
||||||
hasher.Write(msg.Payload.SenderUAddr)
|
key, err := self.dpa.Store(buf, int64(buf.Len()), swg, wwg)
|
||||||
hasher.Write(msg.Payload.SenderOAddr)
|
if err != nil {
|
||||||
hasher.Write(msg.Payload.Topic[:])
|
log.Warn("Could not store in swarm", "err", err)
|
||||||
hasher.Write(msg.Payload.Payload)
|
return pssDigest{}, err
|
||||||
b := hasher.Sum([]byte{})
|
}
|
||||||
return pssDigest(binary.BigEndian.Uint32(b))
|
log.Trace("Stored msg in swarm", "key", key)
|
||||||
|
digest := pssDigest{}
|
||||||
|
copy(digest[:], key[:digestLength])
|
||||||
|
return digest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new Pss instance. A node should only need one of these
|
// Creates a new Pss instance. A node should only need one of these
|
||||||
//
|
//
|
||||||
// TODO error check overlay integrity
|
// TODO: error check overlay integrity
|
||||||
func NewPss(k Overlay, params *PssParams) *Pss {
|
func NewPss(k network.Overlay, dpa *storage.DPA, params *PssParams) *Pss {
|
||||||
return &Pss{
|
return &Pss{
|
||||||
Overlay: k,
|
Overlay: k,
|
||||||
//peerPool: make(map[pot.Address]map[PssTopic]*PssReadWriter, PssPeerCapacity),
|
|
||||||
peerPool: make(map[pot.Address]map[PssTopic]p2p.MsgReadWriter, PssPeerCapacity),
|
peerPool: make(map[pot.Address]map[PssTopic]p2p.MsgReadWriter, PssPeerCapacity),
|
||||||
handlers: make(map[PssTopic]func([]byte, *p2p.Peer, []byte) error),
|
handlers: make(map[PssTopic]map[*pssHandler]bool),
|
||||||
events: make(map[PssTopic]*event.Feed),
|
|
||||||
fwdcache: make(map[pssDigest]pssCacheEntry),
|
fwdcache: make(map[pssDigest]pssCacheEntry),
|
||||||
cachettl: params.Cachettl,
|
cachettl: params.Cachettl,
|
||||||
hasher: storage.MakeHashFunc,
|
dpa: dpa,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Pss) Start(srv *p2p.Server) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Pss) Stop() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Pss) Protocols() []p2p.Protocol {
|
||||||
|
return []p2p.Protocol{
|
||||||
|
p2p.Protocol{
|
||||||
|
Name: pssSpec.Name,
|
||||||
|
Version: pssSpec.Version,
|
||||||
|
Length: pssSpec.Length(),
|
||||||
|
Run: self.Run,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Pss) Run(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||||
|
pp := protocols.NewPeer(p, rw, pssSpec)
|
||||||
|
return pp.Run(self.handlePssMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Pss) APIs() []rpc.API {
|
||||||
|
return []rpc.API{
|
||||||
|
rpc.API {
|
||||||
|
Namespace: "pss",
|
||||||
|
Version: "0.1",
|
||||||
|
Service: NewPssAPI(self),
|
||||||
|
Public: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Takes the generated PssTopic of a protocol/chatroom etc, and links a handler function to it
|
||||||
|
// This allows the implementer to retrieve the right handler functions (invoke the right protocol)
|
||||||
|
// for an incoming message by inspecting the topic on it.
|
||||||
|
// a topic allows for multiple handlers
|
||||||
|
// returns a deregister function which needs to be called to deregister the handler
|
||||||
|
// (similar to event.Subscription.Unsubscribe())
|
||||||
|
func (self *Pss) Register(topic *PssTopic, handler pssHandler) func() {
|
||||||
|
self.lock.Lock()
|
||||||
|
defer self.lock.Unlock()
|
||||||
|
handlers := self.handlers[*topic]
|
||||||
|
if handlers == nil {
|
||||||
|
handlers = make(map[*pssHandler]bool)
|
||||||
|
self.handlers[*topic] = handlers
|
||||||
|
}
|
||||||
|
handlers[&handler] = true
|
||||||
|
return func() { self.deregister(topic, &handler) }
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Pss) deregister(topic *PssTopic, h *pssHandler) {
|
||||||
|
self.lock.Lock()
|
||||||
|
defer self.lock.Unlock()
|
||||||
|
handlers := self.handlers[*topic]
|
||||||
|
if len(handlers) == 1 {
|
||||||
|
delete(self.handlers, *topic)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(handlers, h)
|
||||||
|
}
|
||||||
|
|
||||||
// enables to set address of node, to avoid backwards forwarding
|
// enables to set address of node, to avoid backwards forwarding
|
||||||
//
|
//
|
||||||
// currently not in use as forwarder address is not known in the handler function hooked to the pss dispatcher.
|
// currently not in use as forwarder address is not known in the handler function hooked to the pss dispatcher.
|
||||||
// it is included as a courtesy to custom transport layers that may want to implement this
|
// it is included as a courtesy to custom transport layers that may want to implement this
|
||||||
func (self *Pss) AddToCache(addr []byte, msg *PssMsg) error {
|
func (self *Pss) AddToCache(addr []byte, msg *PssMsg) error {
|
||||||
digest := self.hashMsg(msg)
|
//digest := self.hashMsg(msg)
|
||||||
|
digest, err := self.storeMsg(msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return self.addFwdCacheSender(addr, digest)
|
return self.addFwdCacheSender(addr, digest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,35 +301,115 @@ func (self *Pss) checkFwdCache(addr []byte, digest pssDigest) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes the generated PssTopic of a protocol, and links a handler function to it
|
func (self *Pss) getHandlers(topic PssTopic) map[*pssHandler]bool {
|
||||||
// This allows the implementer to retrieve the right handler function (invoke the right protocol) for an incoming message by inspecting the topic on it.
|
|
||||||
func (self *Pss) Register(topic PssTopic, handler func(msg []byte, p *p2p.Peer, from []byte) error) error {
|
|
||||||
self.lock.Lock()
|
|
||||||
defer self.lock.Unlock()
|
|
||||||
self.handlers[topic] = func(msg []byte, p *p2p.Peer, from []byte) error {
|
|
||||||
self.alertSubscribers(&topic, msg)
|
|
||||||
return handler(msg, p, from)
|
|
||||||
}
|
|
||||||
self.registerFeed(topic)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Pss) Subscribe(topic *PssTopic, ch chan []byte) (event.Subscription, error) {
|
|
||||||
_, ok := self.events[*topic]
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("No feed registered for topic %v", topic)
|
|
||||||
}
|
|
||||||
sub := self.events[*topic].Subscribe(ch)
|
|
||||||
log.Trace("new pss subscribe", "topic", topic, "sub", sub)
|
|
||||||
return sub, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Pss) GetHandler(topic PssTopic) func([]byte, *p2p.Peer, []byte) error {
|
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
return self.handlers[topic]
|
return self.handlers[topic]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
func (self *Pss) handlePssMsg(msg interface{}) error {
|
||||||
|
pssmsg := msg.(*PssMsg)
|
||||||
|
|
||||||
|
if !self.isSelfRecipient(pssmsg) {
|
||||||
|
log.Trace("pss was for someone else :'( ... forwarding")
|
||||||
|
return self.Forward(pssmsg)
|
||||||
|
}
|
||||||
|
log.Trace("pss for us, yay! ... let's process!")
|
||||||
|
return self.Process(pssmsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// processes a message with self as recipient
|
||||||
|
func (self *Pss) Process(pssmsg *PssMsg) error {
|
||||||
|
env := pssmsg.Payload
|
||||||
|
payload := env.Payload
|
||||||
|
handlers := self.getHandlers(env.Topic)
|
||||||
|
if len(handlers) == 0 {
|
||||||
|
return fmt.Errorf("No registered handler for topic '%s'", env.Topic)
|
||||||
|
}
|
||||||
|
nid, _ := discover.HexID("0x00")
|
||||||
|
p := p2p.NewPeer(nid, fmt.Sprintf("%x", env.From), []p2p.Cap{})
|
||||||
|
for f := range handlers {
|
||||||
|
err := (*f)(payload, p, env.From)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sends a message using pss. The message could be anything at all, and will be handled by whichever handler function is mapped to PssTopic using *Pss.Register()
|
||||||
|
//
|
||||||
|
// The to address is a swarm overlay address
|
||||||
|
func (self *Pss) Send(to []byte, topic PssTopic, msg []byte) error {
|
||||||
|
sender := self.Overlay.BaseAddr()
|
||||||
|
pssenv := NewPssEnvelope(sender, topic, msg)
|
||||||
|
pssmsg := &PssMsg{
|
||||||
|
To: to,
|
||||||
|
Payload: pssenv,
|
||||||
|
}
|
||||||
|
return self.Forward(pssmsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forwards a pss message to the peer(s) closest to the to address
|
||||||
|
//
|
||||||
|
// Handlers that want to pass on a message should call this directly
|
||||||
|
func (self *Pss) Forward(msg *PssMsg) error {
|
||||||
|
|
||||||
|
if self.isSelfRecipient(msg) {
|
||||||
|
return errorForwardToSelf
|
||||||
|
}
|
||||||
|
|
||||||
|
digest, err := self.storeMsg(msg)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(fmt.Sprintf("could not store message %v to cache: %v", msg, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.checkFwdCache(nil, digest) {
|
||||||
|
log.Trace(fmt.Sprintf("pss relay block-cache match: FROM %x TO %x", common.ByteLabel(self.Overlay.BaseAddr()), common.ByteLabel(msg.To)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:check integrity of message
|
||||||
|
sent := 0
|
||||||
|
|
||||||
|
// send with kademlia
|
||||||
|
// find the closest peer to the recipient and attempt to send
|
||||||
|
self.Overlay.EachConn(msg.To, 256, func(op network.OverlayConn, po int, isproxbin bool) bool {
|
||||||
|
p, ok := op.(senderPeer)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
addr := self.Overlay.BaseAddr()
|
||||||
|
sendMsg := fmt.Sprintf("%x: msg to %x via %x", common.ByteLabel(addr), common.ByteLabel(msg.To), common.ByteLabel(p.Address()))
|
||||||
|
if self.checkFwdCache(p.Address(), digest) {
|
||||||
|
log.Info(fmt.Sprintf("%v: peer already forwarded to", sendMsg))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
err := p.Send(msg)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(fmt.Sprintf("%v: failed forwarding: %v", sendMsg, err))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
log.Trace(fmt.Sprintf("%v: successfully forwarded", sendMsg))
|
||||||
|
sent++
|
||||||
|
// if equality holds, p is always the first peer given in the iterator
|
||||||
|
if bytes.Equal(msg.To, p.Address()) || !isproxbin {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
log.Trace(fmt.Sprintf("%x is in proxbin, keep forwarding", common.ByteLabel(p.Address())))
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
if sent == 0 {
|
||||||
|
log.Error("PSS: unable to forward to any peers")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addFwdCacheExpire(digest)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Links a pss peer address and topic to a dedicated p2p.MsgReadWriter in the pss peerpool, and runs the specificed protocol on this p2p.MsgReadWriter and the specified peer
|
// Links a pss peer address and topic to a dedicated p2p.MsgReadWriter in the pss peerpool, and runs the specificed protocol on this p2p.MsgReadWriter and the specified peer
|
||||||
//
|
//
|
||||||
// The effect is that now we have a "virtual" protocol running on an artificial p2p.Peer, which can be looked up and piped to through Pss using swarm overlay address and topic
|
// The effect is that now we have a "virtual" protocol running on an artificial p2p.Peer, which can be looked up and piped to through Pss using swarm overlay address and topic
|
||||||
|
|
@ -249,121 +420,32 @@ func (self *Pss) AddPeer(p *p2p.Peer, addr pot.Address, run adapters.RunProtocol
|
||||||
go func() {
|
go func() {
|
||||||
err := run(p, rw)
|
err := run(p, rw)
|
||||||
log.Warn(fmt.Sprintf("pss vprotocol quit on addr %v topic %v: %v", addr, topic, err))
|
log.Warn(fmt.Sprintf("pss vprotocol quit on addr %v topic %v: %v", addr, topic, err))
|
||||||
|
self.removePeerTopic(rw, topic)
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes a pss peer from the pss peerpool
|
|
||||||
func (self *Pss) RemovePeer(id pot.Address) {
|
|
||||||
self.lock.Lock()
|
|
||||||
defer self.lock.Unlock()
|
|
||||||
self.peerPool[id] = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Pss) addPeerTopic(id pot.Address, topic PssTopic, rw p2p.MsgReadWriter) error {
|
func (self *Pss) addPeerTopic(id pot.Address, topic PssTopic, rw p2p.MsgReadWriter) error {
|
||||||
if self.peerPool[id][topic] == nil {
|
if self.peerPool[id] == nil {
|
||||||
self.peerPool[id] = make(map[PssTopic]p2p.MsgReadWriter, PssPeerTopicDefaultCapacity)
|
self.peerPool[id] = make(map[PssTopic]p2p.MsgReadWriter, PssPeerTopicDefaultCapacity)
|
||||||
}
|
}
|
||||||
self.peerPool[id][topic] = rw
|
self.peerPool[id][topic] = rw
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Pss) removePeerTopic(id pot.Address, topic PssTopic) {
|
func (self *Pss) removePeerTopic(rw p2p.MsgReadWriter, topic PssTopic) {
|
||||||
self.peerPool[id][topic] = nil
|
prw, ok := rw.(*PssReadWriter)
|
||||||
return
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(self.peerPool[prw.To], topic)
|
||||||
|
if len(self.peerPool[prw.To]) == 0 {
|
||||||
|
delete(self.peerPool, prw.To)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Pss) isActive(id pot.Address, topic PssTopic) bool {
|
func (self *Pss) isActive(id pot.Address, topic PssTopic) bool {
|
||||||
if self.peerPool[id][topic] == nil {
|
return self.peerPool[id][topic] != nil
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Pss) registerFeed(topic PssTopic) {
|
|
||||||
self.events[topic] = &event.Feed{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Pss) alertSubscribers(topic *PssTopic, msg []byte) error {
|
|
||||||
feed, ok := self.events[*topic]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("No subscriptions registered for topic %v", topic)
|
|
||||||
}
|
|
||||||
numsent := feed.Send(msg)
|
|
||||||
log.Trace(fmt.Sprintf("pss sent to %d subscribers", numsent))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sends a message using pss. The message could be anything at all, and will be handled by whichever handler function is mapped to PssTopic using *Pss.Register()
|
|
||||||
//
|
|
||||||
// The to address is a swarm overlay address
|
|
||||||
func (self *Pss) Send(to []byte, topic PssTopic, msg []byte) error {
|
|
||||||
|
|
||||||
pssenv := pssEnvelope{
|
|
||||||
SenderOAddr: self.Overlay.GetAddr().OverlayAddr(),
|
|
||||||
SenderUAddr: self.Overlay.GetAddr().UnderlayAddr(),
|
|
||||||
Topic: topic,
|
|
||||||
TTL: DefaultTTL,
|
|
||||||
Payload: msg,
|
|
||||||
}
|
|
||||||
|
|
||||||
pssmsg := &PssMsg{
|
|
||||||
Payload: pssenv,
|
|
||||||
}
|
|
||||||
pssmsg.SetRecipient(to)
|
|
||||||
|
|
||||||
return self.Forward(pssmsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Forwards a pss message to the peer(s) closest to the to address
|
|
||||||
//
|
|
||||||
// Handlers that want to pass on a message should call this directly
|
|
||||||
func (self *Pss) Forward(msg *PssMsg) error {
|
|
||||||
|
|
||||||
if self.IsSelfRecipient(msg) {
|
|
||||||
return errorForwardToSelf
|
|
||||||
}
|
|
||||||
|
|
||||||
digest := self.hashMsg(msg)
|
|
||||||
|
|
||||||
if self.checkFwdCache(nil, digest) {
|
|
||||||
log.Trace(fmt.Sprintf("pss relay block-cache match: FROM %x TO %x", common.ByteLabel(self.Overlay.GetAddr().OverlayAddr()), common.ByteLabel(msg.GetRecipient())))
|
|
||||||
//return errorBlockByCache
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO:check integrity of message
|
|
||||||
|
|
||||||
sent := 0
|
|
||||||
|
|
||||||
// send with kademlia
|
|
||||||
// find the closest peer to the recipient and attempt to send
|
|
||||||
self.Overlay.EachLivePeer(msg.GetRecipient(), 256, func(p Peer, po int, isproxbin bool) bool {
|
|
||||||
if self.checkFwdCache(p.OverlayAddr(), digest) {
|
|
||||||
log.Warn(fmt.Sprintf("BOUNCE DEFER PSS-relay FROM %x TO %x THRU %x:", common.ByteLabel(self.Overlay.GetAddr().OverlayAddr()), common.ByteLabel(msg.GetRecipient()), common.ByteLabel(p.OverlayAddr())))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
log.Warn(fmt.Sprintf("Attempting PSS-relay FROM %x TO %x THRU %x", common.ByteLabel(self.Overlay.GetAddr().OverlayAddr()), common.ByteLabel(msg.GetRecipient()), common.ByteLabel(p.OverlayAddr())))
|
|
||||||
err := p.Send(msg)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn(fmt.Sprintf("FAILED PSS-relay FROM %x TO %x THRU %x: %v", common.ByteLabel(self.Overlay.GetAddr().OverlayAddr()), common.ByteLabel(msg.GetRecipient()), common.ByteLabel(p.OverlayAddr()), err))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
sent++
|
|
||||||
if bytes.Equal(msg.GetRecipient(), p.OverlayAddr()) || !isproxbin {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
log.Trace(fmt.Sprintf("%x is in proxbin, so we continue sending", common.ByteLabel(p.OverlayAddr())))
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if sent == 0 {
|
|
||||||
return fmt.Errorf("PSS Was not able to send to any peers")
|
|
||||||
} else {
|
|
||||||
self.addFwdCacheExpire(digest)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenience object that:
|
// Convenience object that:
|
||||||
|
|
@ -374,36 +456,34 @@ func (self *Pss) Forward(msg *PssMsg) error {
|
||||||
// Implements p2p.MsgReadWriter
|
// Implements p2p.MsgReadWriter
|
||||||
type PssReadWriter struct {
|
type PssReadWriter struct {
|
||||||
*Pss
|
*Pss
|
||||||
RecipientOAddr pot.Address
|
To pot.Address
|
||||||
LastActive time.Time
|
LastActive time.Time
|
||||||
rw chan p2p.Msg
|
rw chan p2p.Msg
|
||||||
ct *protocols.CodeMap
|
spec *protocols.Spec
|
||||||
topic *PssTopic
|
topic *PssTopic
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements p2p.MsgReader
|
// Implements p2p.MsgReader
|
||||||
func (prw PssReadWriter) ReadMsg() (p2p.Msg, error) {
|
func (prw PssReadWriter) ReadMsg() (p2p.Msg, error) {
|
||||||
msg := <-prw.rw
|
msg := <-prw.rw
|
||||||
|
|
||||||
log.Trace(fmt.Sprintf("pssrw readmsg: %v", msg))
|
log.Trace(fmt.Sprintf("pssrw readmsg: %v", msg))
|
||||||
|
|
||||||
return msg, nil
|
return msg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements p2p.MsgWriter
|
// Implements p2p.MsgWriter
|
||||||
func (prw PssReadWriter) WriteMsg(msg p2p.Msg) error {
|
func (prw PssReadWriter) WriteMsg(msg p2p.Msg) error {
|
||||||
log.Trace(fmt.Sprintf("pssrw writemsg: %v", msg))
|
log.Trace(fmt.Sprintf("pssrw writemsg: %v", msg))
|
||||||
ifc, found := prw.ct.GetInterface(msg.Code)
|
ifc, found := prw.spec.NewMsg(msg.Code)
|
||||||
if !found {
|
if !found {
|
||||||
return fmt.Errorf("Writemsg couldn't find matching interface for code %d", msg.Code)
|
return fmt.Errorf("Writemsg couldn't find matching interface for code %d", msg.Code)
|
||||||
}
|
}
|
||||||
msg.Decode(ifc)
|
msg.Decode(ifc)
|
||||||
|
|
||||||
to := prw.RecipientOAddr.Bytes()
|
pmsg, err := newProtocolMsg(msg.Code, ifc)
|
||||||
|
if err != nil {
|
||||||
pmsg, _ := makeMsg(msg.Code, ifc)
|
return err
|
||||||
|
}
|
||||||
return prw.Pss.Send(to, *prw.topic, pmsg)
|
return prw.Pss.Send(prw.To.Bytes(), *prw.topic, pmsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Injects a p2p.Msg into the MsgReadWriter, so that it appears on the associated p2p.MsgReader
|
// Injects a p2p.Msg into the MsgReadWriter, so that it appears on the associated p2p.MsgReader
|
||||||
|
|
@ -416,50 +496,41 @@ func (prw PssReadWriter) injectMsg(msg p2p.Msg) error {
|
||||||
// Convenience object for passing messages in and out of the p2p layer
|
// Convenience object for passing messages in and out of the p2p layer
|
||||||
type PssProtocol struct {
|
type PssProtocol struct {
|
||||||
*Pss
|
*Pss
|
||||||
virtualProtocol *p2p.Protocol
|
proto *p2p.Protocol
|
||||||
topic *PssTopic
|
topic *PssTopic
|
||||||
ct *protocols.CodeMap
|
spec *protocols.Spec
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
func NewPssProtocol(pss *Pss, topic *PssTopic, ct *protocols.CodeMap, targetprotocol *p2p.Protocol) *PssProtocol {
|
//func RegisterPssProtocol(pss *Pss, topic *PssTopic, spec *protocols.Spec, targetprotocol *p2p.Protocol) *PssProtocol {
|
||||||
|
func RegisterPssProtocol(pss *Pss, topic *PssTopic, spec *protocols.Spec, targetprotocol *p2p.Protocol) error {
|
||||||
pp := &PssProtocol{
|
pp := &PssProtocol{
|
||||||
Pss: pss,
|
Pss: pss,
|
||||||
virtualProtocol: targetprotocol,
|
proto: targetprotocol,
|
||||||
topic: topic,
|
topic: topic,
|
||||||
ct: ct,
|
spec: spec,
|
||||||
}
|
}
|
||||||
return pp
|
pss.Register(topic, pp.handle)
|
||||||
}
|
//return pp
|
||||||
|
return nil
|
||||||
// Retrieves a convenience method for passing an incoming message into the p2p layer
|
|
||||||
//
|
|
||||||
// If the implementer wishes to use the p2p.Protocol (or p2p/protocols) message handling, this handler can be directly registered as a handler for the PssMsg structure
|
|
||||||
func (self *PssProtocol) GetHandler() func([]byte, *p2p.Peer, []byte) error {
|
|
||||||
return self.handle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PssProtocol) handle(msg []byte, p *p2p.Peer, senderAddr []byte) error {
|
func (self *PssProtocol) handle(msg []byte, p *p2p.Peer, senderAddr []byte) error {
|
||||||
hashoaddr := pot.NewHashAddressFromBytes(senderAddr).Address
|
hashoaddr := pot.NewHashAddressFromBytes(senderAddr).Address
|
||||||
if !self.isActive(hashoaddr, *self.topic) {
|
if !self.isActive(hashoaddr, *self.topic) {
|
||||||
rw := &PssReadWriter{
|
rw := &PssReadWriter{
|
||||||
Pss: self.Pss,
|
Pss: self.Pss,
|
||||||
RecipientOAddr: hashoaddr,
|
To: hashoaddr,
|
||||||
rw: make(chan p2p.Msg),
|
rw: make(chan p2p.Msg),
|
||||||
ct: self.ct,
|
spec: self.spec,
|
||||||
topic: self.topic,
|
topic: self.topic,
|
||||||
}
|
}
|
||||||
self.Pss.AddPeer(p, hashoaddr, self.virtualProtocol.Run, *self.topic, rw)
|
self.Pss.AddPeer(p, hashoaddr, self.proto.Run, *self.topic, rw)
|
||||||
}
|
}
|
||||||
|
|
||||||
payload := &pssPayload{}
|
pmsg, err := ToP2pMsg(msg)
|
||||||
rlp.DecodeBytes(msg, payload)
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not decode pssmsg")
|
||||||
pmsg := p2p.Msg{
|
|
||||||
Code: payload.Code,
|
|
||||||
Size: uint32(len(payload.Data)),
|
|
||||||
ReceivedAt: time.Now(),
|
|
||||||
Payload: bytes.NewBuffer(payload.Data),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vrw := self.Pss.peerPool[hashoaddr][*self.topic].(*PssReadWriter)
|
vrw := self.Pss.peerPool[hashoaddr][*self.topic].(*PssReadWriter)
|
||||||
|
|
@ -468,26 +539,11 @@ func (self *PssProtocol) handle(msg []byte, p *p2p.Peer, senderAddr []byte) erro
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Pss) IsSelfRecipient(msg *PssMsg) bool {
|
func (self *Pss) isSelfRecipient(msg *PssMsg) bool {
|
||||||
if bytes.Equal(msg.GetRecipient(), self.Overlay.GetAddr().OverlayAddr()) {
|
return bytes.Equal(msg.To, self.Overlay.BaseAddr())
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Pss) GetPingHandler() func([]byte, *p2p.Peer, []byte) error {
|
func newProtocolMsg(code uint64, msg interface{}) ([]byte, error) {
|
||||||
pingtopic, _ := MakeTopic(pingTopicName, pingTopicVersion)
|
|
||||||
return func(msg []byte, p *p2p.Peer, from []byte) error {
|
|
||||||
if bytes.Equal([]byte("ping"), msg) {
|
|
||||||
log.Trace(fmt.Sprintf("swarm pss ping from %x sending pong", common.ByteLabel(from)))
|
|
||||||
self.Send(from, pingtopic, []byte("pong"))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pre-Whisper placeholder
|
|
||||||
func makeMsg(code uint64, msg interface{}) ([]byte, error) {
|
|
||||||
|
|
||||||
rlpdata, err := rlp.EncodeToBytes(msg)
|
rlpdata, err := rlp.EncodeToBytes(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -497,30 +553,39 @@ func makeMsg(code uint64, msg interface{}) ([]byte, error) {
|
||||||
// previous attempts corrupted nested structs in the payload iself upon deserializing
|
// previous attempts corrupted nested structs in the payload iself upon deserializing
|
||||||
// therefore we use two separate []byte fields instead of peerAddr
|
// therefore we use two separate []byte fields instead of peerAddr
|
||||||
// TODO verify that nested structs cannot be used in rlp
|
// TODO verify that nested structs cannot be used in rlp
|
||||||
smsg := &pssPayload{
|
smsg := &PssProtocolMsg{
|
||||||
Code: code,
|
Code: code,
|
||||||
Size: uint32(len(rlpdata)),
|
Size: uint32(len(rlpdata)),
|
||||||
Data: rlpdata,
|
Payload: rlpdata,
|
||||||
}
|
}
|
||||||
|
|
||||||
rlpbundle, err := rlp.EncodeToBytes(smsg)
|
return rlp.EncodeToBytes(smsg)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return rlpbundle, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compiles a new PssTopic from a given name and version.
|
// constructs a new PssTopic from a given name and version.
|
||||||
//
|
//
|
||||||
// Analogous to the name and version members of p2p.Protocol
|
// Analogous to the name and version members of p2p.Protocol
|
||||||
func MakeTopic(s string, v int) (PssTopic, error) {
|
func NewTopic(s string, v int) (topic PssTopic) {
|
||||||
t := [TopicLength]byte{}
|
h := sha3.NewKeccak256()
|
||||||
if len(s)+4 <= TopicLength {
|
h.Write([]byte(s))
|
||||||
copy(t[4:len(s)+4], s)
|
buf := make([]byte, TopicLength / 8)
|
||||||
} else {
|
binary.PutUvarint(buf, uint64(v))
|
||||||
return t, fmt.Errorf("topic '%t' too long", s)
|
h.Write(buf)
|
||||||
}
|
copy(topic[:], h.Sum(buf)[:])
|
||||||
binary.PutVarint(t[:4], int64(v))
|
return topic
|
||||||
return t, nil
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func ToP2pMsg(msg []byte) (p2p.Msg, error) {
|
||||||
|
payload := &PssProtocolMsg{}
|
||||||
|
if err := rlp.DecodeBytes(msg, payload); err != nil {
|
||||||
|
return p2p.Msg{}, fmt.Errorf("pss protocol handler unable to decode payload as p2p message: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p2p.Msg{
|
||||||
|
Code: payload.Code,
|
||||||
|
Size: uint32(len(payload.Payload)),
|
||||||
|
ReceivedAt: time.Now(),
|
||||||
|
Payload: bytes.NewBuffer(payload.Payload),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -1,37 +1,417 @@
|
||||||
package network
|
package pss
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"io/ioutil"
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/protocols"
|
"github.com/ethereum/go-ethereum/p2p/protocols"
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/network"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
protocolName = "foo"
|
pssServiceName = "pss"
|
||||||
protocolVersion = 42
|
bzzServiceName = "bzz"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var services = newServices()
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
adapters.RegisterServices(services)
|
||||||
h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true)))
|
h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true)))
|
||||||
log.Root().SetHandler(h)
|
log.Root().SetHandler(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPssCache(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
to, _ := hex.DecodeString("08090a0b0c0d0e0f1011121314150001020304050607161718191a1b1c1d1e1f")
|
||||||
|
oaddr, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
|
||||||
|
//uaddr, _ := hex.DecodeString("101112131415161718191a1b1c1d1e1f000102030405060708090a0b0c0d0e0f")
|
||||||
|
//proofbytes := []byte{241, 172, 117, 105, 88, 154, 82, 33, 176, 188, 91, 244, 245, 85, 86, 16, 120, 232, 70, 45, 182, 188, 99, 103, 157, 3, 202, 121, 252, 21, 129, 22}
|
||||||
|
proofbytes, _ := hex.DecodeString("ad312dca94df401555cfdeb85a6a1f87fb8f240f08dc36af246bd9d4d41efd89")
|
||||||
|
ps := newTestPss(oaddr)
|
||||||
|
pp := NewPssParams()
|
||||||
|
data := []byte("foo")
|
||||||
|
datatwo := []byte("bar")
|
||||||
|
fwdaddr := network.RandomAddr()
|
||||||
|
msg := &PssMsg{
|
||||||
|
Payload: &PssEnvelope{
|
||||||
|
TTL: 0,
|
||||||
|
From: oaddr,
|
||||||
|
Topic: pssPingTopic,
|
||||||
|
Payload: data,
|
||||||
|
},
|
||||||
|
To: to,
|
||||||
|
}
|
||||||
|
|
||||||
|
msgtwo := &PssMsg{
|
||||||
|
Payload: &PssEnvelope{
|
||||||
|
TTL: 0,
|
||||||
|
From: oaddr,
|
||||||
|
Topic: pssPingTopic,
|
||||||
|
Payload: datatwo,
|
||||||
|
},
|
||||||
|
To: to,
|
||||||
|
}
|
||||||
|
|
||||||
|
digest, err := ps.storeMsg(msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not store cache msgone: %v", err)
|
||||||
|
}
|
||||||
|
digesttwo, err := ps.storeMsg(msgtwo)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not store cache msgtwo: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(digest[:], proofbytes) {
|
||||||
|
t.Fatalf("digest - got: %x, expected: %x", digest, proofbytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
if digest == digesttwo {
|
||||||
|
t.Fatalf("different msgs return same crc: %d", digesttwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the sender cache
|
||||||
|
err = ps.addFwdCacheSender(fwdaddr.Over(), digest)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("write to pss sender cache failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ps.checkFwdCache(fwdaddr.Over(), digest) {
|
||||||
|
t.Fatalf("message %v should have SENDER record in cache but checkCache returned false", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ps.checkFwdCache(fwdaddr.Over(), digesttwo) {
|
||||||
|
t.Fatalf("message %v should NOT have SENDER record in cache but checkCache returned true", msgtwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the expire cache
|
||||||
|
err = ps.addFwdCacheExpire(digest)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("write to pss expire cache failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ps.checkFwdCache(nil, digest) {
|
||||||
|
t.Fatalf("message %v should have EXPIRE record in cache but checkCache returned false", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ps.checkFwdCache(nil, digesttwo) {
|
||||||
|
t.Fatalf("message %v should NOT have EXPIRE record in cache but checkCache returned true", msgtwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(pp.Cachettl)
|
||||||
|
if ps.checkFwdCache(nil, digest) {
|
||||||
|
t.Fatalf("message %v should have expired from cache but checkCache returned true", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ps.AddToCache(fwdaddr.Over(), msgtwo)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("public accessor cache write failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ps.checkFwdCache(fwdaddr.Over(), digesttwo) {
|
||||||
|
t.Fatalf("message %v should have SENDER record in cache but checkCache returned false", msgtwo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPssRegisterHandler(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
addr := network.RandomAddr()
|
||||||
|
ps := newTestPss(addr.OAddr)
|
||||||
|
from := network.RandomAddr()
|
||||||
|
payload := []byte("payload")
|
||||||
|
topic := NewTopic(pssSpec.Name, int(pssSpec.Version))
|
||||||
|
wrongtopic := NewTopic("foo", 42)
|
||||||
|
checkMsg := func(msg []byte, p *p2p.Peer, sender []byte) error {
|
||||||
|
if !bytes.Equal(from.OAddr, sender) {
|
||||||
|
return fmt.Errorf("sender mismatch. expected %x, got %x", from.OAddr, sender)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(msg, payload) {
|
||||||
|
return fmt.Errorf("sender mismatch. expected %x, got %x", msg, payload)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
deregister := ps.Register(&topic, checkMsg)
|
||||||
|
pssmsg := &PssMsg{Payload: NewPssEnvelope(from.OAddr, topic, payload)}
|
||||||
|
err = ps.Process(pssmsg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var i int
|
||||||
|
err = ps.Process(&PssMsg{Payload: NewPssEnvelope(from.OAddr, wrongtopic, payload)})
|
||||||
|
expErr := ""
|
||||||
|
if err == nil || err.Error() == expErr {
|
||||||
|
t.Fatalf("unhandled topic expected '%v', got '%v'", expErr, err)
|
||||||
|
}
|
||||||
|
deregister2 := ps.Register(&topic, func(msg []byte, p *p2p.Peer, sender []byte) error { i++; return nil })
|
||||||
|
err = ps.Process(pssmsg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if i != 1 {
|
||||||
|
t.Fatalf("second registerer handler did not run")
|
||||||
|
}
|
||||||
|
deregister()
|
||||||
|
deregister2()
|
||||||
|
err = ps.Process(&PssMsg{Payload: NewPssEnvelope(from.OAddr, topic, payload)})
|
||||||
|
expErr = ""
|
||||||
|
if err == nil || err.Error() == expErr {
|
||||||
|
t.Fatalf("reregister handler expected %v, got %v", expErr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPssSimpleLinear(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
nodeconfig := adapters.RandomNodeConfig()
|
||||||
|
addr := network.NewAddrFromNodeId(nodeconfig.Id)
|
||||||
|
_ = p2ptest.NewTestPeerPool()
|
||||||
|
ps := newTestPss(addr.Over())
|
||||||
|
|
||||||
|
ping := &pssPing{
|
||||||
|
quitC: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
err = RegisterPssProtocol(ps, &pssPingTopic, pssPingProtocol, newPssPingProtocol(ping.pssPingHandler))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to register virtual protocol in pss: %v", err)
|
||||||
|
}
|
||||||
|
run := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||||
|
id := p.ID()
|
||||||
|
bp := &testPssPeer{
|
||||||
|
Peer: protocols.NewPeer(p, rw, pssSpec),
|
||||||
|
addr: network.ToOverlayAddr(id[:]),
|
||||||
|
}
|
||||||
|
ps.Overlay.On(bp)
|
||||||
|
defer ps.Overlay.Off(bp)
|
||||||
|
log.Debug(fmt.Sprintf("%v", ps.Overlay))
|
||||||
|
return bp.Run(ps.handlePssMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
pt := p2ptest.NewProtocolTester(t, nodeconfig.Id, 2, run)
|
||||||
|
|
||||||
|
msg := newPssPingMsg(ps, network.ToOverlayAddr(pt.Ids[0].Bytes()), pssPingProtocol, pssPingTopic, []byte{1, 2, 3})
|
||||||
|
|
||||||
|
exchange := p2ptest.Exchange{
|
||||||
|
Expects: []p2ptest.Expect{
|
||||||
|
p2ptest.Expect{
|
||||||
|
Code: 0,
|
||||||
|
Msg: msg,
|
||||||
|
Peer: pt.Ids[0],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Triggers: []p2ptest.Trigger{
|
||||||
|
p2ptest.Trigger{
|
||||||
|
Code: 0,
|
||||||
|
Msg: msg,
|
||||||
|
Peer: pt.Ids[1],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = pt.TestExchanges(exchange)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("exchange failed %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPssFullRandom10_5_5(t *testing.T) {
|
||||||
|
adapter := adapters.NewSimAdapter(services)
|
||||||
|
testPssFullRandom(t, adapter, 10, 5, 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPssFullRandom(t *testing.T, adapter adapters.NodeAdapter, nodecount int, fullnodecount int, msgcount int) {
|
||||||
|
var lastid *adapters.NodeId = nil
|
||||||
|
|
||||||
|
nodeCount := 5
|
||||||
|
net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{
|
||||||
|
Id: "0",
|
||||||
|
DefaultService: "psstest",
|
||||||
|
})
|
||||||
|
defer net.Shutdown()
|
||||||
|
|
||||||
|
trigger := make(chan *adapters.NodeId)
|
||||||
|
ids := make([]*adapters.NodeId, nodeCount)
|
||||||
|
|
||||||
|
for i := 0; i < nodeCount; i++ {
|
||||||
|
nodeconfig := adapters.RandomNodeConfig()
|
||||||
|
nodeconfig.Service = "psstest"
|
||||||
|
node, err := net.NewNodeWithConfig(nodeconfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error starting node: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := net.Start(node.ID()); err != nil {
|
||||||
|
t.Fatalf("error starting node %s: %s", node.ID().Label(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := triggerChecks(trigger, net, node.ID()); err != nil {
|
||||||
|
t.Fatal("error triggering checks for node %s: %s", node.ID().Label(), err)
|
||||||
|
}
|
||||||
|
ids[i] = node.ID()
|
||||||
|
}
|
||||||
|
|
||||||
|
// run a simulation which connects the 10 nodes in a ring and waits
|
||||||
|
// for full peer discovery
|
||||||
|
action := func(ctx context.Context) error {
|
||||||
|
for i, id := range ids {
|
||||||
|
var peerId *adapters.NodeId
|
||||||
|
if i == 0 {
|
||||||
|
peerId = ids[len(ids)-1]
|
||||||
|
} else {
|
||||||
|
peerId = ids[i-1]
|
||||||
|
}
|
||||||
|
if err := net.Connect(id, peerId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
check := func(ctx context.Context, id *adapters.NodeId) (bool, error) {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return false, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
node := net.GetNode(id)
|
||||||
|
if node == nil {
|
||||||
|
return false, fmt.Errorf("unknown node: %s", id)
|
||||||
|
}
|
||||||
|
client, err := node.Client()
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("error getting node client: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("in check", "node", id)
|
||||||
|
|
||||||
|
if lastid != nil {
|
||||||
|
//msg := pssPingMsg{Created: time.Now(),}
|
||||||
|
client.CallContext(context.Background(), nil, "pss_sendRaw", pssPingTopic, PssAPIMsg{
|
||||||
|
Addr: lastid.Bytes(),
|
||||||
|
Msg: []byte{1, 2, 3},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
lastid = id
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout := 5 * time.Second
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
result := simulations.NewSimulation(net).Run(ctx, &simulations.Step{
|
||||||
|
Action: action,
|
||||||
|
Trigger: trigger,
|
||||||
|
Expect: &simulations.Expectation{
|
||||||
|
Nodes: ids,
|
||||||
|
Check: check,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if result.Error != nil {
|
||||||
|
t.Fatalf("simulation failed: %s", result.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("Simulation Passed:")
|
||||||
|
t.Logf("Duration: %s", result.FinishedAt.Sub(result.StartedAt))
|
||||||
|
|
||||||
|
time.Sleep(time.Second * 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// triggerChecks triggers a simulation step check whenever a peer is added or
|
||||||
|
// removed from the given node
|
||||||
|
func triggerChecks(trigger chan *adapters.NodeId, net *simulations.Network, id *adapters.NodeId) error {
|
||||||
|
node := net.GetNode(id)
|
||||||
|
if node == nil {
|
||||||
|
return fmt.Errorf("unknown node: %s", id)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
trigger <- id
|
||||||
|
}()
|
||||||
|
/*
|
||||||
|
client, err := node.Client()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
events := make(chan PssAPIMsg)
|
||||||
|
sub, err := client.Subscribe(context.Background(), "pss", events, "newMsg", topic)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error getting peer events for node %v: %s", id, err)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
defer sub.Unsubscribe()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case msg := <-events:
|
||||||
|
log.Warn("pss rpc got msg", "msg", msg)
|
||||||
|
trigger <- id
|
||||||
|
case err := <-sub.Err():
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("error getting peer events for node %v", id), "err", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
*/
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newServices() adapters.Services {
|
||||||
|
|
||||||
|
return adapters.Services{
|
||||||
|
"psstest": func(id *adapters.NodeId, snapshot []byte) []node.Service {
|
||||||
|
addr := network.NewAddrFromNodeId(id)
|
||||||
|
|
||||||
|
kadparams := network.NewKadParams()
|
||||||
|
kadparams.MinProxBinSize = 2
|
||||||
|
kadparams.MaxBinSize = 3
|
||||||
|
kadparams.MinBinSize = 1
|
||||||
|
kadparams.MaxRetries = 1000
|
||||||
|
kadparams.RetryExponent = 2
|
||||||
|
kadparams.RetryInterval = 1000000
|
||||||
|
kademlia := network.NewKademlia(addr.Over(), kadparams)
|
||||||
|
|
||||||
|
config := &network.BzzConfig{
|
||||||
|
OverlayAddr: addr.Over(),
|
||||||
|
UnderlayAddr: addr.Under(),
|
||||||
|
HiveParams: network.NewHiveParams(),
|
||||||
|
}
|
||||||
|
|
||||||
|
config.HiveParams.KeepAliveInterval = time.Second
|
||||||
|
|
||||||
|
cachedir, err := ioutil.TempDir("", "pss-cache")
|
||||||
|
if err != nil {
|
||||||
|
log.Error("create pss cache tmpdir failed", "error", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
dpa, err := storage.NewLocalDPA(cachedir)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("local dpa creation failed", "error", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pssp := NewPssParams()
|
||||||
|
|
||||||
|
return []node.Service{network.NewBzz(config, kademlia, adapters.NewSimStateStore()), NewPss(kademlia, dpa, pssp)}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// example protocol implementation peer
|
// example protocol implementation peer
|
||||||
// message handlers are methods of this
|
// message handlers are methods of this
|
||||||
// channels allow receipt reporting from p2p.Protocol message handler
|
// channels allow receipt reporting from p2p.Protocol message handler
|
||||||
|
|
@ -59,27 +439,19 @@ type pssTestNode struct {
|
||||||
apifunc func() []rpc.API
|
apifunc func() []rpc.API
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *pssTestNode) Add(peer Peer) error {
|
func (n *pssTestNode) Add(peer *bzzPeer) error {
|
||||||
err := n.Hive.Add(peer)
|
err := n.Hive.Add(peer)
|
||||||
time.Sleep(time.Millisecond * 250)
|
time.Sleep(time.Millisecond * 250)
|
||||||
n.triggerCheck()
|
n.triggerCheck()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *pssTestNode) Remove(peer Peer) {
|
|
||||||
n.Hive.Remove(peer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *pssTestNode) hiveKeepAlive() <-chan time.Time {
|
|
||||||
return time.Tick(time.Second * 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *pssTestNode) triggerCheck() {
|
func (n *pssTestNode) triggerCheck() {
|
||||||
go func() { n.trigger <- n.id }()
|
go func() { n.trigger <- n.id }()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *pssTestNode) OverlayAddr() []byte {
|
func (n *pssTestNode) OverlayAddr() []byte {
|
||||||
return n.Pss.Overlay.GetAddr().OverlayAddr()
|
return n.Pss.Overlay.BaseAddr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *pssTestNode) UnderlayAddr() []byte {
|
func (n *pssTestNode) UnderlayAddr() []byte {
|
||||||
|
|
@ -102,8 +474,9 @@ type pssTestService struct {
|
||||||
|
|
||||||
func newPssTestService(t *testing.T, handlefunc func(interface{}) error, testnode *pssTestNode) *pssTestService {
|
func newPssTestService(t *testing.T, handlefunc func(interface{}) error, testnode *pssTestNode) *pssTestService {
|
||||||
hp := NewHiveParams()
|
hp := NewHiveParams()
|
||||||
//hp.CallInterval = 250
|
hp.KeepAliveInterval = 300
|
||||||
testnode.Hive = NewHive(hp, testnode.Pss.Overlay)
|
bzz := NewBzz(testnode.OverlayAddr(), testnode.UnderlayAddr(), newTestStore())
|
||||||
|
testnode.Hive = NewHive(hp, testnode.Pss.Overlay, bzz)
|
||||||
return &pssTestService{
|
return &pssTestService{
|
||||||
//nid := adapters.NewNodeId(addr.UnderlayAddr())
|
//nid := adapters.NewNodeId(addr.UnderlayAddr())
|
||||||
msgFunc: handlefunc,
|
msgFunc: handlefunc,
|
||||||
|
|
@ -111,8 +484,8 @@ func newPssTestService(t *testing.T, handlefunc func(interface{}) error, testnod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *pssTestService) Start(server p2p.Server) error {
|
func (self *pssTestService) Start(server *p2p.Server) error {
|
||||||
return self.node.Hive.Start(server, self.node.hiveKeepAlive)
|
return self.node.Hive.Start(server)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *pssTestService) Stop() error {
|
func (self *pssTestService) Stop() error {
|
||||||
|
|
@ -121,24 +494,13 @@ func (self *pssTestService) Stop() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *pssTestService) Protocols() []p2p.Protocol {
|
func (self *pssTestService) Protocols() []p2p.Protocol {
|
||||||
ct := BzzCodeMap()
|
bzz := NewBzz(self.node.OverlayAddr(), self.node.UnderlayAddr(), newTestStore())
|
||||||
for _, m := range DiscoveryMsgs {
|
return append(self.node.Hive.Protocols(), p2p.Protocol{
|
||||||
ct.Register(m)
|
Name: PssProtocolName,
|
||||||
}
|
Version: PssProtocolVersion,
|
||||||
ct.Register(&PssMsg{})
|
Length: PssProtocol.Length(),
|
||||||
|
Run: bzz.RunProtocol(PssProtocol, self.Run),
|
||||||
srv := func(p Peer) error {
|
})
|
||||||
p.Register(&PssMsg{}, self.msgFunc)
|
|
||||||
self.node.Add(p)
|
|
||||||
p.DisconnectHook(func(err error) {
|
|
||||||
self.node.Remove(p)
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
proto := Bzz(self.node.OverlayAddr(), self.node.UnderlayAddr(), ct, srv, nil, nil)
|
|
||||||
|
|
||||||
return []p2p.Protocol{*proto}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *pssTestService) APIs() []rpc.API {
|
func (self *pssTestService) APIs() []rpc.API {
|
||||||
|
|
@ -153,142 +515,15 @@ func (self *pssTestService) APIs() []rpc.API {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPssCache(t *testing.T) {
|
func (self *pssTestService) Run(peer *bzzPeer) error {
|
||||||
var err error
|
self.node.Add(peer)
|
||||||
to, _ := hex.DecodeString("08090a0b0c0d0e0f1011121314150001020304050607161718191a1b1c1d1e1f")
|
defer self.node.Remove(peer)
|
||||||
oaddr, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
|
return peer.Run(self.msgFunc)
|
||||||
uaddr, _ := hex.DecodeString("101112131415161718191a1b1c1d1e1f000102030405060708090a0b0c0d0e0f")
|
|
||||||
ps := makePss(oaddr)
|
|
||||||
pp := NewPssParams()
|
|
||||||
topic, _ := MakeTopic(protocolName, protocolVersion)
|
|
||||||
data := []byte("foo")
|
|
||||||
fwdaddr := RandomAddr()
|
|
||||||
msg := &PssMsg{
|
|
||||||
Payload: pssEnvelope{
|
|
||||||
TTL: 0,
|
|
||||||
SenderOAddr: oaddr,
|
|
||||||
SenderUAddr: uaddr,
|
|
||||||
Topic: topic,
|
|
||||||
Payload: data,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
msg.SetRecipient(to)
|
|
||||||
|
|
||||||
msgtwo := &PssMsg{
|
|
||||||
Payload: pssEnvelope{
|
|
||||||
TTL: 0,
|
|
||||||
SenderOAddr: uaddr,
|
|
||||||
SenderUAddr: oaddr,
|
|
||||||
Topic: topic,
|
|
||||||
Payload: data,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
msgtwo.SetRecipient(to)
|
|
||||||
|
|
||||||
digest := ps.hashMsg(msg)
|
|
||||||
digesttwo := ps.hashMsg(msgtwo)
|
|
||||||
|
|
||||||
if digest != 3595343914 {
|
|
||||||
t.Fatalf("digest - got: %d, expected: %d", digest, 3595343914)
|
|
||||||
}
|
|
||||||
|
|
||||||
if digest == digesttwo {
|
|
||||||
t.Fatalf("different msgs return same crc: %d", digesttwo)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check the sender cache
|
|
||||||
err = ps.addFwdCacheSender(fwdaddr.OverlayAddr(), digest)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("write to pss sender cache failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ps.checkFwdCache(fwdaddr.OverlayAddr(), digest) {
|
|
||||||
t.Fatalf("message %v should have SENDER record in cache but checkCache returned false", msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ps.checkFwdCache(fwdaddr.OverlayAddr(), digesttwo) {
|
|
||||||
t.Fatalf("message %v should NOT have SENDER record in cache but checkCache returned true", msgtwo)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check the expire cache
|
|
||||||
err = ps.addFwdCacheExpire(digest)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("write to pss expire cache failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ps.checkFwdCache(nil, digest) {
|
|
||||||
t.Fatalf("message %v should have EXPIRE record in cache but checkCache returned false", msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ps.checkFwdCache(nil, digesttwo) {
|
|
||||||
t.Fatalf("message %v should NOT have EXPIRE record in cache but checkCache returned true", msgtwo)
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(pp.Cachettl)
|
|
||||||
if ps.checkFwdCache(nil, digest) {
|
|
||||||
t.Fatalf("message %v should have expired from cache but checkCache returned true", msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ps.AddToCache(fwdaddr.OverlayAddr(), msgtwo)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("public accessor cache write failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ps.checkFwdCache(fwdaddr.OverlayAddr(), digesttwo) {
|
|
||||||
t.Fatalf("message %v should have SENDER record in cache but checkCache returned false", msgtwo)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func TestPssRegisterHandler(t *testing.T) {
|
/*
|
||||||
var topic PssTopic
|
|
||||||
var err error
|
|
||||||
addr := RandomAddr()
|
|
||||||
ps := makePss(addr.UnderlayAddr())
|
|
||||||
|
|
||||||
topic, _ = MakeTopic(protocolName, protocolVersion)
|
|
||||||
err = ps.Register(topic, func(msg []byte, p *p2p.Peer, sender []byte) error { return nil })
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("couldnt register protocol 'foo' v 42: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
topic, _ = MakeTopic(protocolName, protocolVersion)
|
|
||||||
err = ps.Register(topic, func(msg []byte, p *p2p.Peer, sender []byte) error { return nil })
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("register protocol 'abc..789' v 65536 should have failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPssFullRandom10_10_5(t *testing.T) {
|
|
||||||
testPssFullRandom(t, 10, 10, 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPssFullRandom50_50_5(t *testing.T) {
|
|
||||||
testPssFullRandom(t, 50, 50, 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPssFullRandom50_50_25(t *testing.T) {
|
|
||||||
testPssFullRandom(t, 50, 50, 25)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPssFullRandom10_100_50(t *testing.T) {
|
|
||||||
testPssFullRandom(t, 10, 100, 50)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPssFullRandom50_100_50(t *testing.T) {
|
|
||||||
testPssFullRandom(t, 50, 100, 50)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPssFullRandom100_100_5(t *testing.T) {
|
|
||||||
testPssFullRandom(t, 100, 100, 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPssFullRandom100_100_25(t *testing.T) {
|
|
||||||
testPssFullRandom(t, 100, 100, 25)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPssFullRandom100_100_50(t *testing.T) {
|
|
||||||
testPssFullRandom(t, 100, 100, 50)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes int) {
|
func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes int) {
|
||||||
var action func(ctx context.Context) error
|
var action func(ctx context.Context) error
|
||||||
|
|
@ -305,7 +540,9 @@ func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes in
|
||||||
expectnodesids := []*adapters.NodeId{} // the nodes to expect on (needed by checker)
|
expectnodesids := []*adapters.NodeId{} // the nodes to expect on (needed by checker)
|
||||||
expectnodesresults := make(map[*adapters.NodeId][]int) // which messages expect actually got
|
expectnodesresults := make(map[*adapters.NodeId][]int) // which messages expect actually got
|
||||||
|
|
||||||
vct := protocols.NewCodeMap(protocolName, protocolVersion, 65535, &pssTestPayload{})
|
vct := protocols.NewCodeMap(map[uint64]interface{}{
|
||||||
|
0: pssTestPayload{},
|
||||||
|
})
|
||||||
topic, _ := MakeTopic(protocolName, protocolVersion)
|
topic, _ := MakeTopic(protocolName, protocolVersion)
|
||||||
|
|
||||||
trigger := make(chan *adapters.NodeId)
|
trigger := make(chan *adapters.NodeId)
|
||||||
|
|
@ -471,8 +708,8 @@ func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes in
|
||||||
|
|
||||||
for i := 0; i < len(sends); i += 2 {
|
for i := 0; i < len(sends); i += 2 {
|
||||||
t.Logf("Pss #%d: oaddr %x -> %x (uaddr %x -> %x)", i/2+1,
|
t.Logf("Pss #%d: oaddr %x -> %x (uaddr %x -> %x)", i/2+1,
|
||||||
common.ByteLabel(nodes[fullnodes[sends[i]]].Pss.GetAddr().OverlayAddr()),
|
common.ByteLabel(nodes[fullnodes[sends[i]]].Pss.BaseAddr()),
|
||||||
common.ByteLabel(nodes[fullnodes[sends[i+1]]].Pss.GetAddr().OverlayAddr()),
|
common.ByteLabel(nodes[fullnodes[sends[i+1]]].Pss.BaseAddr()),
|
||||||
common.ByteLabel(fullnodes[sends[i]].Bytes()),
|
common.ByteLabel(fullnodes[sends[i]].Bytes()),
|
||||||
common.ByteLabel(fullnodes[sends[i+1]].Bytes()))
|
common.ByteLabel(fullnodes[sends[i+1]].Bytes()))
|
||||||
}
|
}
|
||||||
|
|
@ -484,15 +721,15 @@ func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes in
|
||||||
fails++
|
fails++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.Logf("Node oaddr %x (uaddr %x) was sent %d msgs, of which %d failed", common.ByteLabel(nodes[id].Pss.GetAddr().OverlayAddr()), common.ByteLabel(id.Bytes()), len(results), fails)
|
t.Logf("Node oaddr %x (uaddr %x) was sent %d msgs, of which %d failed", common.ByteLabel(nodes[id].Pss.BaseAddr()), common.ByteLabel(id.Bytes()), len(results), fails)
|
||||||
totalfails += fails
|
totalfails += fails
|
||||||
}
|
}
|
||||||
t.Logf("Total sent: %d, total fail: %d (%.2f%%)", len(sends)/2, totalfails, (float32(totalfails)/float32(len(sends)/2+1))*100)
|
t.Logf("Total sent: %d, total fail: %d (%.2f%%)", len(sends)/2, totalfails, (float32(totalfails)/float32(len(sends)/2+1))*100)
|
||||||
|
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
logstring := fmt.Sprintf("Node oaddr %x kademlia: ", common.ByteLabel(node.Pss.Overlay.GetAddr().OverlayAddr()))
|
logstring := fmt.Sprintf("Node oaddr %x kademlia: ", common.ByteLabel(node.Pss.Overlay.BaseAddr()))
|
||||||
node.Pss.Overlay.EachLivePeer(nil, 256, func(p Peer, po int, isprox bool) bool {
|
node.Pss.Overlay.EachConn(nil, 256, func(p Peer, po int, isprox bool) bool {
|
||||||
logstring += fmt.Sprintf("%x ", common.ByteLabel(p.OverlayAddr()))
|
logstring += fmt.Sprintf("%x ", common.ByteLabel(p.Over()))
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
t.Log(logstring)
|
t.Log(logstring)
|
||||||
|
|
@ -511,7 +748,8 @@ func TestPssFullLinearEcho(t *testing.T) {
|
||||||
var firstpssnode *adapters.NodeId
|
var firstpssnode *adapters.NodeId
|
||||||
var secondpssnode *adapters.NodeId
|
var secondpssnode *adapters.NodeId
|
||||||
|
|
||||||
vct := protocols.NewCodeMap(protocolName, protocolVersion, 65535, &pssTestPayload{})
|
vct := protocols.NewCodeMap(protocolName, protocolVersion, ProtocolMaxMsgSize)
|
||||||
|
vct.Register(0, &pssTestPayload{})
|
||||||
topic, _ := MakeTopic(protocolName, protocolVersion)
|
topic, _ := MakeTopic(protocolName, protocolVersion)
|
||||||
|
|
||||||
fullnodes := []*adapters.NodeId{}
|
fullnodes := []*adapters.NodeId{}
|
||||||
|
|
@ -538,15 +776,15 @@ func TestPssFullLinearEcho(t *testing.T) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
/*for i, id := range ids {
|
// for i, id := range ids {
|
||||||
var peerId *adapters.NodeId
|
// var peerId *adapters.NodeId
|
||||||
if i != 0 {
|
// if i != 0 {
|
||||||
peerId = ids[i-1]
|
// peerId = ids[i-1]
|
||||||
if err := net.Connect(id, peerId); err != nil {
|
// if err := net.Connect(id, peerId); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}*/
|
// }
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
check = func(ctx context.Context, id *adapters.NodeId) (bool, error) {
|
check = func(ctx context.Context, id *adapters.NodeId) (bool, error) {
|
||||||
|
|
@ -559,9 +797,8 @@ func TestPssFullLinearEcho(t *testing.T) {
|
||||||
node, ok := nodes[id]
|
node, ok := nodes[id]
|
||||||
if !ok {
|
if !ok {
|
||||||
return false, fmt.Errorf("unknown node: %s (%v)", id, node)
|
return false, fmt.Errorf("unknown node: %s (%v)", id, node)
|
||||||
} else {
|
|
||||||
log.Trace(fmt.Sprintf("sim check ok node %v", id))
|
|
||||||
}
|
}
|
||||||
|
log.Trace(fmt.Sprintf("sim check ok node %v", id))
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
@ -589,7 +826,7 @@ func TestPssFullLinearEcho(t *testing.T) {
|
||||||
// first find a node that we're connected to
|
// first find a node that we're connected to
|
||||||
for firstpssnode == nonode {
|
for firstpssnode == nonode {
|
||||||
log.Debug(fmt.Sprintf("Waiting for pss relaypeer for %x close to %x ...", common.ByteLabel(nodes[fullnodes[0]].OverlayAddr()), common.ByteLabel(nodes[ids[1]].OverlayAddr())))
|
log.Debug(fmt.Sprintf("Waiting for pss relaypeer for %x close to %x ...", common.ByteLabel(nodes[fullnodes[0]].OverlayAddr()), common.ByteLabel(nodes[ids[1]].OverlayAddr())))
|
||||||
nodes[fullnodes[0]].Pss.Overlay.EachLivePeer(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool {
|
nodes[fullnodes[0]].Pss.Overlay.EachConn(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool {
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
if id.NodeID == p.ID() {
|
if id.NodeID == p.ID() {
|
||||||
firstpssnode = id
|
firstpssnode = id
|
||||||
|
|
@ -609,7 +846,7 @@ func TestPssFullLinearEcho(t *testing.T) {
|
||||||
// then find the node it's connected to
|
// then find the node it's connected to
|
||||||
for secondpssnode == nonode {
|
for secondpssnode == nonode {
|
||||||
log.Debug(fmt.Sprintf("PSS kademlia: Waiting for recipientpeer for %x close to %x ...", common.ByteLabel(nodes[firstpssnode].OverlayAddr()), common.ByteLabel(nodes[fullnodes[1]].OverlayAddr())))
|
log.Debug(fmt.Sprintf("PSS kademlia: Waiting for recipientpeer for %x close to %x ...", common.ByteLabel(nodes[firstpssnode].OverlayAddr()), common.ByteLabel(nodes[fullnodes[1]].OverlayAddr())))
|
||||||
nodes[firstpssnode].Pss.Overlay.EachLivePeer(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool {
|
nodes[firstpssnode].Pss.Overlay.Eachc(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool {
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
if id.NodeID == p.ID() && id.NodeID != fullnodes[0].NodeID {
|
if id.NodeID == p.ID() && id.NodeID != fullnodes[0].NodeID {
|
||||||
secondpssnode = id
|
secondpssnode = id
|
||||||
|
|
@ -692,7 +929,8 @@ func TestPssFullWS(t *testing.T) {
|
||||||
|
|
||||||
var firstpssnode, secondpssnode *adapters.NodeId
|
var firstpssnode, secondpssnode *adapters.NodeId
|
||||||
fullnodes := []*adapters.NodeId{}
|
fullnodes := []*adapters.NodeId{}
|
||||||
vct := protocols.NewCodeMap(protocolName, protocolVersion, 65535, &pssTestPayload{})
|
vct := protocols.NewCodeMap(protocolName, protocolVersion, ProtocolMaxMsgSize)
|
||||||
|
vct.Register(0, &pssTestPayload{})
|
||||||
topic, _ := MakeTopic(pingTopicName, pingTopicVersion)
|
topic, _ := MakeTopic(pingTopicName, pingTopicVersion)
|
||||||
|
|
||||||
trigger := make(chan *adapters.NodeId)
|
trigger := make(chan *adapters.NodeId)
|
||||||
|
|
@ -788,7 +1026,7 @@ func TestPssFullWS(t *testing.T) {
|
||||||
// then find the node it's connected to
|
// then find the node it's connected to
|
||||||
for secondpssnode == nonode {
|
for secondpssnode == nonode {
|
||||||
log.Debug(fmt.Sprintf("PSS kademlia: Waiting for recipientpeer for %x close to %x ...", common.ByteLabel(nodes[firstpssnode].OverlayAddr()), common.ByteLabel(nodes[fullnodes[1]].OverlayAddr())))
|
log.Debug(fmt.Sprintf("PSS kademlia: Waiting for recipientpeer for %x close to %x ...", common.ByteLabel(nodes[firstpssnode].OverlayAddr()), common.ByteLabel(nodes[fullnodes[1]].OverlayAddr())))
|
||||||
nodes[firstpssnode].Pss.Overlay.EachLivePeer(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool {
|
nodes[firstpssnode].Pss.Overlay.EachConn(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool {
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
if id.NodeID == p.ID() && id.NodeID != fullnodes[0].NodeID {
|
if id.NodeID == p.ID() && id.NodeID != fullnodes[0].NodeID {
|
||||||
secondpssnode = id
|
secondpssnode = id
|
||||||
|
|
@ -855,7 +1093,7 @@ func TestPssFullWS(t *testing.T) {
|
||||||
action = func(ctx context.Context) error {
|
action = func(ctx context.Context) error {
|
||||||
go func() {
|
go func() {
|
||||||
clientrecv.EthSubscribe(ctx, ch, "newMsg", topic)
|
clientrecv.EthSubscribe(ctx, ch, "newMsg", topic)
|
||||||
clientsend.Call(nil, "eth_sendRaw", nodes[secondpssnode].Pss.Overlay.GetAddr().OverlayAddr(), topic, []byte("ping"))
|
clientsend.Call(nil, "eth_sendRaw", nodes[secondpssnode].Pss.Overlay.BaseAddr(), topic, []byte("ping"))
|
||||||
trigger <- secondpssnode
|
trigger <- secondpssnode
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -935,7 +1173,7 @@ func newPssSimulationTester(t *testing.T, numnodes int, numfullnodes int, trigge
|
||||||
|
|
||||||
if testpeers[id] != nil {
|
if testpeers[id] != nil {
|
||||||
handlefunc = makePssHandleProtocol(psss[id])
|
handlefunc = makePssHandleProtocol(psss[id])
|
||||||
log.Trace(fmt.Sprintf("Making full protocol id %x addr %x (testpeers %p)", common.ByteLabel(id.Bytes()), common.ByteLabel(addr.OverlayAddr()), testpeers))
|
log.Trace(fmt.Sprintf("Making full protocol id %x addr %x (testpeers %p)", common.ByteLabel(id.Bytes()), common.ByteLabel(addr.Over()), testpeers))
|
||||||
} else {
|
} else {
|
||||||
handlefunc = makePssHandleForward(psss[id])
|
handlefunc = makePssHandleForward(psss[id])
|
||||||
}
|
}
|
||||||
|
|
@ -965,7 +1203,7 @@ func newPssSimulationTester(t *testing.T, numnodes int, numfullnodes int, trigge
|
||||||
}
|
}
|
||||||
for i, conf := range configs {
|
for i, conf := range configs {
|
||||||
addr := NewPeerAddrFromNodeId(conf.Id)
|
addr := NewPeerAddrFromNodeId(conf.Id)
|
||||||
psss[conf.Id] = makePss(addr.OverlayAddr())
|
psss[conf.Id] = makePss(addr.Over())
|
||||||
if i < numfullnodes {
|
if i < numfullnodes {
|
||||||
tp := &pssTestPeer{
|
tp := &pssTestPeer{
|
||||||
Peer: &protocols.Peer{
|
Peer: &protocols.Peer{
|
||||||
|
|
@ -992,13 +1230,73 @@ func newPssSimulationTester(t *testing.T, numnodes int, numfullnodes int, trigge
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePss(addr []byte) *Pss {
|
func makePss(addr []byte) *Pss {
|
||||||
kp := NewKadParams()
|
|
||||||
|
// set up storage
|
||||||
|
cachedir, err := ioutil.TempDir("", "pss-cache")
|
||||||
|
if err != nil {
|
||||||
|
log.Error("create pss cache tmpdir failed", "error", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
dpa, err := storage.NewLocalDPA(cachedir)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("local dpa creation failed", "error", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
// cannot use pyramidchunker as it still lacks joinfunc TestPssRegisterHandler(t *testing.T) {
|
||||||
|
addr := RandomAddr()
|
||||||
|
ps := newTestPss(addr.UnderlayAddr())
|
||||||
|
from := RandomAddr()
|
||||||
|
payload := []byte("payload")
|
||||||
|
topic := NewTopic(protocolName, protocolVersion)
|
||||||
|
checkMsg := func(msg []byte, p *p2p.Peer, sender []byte) error {
|
||||||
|
if !bytes.Equal(from.OverlayAddr(), sender) {
|
||||||
|
return fmt.Errorf("sender mismatch. expected %x, got %x", from.OverlayAddr(), sender)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(msg, payload) {
|
||||||
|
return fmt.Errorf("sender mismatch. expected %x, got %x", msg, payload)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(from.UnderlayAddr(), p.ID()) {
|
||||||
|
return fmt.Errorf("sender mismatch. expected %x, got %x", from.UnderlayAddr(), p.ID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deregister := ps.Register(topic, checkMsg)
|
||||||
|
pssmsg := &PssMsg{Data: NewPssEnvelope(from, topic, payload)}
|
||||||
|
err = ps.Process(pssmsg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var i int
|
||||||
|
err = ps.Process(&PssMsg{Data: NewPssEnvelope(from, []byte("topic"), payload)})
|
||||||
|
expErr := ""
|
||||||
|
if err == nil || err.Error() != expErr {
|
||||||
|
t.Fatalf("unhandled topic expected %v, got %v", expErr, err)
|
||||||
|
}
|
||||||
|
deregister2 := ps.Register(topic, func(msg []byte, p *p2p.Peer, sender []byte) error { i++; return nil })
|
||||||
|
ps.Process(pssmsg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if i != 1 {
|
||||||
|
t.Fatalf("second registerer handler did not run")
|
||||||
|
}
|
||||||
|
deregister()
|
||||||
|
deregister2()
|
||||||
|
err = ps.Process(&PssMsg{Data: NewPssEnvelope(from, topic, payload)})
|
||||||
|
expErr = ""
|
||||||
|
if err == nil || err.Error() != expErr {
|
||||||
|
t.Fatalf("reregister handler expected %v, got %v", expErr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// dpa.Chunker = storage.NewPyramidChunker(storage.NewChunkerParams())
|
||||||
|
|
||||||
|
kp := network.NewKadParams()
|
||||||
kp.MinProxBinSize = 3
|
kp.MinProxBinSize = 3
|
||||||
|
|
||||||
pp := NewPssParams()
|
pp := NewPssParams()
|
||||||
|
|
||||||
overlay := NewKademlia(addr, kp)
|
overlay := network.NewKademlia(addr, kp)
|
||||||
ps := NewPss(overlay, pp)
|
ps := NewPss(overlay, dpa, pp)
|
||||||
//overlay.Prune(time.Tick(time.Millisecond * 250))
|
//overlay.Prune(time.Tick(time.Millisecond * 250))
|
||||||
return ps
|
return ps
|
||||||
}
|
}
|
||||||
|
|
@ -1018,35 +1316,6 @@ func makeCustomProtocol(name string, version int, ct *protocols.CodeMap, testpee
|
||||||
return protocols.NewProtocol(name, uint(version), run, ct, nil, nil)
|
return protocols.NewProtocol(name, uint(version), run, ct, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeFakeMsg(ps *Pss, ct *protocols.CodeMap, topic PssTopic, senderaddr PeerAddr, content string) PssMsg {
|
|
||||||
data := pssTestPayload{}
|
|
||||||
code, found := ct.GetCode(&data)
|
|
||||||
if !found {
|
|
||||||
return PssMsg{}
|
|
||||||
}
|
|
||||||
|
|
||||||
data.Data = content
|
|
||||||
|
|
||||||
rlpbundle, err := makeMsg(code, data)
|
|
||||||
if err != nil {
|
|
||||||
return PssMsg{}
|
|
||||||
}
|
|
||||||
|
|
||||||
pssenv := pssEnvelope{
|
|
||||||
SenderOAddr: senderaddr.OverlayAddr(),
|
|
||||||
SenderUAddr: senderaddr.UnderlayAddr(),
|
|
||||||
Topic: topic,
|
|
||||||
TTL: DefaultTTL,
|
|
||||||
Payload: rlpbundle,
|
|
||||||
}
|
|
||||||
pssmsg := PssMsg{
|
|
||||||
Payload: pssenv,
|
|
||||||
}
|
|
||||||
pssmsg.SetRecipient(ps.Overlay.GetAddr().OverlayAddr())
|
|
||||||
|
|
||||||
return pssmsg
|
|
||||||
}
|
|
||||||
|
|
||||||
func makePssHandleForward(ps *Pss) func(msg interface{}) error {
|
func makePssHandleForward(ps *Pss) func(msg interface{}) error {
|
||||||
// for the simple check it passes on the message if it's not for us
|
// for the simple check it passes on the message if it's not for us
|
||||||
return func(msg interface{}) error {
|
return func(msg interface{}) error {
|
||||||
|
|
@ -1110,3 +1379,4 @@ func (ptp *pssTestPeer) SimpleHandlePssPayload(msg interface{}) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
76
swarm/pss/pssapi.go
Normal file
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/log"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/swarm/api"
|
"github.com/ethereum/go-ethereum/swarm/api"
|
||||||
httpapi "github.com/ethereum/go-ethereum/swarm/api/http"
|
httpapi "github.com/ethereum/go-ethereum/swarm/api/http"
|
||||||
"github.com/ethereum/go-ethereum/swarm/fuse"
|
"github.com/ethereum/go-ethereum/swarm/fuse"
|
||||||
"github.com/ethereum/go-ethereum/swarm/network"
|
"github.com/ethereum/go-ethereum/swarm/network"
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/pss"
|
||||||
)
|
)
|
||||||
|
|
||||||
// the swarm stack
|
// the swarm stack
|
||||||
|
|
@ -162,7 +162,7 @@ Start is called when the stack is started
|
||||||
* TODO: start subservices like sword, swear, swarmdns
|
* TODO: start subservices like sword, swear, swarmdns
|
||||||
*/
|
*/
|
||||||
// implements the node.Service interface
|
// implements the node.Service interface
|
||||||
func (self *Swarm) Start(net p2p.Server) error {
|
func (self *Swarm) Start(net *p2p.Server) error {
|
||||||
// set chequebook
|
// set chequebook
|
||||||
if self.swapEnabled {
|
if self.swapEnabled {
|
||||||
ctx := context.Background() // The initial setup has no deadline.
|
ctx := context.Background() // The initial setup has no deadline.
|
||||||
|
|
@ -182,9 +182,10 @@ func (self *Swarm) Start(net p2p.Server) error {
|
||||||
func() <-chan time.Time {
|
func() <-chan time.Time {
|
||||||
return time.NewTicker(time.Second).C
|
return time.NewTicker(time.Second).C
|
||||||
},
|
},
|
||||||
|
nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Info(fmt.Sprintf("Swarm network started on bzz address: %v", self.hive.GetAddr()))
|
log.Info(fmt.Sprintf("Swarm network started on bzz address: %v", self.hive.BaseAddr()))
|
||||||
|
|
||||||
if self.pssEnabled {
|
if self.pssEnabled {
|
||||||
pssparams := network.NewPssParams()
|
pssparams := network.NewPssParams()
|
||||||
|
|
@ -238,48 +239,46 @@ func (self *Swarm) Stop() error {
|
||||||
// implements the node.Service interface
|
// implements the node.Service interface
|
||||||
func (self *Swarm) Protocols() []p2p.Protocol {
|
func (self *Swarm) Protocols() []p2p.Protocol {
|
||||||
ct := network.BzzCodeMap()
|
ct := network.BzzCodeMap()
|
||||||
for _, m := range network.DiscoveryMsgs {
|
|
||||||
ct.Register(m)
|
|
||||||
}
|
|
||||||
if self.pssEnabled {
|
if self.pssEnabled {
|
||||||
ct.Register(&network.PssMsg{})
|
ct.Register(1, &network.PssMsg{})
|
||||||
}
|
}
|
||||||
|
ct.Register(2, network.DiscoveryMsgs...)
|
||||||
|
|
||||||
srv := func(p network.Peer) error {
|
// srv := func(p network.Peer) error {
|
||||||
if self.pssEnabled {
|
// if self.pssEnabled {
|
||||||
p.Register(&network.PssMsg{}, func(msg interface{}) error {
|
// p.Register(&network.PssMsg{}, func(msg interface{}) error {
|
||||||
pssmsg := msg.(*network.PssMsg)
|
// pssmsg := msg.(*network.PssMsg)
|
||||||
|
//
|
||||||
if self.pss.IsSelfRecipient(pssmsg) {
|
// if self.pss.IsSelfRecipient(pssmsg) {
|
||||||
log.Trace("pss for us, yay! ... let's process!")
|
// log.Trace("pss for us, yay! ... let's process!")
|
||||||
env := pssmsg.Payload
|
// env := pssmsg.Payload
|
||||||
umsg := env.Payload
|
// umsg := env.Payload
|
||||||
f := self.pss.GetHandler(env.Topic)
|
// f := self.pss.GetHandler(env.Topic)
|
||||||
if f == nil {
|
// if f == nil {
|
||||||
return fmt.Errorf("No registered handler for topic '%s'", env.Topic)
|
// return fmt.Errorf("No registered handler for topic '%s'", env.Topic)
|
||||||
}
|
// }
|
||||||
nid := adapters.NewNodeId(env.SenderUAddr)
|
// nid := adapters.NewNodeId(env.SenderUAddr)
|
||||||
p := p2p.NewPeer(nid.NodeID, fmt.Sprintf("%x", common.ByteLabel(nid.Bytes())), []p2p.Cap{})
|
// p := p2p.NewPeer(nid.NodeID, fmt.Sprintf("%x", common.ByteLabel(nid.Bytes())), []p2p.Cap{})
|
||||||
return f(umsg, p, env.SenderOAddr)
|
// return f(umsg, p, env.SenderOAddr)
|
||||||
} else {
|
// } else {
|
||||||
log.Trace("pss was for someone else :'( ... forwarding")
|
// log.Trace("pss was for someone else :'( ... forwarding")
|
||||||
return self.pss.Forward(pssmsg)
|
// return self.pss.Forward(pssmsg)
|
||||||
}
|
// }
|
||||||
return nil
|
// return nil
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
self.hive.Add(p)
|
// self.hive.Add(p)
|
||||||
p.DisconnectHook(func(err error) {
|
// p.DisconnectHook(func(err error) {
|
||||||
self.hive.Remove(p)
|
// self.hive.Remove(p)
|
||||||
})
|
// })
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
proto := network.Bzz(
|
proto := network.Bzz(
|
||||||
self.hive.Overlay.GetAddr().OverlayAddr(),
|
self.hive.Overlay.GetAddr().Over(),
|
||||||
self.hive.Overlay.GetAddr().UnderlayAddr(),
|
self.hive.Overlay.GetAddr().Under(),
|
||||||
ct,
|
ct,
|
||||||
srv,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -48,14 +48,16 @@ func main() {
|
||||||
shh := whisper.New()
|
shh := whisper.New()
|
||||||
|
|
||||||
// Create an Ethereum peer to communicate through
|
// Create an Ethereum peer to communicate through
|
||||||
server := p2p.NewServer(p2p.Config{
|
server := &p2p.Server{
|
||||||
PrivateKey: key,
|
Config: p2p.Config{
|
||||||
MaxPeers: 10,
|
PrivateKey: key,
|
||||||
Name: name,
|
MaxPeers: 10,
|
||||||
Protocols: []p2p.Protocol{shh.Protocol()},
|
Name: name,
|
||||||
ListenAddr: ":30300",
|
Protocols: []p2p.Protocol{shh.Protocol()},
|
||||||
NAT: nat.Any(),
|
ListenAddr: ":30300",
|
||||||
})
|
NAT: nat.Any(),
|
||||||
|
},
|
||||||
|
}
|
||||||
fmt.Println("Starting Ethereum peer...")
|
fmt.Println("Starting Ethereum peer...")
|
||||||
if err := server.Start(); err != nil {
|
if err := server.Start(); err != nil {
|
||||||
fmt.Printf("Failed to start Ethereum peer: %v.\n", err)
|
fmt.Printf("Failed to start Ethereum peer: %v.\n", err)
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ func (self *Whisper) Send(envelope *Envelope) error {
|
||||||
|
|
||||||
// Start implements node.Service, starting the background data propagation thread
|
// Start implements node.Service, starting the background data propagation thread
|
||||||
// of the Whisper protocol.
|
// of the Whisper protocol.
|
||||||
func (self *Whisper) Start(p2p.Server) error {
|
func (self *Whisper) Start(*p2p.Server) error {
|
||||||
log.Info(fmt.Sprint("Whisper started"))
|
log.Info(fmt.Sprint("Whisper started"))
|
||||||
go self.update()
|
go self.update()
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ type TestData struct {
|
||||||
type TestNode struct {
|
type TestNode struct {
|
||||||
shh *Whisper
|
shh *Whisper
|
||||||
id *ecdsa.PrivateKey
|
id *ecdsa.PrivateKey
|
||||||
server p2p.Server
|
server *p2p.Server
|
||||||
filerId string
|
filerId string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,17 +140,19 @@ func initialize(t *testing.T) {
|
||||||
peers = append(peers, peer)
|
peers = append(peers, peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
node.server = p2p.NewServer(p2p.Config{
|
node.server = &p2p.Server{
|
||||||
PrivateKey: node.id,
|
Config: p2p.Config{
|
||||||
MaxPeers: NumNodes/2 + 1,
|
PrivateKey: node.id,
|
||||||
Name: name,
|
MaxPeers: NumNodes/2 + 1,
|
||||||
Protocols: node.shh.Protocols(),
|
Name: name,
|
||||||
ListenAddr: addr,
|
Protocols: node.shh.Protocols(),
|
||||||
NAT: nat.Any(),
|
ListenAddr: addr,
|
||||||
BootstrapNodes: peers,
|
NAT: nat.Any(),
|
||||||
StaticNodes: peers,
|
BootstrapNodes: peers,
|
||||||
TrustedNodes: peers,
|
StaticNodes: peers,
|
||||||
})
|
TrustedNodes: peers,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
err = node.server.Start()
|
err = node.server.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -396,7 +396,7 @@ func (w *Whisper) Send(envelope *Envelope) error {
|
||||||
|
|
||||||
// Start implements node.Service, starting the background data propagation thread
|
// Start implements node.Service, starting the background data propagation thread
|
||||||
// of the Whisper protocol.
|
// of the Whisper protocol.
|
||||||
func (w *Whisper) Start(p2p.Server) error {
|
func (w *Whisper) Start(*p2p.Server) error {
|
||||||
log.Info("started whisper v." + ProtocolVersionStr)
|
log.Info("started whisper v." + ProtocolVersionStr)
|
||||||
go w.update()
|
go w.update()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue