mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 09:23:48 +00:00
747 lines
21 KiB
Go
747 lines
21 KiB
Go
package pss
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/ecdsa"
|
|
"encoding/hex"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math/rand"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
"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/rpc"
|
|
"github.com/ethereum/go-ethereum/swarm/network"
|
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
|
)
|
|
|
|
const (
|
|
pssServiceName = "pss"
|
|
bzzServiceName = "bzz"
|
|
)
|
|
|
|
var (
|
|
initOnce = sync.Once{}
|
|
snapshotfile string
|
|
debugdebugflag = flag.Bool("vv", false, "veryverbose")
|
|
debugflag = flag.Bool("v", false, "verbose")
|
|
snapshotflag = flag.String("s", "", "snapshot filename")
|
|
messagesflag = flag.Int("m", 0, "number of messages to generate (default = number of nodes). Ignored if -s is not set")
|
|
addresssizeflag = flag.Int("b", 32, "number of bytes to use for address. Ignored if -s is not set")
|
|
adaptertypeflag = flag.String("a", "sim", "Adapter type to use. Ignored if -s is not set")
|
|
messagedelayflag = flag.Int("d", 1000, "Message max delay period, in ms")
|
|
w *whisper.Whisper
|
|
wapi *whisper.PublicWhisperAPI
|
|
psslogmain log.Logger
|
|
pssprotocols map[string]*protoCtrl
|
|
useHandshake bool
|
|
)
|
|
|
|
var services = newServices()
|
|
|
|
func init() {
|
|
flag.Parse()
|
|
rand.Seed(time.Now().Unix())
|
|
|
|
adapters.RegisterServices(services)
|
|
initTest()
|
|
}
|
|
|
|
func initTest() {
|
|
initOnce.Do(
|
|
func() {
|
|
loglevel := log.LvlInfo
|
|
if *debugflag {
|
|
loglevel = log.LvlDebug
|
|
} else if *debugdebugflag {
|
|
loglevel = log.LvlTrace
|
|
}
|
|
|
|
psslogmain = log.New("psslog", "*")
|
|
hs := log.StreamHandler(os.Stderr, log.TerminalFormat(true))
|
|
hf := log.LvlFilterHandler(loglevel, hs)
|
|
h := log.CallerFileHandler(hf)
|
|
log.Root().SetHandler(h)
|
|
|
|
w = whisper.New(&whisper.DefaultConfig)
|
|
wapi = whisper.NewPublicWhisperAPI(w)
|
|
|
|
pssprotocols = make(map[string]*protoCtrl)
|
|
},
|
|
)
|
|
}
|
|
|
|
// test that topic conversion functions give predictable results
|
|
func TestTopic(t *testing.T) {
|
|
|
|
api := &API{}
|
|
|
|
topicstr := strings.Join([]string{PingProtocol.Name, strconv.Itoa(int(PingProtocol.Version))}, ":")
|
|
|
|
// bytestotopic is the authoritative topic conversion source
|
|
topicobj := BytesToTopic([]byte(topicstr))
|
|
|
|
// string to topic and bytes to topic must match
|
|
topicapiobj, _ := api.StringToTopic(topicstr)
|
|
if topicobj != topicapiobj {
|
|
t.Fatalf("bytes and string topic conversion mismatch; %s != %s", topicobj, topicapiobj)
|
|
}
|
|
|
|
// string representation of topichex
|
|
topichex := topicobj.String()
|
|
|
|
// protocoltopic wrapper on pingtopic should be same as topicstring
|
|
// check that it matches
|
|
pingtopichex := PingTopic.String()
|
|
if topichex != pingtopichex {
|
|
t.Fatalf("protocol topic conversion mismatch; %s != %s", topichex, pingtopichex)
|
|
}
|
|
|
|
// json marshal of topic
|
|
topicjsonout, err := topicobj.MarshalJSON()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(topicjsonout)[1:len(topicjsonout)-1] != topichex {
|
|
t.Fatalf("topic json marshal mismatch; %s != \"%s\"", topicjsonout, topichex)
|
|
}
|
|
|
|
// json unmarshal of topic
|
|
var topicjsonin Topic
|
|
topicjsonin.UnmarshalJSON(topicjsonout)
|
|
if topicjsonin != topicobj {
|
|
t.Fatalf("topic json unmarshal mismatch: %x != %x", topicjsonin, topicobj)
|
|
}
|
|
}
|
|
|
|
// test if we can insert into cache, match items with cache and cache expiry
|
|
func TestCache(t *testing.T) {
|
|
var err error
|
|
to, _ := hex.DecodeString("08090a0b0c0d0e0f1011121314150001020304050607161718191a1b1c1d1e1f")
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
keys, err := wapi.NewKeyPair(ctx)
|
|
privkey, err := w.GetPrivateKey(keys)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ps := newTestPss(privkey, nil, nil)
|
|
pp := NewPssParams(privkey)
|
|
data := []byte("foo")
|
|
datatwo := []byte("bar")
|
|
wparams := &whisper.MessageParams{
|
|
TTL: defaultWhisperTTL,
|
|
Src: privkey,
|
|
Dst: &privkey.PublicKey,
|
|
Topic: whisper.TopicType(PingTopic),
|
|
WorkTime: defaultWhisperWorkTime,
|
|
PoW: defaultWhisperPoW,
|
|
Payload: data,
|
|
}
|
|
woutmsg, err := whisper.NewSentMessage(wparams)
|
|
env, err := woutmsg.Wrap(wparams)
|
|
msg := &PssMsg{
|
|
Payload: env,
|
|
To: to,
|
|
}
|
|
wparams.Payload = datatwo
|
|
woutmsg, err = whisper.NewSentMessage(wparams)
|
|
envtwo, err := woutmsg.Wrap(wparams)
|
|
msgtwo := &PssMsg{
|
|
Payload: envtwo,
|
|
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 digest == digesttwo {
|
|
t.Fatalf("different msgs return same hash: %d", digesttwo)
|
|
}
|
|
|
|
// check the cache
|
|
err = ps.addFwdCache(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)
|
|
}
|
|
}
|
|
|
|
// matching of address hints; whether a message could be or is for the node
|
|
func TestAddressMatch(t *testing.T) {
|
|
|
|
localaddr := network.RandomAddr().Over()
|
|
copy(localaddr[:8], []byte("deadbeef"))
|
|
remoteaddr := []byte("feedbeef")
|
|
kadparams := network.NewKadParams()
|
|
kad := network.NewKademlia(localaddr, kadparams)
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
keys, err := wapi.NewKeyPair(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Could not generate private key: %v", err)
|
|
}
|
|
privkey, err := w.GetPrivateKey(keys)
|
|
pssp := NewPssParams(privkey)
|
|
ps := NewPss(kad, nil, pssp)
|
|
|
|
pssmsg := &PssMsg{
|
|
To: remoteaddr,
|
|
Payload: &whisper.Envelope{},
|
|
}
|
|
|
|
// differ from first byte
|
|
if ps.isSelfRecipient(pssmsg) {
|
|
t.Fatalf("isSelfRecipient true but %x != %x", remoteaddr, localaddr)
|
|
}
|
|
if ps.isSelfPossibleRecipient(pssmsg) {
|
|
t.Fatalf("isSelfPossibleRecipient true but %x != %x", remoteaddr[:8], localaddr[:8])
|
|
}
|
|
|
|
// 8 first bytes same
|
|
copy(remoteaddr[:4], localaddr[:4])
|
|
if ps.isSelfRecipient(pssmsg) {
|
|
t.Fatalf("isSelfRecipient true but %x != %x", remoteaddr, localaddr)
|
|
}
|
|
if !ps.isSelfPossibleRecipient(pssmsg) {
|
|
t.Fatalf("isSelfPossibleRecipient false but %x == %x", remoteaddr[:8], localaddr[:8])
|
|
}
|
|
|
|
// all bytes same
|
|
pssmsg.To = localaddr
|
|
if !ps.isSelfRecipient(pssmsg) {
|
|
t.Fatalf("isSelfRecipient false but %x == %x", remoteaddr, localaddr)
|
|
}
|
|
if !ps.isSelfPossibleRecipient(pssmsg) {
|
|
t.Fatalf("isSelfPossibleRecipient false but %x == %x", remoteaddr[:8], localaddr[:8])
|
|
}
|
|
}
|
|
|
|
// set and generate pubkeys and symkeys
|
|
func TestKeys(t *testing.T) {
|
|
// make our key and init pss with it
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
ourkeys, err := wapi.NewKeyPair(ctx)
|
|
if err != nil {
|
|
t.Fatalf("create 'our' key fail")
|
|
}
|
|
ctx, cancel = context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
theirkeys, err := wapi.NewKeyPair(ctx)
|
|
if err != nil {
|
|
t.Fatalf("create 'their' key fail")
|
|
}
|
|
ourprivkey, err := w.GetPrivateKey(ourkeys)
|
|
if err != nil {
|
|
t.Fatalf("failed to retrieve 'our' private key")
|
|
}
|
|
theirprivkey, err := w.GetPrivateKey(theirkeys)
|
|
if err != nil {
|
|
t.Fatalf("failed to retrieve 'their' private key")
|
|
}
|
|
ps := newTestPss(ourprivkey, nil, nil)
|
|
|
|
// set up peer with mock address, mapped to mocked publicaddress and with mocked symkey
|
|
addr := make(PssAddress, 32)
|
|
copy(addr, network.RandomAddr().Over())
|
|
outkey := network.RandomAddr().Over()
|
|
topicobj := BytesToTopic([]byte("foo:42"))
|
|
ps.SetPeerPublicKey(&theirprivkey.PublicKey, topicobj, &addr)
|
|
outkeyid, err := ps.SetSymmetricKey(outkey, topicobj, &addr, false)
|
|
if err != nil {
|
|
t.Fatalf("failed to set 'our' outgoing symmetric key")
|
|
}
|
|
|
|
// make a symmetric key that we will send to peer for encrypting messages to us
|
|
inkeyid, err := ps.generateSymmetricKey(topicobj, &addr, true)
|
|
if err != nil {
|
|
t.Fatalf("failed to set 'our' incoming symmetric key")
|
|
}
|
|
|
|
// get the key back from whisper, check that it's still the same
|
|
outkeyback, err := ps.w.GetSymKey(outkeyid)
|
|
if err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
inkey, err := ps.w.GetSymKey(inkeyid)
|
|
if err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
if !bytes.Equal(outkeyback, outkey) {
|
|
t.Fatalf("passed outgoing symkey doesnt equal stored: %x / %x", outkey, outkeyback)
|
|
}
|
|
|
|
t.Logf("symout: %v", outkeyback)
|
|
t.Logf("symin: %v", inkey)
|
|
|
|
// check that the key is stored in the peerpool
|
|
psp := ps.symKeyPool[inkeyid][topicobj]
|
|
if psp.address != &addr {
|
|
t.Fatalf("inkey address does not match; %p != %p", psp.address, &addr)
|
|
}
|
|
}
|
|
|
|
type pssTestPeer struct {
|
|
*protocols.Peer
|
|
addr []byte
|
|
}
|
|
|
|
func (t *pssTestPeer) Address() []byte {
|
|
return t.addr
|
|
}
|
|
|
|
func (t *pssTestPeer) Update(addr network.OverlayAddr) network.OverlayAddr {
|
|
return addr
|
|
}
|
|
|
|
func (t *pssTestPeer) Off() network.OverlayAddr {
|
|
return &pssTestPeer{}
|
|
}
|
|
|
|
// forwarding should skip peers that do not have matching pss capabilities
|
|
func TestMismatch(t *testing.T) {
|
|
|
|
// create privkey for forwarder node
|
|
privkey, err := crypto.GenerateKey()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// initialize overlay
|
|
baseaddr := network.RandomAddr()
|
|
kad := network.NewKademlia((baseaddr).Over(), network.NewKadParams())
|
|
rw := &p2p.MsgPipeRW{}
|
|
|
|
// one peer has a mismatching version of pss
|
|
wrongpssaddr := network.RandomAddr()
|
|
wrongpsscap := p2p.Cap{
|
|
Name: pssProtocolName,
|
|
Version: 0,
|
|
}
|
|
nid, _ := discover.HexID("0x01")
|
|
wrongpsspeer := &pssTestPeer{
|
|
Peer: protocols.NewPeer(p2p.NewPeer(nid, common.ToHex(wrongpssaddr.Over()), []p2p.Cap{wrongpsscap}), rw, nil),
|
|
addr: wrongpssaddr.Over(),
|
|
}
|
|
|
|
// one peer doesn't even have pss (boo!)
|
|
nopssaddr := network.RandomAddr()
|
|
nopsscap := p2p.Cap{
|
|
Name: "nopss",
|
|
Version: 1,
|
|
}
|
|
nid, _ = discover.HexID("0x02")
|
|
nopsspeer := &pssTestPeer{
|
|
Peer: protocols.NewPeer(p2p.NewPeer(nid, common.ToHex(nopssaddr.Over()), []p2p.Cap{nopsscap}), rw, nil),
|
|
addr: nopssaddr.Over(),
|
|
}
|
|
|
|
// add peers to kademlia and activate them
|
|
// it's safe so don't check errors
|
|
kad.Register([]network.OverlayAddr{wrongpsspeer})
|
|
kad.On(wrongpsspeer)
|
|
kad.Register([]network.OverlayAddr{nopsspeer})
|
|
kad.On(nopsspeer)
|
|
|
|
// create pss
|
|
pssmsg := &PssMsg{
|
|
To: []byte{},
|
|
Expire: uint32(time.Now().Add(time.Second).Unix()),
|
|
Payload: &whisper.Envelope{},
|
|
}
|
|
ps := newTestPss(privkey, kad, nil)
|
|
|
|
// run the forward
|
|
// it is enough that it completes; trying to send to incapable peers would create segfault
|
|
ps.forward(pssmsg)
|
|
|
|
}
|
|
|
|
func BenchmarkSymkeyBruteforceChangeaddr(b *testing.B) {
|
|
for i := 100; i < 100000; i = i * 10 {
|
|
for j := 32; j < 10000; j = j * 8 {
|
|
b.Run(fmt.Sprintf("%d/%d", i, j), benchmarkSymkeyBruteforceChangeaddr)
|
|
}
|
|
//b.Run(fmt.Sprintf("%d", i), benchmarkSymkeyBruteforceChangeaddr)
|
|
}
|
|
}
|
|
|
|
// decrypt performance using symkey cache, worst case
|
|
// (decrypt key always last in cache)
|
|
func benchmarkSymkeyBruteforceChangeaddr(b *testing.B) {
|
|
keycountstring := strings.Split(b.Name(), "/")
|
|
cachesize := int64(0)
|
|
var ps *Pss
|
|
if len(keycountstring) < 2 {
|
|
b.Fatalf("benchmark called without count param")
|
|
}
|
|
keycount, err := strconv.ParseInt(keycountstring[1], 10, 0)
|
|
if err != nil {
|
|
b.Fatalf("benchmark called with invalid count param '%s': %v", keycountstring[1], err)
|
|
}
|
|
if len(keycountstring) == 3 {
|
|
cachesize, err = strconv.ParseInt(keycountstring[2], 10, 0)
|
|
if err != nil {
|
|
b.Fatalf("benchmark called with invalid cachesize '%s': %v", keycountstring[2], err)
|
|
}
|
|
}
|
|
pssmsgs := make([]*PssMsg, 0, keycount)
|
|
var keyid string
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
keys, err := wapi.NewKeyPair(ctx)
|
|
privkey, err := w.GetPrivateKey(keys)
|
|
if cachesize > 0 {
|
|
ps = newTestPss(privkey, nil, &PssParams{SymKeyCacheCapacity: int(cachesize)})
|
|
} else {
|
|
ps = newTestPss(privkey, nil, nil)
|
|
}
|
|
topic := BytesToTopic([]byte("foo"))
|
|
for i := 0; i < int(keycount); i++ {
|
|
to := make(PssAddress, 32)
|
|
copy(to[:], network.RandomAddr().Over())
|
|
keyid, err = ps.generateSymmetricKey(topic, &to, true)
|
|
if err != nil {
|
|
b.Fatalf("cant generate symkey #%d: %v", i, err)
|
|
}
|
|
symkey, err := ps.w.GetSymKey(keyid)
|
|
if err != nil {
|
|
b.Fatalf("could not retrieve symkey %s: %v", keyid, err)
|
|
}
|
|
wparams := &whisper.MessageParams{
|
|
TTL: defaultWhisperTTL,
|
|
KeySym: symkey,
|
|
Topic: whisper.TopicType(topic),
|
|
WorkTime: defaultWhisperWorkTime,
|
|
PoW: defaultWhisperPoW,
|
|
Payload: []byte("xyzzy"),
|
|
Padding: []byte("1234567890abcdef"),
|
|
}
|
|
woutmsg, err := whisper.NewSentMessage(wparams)
|
|
if err != nil {
|
|
b.Fatalf("could not create whisper message: %v", err)
|
|
}
|
|
env, err := woutmsg.Wrap(wparams)
|
|
if err != nil {
|
|
b.Fatalf("could not generate whisper envelope: %v", err)
|
|
}
|
|
ps.Register(&topic, func(msg []byte, p *p2p.Peer, asymmetric bool, keyid string) error {
|
|
return nil
|
|
})
|
|
pssmsgs = append(pssmsgs, &PssMsg{
|
|
To: to,
|
|
Payload: env,
|
|
})
|
|
}
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
if !ps.process(pssmsgs[len(pssmsgs)-(i%len(pssmsgs))-1]) {
|
|
b.Fatalf("pss processing failed: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkSymkeyBruteforceSameaddr(b *testing.B) {
|
|
for i := 100; i < 100000; i = i * 10 {
|
|
for j := 32; j < 10000; j = j * 8 {
|
|
b.Run(fmt.Sprintf("%d/%d", i, j), benchmarkSymkeyBruteforceSameaddr)
|
|
}
|
|
}
|
|
}
|
|
|
|
// decrypt performance using symkey cache, best case
|
|
// (decrypt key always first in cache)
|
|
func benchmarkSymkeyBruteforceSameaddr(b *testing.B) {
|
|
var keyid string
|
|
var ps *Pss
|
|
cachesize := int64(0)
|
|
keycountstring := strings.Split(b.Name(), "/")
|
|
if len(keycountstring) < 2 {
|
|
b.Fatalf("benchmark called without count param")
|
|
}
|
|
keycount, err := strconv.ParseInt(keycountstring[1], 10, 0)
|
|
if err != nil {
|
|
b.Fatalf("benchmark called with invalid count param '%s': %v", keycountstring[1], err)
|
|
}
|
|
if len(keycountstring) == 3 {
|
|
cachesize, err = strconv.ParseInt(keycountstring[2], 10, 0)
|
|
if err != nil {
|
|
b.Fatalf("benchmark called with invalid cachesize '%s': %v", keycountstring[2], err)
|
|
}
|
|
}
|
|
addr := make([]PssAddress, keycount)
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
keys, err := wapi.NewKeyPair(ctx)
|
|
privkey, err := w.GetPrivateKey(keys)
|
|
if cachesize > 0 {
|
|
ps = newTestPss(privkey, nil, &PssParams{SymKeyCacheCapacity: int(cachesize)})
|
|
} else {
|
|
ps = newTestPss(privkey, nil, nil)
|
|
}
|
|
topic := BytesToTopic([]byte("foo"))
|
|
for i := 0; i < int(keycount); i++ {
|
|
copy(addr[i], network.RandomAddr().Over())
|
|
keyid, err = ps.generateSymmetricKey(topic, &addr[i], true)
|
|
if err != nil {
|
|
b.Fatalf("cant generate symkey #%d: %v", i, err)
|
|
}
|
|
|
|
}
|
|
symkey, err := ps.w.GetSymKey(keyid)
|
|
if err != nil {
|
|
b.Fatalf("could not retrieve symkey %s: %v", keyid, err)
|
|
}
|
|
wparams := &whisper.MessageParams{
|
|
TTL: defaultWhisperTTL,
|
|
KeySym: symkey,
|
|
Topic: whisper.TopicType(topic),
|
|
WorkTime: defaultWhisperWorkTime,
|
|
PoW: defaultWhisperPoW,
|
|
Payload: []byte("xyzzy"),
|
|
Padding: []byte("1234567890abcdef"),
|
|
}
|
|
woutmsg, err := whisper.NewSentMessage(wparams)
|
|
if err != nil {
|
|
b.Fatalf("could not create whisper message: %v", err)
|
|
}
|
|
env, err := woutmsg.Wrap(wparams)
|
|
if err != nil {
|
|
b.Fatalf("could not generate whisper envelope: %v", err)
|
|
}
|
|
ps.Register(&topic, func(msg []byte, p *p2p.Peer, asymmetric bool, keyid string) error {
|
|
return nil
|
|
})
|
|
pssmsg := &PssMsg{
|
|
To: addr[len(addr)-1][:],
|
|
Payload: env,
|
|
}
|
|
for i := 0; i < b.N; i++ {
|
|
if !ps.process(pssmsg) {
|
|
b.Fatalf("pss processing failed: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// setup simulated network and connect nodes in circle
|
|
func setupNetwork(numnodes int) (clients []*rpc.Client, err error) {
|
|
nodes := make([]*simulations.Node, numnodes)
|
|
clients = make([]*rpc.Client, numnodes)
|
|
if numnodes < 2 {
|
|
return nil, fmt.Errorf("Minimum two nodes in network")
|
|
}
|
|
adapter := adapters.NewSimAdapter(services)
|
|
net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{
|
|
ID: "0",
|
|
DefaultService: "bzz",
|
|
})
|
|
for i := 0; i < numnodes; i++ {
|
|
nodes[i], err = net.NewNodeWithConfig(&adapters.NodeConfig{
|
|
Services: []string{"bzz", pssProtocolName},
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating node 1: %v", err)
|
|
}
|
|
err = net.Start(nodes[i].ID())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error starting node 1: %v", err)
|
|
}
|
|
if i > 0 {
|
|
err = net.Connect(nodes[i].ID(), nodes[i-1].ID())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error connecting nodes: %v", err)
|
|
}
|
|
}
|
|
clients[i], err = nodes[i].Client()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("create node 1 rpc client fail: %v", err)
|
|
}
|
|
}
|
|
if numnodes > 2 {
|
|
err = net.Connect(nodes[0].ID(), nodes[len(nodes)-1].ID())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error connecting first and last nodes")
|
|
}
|
|
}
|
|
return clients, nil
|
|
}
|
|
|
|
func newServices() adapters.Services {
|
|
stateStore := newStateStore()
|
|
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{
|
|
pssProtocolName: func(ctx *adapters.ServiceContext) (node.Service, error) {
|
|
cachedir, err := ioutil.TempDir("", "pss-cache")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("create pss cache tmpdir failed: %v", err)
|
|
}
|
|
dpa, err := storage.NewLocalDPA(cachedir)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("local dpa creation failed: %v", err)
|
|
}
|
|
|
|
// execadapter does not exec init()
|
|
initTest()
|
|
|
|
ctxlocal, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
keys, err := wapi.NewKeyPair(ctxlocal)
|
|
privkey, err := w.GetPrivateKey(keys)
|
|
pssp := NewPssParams(privkey)
|
|
pssp.MsgTTL = time.Second * 30
|
|
pskad := kademlia(ctx.Config.ID)
|
|
ps := NewPss(pskad, dpa, pssp)
|
|
|
|
ping := &Ping{
|
|
OutC: make(chan bool),
|
|
Pong: true,
|
|
}
|
|
p2pp := NewPingProtocol(ping)
|
|
pp, err := RegisterProtocol(ps, &PingTopic, PingProtocol, p2pp, &ProtocolParams{Asymmetric: true})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if useHandshake {
|
|
SetHandshakeController(ps, NewHandshakeParams())
|
|
}
|
|
ps.Register(&PingTopic, pp.Handle)
|
|
ps.addAPI(rpc.API{
|
|
Namespace: "psstest",
|
|
Version: "0.3",
|
|
Service: NewAPITest(ps),
|
|
Public: false,
|
|
})
|
|
if err != nil {
|
|
log.Error("Couldnt register pss protocol", "err", err)
|
|
os.Exit(1)
|
|
}
|
|
pssprotocols[ctx.Config.ID.String()] = &protoCtrl{
|
|
C: ping.OutC,
|
|
protocol: pp,
|
|
run: p2pp.Run,
|
|
}
|
|
return ps, nil
|
|
},
|
|
"bzz": func(ctx *adapters.ServiceContext) (node.Service, error) {
|
|
addr := network.NewAddrFromNodeID(ctx.Config.ID)
|
|
hp := network.NewHiveParams()
|
|
hp.Discovery = false
|
|
config := &network.BzzConfig{
|
|
OverlayAddr: addr.Over(),
|
|
UnderlayAddr: addr.Under(),
|
|
HiveParams: hp,
|
|
}
|
|
return network.NewBzz(config, kademlia(ctx.Config.ID), stateStore), nil
|
|
},
|
|
}
|
|
}
|
|
|
|
func newTestPss(privkey *ecdsa.PrivateKey, overlay network.Overlay, ppextra *PssParams) *Pss {
|
|
|
|
var nid discover.NodeID
|
|
copy(nid[:], crypto.FromECDSAPub(&privkey.PublicKey))
|
|
addr := network.NewAddrFromNodeID(nid)
|
|
|
|
// set up storage
|
|
cachedir, err := ioutil.TempDir("", "pss-cache")
|
|
if err != nil {
|
|
log.Error("create pss cache tmpdir failed", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
dpa, err := storage.NewLocalDPA(cachedir)
|
|
if err != nil {
|
|
log.Error("local dpa creation failed", "error", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// set up routing if kademlia is not passed to us
|
|
if overlay == nil {
|
|
kp := network.NewKadParams()
|
|
kp.MinProxBinSize = 3
|
|
overlay = network.NewKademlia(addr.Over(), kp)
|
|
}
|
|
|
|
// create pss
|
|
pp := NewPssParams(privkey)
|
|
if ppextra != nil {
|
|
pp.SymKeyCacheCapacity = ppextra.SymKeyCacheCapacity
|
|
}
|
|
ps := NewPss(overlay, dpa, pp)
|
|
|
|
return ps
|
|
}
|
|
|
|
// API calls for test/development use
|
|
type APITest struct {
|
|
*Pss
|
|
}
|
|
|
|
func NewAPITest(ps *Pss) *APITest {
|
|
return &APITest{Pss: ps}
|
|
}
|
|
|
|
func (apitest *APITest) SetSymKeys(pubkeyid string, recvsymkey []byte, sendsymkey []byte, limit uint16, topic Topic, to PssAddress) ([2]string, error) {
|
|
recvsymkeyid, err := apitest.SetSymmetricKey(recvsymkey, topic, &to, true)
|
|
if err != nil {
|
|
return [2]string{}, err
|
|
}
|
|
sendsymkeyid, err := apitest.SetSymmetricKey(sendsymkey, topic, &to, false)
|
|
if err != nil {
|
|
return [2]string{}, err
|
|
}
|
|
return [2]string{recvsymkeyid, sendsymkeyid}, nil
|
|
}
|
|
|
|
func (apitest *APITest) Clean() (int, error) {
|
|
return apitest.Pss.cleanKeys(), nil
|
|
}
|