mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 15:16:43 +00:00
1462 lines
41 KiB
Go
1462 lines
41 KiB
Go
package pss
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math/rand"
|
|
"os"
|
|
"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/p2p/simulations"
|
|
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
|
p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
|
|
"github.com/ethereum/go-ethereum/pot"
|
|
"github.com/ethereum/go-ethereum/swarm/network"
|
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
|
)
|
|
|
|
const (
|
|
pssServiceName = "pss"
|
|
bzzServiceName = "bzz"
|
|
)
|
|
|
|
var services = newServices()
|
|
|
|
func init() {
|
|
adapters.RegisterServices(services)
|
|
h := log.CallerFileHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(true)))
|
|
log.Root().SetHandler(h)
|
|
}
|
|
|
|
func TestPssCache(t *testing.T) {
|
|
var err error
|
|
to, _ := hex.DecodeString("08090a0b0c0d0e0f1011121314150001020304050607161718191a1b1c1d1e1f")
|
|
oaddr, _ := hex.DecodeString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
|
|
//uaddr, _ := hex.DecodeString("101112131415161718191a1b1c1d1e1f000102030405060708090a0b0c0d0e0f")
|
|
//proofbytes := []byte{241, 172, 117, 105, 88, 154, 82, 33, 176, 188, 91, 244, 245, 85, 86, 16, 120, 232, 70, 45, 182, 188, 99, 103, 157, 3, 202, 121, 252, 21, 129, 22}
|
|
proofbytes, _ := hex.DecodeString("ad312dca94df401555cfdeb85a6a1f87fb8f240f08dc36af246bd9d4d41efd89")
|
|
ps := newTestPss(oaddr)
|
|
pp := NewPssParams()
|
|
data := []byte("foo")
|
|
datatwo := []byte("bar")
|
|
fwdaddr := network.RandomAddr()
|
|
msg := &PssMsg{
|
|
Payload: &PssEnvelope{
|
|
TTL: 0,
|
|
From: oaddr,
|
|
Topic: pssPingTopic,
|
|
Payload: data,
|
|
},
|
|
To: to,
|
|
}
|
|
|
|
msgtwo := &PssMsg{
|
|
Payload: &PssEnvelope{
|
|
TTL: 0,
|
|
From: oaddr,
|
|
Topic: pssPingTopic,
|
|
Payload: datatwo,
|
|
},
|
|
To: to,
|
|
}
|
|
|
|
digest, err := ps.storeMsg(msg)
|
|
if err != nil {
|
|
t.Fatalf("could not store cache msgone: %v", err)
|
|
}
|
|
digesttwo, err := ps.storeMsg(msgtwo)
|
|
if err != nil {
|
|
t.Fatalf("could not store cache msgtwo: %v", err)
|
|
}
|
|
|
|
if !bytes.Equal(digest[:], proofbytes) {
|
|
t.Fatalf("digest - got: %x, expected: %x", digest, proofbytes)
|
|
}
|
|
|
|
if digest == digesttwo {
|
|
t.Fatalf("different msgs return same crc: %d", digesttwo)
|
|
}
|
|
|
|
// check the sender cache
|
|
err = ps.addFwdCacheSender(fwdaddr.Over(), digest)
|
|
if err != nil {
|
|
t.Fatalf("write to pss sender cache failed: %v", err)
|
|
}
|
|
|
|
if !ps.checkFwdCache(fwdaddr.Over(), digest) {
|
|
t.Fatalf("message %v should have SENDER record in cache but checkCache returned false", msg)
|
|
}
|
|
|
|
if ps.checkFwdCache(fwdaddr.Over(), digesttwo) {
|
|
t.Fatalf("message %v should NOT have SENDER record in cache but checkCache returned true", msgtwo)
|
|
}
|
|
|
|
// check the expire cache
|
|
err = ps.addFwdCacheExpire(digest)
|
|
if err != nil {
|
|
t.Fatalf("write to pss expire cache failed: %v", err)
|
|
}
|
|
|
|
if !ps.checkFwdCache(nil, digest) {
|
|
t.Fatalf("message %v should have EXPIRE record in cache but checkCache returned false", msg)
|
|
}
|
|
|
|
if ps.checkFwdCache(nil, digesttwo) {
|
|
t.Fatalf("message %v should NOT have EXPIRE record in cache but checkCache returned true", msgtwo)
|
|
}
|
|
|
|
time.Sleep(pp.Cachettl)
|
|
if ps.checkFwdCache(nil, digest) {
|
|
t.Fatalf("message %v should have expired from cache but checkCache returned true", msg)
|
|
}
|
|
|
|
err = ps.AddToCache(fwdaddr.Over(), msgtwo)
|
|
if err != nil {
|
|
t.Fatalf("public accessor cache write failed: %v", err)
|
|
}
|
|
|
|
if !ps.checkFwdCache(fwdaddr.Over(), digesttwo) {
|
|
t.Fatalf("message %v should have SENDER record in cache but checkCache returned false", msgtwo)
|
|
}
|
|
}
|
|
|
|
func TestPssRegisterHandler(t *testing.T) {
|
|
var err error
|
|
addr := network.RandomAddr()
|
|
ps := newTestPss(addr.OAddr)
|
|
from := network.RandomAddr()
|
|
payload := []byte("payload")
|
|
topic := NewTopic(pssSpec.Name, int(pssSpec.Version))
|
|
wrongtopic := NewTopic("foo", 42)
|
|
checkMsg := func(msg []byte, p *p2p.Peer, sender []byte) error {
|
|
if !bytes.Equal(from.OAddr, sender) {
|
|
return fmt.Errorf("sender mismatch. expected %x, got %x", from.OAddr, sender)
|
|
}
|
|
if !bytes.Equal(msg, payload) {
|
|
return fmt.Errorf("sender mismatch. expected %x, got %x", msg, payload)
|
|
}
|
|
return nil
|
|
}
|
|
deregister := ps.Register(&topic, checkMsg)
|
|
pssmsg := &PssMsg{Payload: NewPssEnvelope(from.OAddr, topic, payload)}
|
|
err = ps.Process(pssmsg)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var i int
|
|
err = ps.Process(&PssMsg{Payload: NewPssEnvelope(from.OAddr, wrongtopic, payload)})
|
|
expErr := ""
|
|
if err == nil || err.Error() == expErr {
|
|
t.Fatalf("unhandled topic expected '%v', got '%v'", expErr, err)
|
|
}
|
|
deregister2 := ps.Register(&topic, func(msg []byte, p *p2p.Peer, sender []byte) error { i++; return nil })
|
|
err = ps.Process(pssmsg)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if i != 1 {
|
|
t.Fatalf("second registerer handler did not run")
|
|
}
|
|
deregister()
|
|
deregister2()
|
|
err = ps.Process(&PssMsg{Payload: NewPssEnvelope(from.OAddr, topic, payload)})
|
|
expErr = ""
|
|
if err == nil || err.Error() == expErr {
|
|
t.Fatalf("reregister handler expected %v, got %v", expErr, err)
|
|
}
|
|
}
|
|
|
|
func TestPssSimpleLinear(t *testing.T) {
|
|
var err error
|
|
nodeconfig := adapters.RandomNodeConfig()
|
|
addr := network.NewAddrFromNodeID(nodeconfig.ID)
|
|
_ = p2ptest.NewTestPeerPool()
|
|
ps := newTestPss(addr.Over())
|
|
|
|
ping := &pssPing{
|
|
quitC: make(chan struct{}),
|
|
}
|
|
|
|
err = RegisterPssProtocol(ps, &pssPingTopic, pssPingProtocol, newPssPingProtocol(ping.pssPingHandler))
|
|
|
|
if err != nil {
|
|
t.Fatalf("Failed to register virtual protocol in pss: %v", err)
|
|
}
|
|
run := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
|
id := p.ID()
|
|
pp := protocols.NewPeer(p, rw, pssSpec)
|
|
bp := &testPssPeer{
|
|
Peer: pp,
|
|
addr: network.ToOverlayAddr(id[:]),
|
|
}
|
|
h := pot.NewHashAddressFromBytes(bp.addr)
|
|
ps.fwdPool[h.Address] = pp
|
|
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 discover.NodeID
|
|
|
|
nodeCount := 5
|
|
net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{
|
|
ID: "0",
|
|
})
|
|
defer net.Shutdown()
|
|
|
|
trigger := make(chan discover.NodeID)
|
|
ids := make([]discover.NodeID, nodeCount)
|
|
fullids := ids[0:fullnodecount]
|
|
fullpeers := [][]byte{}
|
|
|
|
for i := 0; i < nodeCount; i++ {
|
|
nodeconfig := adapters.RandomNodeConfig()
|
|
nodeconfig.Services = []string{"bzz", "pss"}
|
|
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().TerminalString(), err)
|
|
}
|
|
|
|
if err := triggerChecks(trigger, net, node.ID()); err != nil {
|
|
t.Fatal("error triggering checks for node %s: %s", node.ID().TerminalString(), err)
|
|
}
|
|
ids[i] = node.ID()
|
|
if i < fullnodecount {
|
|
fullpeers = append(fullpeers, network.ToOverlayAddr(node.ID().Bytes()))
|
|
}
|
|
}
|
|
|
|
// 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 discover.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 discover.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)
|
|
}
|
|
|
|
for _, fid := range fullids {
|
|
if fid == id {
|
|
fpeeridx := rand.Int() % (fullnodecount - 1)
|
|
log.Debug(fmt.Sprintf("fpeeridx %d, fpeer len %d", fpeeridx, len(fullpeers)))
|
|
if bytes.Equal(fullpeers[fpeeridx], network.ToOverlayAddr(fid.Bytes())) {
|
|
fpeeridx++
|
|
}
|
|
msg := pssPingMsg{Created: time.Now()}
|
|
code, _ := pssPingProtocol.GetCode(&pssPingMsg{})
|
|
pmsg, _ := newProtocolMsg(code, msg)
|
|
client.CallContext(context.Background(), nil, "pss_sendRaw", pssPingTopic, PssAPIMsg{
|
|
Addr: fullpeers[fpeeridx],
|
|
Msg: pmsg,
|
|
})
|
|
}
|
|
}
|
|
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)
|
|
}
|
|
|
|
trigger = make(chan discover.NodeID)
|
|
|
|
action = func(ctx context.Context) error {
|
|
return nil
|
|
}
|
|
check = func(ctx context.Context, id discover.NodeID) (bool, error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
return false, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
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: fullids,
|
|
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 discover.NodeID, net *simulations.Network, id discover.NodeID) error {
|
|
|
|
gotpeer := make(map[discover.NodeID]bool)
|
|
|
|
node := net.GetNode(id)
|
|
if node == nil {
|
|
return fmt.Errorf("unknown node: %s", id)
|
|
}
|
|
client, err := node.Client()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
peerevents := make(chan *p2p.PeerEvent)
|
|
peersub, err := client.Subscribe(context.Background(), "admin", peerevents, "peerEvents")
|
|
if err != nil {
|
|
return fmt.Errorf("error getting peer events for node %v: %s", id, err)
|
|
}
|
|
|
|
msgevents := make(chan PssAPIMsg)
|
|
msgsub, err := client.Subscribe(context.Background(), "pss", msgevents, "newMsg", pssPingTopic)
|
|
if err != nil {
|
|
return fmt.Errorf("error getting peer events for node %v: %s", id, err)
|
|
}
|
|
|
|
go func() {
|
|
defer msgsub.Unsubscribe()
|
|
defer peersub.Unsubscribe()
|
|
for {
|
|
select {
|
|
case event := <-peerevents:
|
|
if event.Type == "add" && !gotpeer[event.Peer] {
|
|
trigger <- id
|
|
gotpeer[event.Peer] = true
|
|
}
|
|
case <-msgevents:
|
|
trigger <- id
|
|
case err := <-peersub.Err():
|
|
if err != nil {
|
|
log.Error(fmt.Sprintf("error getting peer events for node %v", id), "err", err)
|
|
}
|
|
return
|
|
|
|
case err := <-msgsub.Err():
|
|
if err != nil {
|
|
log.Error(fmt.Sprintf("error getting msg for node %v", id), "err", err)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
return nil
|
|
}
|
|
|
|
func newServices() adapters.Services {
|
|
stateStore := adapters.NewSimStateStore()
|
|
kademlias := make(map[discover.NodeID]*network.Kademlia)
|
|
kademlia := func(id discover.NodeID) *network.Kademlia {
|
|
if k, ok := kademlias[id]; ok {
|
|
return k
|
|
}
|
|
addr := network.NewAddrFromNodeID(id)
|
|
params := network.NewKadParams()
|
|
params.MinProxBinSize = 2
|
|
params.MaxBinSize = 3
|
|
params.MinBinSize = 1
|
|
params.MaxRetries = 1000
|
|
params.RetryExponent = 2
|
|
params.RetryInterval = 1000000
|
|
kademlias[id] = network.NewKademlia(addr.Over(), params)
|
|
return kademlias[id]
|
|
}
|
|
return adapters.Services{
|
|
"pss": func(id discover.NodeID, snapshot []byte) node.Service {
|
|
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()
|
|
ps := NewPss(kademlia(id), dpa, pssp)
|
|
|
|
ping := &pssPing{
|
|
quitC: make(chan struct{}),
|
|
}
|
|
err = RegisterPssProtocol(ps, &pssPingTopic, pssPingProtocol, newPssPingProtocol(ping.pssPingHandler))
|
|
if err != nil {
|
|
log.Error("Couldnt register pss protocol", "err", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
return ps
|
|
},
|
|
"bzz": func(id discover.NodeID, snapshot []byte) node.Service {
|
|
addr := network.NewAddrFromNodeID(id)
|
|
config := &network.BzzConfig{
|
|
OverlayAddr: addr.Over(),
|
|
UnderlayAddr: addr.Under(),
|
|
HiveParams: network.NewHiveParams(),
|
|
}
|
|
return network.NewBzz(config, kademlia(id), stateStore)
|
|
},
|
|
}
|
|
}
|
|
|
|
/*
|
|
// example protocol implementation peer
|
|
// message handlers are methods of this
|
|
// channels allow receipt reporting from p2p.Protocol message handler
|
|
type pssTestPeer struct {
|
|
*protocols.Peer
|
|
hasProtocol bool
|
|
successC chan bool
|
|
resultC chan int
|
|
}
|
|
|
|
// example node simulation peer
|
|
// modeled from swarm/network/simulations/discovery/discovery_test.go - commit 08b1e42f
|
|
// contains reporting channel for expect results so we can collect all async incoming msgs before deciding results
|
|
type pssTestNode struct {
|
|
*Hive
|
|
*Pss
|
|
|
|
id discover.NodeID
|
|
network *simulations.Network
|
|
trigger chan discover.NodeID
|
|
run adapters.RunProtocol
|
|
ct *protocols.CodeMap
|
|
expectC chan []int
|
|
ws *http.Handler
|
|
apifunc func() []rpc.API
|
|
}
|
|
|
|
func (n *pssTestNode) Add(peer *bzzPeer) error {
|
|
err := n.Hive.Add(peer)
|
|
time.Sleep(time.Millisecond * 250)
|
|
n.triggerCheck()
|
|
return err
|
|
}
|
|
|
|
func (n *pssTestNode) triggerCheck() {
|
|
go func() { n.trigger <- n.id }()
|
|
}
|
|
|
|
func (n *pssTestNode) OverlayAddr() []byte {
|
|
return n.Pss.Overlay.BaseAddr()
|
|
}
|
|
|
|
func (n *pssTestNode) UnderlayAddr() []byte {
|
|
return n.id.Bytes()
|
|
}
|
|
|
|
// the content of the msgs we're sending in the tests
|
|
type pssTestPayload struct {
|
|
Data string
|
|
}
|
|
|
|
func (m *pssTestPayload) String() string {
|
|
return m.Data
|
|
}
|
|
|
|
type pssTestService struct {
|
|
node *pssTestNode // get addrs from this
|
|
msgFunc func(interface{}) error
|
|
}
|
|
|
|
func newPssTestService(t *testing.T, handlefunc func(interface{}) error, testnode *pssTestNode) *pssTestService {
|
|
hp := NewHiveParams()
|
|
hp.KeepAliveInterval = 300
|
|
bzz := NewBzz(testnode.OverlayAddr(), testnode.UnderlayAddr(), newTestStore())
|
|
testnode.Hive = NewHive(hp, testnode.Pss.Overlay, bzz)
|
|
return &pssTestService{
|
|
//nid := adapters.NewNodeID(addr.UnderlayAddr())
|
|
msgFunc: handlefunc,
|
|
node: testnode,
|
|
}
|
|
}
|
|
|
|
func (self *pssTestService) Start(server *p2p.Server) error {
|
|
return self.node.Hive.Start(server)
|
|
}
|
|
|
|
func (self *pssTestService) Stop() error {
|
|
self.node.Hive.Stop()
|
|
return nil
|
|
}
|
|
|
|
func (self *pssTestService) Protocols() []p2p.Protocol {
|
|
bzz := NewBzz(self.node.OverlayAddr(), self.node.UnderlayAddr(), newTestStore())
|
|
return append(self.node.Hive.Protocols(), p2p.Protocol{
|
|
Name: PssProtocolName,
|
|
Version: PssProtocolVersion,
|
|
Length: PssProtocol.Length(),
|
|
Run: bzz.RunProtocol(PssProtocol, self.Run),
|
|
})
|
|
}
|
|
|
|
func (self *pssTestService) APIs() []rpc.API {
|
|
return []rpc.API{
|
|
rpc.API{
|
|
Namespace: "eth",
|
|
Version: "0.1/pss",
|
|
Service: NewPssApi(self.node.Pss),
|
|
Public: true,
|
|
},
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (self *pssTestService) Run(peer *bzzPeer) error {
|
|
self.node.Add(peer)
|
|
defer self.node.Remove(peer)
|
|
return peer.Run(self.msgFunc)
|
|
}
|
|
*/
|
|
|
|
/*
|
|
|
|
|
|
func testPssFullRandom(t *testing.T, numsends int, numnodes int, numfullnodes int) {
|
|
var action func(ctx context.Context) error
|
|
var i int
|
|
var check func(ctx context.Context, id discover.NodeID) (bool, error)
|
|
var ctx context.Context
|
|
var result *simulations.StepResult
|
|
var timeout time.Duration
|
|
var cancel context.CancelFunc
|
|
|
|
fullnodes := []discover.NodeID{}
|
|
sends := []int{} // sender/receiver ids array indices pairs
|
|
expectnodes := make(map[discover.NodeID]int) // how many messages we're expecting on each respective node
|
|
expectnodesids := []discover.NodeID{} // the nodes to expect on (needed by checker)
|
|
expectnodesresults := make(map[discover.NodeID][]int) // which messages expect actually got
|
|
|
|
vct := protocols.NewCodeMap(map[uint64]interface{}{
|
|
0: pssTestPayload{},
|
|
})
|
|
topic, _ := MakeTopic(protocolName, protocolVersion)
|
|
|
|
trigger := make(chan discover.NodeID)
|
|
testpeers := make(map[discover.NodeID]*pssTestPeer)
|
|
net, nodes := newPssSimulationTester(t, numnodes, numfullnodes, trigger, vct, protocolName, protocolVersion, testpeers)
|
|
|
|
ids := []discover.NodeID{}
|
|
|
|
// connect the peers
|
|
action = func(ctx context.Context) error {
|
|
for id, _ := range nodes {
|
|
ids = append(ids, id)
|
|
if _, ok := testpeers[id]; ok {
|
|
log.Trace(fmt.Sprintf("adding fullnode %x to testpeers %p", common.ByteLabel(id.Bytes()), testpeers))
|
|
fullnodes = append(fullnodes, id)
|
|
}
|
|
}
|
|
for i, id := range ids {
|
|
var peerID discover.NodeID
|
|
if i != 0 {
|
|
peerID = ids[i-1]
|
|
if err := net.Connect(id, peerID); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
check = func(ctx context.Context, id discover.NodeID) (bool, error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
return false, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
node, ok := nodes[id]
|
|
if !ok {
|
|
return false, fmt.Errorf("unknown node: %s (%v)", id, node)
|
|
} else {
|
|
log.Trace(fmt.Sprintf("sim check ok node %v", id))
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
timeout = 10 * time.Second
|
|
ctx, cancel = context.WithTimeout(context.Background(), timeout)
|
|
|
|
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)
|
|
}
|
|
cancel()
|
|
|
|
// ensure that we didn't get lost in concurrency issues
|
|
if len(fullnodes) != numfullnodes {
|
|
t.Fatalf("corrupt fullnodes array, expected %d, have %d", numfullnodes, len(fullnodes))
|
|
}
|
|
|
|
// ensure that the channel is clean
|
|
trigger = make(chan discover.NodeID)
|
|
|
|
// randomly decide which nodes to send to and from
|
|
rand.Seed(time.Now().Unix())
|
|
for i = 0; i < numsends; i++ {
|
|
s := rand.Int() % numfullnodes
|
|
r := s
|
|
for r == s {
|
|
r = rand.Int() % numfullnodes
|
|
}
|
|
log.Trace(fmt.Sprintf("rnd pss: idx %d->%d (%x -> %x)", s, r, common.ByteLabel(fullnodes[s].Bytes()), common.ByteLabel(fullnodes[r].Bytes())))
|
|
expectnodes[fullnodes[r]]++
|
|
sends = append(sends, s, r)
|
|
}
|
|
|
|
// distinct array of nodes to expect on
|
|
for k, _ := range expectnodes {
|
|
expectnodesids = append(expectnodesids, k)
|
|
}
|
|
|
|
// wait a bit for the kademlias to fill up
|
|
z, _ := time.ParseDuration(fmt.Sprintf("%dms", (numnodes * 25)))
|
|
if z.Seconds() < 1.0 {
|
|
z = time.Second
|
|
}
|
|
time.Sleep(z)
|
|
|
|
// send and monitor receive of pss
|
|
action = func(ctx context.Context) error {
|
|
code, _ := vct.GetCode(&pssTestPayload{})
|
|
|
|
for i := 0; i < len(sends); i += 2 {
|
|
msgbytes, _ := makeMsg(code, &pssTestPayload{
|
|
Data: fmt.Sprintf("%v", i+1),
|
|
})
|
|
go func(i int, expectnodesresults map[discover.NodeID][]int) {
|
|
expectnode := fullnodes[sends[i+1]] // the receiving node
|
|
sendnode := fullnodes[sends[i]] // the sending node
|
|
oaddr := nodes[expectnode].OverlayAddr()
|
|
err := nodes[sendnode].Pss.Send(oaddr, topic, msgbytes)
|
|
if err != nil {
|
|
t.Fatalf("could not send pss: %v", err)
|
|
}
|
|
|
|
select {
|
|
// if the pss is delivered
|
|
case <-testpeers[expectnode].successC:
|
|
log.Trace(fmt.Sprintf("got successC from node %x", common.ByteLabel(expectnode.Bytes())))
|
|
expectnodesresults[expectnode] = append(expectnodesresults[expectnode], <-testpeers[expectnode].resultC)
|
|
// if not we time out, -1 means fail tick
|
|
case <-time.NewTimer(time.Second).C:
|
|
log.Trace(fmt.Sprintf("result timed out on node %x", common.ByteLabel(expectnode.Bytes())))
|
|
expectnodesresults[expectnode] = append(expectnodesresults[expectnode], -1)
|
|
}
|
|
|
|
// we can safely send to the check handler if we got feedback for all msgs we sent to a particular node
|
|
if len(expectnodesresults[expectnode]) == expectnodes[expectnode] {
|
|
trigger <- expectnode
|
|
nodes[expectnode].expectC <- expectnodesresults[expectnode]
|
|
}
|
|
}(i, expectnodesresults)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// results
|
|
check = func(ctx context.Context, id discover.NodeID) (bool, error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
return false, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
receives := <-nodes[id].expectC
|
|
log.Trace(fmt.Sprintf("expect received %d msgs on from node %x: %v", len(receives), common.ByteLabel(id.Bytes()), receives))
|
|
return true, nil
|
|
}
|
|
|
|
timeout = 10 * 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: expectnodesids,
|
|
Check: check,
|
|
},
|
|
})
|
|
if result.Error != nil {
|
|
t.Fatalf("simulation failed: %s", result.Error)
|
|
}
|
|
|
|
t.Log("Simulation Passed:")
|
|
|
|
for i := 0; i < len(sends); i += 2 {
|
|
t.Logf("Pss #%d: oaddr %x -> %x (uaddr %x -> %x)", i/2+1,
|
|
common.ByteLabel(nodes[fullnodes[sends[i]]].Pss.BaseAddr()),
|
|
common.ByteLabel(nodes[fullnodes[sends[i+1]]].Pss.BaseAddr()),
|
|
common.ByteLabel(fullnodes[sends[i]].Bytes()),
|
|
common.ByteLabel(fullnodes[sends[i+1]].Bytes()))
|
|
}
|
|
totalfails := 0
|
|
for id, results := range expectnodesresults {
|
|
fails := 0
|
|
for _, r := range results {
|
|
if r == -1 {
|
|
fails++
|
|
}
|
|
}
|
|
t.Logf("Node oaddr %x (uaddr %x) was sent %d msgs, of which %d failed", common.ByteLabel(nodes[id].Pss.BaseAddr()), common.ByteLabel(id.Bytes()), len(results), fails)
|
|
totalfails += fails
|
|
}
|
|
t.Logf("Total sent: %d, total fail: %d (%.2f%%)", len(sends)/2, totalfails, (float32(totalfails)/float32(len(sends)/2+1))*100)
|
|
|
|
for _, node := range nodes {
|
|
logstring := fmt.Sprintf("Node oaddr %x kademlia: ", common.ByteLabel(node.Pss.Overlay.BaseAddr()))
|
|
node.Pss.Overlay.EachConn(nil, 256, func(p Peer, po int, isprox bool) bool {
|
|
logstring += fmt.Sprintf("%x ", common.ByteLabel(p.Over()))
|
|
return true
|
|
})
|
|
t.Log(logstring)
|
|
}
|
|
}
|
|
|
|
func TestPssFullLinearEcho(t *testing.T) {
|
|
|
|
var action func(ctx context.Context) error
|
|
var check func(ctx context.Context, id discover.NodeID) (bool, error)
|
|
var ctx context.Context
|
|
var result *simulations.StepResult
|
|
var timeout time.Duration
|
|
var cancel context.CancelFunc
|
|
|
|
var firstpssnode discover.NodeID
|
|
var secondpssnode discover.NodeID
|
|
|
|
vct := protocols.NewCodeMap(protocolName, protocolVersion, ProtocolMaxMsgSize)
|
|
vct.Register(0, &pssTestPayload{})
|
|
topic, _ := MakeTopic(protocolName, protocolVersion)
|
|
|
|
fullnodes := []discover.NodeID{}
|
|
trigger := make(chan discover.NodeID)
|
|
testpeers := make(map[discover.NodeID]*pssTestPeer)
|
|
net, nodes := newPssSimulationTester(t, 3, 2, trigger, vct, protocolName, protocolVersion, testpeers)
|
|
ids := []discover.NodeID{} // ohh risky! but the action for a specific id should come before the expect anyway
|
|
|
|
action = func(ctx context.Context) error {
|
|
var thinnodeid discover.NodeID
|
|
for id, _ := range nodes {
|
|
ids = append(ids, id)
|
|
if _, ok := testpeers[id]; ok {
|
|
log.Trace(fmt.Sprintf("adding fullnode %x to testpeers %p", common.ByteLabel(id.Bytes()), testpeers))
|
|
fullnodes = append(fullnodes, id)
|
|
} else {
|
|
thinnodeid = id
|
|
}
|
|
}
|
|
if err := net.Connect(fullnodes[0], thinnodeid); err != nil {
|
|
return err
|
|
}
|
|
if err := net.Connect(thinnodeid, fullnodes[1]); err != nil {
|
|
return err
|
|
}
|
|
|
|
// for i, id := range ids {
|
|
// var peerID discover.NodeID
|
|
// if i != 0 {
|
|
// peerID = ids[i-1]
|
|
// if err := net.Connect(id, peerID); err != nil {
|
|
// return err
|
|
// }
|
|
// }
|
|
// }
|
|
return nil
|
|
}
|
|
check = func(ctx context.Context, id discover.NodeID) (bool, error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
return false, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
node, ok := nodes[id]
|
|
if !ok {
|
|
return false, fmt.Errorf("unknown node: %s (%v)", id, node)
|
|
}
|
|
log.Trace(fmt.Sprintf("sim check ok node %v", id))
|
|
|
|
return true, nil
|
|
}
|
|
|
|
timeout = 10 * time.Second
|
|
ctx, cancel = context.WithTimeout(context.Background(), timeout)
|
|
|
|
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)
|
|
}
|
|
cancel()
|
|
|
|
nonode := &adapters.NodeID{}
|
|
firstpssnode = nonode
|
|
secondpssnode = nonode
|
|
|
|
// first find a node that we're connected to
|
|
for firstpssnode == nonode {
|
|
log.Debug(fmt.Sprintf("Waiting for pss relaypeer for %x close to %x ...", common.ByteLabel(nodes[fullnodes[0]].OverlayAddr()), common.ByteLabel(nodes[ids[1]].OverlayAddr())))
|
|
nodes[fullnodes[0]].Pss.Overlay.EachConn(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool {
|
|
for _, id := range ids {
|
|
if id.NodeID == p.ID() {
|
|
firstpssnode = id
|
|
log.Debug(fmt.Sprintf("PSS relay found; relaynode %v kademlia %v", common.ByteLabel(id.Bytes()), common.ByteLabel(firstpssnode.Bytes())))
|
|
}
|
|
}
|
|
if firstpssnode == nonode {
|
|
return true
|
|
}
|
|
return false
|
|
})
|
|
if firstpssnode == nonode {
|
|
time.Sleep(time.Millisecond * 100)
|
|
}
|
|
}
|
|
|
|
// then find the node it's connected to
|
|
for secondpssnode == nonode {
|
|
log.Debug(fmt.Sprintf("PSS kademlia: Waiting for recipientpeer for %x close to %x ...", common.ByteLabel(nodes[firstpssnode].OverlayAddr()), common.ByteLabel(nodes[fullnodes[1]].OverlayAddr())))
|
|
nodes[firstpssnode].Pss.Overlay.Eachc(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool {
|
|
for _, id := range ids {
|
|
if id.NodeID == p.ID() && id.NodeID != fullnodes[0].NodeID {
|
|
secondpssnode = id
|
|
log.Debug(fmt.Sprintf("PSS recipient found; relaynode %v kademlia %v", common.ByteLabel(id.Bytes()), common.ByteLabel(secondpssnode.Bytes())))
|
|
}
|
|
}
|
|
if secondpssnode == nonode {
|
|
return true
|
|
}
|
|
return false
|
|
})
|
|
if secondpssnode == nonode {
|
|
time.Sleep(time.Millisecond * 100)
|
|
}
|
|
}
|
|
|
|
action = func(ctx context.Context) error {
|
|
code, _ := vct.GetCode(&pssTestPayload{})
|
|
msgbytes, _ := makeMsg(code, &pssTestPayload{
|
|
Data: "ping",
|
|
})
|
|
|
|
go func() {
|
|
oaddr := nodes[secondpssnode].OverlayAddr()
|
|
err := nodes[ids[0]].Pss.Send(oaddr, topic, msgbytes)
|
|
if err != nil {
|
|
t.Fatalf("could not send pss: %v", err)
|
|
}
|
|
trigger <- ids[0]
|
|
}()
|
|
|
|
return nil
|
|
}
|
|
check = func(ctx context.Context, id discover.NodeID) (bool, error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
return false, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
// also need to know if the protocolpeer is set up
|
|
time.Sleep(time.Millisecond * 100)
|
|
return <-testpeers[ids[0]].successC, nil
|
|
//return true, nil
|
|
}
|
|
|
|
timeout = 10 * 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: []discover.NodeID{ids[0]},
|
|
Check: check,
|
|
},
|
|
})
|
|
if result.Error != nil {
|
|
t.Fatalf("simulation failed: %s", result.Error)
|
|
}
|
|
|
|
t.Log("Simulation Passed:")
|
|
}
|
|
|
|
func TestPssFullWS(t *testing.T) {
|
|
|
|
// settings for ws servers
|
|
var srvsendep = "localhost:18546"
|
|
var srvrecvep = "localhost:18547"
|
|
var clientrecvok, clientsendok bool
|
|
var clientrecv, clientsend *rpc.Client
|
|
|
|
var action func(ctx context.Context) error
|
|
var check func(ctx context.Context, id discover.NodeID) (bool, error)
|
|
var ctx context.Context
|
|
var result *simulations.StepResult
|
|
var timeout time.Duration
|
|
var cancel context.CancelFunc
|
|
|
|
var firstpssnode, secondpssnode discover.NodeID
|
|
fullnodes := []discover.NodeID{}
|
|
vct := protocols.NewCodeMap(protocolName, protocolVersion, ProtocolMaxMsgSize)
|
|
vct.Register(0, &pssTestPayload{})
|
|
topic, _ := MakeTopic(pingTopicName, pingTopicVersion)
|
|
|
|
trigger := make(chan discover.NodeID)
|
|
testpeers := make(map[discover.NodeID]*pssTestPeer)
|
|
simnet, nodes := newPssSimulationTester(t, 3, 2, trigger, vct, protocolName, protocolVersion, testpeers)
|
|
ids := []discover.NodeID{} // ohh risky! but the action for a specific id should come before the expect anyway
|
|
|
|
action = func(ctx context.Context) error {
|
|
var thinnodeid discover.NodeID
|
|
for id, node := range nodes {
|
|
ids = append(ids, id)
|
|
if _, ok := testpeers[id]; ok {
|
|
log.Trace(fmt.Sprintf("adding fullnode %x to testpeers %p", common.ByteLabel(id.Bytes()), testpeers))
|
|
fullnodes = append(fullnodes, id)
|
|
node.Pss.Register(topic, node.Pss.GetPingHandler())
|
|
srv := rpc.NewServer()
|
|
for _, rpcapi := range node.apifunc() {
|
|
srv.RegisterName(rpcapi.Namespace, rpcapi.Service)
|
|
}
|
|
ws := srv.WebsocketHandler([]string{"*"})
|
|
node.ws = &ws
|
|
} else {
|
|
thinnodeid = id
|
|
}
|
|
}
|
|
if err := simnet.Connect(fullnodes[0], thinnodeid); err != nil {
|
|
return err
|
|
}
|
|
if err := simnet.Connect(thinnodeid, fullnodes[1]); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
check = func(ctx context.Context, id discover.NodeID) (bool, error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
return false, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
node, ok := nodes[id]
|
|
if !ok {
|
|
return false, fmt.Errorf("unknown node: %s (%v)", id, node)
|
|
} else {
|
|
log.Trace(fmt.Sprintf("sim check ok node %v", id))
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
timeout = 10 * time.Second
|
|
ctx, cancel = context.WithTimeout(context.Background(), timeout)
|
|
|
|
result = simulations.NewSimulation(simnet).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)
|
|
}
|
|
cancel()
|
|
|
|
nonode := &adapters.NodeID{}
|
|
firstpssnode = nonode
|
|
secondpssnode = nonode
|
|
|
|
// first find a node that we're connected to
|
|
for firstpssnode == nonode {
|
|
log.Debug(fmt.Sprintf("Waiting for pss relaypeer for %x close to %x ...", common.ByteLabel(nodes[fullnodes[0]].OverlayAddr()), common.ByteLabel(nodes[fullnodes[1]].OverlayAddr())))
|
|
nodes[fullnodes[0]].Pss.Overlay.EachLivePeer(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool {
|
|
for _, id := range ids {
|
|
if id.NodeID == p.ID() {
|
|
firstpssnode = id
|
|
log.Debug(fmt.Sprintf("PSS relay found; relaynode %x", common.ByteLabel(nodes[firstpssnode].OverlayAddr())))
|
|
}
|
|
}
|
|
if firstpssnode == nonode {
|
|
return true
|
|
}
|
|
return false
|
|
})
|
|
if firstpssnode == nonode {
|
|
time.Sleep(time.Millisecond * 100)
|
|
}
|
|
}
|
|
|
|
// then find the node it's connected to
|
|
for secondpssnode == nonode {
|
|
log.Debug(fmt.Sprintf("PSS kademlia: Waiting for recipientpeer for %x close to %x ...", common.ByteLabel(nodes[firstpssnode].OverlayAddr()), common.ByteLabel(nodes[fullnodes[1]].OverlayAddr())))
|
|
nodes[firstpssnode].Pss.Overlay.EachConn(nodes[fullnodes[1]].OverlayAddr(), 256, func(p Peer, po int, isprox bool) bool {
|
|
for _, id := range ids {
|
|
if id.NodeID == p.ID() && id.NodeID != fullnodes[0].NodeID {
|
|
secondpssnode = id
|
|
log.Debug(fmt.Sprintf("PSS recipient found; relaynode %x", common.ByteLabel(nodes[secondpssnode].OverlayAddr())))
|
|
}
|
|
}
|
|
if secondpssnode == nonode {
|
|
return true
|
|
}
|
|
return false
|
|
})
|
|
if secondpssnode == nonode {
|
|
time.Sleep(time.Millisecond * 100)
|
|
}
|
|
}
|
|
|
|
srvrecvl, err := net.Listen("tcp", srvrecvep)
|
|
if err != nil {
|
|
t.Fatalf("Tcp (recv) on %s failed: %v", srvrecvep, err)
|
|
}
|
|
go func() {
|
|
err := http.Serve(srvrecvl, *nodes[fullnodes[1]].ws)
|
|
if err != nil {
|
|
t.Fatalf("http serve (recv) on %s failed: %v", srvrecvep, err)
|
|
}
|
|
}()
|
|
|
|
srvsendl, err := net.Listen("tcp", srvsendep)
|
|
if err != nil {
|
|
t.Fatalf("Tcp (send) on %s failed: %v", srvsendep, err)
|
|
}
|
|
go func() {
|
|
err := http.Serve(srvsendl, *nodes[fullnodes[0]].ws)
|
|
if err != nil {
|
|
t.Fatalf("http serve (send) on %s failed: %v", srvrecvep, err)
|
|
}
|
|
}()
|
|
|
|
for !clientrecvok {
|
|
log.Trace("attempting clientrecv connect")
|
|
clientrecv, err = rpc.DialWebsocket(context.Background(), "ws://"+srvrecvep, "ws://localhost")
|
|
if err == nil {
|
|
clientrecvok = true
|
|
} else {
|
|
log.Debug("clientrecv failed, retrying", "error", err)
|
|
time.Sleep(time.Millisecond * 250)
|
|
}
|
|
}
|
|
|
|
for !clientsendok {
|
|
log.Trace("attempting clientsend connect")
|
|
clientsend, err = rpc.DialWebsocket(context.Background(), "ws://"+srvsendep, "ws://localhost")
|
|
if err == nil {
|
|
clientsendok = true
|
|
} else {
|
|
log.Debug("clientsend failed, retrying", "error", err)
|
|
time.Sleep(time.Millisecond * 250)
|
|
}
|
|
}
|
|
|
|
trigger = make(chan discover.NodeID)
|
|
ch := make(chan string)
|
|
|
|
action = func(ctx context.Context) error {
|
|
go func() {
|
|
clientrecv.EthSubscribe(ctx, ch, "newMsg", topic)
|
|
clientsend.Call(nil, "eth_sendRaw", nodes[secondpssnode].Pss.Overlay.BaseAddr(), topic, []byte("ping"))
|
|
trigger <- secondpssnode
|
|
}()
|
|
return nil
|
|
}
|
|
check = func(ctx context.Context, id discover.NodeID) (bool, error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
return false, ctx.Err()
|
|
default:
|
|
}
|
|
|
|
select {
|
|
case msg := <-ch:
|
|
log.Trace(fmt.Sprintf("notify!: %v", msg))
|
|
case <-time.NewTimer(time.Second).C:
|
|
log.Trace(fmt.Sprintf("no notifies :'("))
|
|
}
|
|
// also need to know if the protocolpeer is set up
|
|
|
|
return true, nil
|
|
}
|
|
|
|
timeout = 10 * time.Second
|
|
ctx, cancel = context.WithTimeout(context.Background(), timeout)
|
|
defer cancel()
|
|
|
|
result = simulations.NewSimulation(simnet).Run(ctx, &simulations.Step{
|
|
Action: action,
|
|
Trigger: trigger,
|
|
Expect: &simulations.Expectation{
|
|
Nodes: []discover.NodeID{secondpssnode},
|
|
Check: check,
|
|
},
|
|
})
|
|
if result.Error != nil {
|
|
t.Fatalf("simulation failed: %s", result.Error)
|
|
}
|
|
|
|
t.Log("Simulation Passed:")
|
|
}
|
|
|
|
// test framework below
|
|
|
|
// numnodes: how many nodes to create
|
|
// pssnodeidx: on which node indices to start the pss
|
|
// net: the simulated network
|
|
// trigger: hook needed for simulation event reporting
|
|
// vct: codemap for virtual protocol
|
|
// name: name for virtual protocol (and pss topic)
|
|
// version: name for virtual protocol (and pss topic)
|
|
// testpeers: pss-specific peers, with hook needed for simulation event reporting
|
|
|
|
// the simulation tester constructor is currently a hack to fit previous code with later stack using node.Services to start SimNodes
|
|
|
|
func newPssSimulationTester(t *testing.T, numnodes int, numfullnodes int, trigger chan discover.NodeID, vct *protocols.CodeMap, name string, version int, testpeers map[discover.NodeID]*pssTestPeer) (*simulations.Network, map[discover.NodeID]*pssTestNode) {
|
|
topic, _ := MakeTopic(name, version)
|
|
nodes := make(map[discover.NodeID]*pssTestNode, numnodes)
|
|
psss := make(map[discover.NodeID]*Pss)
|
|
var simnet *simulations.Network
|
|
serviceFunc := func(id discover.NodeID) node.Service {
|
|
node := &pssTestNode{
|
|
Pss: psss[id],
|
|
Hive: nil,
|
|
id: id,
|
|
network: simnet,
|
|
trigger: trigger,
|
|
ct: vct,
|
|
apifunc: func() []rpc.API { return nil },
|
|
expectC: make(chan []int),
|
|
}
|
|
|
|
// set up handlers for the encapsulating PssMsg
|
|
|
|
var handlefunc func(interface{}) error
|
|
|
|
addr := NewPeerAddrFromNodeID(id)
|
|
|
|
if testpeers[id] != nil {
|
|
handlefunc = makePssHandleProtocol(psss[id])
|
|
log.Trace(fmt.Sprintf("Making full protocol id %x addr %x (testpeers %p)", common.ByteLabel(id.Bytes()), common.ByteLabel(addr.Over()), testpeers))
|
|
} else {
|
|
handlefunc = makePssHandleForward(psss[id])
|
|
}
|
|
|
|
// protocols are now registered by invoking node services
|
|
// since adapters.SimNode implements p2p.Server, needed for the services to start, we use this as a convenience wrapper
|
|
|
|
testservice := newPssTestService(t, handlefunc, node)
|
|
|
|
// the network sim wants a adapters.NodeAdapter, so we pass back to it a SimNode
|
|
// this is the SimNode member of the testNode initialized above, but assigned through the service start
|
|
// that is so say: node == testservice.node, but we access it as a member of testservice below for clarity (to the extent that this can be clear)
|
|
|
|
nodes[id] = testservice.node
|
|
testservice.node.apifunc = testservice.APIs
|
|
return testservice
|
|
}
|
|
adapter := adapters.NewSimAdapter(map[string]adapters.ServiceFunc{"pss": serviceFunc})
|
|
simnet = simulations.NewNetwork(adapter, &simulations.NetworkConfig{
|
|
ID: "0",
|
|
Backend: true,
|
|
})
|
|
configs := make([]*adapters.NodeConfig, numnodes)
|
|
for i := 0; i < numnodes; i++ {
|
|
configs[i] = adapters.RandomNodeConfig()
|
|
configs[i].Service = "pss"
|
|
}
|
|
for i, conf := range configs {
|
|
addr := NewPeerAddrFromNodeID(conf.ID)
|
|
psss[conf.ID] = makePss(addr.Over())
|
|
if i < numfullnodes {
|
|
tp := &pssTestPeer{
|
|
Peer: &protocols.Peer{
|
|
Peer: &p2p.Peer{},
|
|
},
|
|
successC: make(chan bool),
|
|
resultC: make(chan int),
|
|
}
|
|
testpeers[conf.ID] = tp
|
|
targetprotocol := makeCustomProtocol(name, version, vct, testpeers[conf.ID])
|
|
pssprotocol := NewPssProtocol(psss[conf.ID], &topic, vct, targetprotocol)
|
|
psss[conf.ID].Register(topic, pssprotocol.GetHandler())
|
|
}
|
|
|
|
if err := simnet.NewNodeWithConfig(conf); err != nil {
|
|
t.Fatalf("error creating node %s: %s", conf.ID.Label(), err)
|
|
}
|
|
if err := simnet.Start(conf.ID); err != nil {
|
|
t.Fatalf("error starting node %s: %s", conf.ID.Label(), err)
|
|
}
|
|
}
|
|
|
|
return simnet, nodes
|
|
}
|
|
|
|
func makePss(addr []byte) *Pss {
|
|
|
|
// set up storage
|
|
cachedir, err := ioutil.TempDir("", "pss-cache")
|
|
if err != nil {
|
|
log.Error("create pss cache tmpdir failed", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
dpa, err := storage.NewLocalDPA(cachedir)
|
|
if err != nil {
|
|
log.Error("local dpa creation failed", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
// cannot use pyramidchunker as it still lacks joinfunc TestPssRegisterHandler(t *testing.T) {
|
|
addr := RandomAddr()
|
|
ps := newTestPss(addr.UnderlayAddr())
|
|
from := RandomAddr()
|
|
payload := []byte("payload")
|
|
topic := NewTopic(protocolName, protocolVersion)
|
|
checkMsg := func(msg []byte, p *p2p.Peer, sender []byte) error {
|
|
if !bytes.Equal(from.OverlayAddr(), sender) {
|
|
return fmt.Errorf("sender mismatch. expected %x, got %x", from.OverlayAddr(), sender)
|
|
}
|
|
if !bytes.Equal(msg, payload) {
|
|
return fmt.Errorf("sender mismatch. expected %x, got %x", msg, payload)
|
|
}
|
|
if !bytes.Equal(from.UnderlayAddr(), p.ID()) {
|
|
return fmt.Errorf("sender mismatch. expected %x, got %x", from.UnderlayAddr(), p.ID())
|
|
}
|
|
}
|
|
deregister := ps.Register(topic, checkMsg)
|
|
pssmsg := &PssMsg{Data: NewPssEnvelope(from, topic, payload)}
|
|
err = ps.Process(pssmsg)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var i int
|
|
err = ps.Process(&PssMsg{Data: NewPssEnvelope(from, []byte("topic"), payload)})
|
|
expErr := ""
|
|
if err == nil || err.Error() != expErr {
|
|
t.Fatalf("unhandled topic expected %v, got %v", expErr, err)
|
|
}
|
|
deregister2 := ps.Register(topic, func(msg []byte, p *p2p.Peer, sender []byte) error { i++; return nil })
|
|
ps.Process(pssmsg)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if i != 1 {
|
|
t.Fatalf("second registerer handler did not run")
|
|
}
|
|
deregister()
|
|
deregister2()
|
|
err = ps.Process(&PssMsg{Data: NewPssEnvelope(from, topic, payload)})
|
|
expErr = ""
|
|
if err == nil || err.Error() != expErr {
|
|
t.Fatalf("reregister handler expected %v, got %v", expErr, err)
|
|
}
|
|
}
|
|
// dpa.Chunker = storage.NewPyramidChunker(storage.NewChunkerParams())
|
|
|
|
kp := network.NewKadParams()
|
|
kp.MinProxBinSize = 3
|
|
|
|
pp := NewPssParams()
|
|
|
|
overlay := network.NewKademlia(addr, kp)
|
|
ps := NewPss(overlay, dpa, pp)
|
|
//overlay.Prune(time.Tick(time.Millisecond * 250))
|
|
return ps
|
|
}
|
|
|
|
func makeCustomProtocol(name string, version int, ct *protocols.CodeMap, testpeer *pssTestPeer) *p2p.Protocol {
|
|
run := func(p *protocols.Peer) error {
|
|
log.Trace(fmt.Sprintf("running pss vprotocol on peer %v", p))
|
|
if testpeer == nil {
|
|
testpeer = &pssTestPeer{}
|
|
}
|
|
testpeer.Peer = p
|
|
p.Register(&pssTestPayload{}, testpeer.SimpleHandlePssPayload)
|
|
err := p.Run()
|
|
return err
|
|
}
|
|
|
|
return protocols.NewProtocol(name, uint(version), run, ct, nil, nil)
|
|
}
|
|
|
|
func makePssHandleForward(ps *Pss) func(msg interface{}) error {
|
|
// for the simple check it passes on the message if it's not for us
|
|
return func(msg interface{}) error {
|
|
pssmsg := msg.(*PssMsg)
|
|
if ps.IsSelfRecipient(pssmsg) {
|
|
log.Trace("pss for us .. yay!")
|
|
} else {
|
|
log.Trace("passing on pss")
|
|
return ps.Forward(pssmsg)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func makePssHandleProtocol(ps *Pss) func(msg interface{}) error {
|
|
return func(msg interface{}) error {
|
|
pssmsg := msg.(*PssMsg)
|
|
|
|
if ps.IsSelfRecipient(pssmsg) {
|
|
log.Trace("pss for us ... let's process!")
|
|
env := pssmsg.Payload
|
|
umsg := env.Payload // this will be rlp encrypted
|
|
f := ps.GetHandler(env.Topic)
|
|
if f == nil {
|
|
return fmt.Errorf("No registered handler for topic '%s'", env.Topic)
|
|
}
|
|
nid := adapters.NewNodeID(env.SenderUAddr)
|
|
p := p2p.NewPeer(nid.NodeID, fmt.Sprintf("%x", common.ByteLabel(nid.Bytes())), []p2p.Cap{})
|
|
return f(umsg, p, env.SenderOAddr)
|
|
} else {
|
|
log.Trace("pss was for someone else :'(")
|
|
return ps.Forward(pssmsg)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// echoes an incoming message
|
|
// it comes in through
|
|
// Any pointer receiver that has protocols.Peer
|
|
func (ptp *pssTestPeer) SimpleHandlePssPayload(msg interface{}) error {
|
|
pmsg := msg.(*pssTestPayload)
|
|
log.Trace(fmt.Sprintf("pssTestPayloadhandler got message %v", pmsg))
|
|
if pmsg.Data == "ping" {
|
|
pmsg.Data = "pong"
|
|
log.Trace(fmt.Sprintf("pssTestPayloadhandler reply %v", pmsg))
|
|
ptp.Send(pmsg)
|
|
} else if pmsg.Data == "pong" {
|
|
ptp.successC <- true
|
|
} else {
|
|
res, err := strconv.Atoi(pmsg.Data)
|
|
if err != nil {
|
|
log.Trace(fmt.Sprintf("pssTestPayloadhandlererr %v", err))
|
|
ptp.successC <- false
|
|
} else {
|
|
log.Trace(fmt.Sprintf("pssTestPayloadhandler sending %d on chan", pmsg))
|
|
ptp.successC <- true
|
|
ptp.resultC <- res
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
*/
|