mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 15:16:43 +00:00
swarm/pss: pssclient, pss tests
reinstated full sim pss core tests pssclient; p2p/protocols mountable rpc proxy for pss ws)
This commit is contained in:
parent
4d02b2d30e
commit
1dcc0e3df8
9 changed files with 964 additions and 1511 deletions
|
|
@ -1,4 +1,4 @@
|
|||
package pss
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
|
@ -9,20 +9,41 @@ import (
|
|||
"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/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/swarm/pss"
|
||||
)
|
||||
|
||||
const (
|
||||
inboxCapacity = 3000
|
||||
outboxCapacity = 100
|
||||
defaultWSHost = 8546
|
||||
addrLen = common.HashLength
|
||||
)
|
||||
|
||||
// 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
|
||||
// SelfHost: local if host to connect from
|
||||
type PssClientConfig struct {
|
||||
SelfHost string
|
||||
RemoteHost string
|
||||
RemotePort int
|
||||
Secure bool
|
||||
}
|
||||
|
||||
func NewPssClientConfig() *PssClientConfig {
|
||||
return &PssClientConfig{
|
||||
SelfHost: "localhost",
|
||||
RemoteHost: "localhost",
|
||||
RemotePort: 8546,
|
||||
}
|
||||
}
|
||||
|
||||
type PssClient struct {
|
||||
localuri string
|
||||
remoteuri string
|
||||
|
|
@ -30,28 +51,26 @@ type PssClient struct {
|
|||
cancel func()
|
||||
subscription *rpc.ClientSubscription
|
||||
topicsC chan []byte
|
||||
msgC chan PssAPIMsg
|
||||
msgC chan pss.PssAPIMsg
|
||||
quitC chan struct{}
|
||||
quitting uint32
|
||||
ws *rpc.Client
|
||||
lock sync.Mutex
|
||||
peerPool map[PssTopic]map[pot.Address]*pssRPCRW
|
||||
protos []*p2p.Protocol
|
||||
peerPool map[pss.PssTopic]map[pot.Address]*pssRPCRW
|
||||
protos map[pss.PssTopic]*p2p.Protocol
|
||||
}
|
||||
|
||||
type pssRPCRW struct {
|
||||
*PssClient
|
||||
topic *PssTopic
|
||||
spec *protocols.Spec
|
||||
topic *pss.PssTopic
|
||||
msgC chan []byte
|
||||
addr pot.Address
|
||||
}
|
||||
|
||||
func (self *PssClient) newpssRPCRW(addr pot.Address, spec *protocols.Spec, topic *PssTopic) *pssRPCRW {
|
||||
func (self *PssClient) newpssRPCRW(addr pot.Address, topic *pss.PssTopic) *pssRPCRW {
|
||||
return &pssRPCRW{
|
||||
PssClient: self,
|
||||
topic: topic,
|
||||
spec: spec,
|
||||
msgC: make(chan []byte),
|
||||
addr: addr,
|
||||
}
|
||||
|
|
@ -59,8 +78,8 @@ func (self *PssClient) newpssRPCRW(addr pot.Address, spec *protocols.Spec, topic
|
|||
|
||||
func (rw *pssRPCRW) ReadMsg() (p2p.Msg, error) {
|
||||
msg := <-rw.msgC
|
||||
log.Warn("pssrpcrw read", "msg", msg)
|
||||
pmsg, err := ToP2pMsg(msg)
|
||||
log.Trace("pssrpcrw read", "msg", msg)
|
||||
pmsg, err := pss.ToP2pMsg(msg)
|
||||
if err != nil {
|
||||
return p2p.Msg{}, err
|
||||
}
|
||||
|
|
@ -69,72 +88,84 @@ func (rw *pssRPCRW) ReadMsg() (p2p.Msg, error) {
|
|||
}
|
||||
|
||||
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)
|
||||
log.Trace("got writemsg pssclient", "msg", msg)
|
||||
rlpdata := make([]byte, msg.Size)
|
||||
msg.Payload.Read(rlpdata)
|
||||
pmsg, err := rlp.EncodeToBytes(pss.PssProtocolMsg{
|
||||
Code: msg.Code,
|
||||
Size: msg.Size,
|
||||
Payload: rlpdata,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not render protocolmessage", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return rw.PssClient.ws.CallContext(rw.PssClient.ctx, nil, "pss_sendRaw", rw.topic, PssAPIMsg{
|
||||
return rw.PssClient.ws.CallContext(rw.PssClient.ctx, nil, "pss_sendPss", rw.topic, pss.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 {
|
||||
func NewPssClient(ctx context.Context, cancel func(), config *PssClientConfig) *PssClient {
|
||||
prefix := "ws"
|
||||
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
if cancel == nil {
|
||||
cancel = func() { return }
|
||||
}
|
||||
|
||||
pssc := &PssClient{
|
||||
msgC: make(chan PssAPIMsg),
|
||||
msgC: make(chan pss.PssAPIMsg),
|
||||
quitC: make(chan struct{}),
|
||||
peerPool: make(map[PssTopic]map[pot.Address]*pssRPCRW),
|
||||
peerPool: make(map[pss.PssTopic]map[pot.Address]*pssRPCRW),
|
||||
protos: make(map[pss.PssTopic]*p2p.Protocol),
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
}
|
||||
|
||||
if remotehost == "" {
|
||||
remotehost = "localhost"
|
||||
if config.RemoteHost == "" {
|
||||
config.RemoteHost = "localhost"
|
||||
}
|
||||
|
||||
if remoteport == 0 {
|
||||
remoteport = node.DefaultWSPort
|
||||
if config.RemotePort == 0 {
|
||||
config.RemotePort = defaultWSHost
|
||||
}
|
||||
|
||||
if originhost == "" {
|
||||
originhost = "localhost"
|
||||
if config.SelfHost == "" {
|
||||
config.SelfHost = "localhost"
|
||||
}
|
||||
|
||||
if secure {
|
||||
if config.Secure {
|
||||
prefix = "wss"
|
||||
}
|
||||
|
||||
pssc.remoteuri = fmt.Sprintf("%s://%s:%d", prefix, remotehost, remoteport)
|
||||
pssc.localuri = fmt.Sprintf("%s://%s", prefix, originhost)
|
||||
pssc.remoteuri = fmt.Sprintf("%s://%s:%d", prefix, config.RemoteHost, config.RemotePort)
|
||||
pssc.localuri = fmt.Sprintf("%s://%s", prefix, config.SelfHost)
|
||||
|
||||
return pssc
|
||||
}
|
||||
|
||||
func NewPssClientWithRPC(ctx context.Context, client *rpc.Client) *PssClient {
|
||||
return &PssClient{
|
||||
msgC: make(chan pss.PssAPIMsg),
|
||||
quitC: make(chan struct{}),
|
||||
peerPool: make(map[pss.PssTopic]map[pot.Address]*pssRPCRW),
|
||||
protos: make(map[pss.PssTopic]*p2p.Protocol),
|
||||
ws: client,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *PssClient) shutdown() {
|
||||
atomic.StoreUint32(&self.quitting, 1)
|
||||
self.cancel()
|
||||
}
|
||||
|
||||
func (self *PssClient) Start() error {
|
||||
if self.ws != nil {
|
||||
return nil
|
||||
}
|
||||
log.Debug("Dialing ws", "src", self.localuri, "dst", self.remoteuri)
|
||||
ws, err := rpc.DialWebsocket(self.ctx, self.remoteuri, self.localuri)
|
||||
if err != nil {
|
||||
|
|
@ -146,11 +177,11 @@ func (self *PssClient) Start() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (self *PssClient) RunProtocol(proto *p2p.Protocol, spec *protocols.Spec) error {
|
||||
topic := NewTopic(spec.Name, int(spec.Version))
|
||||
msgC := make(chan PssAPIMsg)
|
||||
func (self *PssClient) RunProtocol(proto *p2p.Protocol) error {
|
||||
topic := pss.NewTopic(proto.Name, int(proto.Version))
|
||||
msgC := make(chan pss.PssAPIMsg)
|
||||
self.peerPool[topic] = make(map[pot.Address]*pssRPCRW)
|
||||
sub, err := self.ws.Subscribe(self.ctx, "pss", msgC, "newMsg", topic)
|
||||
sub, err := self.ws.Subscribe(self.ctx, "pss", msgC, "receivePss", topic)
|
||||
if err != nil {
|
||||
return fmt.Errorf("pss event subscription failed: %v", err)
|
||||
}
|
||||
|
|
@ -165,7 +196,7 @@ func (self *PssClient) RunProtocol(proto *p2p.Protocol, spec *protocols.Spec) er
|
|||
var addr pot.Address
|
||||
copy(addr[:], msg.Addr)
|
||||
if self.peerPool[topic][addr] == nil {
|
||||
self.peerPool[topic][addr] = self.newpssRPCRW(addr, spec, &topic)
|
||||
self.peerPool[topic][addr] = self.newpssRPCRW(addr, &topic)
|
||||
nid, _ := discover.HexID("0x00")
|
||||
p := p2p.NewPeer(nid, fmt.Sprintf("%v", addr), []p2p.Cap{})
|
||||
go proto.Run(p, self.peerPool[topic][addr])
|
||||
|
|
@ -180,7 +211,7 @@ func (self *PssClient) RunProtocol(proto *p2p.Protocol, spec *protocols.Spec) er
|
|||
}
|
||||
}()
|
||||
|
||||
self.protos = append(self.protos, proto)
|
||||
self.protos[topic] = proto
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -190,14 +221,17 @@ func (self *PssClient) Stop() error {
|
|||
}
|
||||
|
||||
func (self *PssClient) AddPssPeer(addr pot.Address, spec *protocols.Spec) {
|
||||
topic := NewTopic(spec.Name, int(spec.Version))
|
||||
topic := pss.NewTopic(spec.Name, int(spec.Version))
|
||||
if self.peerPool[topic][addr] == nil {
|
||||
self.peerPool[topic][addr] = self.newpssRPCRW(addr, spec, &topic)
|
||||
self.peerPool[topic][addr] = self.newpssRPCRW(addr, &topic)
|
||||
nid, _ := discover.HexID("0x00")
|
||||
p := p2p.NewPeer(nid, fmt.Sprintf("%v", addr), []p2p.Cap{})
|
||||
go self.protos[topic].Run(p, self.peerPool[topic][addr])
|
||||
}
|
||||
}
|
||||
|
||||
func (self *PssClient) RemovePssPeer(addr pot.Address, spec *protocols.Spec) {
|
||||
topic := NewTopic(spec.Name, int(spec.Version))
|
||||
topic := pss.NewTopic(spec.Name, int(spec.Version))
|
||||
delete(self.peerPool[topic], addr)
|
||||
}
|
||||
|
||||
187
swarm/pss/client/client_test.go
Normal file
187
swarm/pss/client/client_test.go
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"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"
|
||||
"github.com/ethereum/go-ethereum/swarm/pss"
|
||||
)
|
||||
|
||||
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{})
|
||||
ps := pss.NewTestPss(nil)
|
||||
ping := &pss.PssPing{
|
||||
QuitC: make(chan struct{}),
|
||||
}
|
||||
proto := newProtocol(ping)
|
||||
_, err := baseTester(t, proto, ps, nil, nil, quitC)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
quitC <- struct{}{}
|
||||
}
|
||||
|
||||
func TestIncoming(t *testing.T) {
|
||||
quitC := make(chan struct{})
|
||||
ps := pss.NewTestPss(nil)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
var addr []byte
|
||||
ping := &pss.PssPing{
|
||||
QuitC: make(chan struct{}),
|
||||
}
|
||||
proto := newProtocol(ping)
|
||||
client, err := baseTester(t, proto, ps, ctx, cancel, quitC)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
client.ws.Call(&addr, "psstest_baseAddr")
|
||||
|
||||
code, _ := pss.PssPingProtocol.GetCode(&pss.PssPingMsg{})
|
||||
rlpbundle, err := pss.NewProtocolMsg(code, &pss.PssPingMsg{
|
||||
Created: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't make pssmsg")
|
||||
}
|
||||
|
||||
pssenv := pss.PssEnvelope{
|
||||
From: addr,
|
||||
Topic: pss.NewTopic(proto.Name, int(proto.Version)),
|
||||
TTL: pss.DefaultTTL,
|
||||
Payload: rlpbundle,
|
||||
}
|
||||
pssmsg := pss.PssMsg{
|
||||
To: addr,
|
||||
Payload: &pssenv,
|
||||
}
|
||||
|
||||
ps.Process(&pssmsg)
|
||||
|
||||
go func() {
|
||||
<-ping.QuitC
|
||||
client.cancel()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-client.ctx.Done():
|
||||
t.Fatalf("outgoing timed out or canceled")
|
||||
default:
|
||||
}
|
||||
quitC <- struct{}{}
|
||||
}
|
||||
|
||||
func TestOutgoing(t *testing.T) {
|
||||
quitC := make(chan struct{})
|
||||
ps := pss.NewTestPss(nil)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*250)
|
||||
var addr []byte
|
||||
var potaddr pot.Address
|
||||
|
||||
ping := &pss.PssPing{
|
||||
QuitC: make(chan struct{}),
|
||||
}
|
||||
proto := newProtocol(ping)
|
||||
client, err := baseTester(t, proto, ps, ctx, cancel, quitC)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
client.ws.Call(&addr, "psstest_baseAddr")
|
||||
copy(potaddr[:], addr)
|
||||
|
||||
msg := &pss.PssPingMsg{
|
||||
Created: time.Now(),
|
||||
}
|
||||
|
||||
topic := pss.NewTopic(pss.PssPingProtocol.Name, int(pss.PssPingProtocol.Version))
|
||||
client.AddPssPeer(potaddr, pss.PssPingProtocol)
|
||||
nid, _ := discover.HexID("0x00")
|
||||
p := p2p.NewPeer(nid, fmt.Sprintf("%v", potaddr), []p2p.Cap{})
|
||||
pp := protocols.NewPeer(p, client.peerPool[topic][potaddr], pss.PssPingProtocol)
|
||||
pp.Send(msg)
|
||||
select {
|
||||
case <-client.ctx.Done():
|
||||
t.Fatalf("outgoing timed out or canceled")
|
||||
default:
|
||||
}
|
||||
quitC <- struct{}{}
|
||||
}
|
||||
|
||||
func baseTester(t *testing.T, proto *p2p.Protocol, ps *pss.Pss, ctx context.Context, cancel func(), quitC chan struct{}) (*PssClient, error) {
|
||||
var err error
|
||||
|
||||
client := newClient(t, ctx, cancel, quitC)
|
||||
|
||||
err = client.Start()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = client.RunProtocol(proto)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func newProtocol(ping *pss.PssPing) *p2p.Protocol {
|
||||
|
||||
return &p2p.Protocol{
|
||||
Name: pss.PssPingProtocol.Name,
|
||||
Version: pss.PssPingProtocol.Version,
|
||||
Length: 1,
|
||||
Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||
pp := protocols.NewPeer(p, rw, pss.PssPingProtocol)
|
||||
pp.Run(ping.PssPingHandler)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newClient(t *testing.T, ctx context.Context, cancel func(), quitC chan struct{}) *PssClient {
|
||||
|
||||
conf := NewPssClientConfig()
|
||||
|
||||
pssclient := NewPssClient(ctx, cancel, conf)
|
||||
|
||||
ps := pss.NewTestPss(nil)
|
||||
srv := rpc.NewServer()
|
||||
srv.RegisterName("pss", pss.NewPssAPI(ps))
|
||||
srv.RegisterName("psstest", pss.NewPssAPITest(ps))
|
||||
ws := srv.WebsocketHandler([]string{"*"})
|
||||
uri := fmt.Sprintf("%s:%d", "localhost", 8546)
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
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
|
||||
}
|
||||
|
|
@ -11,38 +11,74 @@ import (
|
|||
"github.com/ethereum/go-ethereum/p2p/protocols"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
)
|
||||
)
|
||||
|
||||
type pssPingMsg struct {
|
||||
type PssPingMsg struct {
|
||||
Created time.Time
|
||||
}
|
||||
|
||||
type pssPing struct {
|
||||
quitC chan struct{}
|
||||
type PssPing struct {
|
||||
QuitC chan struct{}
|
||||
}
|
||||
|
||||
func (self *pssPing) pssPingHandler(msg interface{}) error {
|
||||
func (self *PssPing) PssPingHandler(msg interface{}) error {
|
||||
log.Warn("got ping", "msg", msg)
|
||||
self.quitC <- struct{}{}
|
||||
self.QuitC <- struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
var pssPingProtocol = &protocols.Spec{
|
||||
var PssPingProtocol = &protocols.Spec{
|
||||
Name: "psstest",
|
||||
Version: 1,
|
||||
MaxMsgSize: 10 * 1024 * 1024,
|
||||
Messages: []interface{}{
|
||||
pssPingMsg{},
|
||||
PssPingMsg{},
|
||||
},
|
||||
}
|
||||
|
||||
var pssPingTopic = NewTopic(pssPingProtocol.Name, int(pssPingProtocol.Version))
|
||||
var PssPingTopic = NewTopic(PssPingProtocol.Name, int(PssPingProtocol.Version))
|
||||
|
||||
func newTestPss(addr []byte) *Pss {
|
||||
func NewPssPingMsg(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
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewTestPss(addr []byte) *Pss {
|
||||
if addr == nil {
|
||||
addr = network.RandomAddr().OAddr
|
||||
}
|
||||
|
||||
|
||||
// set up storage
|
||||
cachedir, err := ioutil.TempDir("", "pss-cache")
|
||||
if err != nil {
|
||||
|
|
@ -54,72 +90,16 @@ func newTestPss(addr []byte) *Pss {
|
|||
log.Error("local dpa creation failed", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
||||
// set up routing
|
||||
kp := network.NewKadParams()
|
||||
kp.MinProxBinSize = 3
|
||||
|
||||
// create pss
|
||||
pp := NewPssParams()
|
||||
pp := NewPssParams(true)
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
|||
207
swarm/pss/pss.go
207
swarm/pss/pss.go
|
|
@ -2,19 +2,18 @@ package pss
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
//"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
//"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/protocols"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
"github.com/ethereum/go-ethereum/pot"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
|
@ -23,14 +22,11 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
DefaultTTL = 6000
|
||||
TopicLength = 32
|
||||
TopicResolverLength = 8
|
||||
PssPeerCapacity = 256
|
||||
PssPeerTopicDefaultCapacity = 8
|
||||
digestLength = 32
|
||||
digestCapacity = 256
|
||||
defaultDigestCacheTTL = time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -42,66 +38,6 @@ type senderPeer interface {
|
|||
Send(interface{}) error
|
||||
}
|
||||
|
||||
// Defines params for Pss
|
||||
type PssParams struct {
|
||||
Cachettl time.Duration
|
||||
}
|
||||
|
||||
// Initializes default params for Pss
|
||||
func NewPssParams() *PssParams {
|
||||
return &PssParams{
|
||||
Cachettl: defaultDigestCacheTTL,
|
||||
}
|
||||
}
|
||||
|
||||
// Encapsulates the message transported over pss.
|
||||
type PssMsg struct {
|
||||
To []byte
|
||||
Payload *PssEnvelope
|
||||
}
|
||||
|
||||
// String representation of PssMsg
|
||||
func (self *PssMsg) String() string {
|
||||
return fmt.Sprintf("PssMsg: Recipient: %x", common.ByteLabel(self.To))
|
||||
}
|
||||
|
||||
// Topic defines the context of a message being transported over pss
|
||||
// It is used by pss to determine what action is to be taken on an incoming message
|
||||
// Typically, one can map protocol handlers for the message payloads by mapping topic to them; see *Pss.Register()
|
||||
type PssTopic [TopicLength]byte
|
||||
|
||||
func (self *PssTopic) String() string {
|
||||
return fmt.Sprintf("%x", self)
|
||||
}
|
||||
|
||||
// Pre-Whisper placeholder, payload of PssMsg
|
||||
type PssEnvelope struct {
|
||||
Topic PssTopic
|
||||
TTL uint16
|
||||
Payload []byte
|
||||
From []byte
|
||||
}
|
||||
|
||||
// creates Pss envelope from sender address, topic and raw payload
|
||||
func NewPssEnvelope(addr []byte, topic PssTopic, payload []byte) *PssEnvelope {
|
||||
return &PssEnvelope{
|
||||
From: addr,
|
||||
Topic: topic,
|
||||
TTL: DefaultTTL,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
func (msg *PssMsg) serialize() []byte {
|
||||
rlpdata, _ := rlp.EncodeToBytes(msg)
|
||||
/*buf := bytes.NewBuffer(nil)
|
||||
buf.Write(self.PssEnvelope.Topic[:])
|
||||
buf.Write(self.PssEnvelope.Payload)
|
||||
buf.Write(self.PssEnvelope.From)
|
||||
return buf.Bytes()*/
|
||||
return rlpdata
|
||||
}
|
||||
|
||||
var pssSpec = &protocols.Spec{
|
||||
Name: "pss",
|
||||
Version: 1,
|
||||
|
|
@ -111,14 +47,6 @@ var pssSpec = &protocols.Spec{
|
|||
},
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
@ -126,9 +54,6 @@ type pssCacheEntry struct {
|
|||
|
||||
type pssDigest [digestLength]byte
|
||||
|
||||
// Message handler func for a topic
|
||||
type pssHandler func(msg []byte, p *p2p.Peer, from []byte) error
|
||||
|
||||
// pss provides sending messages to nodes without having to be directly connected to them.
|
||||
//
|
||||
// The messages are wrapped in a PssMsg structure and routed using the swarm kademlia routing.
|
||||
|
|
@ -142,21 +67,21 @@ type pssHandler func(msg []byte, p *p2p.Peer, from []byte) error
|
|||
// - a dispatcher lookup, mapping protocols to topics
|
||||
// - a message cache to spot messages that previously have been forwarded
|
||||
type Pss struct {
|
||||
network.Overlay // we can get the overlayaddress from this
|
||||
//peerPool map[pot.Address]map[PssTopic]p2p.MsgReadWriter // keep track of all virtual p2p.Peers we are currently speaking to
|
||||
peerPool map[pot.Address]map[PssTopic]p2p.MsgReadWriter // keep track of all virtual p2p.Peers we are currently speaking to
|
||||
fwdPool map[pot.Address]*protocols.Peer // keep track of all peers sitting on the pssmsg routing layer
|
||||
handlers map[PssTopic]map[*pssHandler]bool // topic and version based pss payload handlers
|
||||
fwdcache map[pssDigest]pssCacheEntry // checksum of unique fields from pssmsg mapped to expiry, cache to determine whether to drop msg
|
||||
cachettl time.Duration // how long to keep messages in fwdcache
|
||||
lock sync.Mutex
|
||||
dpa *storage.DPA
|
||||
network.Overlay // we can get the overlayaddress from this
|
||||
peerPool map[pot.Address]map[PssTopic]p2p.MsgReadWriter // keep track of all virtual p2p.Peers we are currently speaking to
|
||||
fwdPool map[pot.Address]*protocols.Peer // keep track of all peers sitting on the pssmsg routing layer
|
||||
handlers map[PssTopic]map[*PssHandler]bool // topic and version based pss payload handlers
|
||||
fwdcache map[pssDigest]pssCacheEntry // checksum of unique fields from pssmsg mapped to expiry, cache to determine whether to drop msg
|
||||
cachettl time.Duration // how long to keep messages in fwdcache
|
||||
lock sync.Mutex
|
||||
dpa *storage.DPA
|
||||
debug bool
|
||||
}
|
||||
|
||||
func (self *Pss) storeMsg(msg *PssMsg) (pssDigest, error) {
|
||||
swg := &sync.WaitGroup{}
|
||||
wwg := &sync.WaitGroup{}
|
||||
buf := bytes.NewReader(msg.serialize())
|
||||
buf := bytes.NewReader(msg.Serialize())
|
||||
key, err := self.dpa.Store(buf, int64(buf.Len()), swg, wwg)
|
||||
if err != nil {
|
||||
log.Warn("Could not store in swarm", "err", err)
|
||||
|
|
@ -176,13 +101,18 @@ func NewPss(k network.Overlay, dpa *storage.DPA, params *PssParams) *Pss {
|
|||
Overlay: k,
|
||||
peerPool: make(map[pot.Address]map[PssTopic]p2p.MsgReadWriter, PssPeerCapacity),
|
||||
fwdPool: make(map[pot.Address]*protocols.Peer),
|
||||
handlers: make(map[PssTopic]map[*pssHandler]bool),
|
||||
handlers: make(map[PssTopic]map[*PssHandler]bool),
|
||||
fwdcache: make(map[pssDigest]pssCacheEntry),
|
||||
cachettl: params.Cachettl,
|
||||
dpa: dpa,
|
||||
debug: params.Debug,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Pss) BaseAddr() []byte {
|
||||
return self.Overlay.BaseAddr()
|
||||
}
|
||||
|
||||
func (self *Pss) Start(srv *p2p.Server) error {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -211,7 +141,7 @@ func (self *Pss) Run(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
|||
}
|
||||
|
||||
func (self *Pss) APIs() []rpc.API {
|
||||
return []rpc.API{
|
||||
apis := []rpc.API{
|
||||
rpc.API{
|
||||
Namespace: "pss",
|
||||
Version: "0.1",
|
||||
|
|
@ -219,6 +149,15 @@ func (self *Pss) APIs() []rpc.API {
|
|||
Public: true,
|
||||
},
|
||||
}
|
||||
if self.debug {
|
||||
apis = append(apis, rpc.API{
|
||||
Namespace: "pss",
|
||||
Version: "0.1",
|
||||
Service: NewPssAPITest(self),
|
||||
Public: true,
|
||||
})
|
||||
}
|
||||
return apis
|
||||
}
|
||||
|
||||
// Takes the generated PssTopic of a protocol/chatroom etc, and links a handler function to it
|
||||
|
|
@ -227,19 +166,19 @@ func (self *Pss) APIs() []rpc.API {
|
|||
// 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() {
|
||||
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)
|
||||
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) {
|
||||
func (self *Pss) deregister(topic *PssTopic, h *PssHandler) {
|
||||
self.lock.Lock()
|
||||
defer self.lock.Unlock()
|
||||
handlers := self.handlers[*topic]
|
||||
|
|
@ -304,7 +243,7 @@ func (self *Pss) checkFwdCache(addr []byte, digest pssDigest) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (self *Pss) getHandlers(topic PssTopic) map[*pssHandler]bool {
|
||||
func (self *Pss) getHandlers(topic PssTopic) map[*PssHandler]bool {
|
||||
self.lock.Lock()
|
||||
defer self.lock.Unlock()
|
||||
return self.handlers[topic]
|
||||
|
|
@ -341,7 +280,7 @@ func (self *Pss) Process(pssmsg *PssMsg) error {
|
|||
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()
|
||||
// Sends a message using 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 {
|
||||
|
|
@ -415,7 +354,7 @@ func (self *Pss) Forward(msg *PssMsg) error {
|
|||
// 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
|
||||
func (self *Pss) AddPeer(p *p2p.Peer, addr pot.Address, run adapters.RunProtocol, topic PssTopic, rw p2p.MsgReadWriter) error {
|
||||
func (self *Pss) AddPeer(p *p2p.Peer, addr pot.Address, run func(*p2p.Peer, p2p.MsgReadWriter) error, topic PssTopic, rw p2p.MsgReadWriter) error {
|
||||
self.lock.Lock()
|
||||
defer self.lock.Unlock()
|
||||
self.addPeerTopic(addr, topic, rw)
|
||||
|
|
@ -446,6 +385,10 @@ func (self *Pss) removePeerTopic(rw p2p.MsgReadWriter, topic PssTopic) {
|
|||
}
|
||||
}
|
||||
|
||||
func (self *Pss) isSelfRecipient(msg *PssMsg) bool {
|
||||
return bytes.Equal(msg.To, self.Overlay.BaseAddr())
|
||||
}
|
||||
|
||||
func (self *Pss) isActive(id pot.Address, topic PssTopic) bool {
|
||||
return self.peerPool[id][topic] != nil
|
||||
}
|
||||
|
|
@ -474,14 +417,14 @@ func (prw PssReadWriter) ReadMsg() (p2p.Msg, error) {
|
|||
|
||||
// Implements p2p.MsgWriter
|
||||
func (prw PssReadWriter) WriteMsg(msg p2p.Msg) error {
|
||||
log.Trace(fmt.Sprintf("pssrw writemsg: %v", msg))
|
||||
ifc, found := prw.spec.NewMsg(msg.Code)
|
||||
if !found {
|
||||
return fmt.Errorf("Writemsg couldn't find matching interface for code %d", msg.Code)
|
||||
}
|
||||
msg.Decode(ifc)
|
||||
|
||||
pmsg, err := newProtocolMsg(msg.Code, ifc)
|
||||
log.Warn("got writemsg pssclient", "msg", msg)
|
||||
rlpdata := make([]byte, msg.Size)
|
||||
msg.Payload.Read(rlpdata)
|
||||
pmsg, err := rlp.EncodeToBytes(PssProtocolMsg{
|
||||
Code: msg.Code,
|
||||
Size: msg.Size,
|
||||
Payload: rlpdata,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -505,20 +448,20 @@ type PssProtocol struct {
|
|||
|
||||
// Constructor
|
||||
//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 {
|
||||
func RegisterPssProtocol(ps *Pss, topic *PssTopic, spec *protocols.Spec, targetprotocol *p2p.Protocol) error {
|
||||
pp := &PssProtocol{
|
||||
Pss: pss,
|
||||
Pss: ps,
|
||||
proto: targetprotocol,
|
||||
topic: topic,
|
||||
spec: spec,
|
||||
}
|
||||
pss.Register(topic, pp.handle)
|
||||
ps.Register(topic, pp.handle)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PssProtocol) handle(msg []byte, p *p2p.Peer, senderAddr []byte) error {
|
||||
hashoaddr := pot.NewHashAddressFromBytes(senderAddr).Address
|
||||
if !self.isActive(hashoaddr, *self.topic) {
|
||||
if !self.Pss.isActive(hashoaddr, *self.topic) {
|
||||
rw := &PssReadWriter{
|
||||
Pss: self.Pss,
|
||||
To: hashoaddr,
|
||||
|
|
@ -539,53 +482,3 @@ func (self *PssProtocol) handle(msg []byte, p *p2p.Peer, senderAddr []byte) erro
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Pss) isSelfRecipient(msg *PssMsg) bool {
|
||||
return bytes.Equal(msg.To, self.Overlay.BaseAddr())
|
||||
}
|
||||
|
||||
func newProtocolMsg(code uint64, msg interface{}) ([]byte, error) {
|
||||
|
||||
rlpdata, err := rlp.EncodeToBytes(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// previous attempts corrupted nested structs in the payload iself upon deserializing
|
||||
// therefore we use two separate []byte fields instead of peerAddr
|
||||
// TODO verify that nested structs cannot be used in rlp
|
||||
smsg := &PssProtocolMsg{
|
||||
Code: code,
|
||||
Size: uint32(len(rlpdata)),
|
||||
Payload: rlpdata,
|
||||
}
|
||||
|
||||
return rlp.EncodeToBytes(smsg)
|
||||
}
|
||||
|
||||
// constructs a new PssTopic from a given name and version.
|
||||
//
|
||||
// Analogous to the name and version members of p2p.Protocol
|
||||
func NewTopic(s string, v int) (topic PssTopic) {
|
||||
h := sha3.NewKeccak256()
|
||||
h.Write([]byte(s))
|
||||
buf := make([]byte, TopicLength/8)
|
||||
binary.PutUvarint(buf, uint64(v))
|
||||
h.Write(buf)
|
||||
copy(topic[:], h.Sum(buf)[:])
|
||||
return topic
|
||||
}
|
||||
|
||||
func ToP2pMsg(msg []byte) (p2p.Msg, error) {
|
||||
payload := &PssProtocolMsg{}
|
||||
if err := rlp.DecodeBytes(msg, payload); err != nil {
|
||||
return p2p.Msg{}, fmt.Errorf("pss protocol handler unable to decode payload as p2p message: %v", err)
|
||||
}
|
||||
|
||||
return p2p.Msg{
|
||||
Code: payload.Code,
|
||||
Size: uint32(len(payload.Payload)),
|
||||
ReceivedAt: time.Now(),
|
||||
Payload: bytes.NewBuffer(payload.Payload),
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,12 +1,14 @@
|
|||
package pss
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
)
|
||||
|
||||
// PssAPI is the RPC API module for Pss
|
||||
|
|
@ -19,15 +21,8 @@ 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) {
|
||||
func (pssapi *PssAPI) ReceivePss(ctx context.Context, topic PssTopic) (*rpc.Subscription, error) {
|
||||
notifier, supported := rpc.NotifierFromContext(ctx)
|
||||
if !supported {
|
||||
return nil, fmt.Errorf("Subscribe not supported")
|
||||
|
|
@ -48,10 +43,9 @@ func (pssapi *PssAPI) NewMsg(ctx context.Context, topic PssTopic) (*rpc.Subscrip
|
|||
|
||||
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))
|
||||
log.Warn(fmt.Sprintf("caught subscription error in pss sub topic %x: %v", topic, err))
|
||||
case <-notifier.Closed():
|
||||
log.Warn(fmt.Sprintf("rpc sub notifier closed"))
|
||||
}
|
||||
|
|
@ -61,16 +55,49 @@ func (pssapi *PssAPI) NewMsg(ctx context.Context, topic PssTopic) (*rpc.Subscrip
|
|||
}
|
||||
|
||||
// SendRaw sends the message (serialized 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 {
|
||||
func (pssapi *PssAPI) SendPss(topic PssTopic, msg PssAPIMsg) error {
|
||||
if pssapi.Pss.debug && bytes.Equal(msg.Addr, pssapi.Pss.BaseAddr()) {
|
||||
log.Warn("Pss debug enabled; send to self shortcircuit", "apimsg", msg, "topic", topic)
|
||||
env := NewPssEnvelope(msg.Addr, topic, msg.Msg)
|
||||
return pssapi.Pss.Process(&PssMsg{
|
||||
To: pssapi.Pss.BaseAddr(),
|
||||
Payload: env,
|
||||
})
|
||||
}
|
||||
return pssapi.Pss.Send(msg.Addr, topic, msg.Msg)
|
||||
/*if err != nil {
|
||||
return fmt.Errorf("send error: %v", err)
|
||||
}
|
||||
return fmt.Errorf("ok sent")
|
||||
return fmt.Errorf("ok sent")*/
|
||||
}
|
||||
|
||||
// PssAPITest are temporary API calls for development use only
|
||||
// These symbols should not be included in production environment
|
||||
type PssAPITest struct {
|
||||
*Pss
|
||||
}
|
||||
|
||||
// NewPssAPI constructs a PssAPI instance
|
||||
func NewPssAPITest(ps *Pss) *PssAPITest {
|
||||
return &PssAPITest{Pss: ps}
|
||||
}
|
||||
|
||||
// temporary for access to overlay while faking kademlia healthy routines
|
||||
func (pssapitest *PssAPITest) GetForwarder(addr []byte) (fwd struct {
|
||||
Addr []byte
|
||||
Count int
|
||||
}) {
|
||||
pssapitest.Pss.Overlay.EachConn(addr, 255, func(op network.OverlayConn, po int, isproxbin bool) bool {
|
||||
if bytes.Equal(fwd.Addr, []byte{}) {
|
||||
fwd.Addr = op.Address()
|
||||
}
|
||||
fwd.Count++
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// BaseAddr gets our own overlayaddress
|
||||
func (pssapi *PssAPI) BaseAddr() ([]byte, error) {
|
||||
log.Warn("inside baseaddr")
|
||||
return pssapi.Pss.Overlay.BaseAddr(), nil
|
||||
func (pssapitest *PssAPITest) BaseAddr() ([]byte, error) {
|
||||
return pssapitest.Pss.BaseAddr(), nil
|
||||
}
|
||||
|
|
|
|||
232
swarm/pss/simulations/service_test.go
Normal file
232
swarm/pss/simulations/service_test.go
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
package pss_simulations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/protocols"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||
"github.com/ethereum/go-ethereum/pot"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
"github.com/ethereum/go-ethereum/swarm/pss"
|
||||
"github.com/ethereum/go-ethereum/swarm/pss/client"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
)
|
||||
|
||||
func init() {
|
||||
h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true)))
|
||||
log.Root().SetHandler(h)
|
||||
}
|
||||
|
||||
// TestPssProtocol starts a pss network along with two test nodes which run
|
||||
// protocols via the pss network, connects those two test nodes and then
|
||||
// waits for them to handshake
|
||||
func TestPssProtocol(t *testing.T) {
|
||||
// define the services
|
||||
w := &testWrapper{}
|
||||
services := adapters.Services{
|
||||
"pss": w.newPssService,
|
||||
"test": w.newTestService,
|
||||
}
|
||||
|
||||
// create the network
|
||||
adapter := adapters.NewSimAdapter(services)
|
||||
net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{
|
||||
ID: "pss",
|
||||
})
|
||||
defer net.Shutdown()
|
||||
startNode := func(service string) *simulations.Node {
|
||||
config := adapters.RandomNodeConfig()
|
||||
config.Services = []string{service}
|
||||
node, err := net.NewNodeWithConfig(config)
|
||||
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().TerminalString(), err)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
// start 20 pss nodes
|
||||
nodeCount := 20
|
||||
for i := 0; i < nodeCount; i++ {
|
||||
startNode("pss")
|
||||
}
|
||||
|
||||
// start two test nodes (they will use the first two pss nodes)
|
||||
node1 := startNode("test")
|
||||
node2 := startNode("test")
|
||||
|
||||
// subscribe to handshake events from both nodes
|
||||
handshakes := make(chan *testHandshake, 2)
|
||||
subscribe := func(client *rpc.Client) *rpc.ClientSubscription {
|
||||
sub, err := client.Subscribe(context.Background(), "test", handshakes, "handshake")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return sub
|
||||
}
|
||||
client1, err := node1.Client()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sub1 := subscribe(client1)
|
||||
defer sub1.Unsubscribe()
|
||||
client2, err := node2.Client()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sub2 := subscribe(client2)
|
||||
defer sub2.Unsubscribe()
|
||||
|
||||
// call AddPeer on node1 with node2's pss address
|
||||
if err := client1.Call(nil, "test_addPeer", network.ToOverlayAddr(w.pssNodes[1].Bytes())); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// wait for both handshakes
|
||||
timeout := time.After(10 * time.Second)
|
||||
for i := 0; i < 2; i++ {
|
||||
select {
|
||||
case hs := <-handshakes:
|
||||
t.Logf("got handshake: %+v", hs)
|
||||
case <-timeout:
|
||||
t.Fatal("timed out waiting for handshakes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testWrapper creates pss and test nodes, assigning pss nodes to test
|
||||
// nodes as they are started
|
||||
type testWrapper struct {
|
||||
pssNodes []discover.NodeID
|
||||
index int
|
||||
}
|
||||
|
||||
func (t *testWrapper) newPssService(ctx *adapters.ServiceContext) (node.Service, error) {
|
||||
// track the pss node's id so we can use it for the test nodes
|
||||
t.pssNodes = append(t.pssNodes, ctx.Config.ID)
|
||||
|
||||
dir, err := ioutil.TempDir("", "pss-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dpa, err := storage.NewLocalDPA(dir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
addr := network.NewAddrFromNodeID(ctx.Config.ID)
|
||||
kad := network.NewKademlia(addr.Over(), network.NewKadParams())
|
||||
return pss.NewPss(kad, dpa, pss.NewPssParams(false)), nil
|
||||
}
|
||||
|
||||
func (t *testWrapper) newTestService(ctx *adapters.ServiceContext) (node.Service, error) {
|
||||
// connect to the next pss node
|
||||
pssNode := t.pssNodes[t.index]
|
||||
t.index++
|
||||
rpcClient, err := ctx.DialRPC(pssNode)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &testService{
|
||||
id: ctx.Config.ID,
|
||||
pss: client.NewPssClientWithRPC(context.Background(), rpcClient),
|
||||
handshakes: make(chan *testHandshake),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type testHandshake struct {
|
||||
ID discover.NodeID
|
||||
}
|
||||
|
||||
// testService runs a simple handshake protocol over pss and exposes an API
|
||||
// so that clients can wait for handshakes to complete
|
||||
type testService struct {
|
||||
id discover.NodeID
|
||||
pss *client.PssClient
|
||||
handshakes chan *testHandshake
|
||||
}
|
||||
|
||||
func (t *testService) Protocols() []p2p.Protocol {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *testService) APIs() []rpc.API {
|
||||
return []rpc.API{{
|
||||
Namespace: "test",
|
||||
Version: "1.0",
|
||||
Service: &TestAPI{t.pss, t.handshakes},
|
||||
}}
|
||||
}
|
||||
|
||||
func (t *testService) Start(*p2p.Server) error {
|
||||
return t.pss.RunProtocol(&p2p.Protocol{
|
||||
Name: "test",
|
||||
Version: 1,
|
||||
Run: t.run,
|
||||
})
|
||||
}
|
||||
|
||||
func (t *testService) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *testService) run(_ *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||
// send a handshake and wait for one back
|
||||
go p2p.Send(rw, 0, &testHandshake{t.id})
|
||||
msg, err := rw.ReadMsg()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer msg.Discard()
|
||||
var hs testHandshake
|
||||
if err := msg.Decode(&hs); err != nil {
|
||||
return err
|
||||
}
|
||||
t.handshakes <- &hs
|
||||
return nil
|
||||
}
|
||||
|
||||
type TestAPI struct {
|
||||
pss *client.PssClient
|
||||
handshakes chan *testHandshake
|
||||
}
|
||||
|
||||
func (t *TestAPI) AddPeer(addr []byte) {
|
||||
t.pss.AddPssPeer(pot.Address(common.BytesToHash(addr)), &protocols.Spec{
|
||||
Name: "test",
|
||||
Version: 1,
|
||||
})
|
||||
}
|
||||
|
||||
func (t *TestAPI) Handshake(ctx context.Context) (*rpc.Subscription, error) {
|
||||
notifier, supported := rpc.NotifierFromContext(ctx)
|
||||
if !supported {
|
||||
return nil, rpc.ErrNotificationsUnsupported
|
||||
}
|
||||
sub := notifier.CreateSubscription()
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case hs := <-t.handshakes:
|
||||
notifier.Notify(sub.ID, hs)
|
||||
case <-sub.Err():
|
||||
return
|
||||
case <-notifier.Closed():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return sub, nil
|
||||
}
|
||||
140
swarm/pss/types.go
Normal file
140
swarm/pss/types.go
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package pss
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
const (
|
||||
TopicLength = 32
|
||||
DefaultTTL = 6000
|
||||
defaultDigestCacheTTL = time.Second
|
||||
)
|
||||
|
||||
// Defines params for Pss
|
||||
type PssParams struct {
|
||||
Cachettl time.Duration
|
||||
Debug bool
|
||||
}
|
||||
|
||||
// Initializes default params for Pss
|
||||
func NewPssParams(debug bool) *PssParams {
|
||||
return &PssParams{
|
||||
Cachettl: defaultDigestCacheTTL,
|
||||
Debug: debug,
|
||||
}
|
||||
}
|
||||
|
||||
// Encapsulates the message transported over pss.
|
||||
type PssMsg struct {
|
||||
To []byte
|
||||
Payload *PssEnvelope
|
||||
}
|
||||
|
||||
func (msg *PssMsg) Serialize() []byte {
|
||||
rlpdata, _ := rlp.EncodeToBytes(msg)
|
||||
return rlpdata
|
||||
}
|
||||
|
||||
// String representation of PssMsg
|
||||
func (self *PssMsg) String() string {
|
||||
return fmt.Sprintf("PssMsg: Recipient: %x", common.ByteLabel(self.To))
|
||||
}
|
||||
|
||||
// Topic defines the context of a message being transported over pss
|
||||
// It is used by pss to determine what action is to be taken on an incoming message
|
||||
// Typically, one can map protocol handlers for the message payloads by mapping topic to them; see *Pss.Register()
|
||||
type PssTopic [TopicLength]byte
|
||||
|
||||
func (self *PssTopic) String() string {
|
||||
return fmt.Sprintf("%x", self)
|
||||
}
|
||||
|
||||
// Pre-Whisper placeholder, payload of PssMsg
|
||||
type PssEnvelope struct {
|
||||
Topic PssTopic
|
||||
TTL uint16
|
||||
Payload []byte
|
||||
From []byte
|
||||
}
|
||||
|
||||
// creates Pss envelope from sender address, topic and raw payload
|
||||
func NewPssEnvelope(addr []byte, topic PssTopic, payload []byte) *PssEnvelope {
|
||||
return &PssEnvelope{
|
||||
From: addr,
|
||||
Topic: topic,
|
||||
TTL: DefaultTTL,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
// encapsulates a protocol msg as PssEnvelope data
|
||||
type PssProtocolMsg struct {
|
||||
Code uint64
|
||||
Size uint32
|
||||
Payload []byte
|
||||
ReceivedAt time.Time
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func NewProtocolMsg(code uint64, msg interface{}) ([]byte, error) {
|
||||
|
||||
rlpdata, err := rlp.EncodeToBytes(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// previous attempts corrupted nested structs in the payload iself upon deserializing
|
||||
// therefore we use two separate []byte fields instead of peerAddr
|
||||
// TODO verify that nested structs cannot be used in rlp
|
||||
smsg := &PssProtocolMsg{
|
||||
Code: code,
|
||||
Size: uint32(len(rlpdata)),
|
||||
Payload: rlpdata,
|
||||
}
|
||||
|
||||
return rlp.EncodeToBytes(smsg)
|
||||
}
|
||||
|
||||
// Message handler func for a topic
|
||||
type PssHandler func(msg []byte, p *p2p.Peer, from []byte) error
|
||||
|
||||
// constructs a new PssTopic from a given name and version.
|
||||
//
|
||||
// Analogous to the name and version members of p2p.Protocol
|
||||
func NewTopic(s string, v int) (topic PssTopic) {
|
||||
h := sha3.NewKeccak256()
|
||||
h.Write([]byte(s))
|
||||
buf := make([]byte, TopicLength/8)
|
||||
binary.PutUvarint(buf, uint64(v))
|
||||
h.Write(buf)
|
||||
copy(topic[:], h.Sum(buf)[:])
|
||||
return topic
|
||||
}
|
||||
|
||||
func ToP2pMsg(msg []byte) (p2p.Msg, error) {
|
||||
payload := &PssProtocolMsg{}
|
||||
if err := rlp.DecodeBytes(msg, payload); err != nil {
|
||||
return p2p.Msg{}, fmt.Errorf("pss protocol handler unable to decode payload as p2p message: %v", err)
|
||||
}
|
||||
|
||||
return p2p.Msg{
|
||||
Code: payload.Code,
|
||||
Size: uint32(len(payload.Payload)),
|
||||
ReceivedAt: time.Now(),
|
||||
Payload: bytes.NewBuffer(payload.Payload),
|
||||
}, nil
|
||||
}
|
||||
Loading…
Reference in a new issue