2018-09-05 15:36:14 +00:00
// Copyright 2018 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package pss
import (
"bytes"
"context"
"crypto/ecdsa"
"encoding/binary"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"math/rand"
"os"
"strconv"
"strings"
"sync"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/metrics/influxdb"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
2018-11-21 15:30:00 +00:00
"github.com/ethereum/go-ethereum/p2p/enode"
2018-09-05 15:36:14 +00:00
"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"
2019-01-14 10:31:28 +00:00
"github.com/ethereum/go-ethereum/swarm/pot"
2018-09-05 15:36:14 +00:00
"github.com/ethereum/go-ethereum/swarm/state"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
)
var (
2019-01-14 10:31:28 +00:00
initOnce = sync . Once { }
loglevel = flag . Int ( "loglevel" , 2 , "logging verbosity" )
longrunning = flag . Bool ( "longrunning" , false , "do run long-running tests" )
w * whisper . Whisper
wapi * whisper . PublicWhisperAPI
psslogmain log . Logger
pssprotocols map [ string ] * protoCtrl
useHandshake bool
noopHandlerFunc = func ( msg [ ] byte , p * p2p . Peer , asymmetric bool , keyid string ) error {
return nil
}
2018-09-05 15:36:14 +00:00
)
func init ( ) {
flag . Parse ( )
rand . Seed ( time . Now ( ) . Unix ( ) )
adapters . RegisterServices ( newServices ( false ) )
initTest ( )
}
func initTest ( ) {
initOnce . Do (
func ( ) {
psslogmain = log . New ( "psslog" , "*" )
hs := log . StreamHandler ( os . Stderr , log . TerminalFormat ( true ) )
2019-01-14 10:31:28 +00:00
hf := log . LvlFilterHandler ( log . Lvl ( * loglevel ) , hs )
2018-09-05 15:36:14 +00:00
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 bit packing of message control flags
func TestMsgParams ( t * testing . T ) {
var ctrl byte
ctrl |= pssControlRaw
p := newMsgParamsFromBytes ( [ ] byte { ctrl } )
m := newPssMsg ( p )
if ! m . isRaw ( ) || m . isSym ( ) {
t . Fatal ( "expected raw=true and sym=false" )
}
ctrl |= pssControlSym
p = newMsgParamsFromBytes ( [ ] byte { ctrl } )
m = newPssMsg ( p )
if ! m . isRaw ( ) || ! m . isSym ( ) {
t . Fatal ( "expected raw=true and sym=true" )
}
ctrl &= 0xff &^ pssControlRaw
p = newMsgParamsFromBytes ( [ ] byte { ctrl } )
m = newPssMsg ( p )
if m . isRaw ( ) || ! m . isSym ( ) {
t . Fatal ( "expected raw=false and sym=true" )
}
}
// 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 ( ) . WithPrivateKey ( privkey )
data := [ ] byte ( "foo" )
datatwo := [ ] byte ( "bar" )
datathree := [ ] byte ( "baz" )
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 ,
}
wparams . Payload = datathree
woutmsg , err = whisper . NewSentMessage ( wparams )
envthree , err := woutmsg . Wrap ( wparams )
msgthree := & PssMsg {
Payload : envthree ,
To : to ,
}
digest := ps . digest ( msg )
if err != nil {
t . Fatalf ( "could not store cache msgone: %v" , err )
}
digesttwo := ps . digest ( msgtwo )
if err != nil {
t . Fatalf ( "could not store cache msgtwo: %v" , err )
}
digestthree := ps . digest ( msgthree )
if err != nil {
t . Fatalf ( "could not store cache msgthree: %v" , err )
}
if digest == digesttwo {
t . Fatalf ( "different msgs return same hash: %d" , digesttwo )
}
// check the cache
err = ps . addFwdCache ( msg )
if err != nil {
t . Fatalf ( "write to pss expire cache failed: %v" , err )
}
if ! ps . checkFwdCache ( msg ) {
t . Fatalf ( "message %v should have EXPIRE record in cache but checkCache returned false" , msg )
}
if ps . checkFwdCache ( msgtwo ) {
t . Fatalf ( "message %v should NOT have EXPIRE record in cache but checkCache returned true" , msgtwo )
}
time . Sleep ( pp . CacheTTL + 1 * time . Second )
err = ps . addFwdCache ( msgthree )
if err != nil {
t . Fatalf ( "write to pss expire cache failed: %v" , err )
}
if ps . checkFwdCache ( msg ) {
t . Fatalf ( "message %v should have expired from cache but checkCache returned true" , msg )
}
if _ , ok := ps . fwdCache [ digestthree ] ; ! ok {
t . Fatalf ( "unexpired message should be in the cache: %v" , digestthree )
}
if _ , ok := ps . fwdCache [ digesttwo ] ; ok {
t . Fatalf ( "expired message should have been cleared from the cache: %v" , digesttwo )
}
}
// 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 ( ) . WithPrivateKey ( privkey )
ps , err := NewPss ( kad , pssp )
if err != nil {
t . Fatal ( err . Error ( ) )
}
pssmsg := & PssMsg {
2019-01-14 10:31:28 +00:00
To : remoteaddr ,
2018-09-05 15:36:14 +00:00
}
// differ from first byte
if ps . isSelfRecipient ( pssmsg ) {
t . Fatalf ( "isSelfRecipient true but %x != %x" , remoteaddr , localaddr )
}
2019-01-14 10:31:28 +00:00
if ps . isSelfPossibleRecipient ( pssmsg , false ) {
2018-09-05 15:36:14 +00:00
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 )
}
2019-01-14 10:31:28 +00:00
if ! ps . isSelfPossibleRecipient ( pssmsg , false ) {
2018-09-05 15:36:14 +00:00
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 )
}
2019-01-14 10:31:28 +00:00
if ! ps . isSelfPossibleRecipient ( pssmsg , false ) {
2018-09-05 15:36:14 +00:00
t . Fatalf ( "isSelfPossibleRecipient false but %x == %x" , remoteaddr [ : 8 ] , localaddr [ : 8 ] )
}
2019-01-14 10:31:28 +00:00
2018-09-05 15:36:14 +00:00
}
2019-01-14 10:31:28 +00:00
// test that message is handled by sender if a prox handler exists and sender is in prox of message
func TestProxShortCircuit ( t * testing . T ) {
// sender node address
localAddr := network . RandomAddr ( ) . Over ( )
localPotAddr := pot . NewAddressFromBytes ( localAddr )
// set up kademlia
kadParams := network . NewKadParams ( )
kad := network . NewKademlia ( localAddr , kadParams )
peerCount := kad . MinBinSize + 1
// set up pss
privKey , err := crypto . GenerateKey ( )
pssp := NewPssParams ( ) . WithPrivateKey ( privKey )
ps , err := NewPss ( kad , pssp )
if err != nil {
t . Fatal ( err . Error ( ) )
}
// create kademlia peers, so we have peers both inside and outside minproxlimit
var peers [ ] * network . Peer
proxMessageAddress := pot . RandomAddressAt ( localPotAddr , peerCount ) . Bytes ( )
distantMessageAddress := pot . RandomAddressAt ( localPotAddr , 0 ) . Bytes ( )
for i := 0 ; i < peerCount ; i ++ {
rw := & p2p . MsgPipeRW { }
ptpPeer := p2p . NewPeer ( enode . ID { } , "wanna be with me? [ ] yes [ ] no" , [ ] p2p . Cap { } )
protoPeer := protocols . NewPeer ( ptpPeer , rw , & protocols . Spec { } )
peerAddr := pot . RandomAddressAt ( localPotAddr , i )
bzzPeer := & network . BzzPeer {
Peer : protoPeer ,
BzzAddr : & network . BzzAddr {
OAddr : peerAddr . Bytes ( ) ,
UAddr : [ ] byte ( fmt . Sprintf ( "%x" , peerAddr [ : ] ) ) ,
} ,
}
peer := network . NewPeer ( bzzPeer , kad )
kad . On ( peer )
peers = append ( peers , peer )
}
// register it marking prox capability
delivered := make ( chan struct { } )
rawHandlerFunc := func ( msg [ ] byte , p * p2p . Peer , asymmetric bool , keyid string ) error {
log . Trace ( "in allowraw handler" )
delivered <- struct { } { }
return nil
}
topic := BytesToTopic ( [ ] byte { 0x2a } )
hndlrProxDereg := ps . Register ( & topic , & handler {
f : rawHandlerFunc ,
caps : & handlerCaps {
raw : true ,
prox : true ,
} ,
} )
defer hndlrProxDereg ( )
// send message too far away for sender to be in prox
// reception of this message should time out
errC := make ( chan error )
go func ( ) {
err := ps . SendRaw ( distantMessageAddress , topic , [ ] byte ( "foo" ) )
if err != nil {
errC <- err
}
} ( )
ctx , cancel := context . WithTimeout ( context . TODO ( ) , time . Second )
defer cancel ( )
select {
case <- delivered :
t . Fatal ( "raw distant message delivered" )
case err := <- errC :
t . Fatal ( err )
case <- ctx . Done ( ) :
}
// send message that should be within sender prox
// this message should be delivered
go func ( ) {
err := ps . SendRaw ( proxMessageAddress , topic , [ ] byte ( "bar" ) )
if err != nil {
errC <- err
}
} ( )
ctx , cancel = context . WithTimeout ( context . TODO ( ) , time . Second )
defer cancel ( )
select {
case <- delivered :
case err := <- errC :
t . Fatal ( err )
case <- ctx . Done ( ) :
t . Fatal ( "raw timeout" )
}
// try the same prox message with sym and asym send
proxAddrPss := PssAddress ( proxMessageAddress )
2019-01-16 09:54:01 +00:00
symKeyId , err := ps . GenerateSymmetricKey ( topic , proxAddrPss , true )
2019-01-14 10:31:28 +00:00
go func ( ) {
err := ps . SendSym ( symKeyId , topic , [ ] byte ( "baz" ) )
if err != nil {
errC <- err
}
} ( )
ctx , cancel = context . WithTimeout ( context . TODO ( ) , time . Second )
defer cancel ( )
select {
case <- delivered :
case err := <- errC :
t . Fatal ( err )
case <- ctx . Done ( ) :
t . Fatal ( "sym timeout" )
}
2019-01-16 09:54:01 +00:00
err = ps . SetPeerPublicKey ( & privKey . PublicKey , topic , proxAddrPss )
2019-01-14 10:31:28 +00:00
if err != nil {
t . Fatal ( err )
}
pubKeyId := hexutil . Encode ( crypto . FromECDSAPub ( & privKey . PublicKey ) )
go func ( ) {
err := ps . SendAsym ( pubKeyId , topic , [ ] byte ( "xyzzy" ) )
if err != nil {
errC <- err
}
} ( )
ctx , cancel = context . WithTimeout ( context . TODO ( ) , time . Second )
defer cancel ( )
select {
case <- delivered :
case err := <- errC :
t . Fatal ( err )
case <- ctx . Done ( ) :
t . Fatal ( "asym timeout" )
}
}
// verify that node can be set as recipient regardless of explicit message address match if minimum one handler of a topic is explicitly set to allow it
// note that in these tests we use the raw capability on handlers for convenience
func TestAddressMatchProx ( t * testing . T ) {
// recipient node address
localAddr := network . RandomAddr ( ) . Over ( )
localPotAddr := pot . NewAddressFromBytes ( localAddr )
// set up kademlia
kadparams := network . NewKadParams ( )
kad := network . NewKademlia ( localAddr , kadparams )
nnPeerCount := kad . MinBinSize
peerCount := nnPeerCount + 2
// set up pss
privKey , err := crypto . GenerateKey ( )
pssp := NewPssParams ( ) . WithPrivateKey ( privKey )
ps , err := NewPss ( kad , pssp )
if err != nil {
t . Fatal ( err . Error ( ) )
}
// create kademlia peers, so we have peers both inside and outside minproxlimit
var peers [ ] * network . Peer
for i := 0 ; i < peerCount ; i ++ {
rw := & p2p . MsgPipeRW { }
ptpPeer := p2p . NewPeer ( enode . ID { } , "362436 call me anytime" , [ ] p2p . Cap { } )
protoPeer := protocols . NewPeer ( ptpPeer , rw , & protocols . Spec { } )
peerAddr := pot . RandomAddressAt ( localPotAddr , i )
bzzPeer := & network . BzzPeer {
Peer : protoPeer ,
BzzAddr : & network . BzzAddr {
OAddr : peerAddr . Bytes ( ) ,
UAddr : [ ] byte ( fmt . Sprintf ( "%x" , peerAddr [ : ] ) ) ,
} ,
}
peer := network . NewPeer ( bzzPeer , kad )
kad . On ( peer )
peers = append ( peers , peer )
}
// TODO: create a test in the network package to make a table with n peers where n-m are proxpeers
// meanwhile test regression for kademlia since we are compiling the test parameters from different packages
var proxes int
var conns int
2019-01-16 09:54:01 +00:00
depth := kad . NeighbourhoodDepth ( )
kad . EachConn ( nil , peerCount , func ( p * network . Peer , po int ) bool {
2019-01-14 10:31:28 +00:00
conns ++
2019-01-16 09:54:01 +00:00
if po >= depth {
2019-01-14 10:31:28 +00:00
proxes ++
}
return true
} )
if proxes != nnPeerCount {
t . Fatalf ( "expected %d proxpeers, have %d" , nnPeerCount , proxes )
} else if conns != peerCount {
t . Fatalf ( "expected %d peers total, have %d" , peerCount , proxes )
}
// remote address distances from localAddr to try and the expected outcomes if we use prox handler
remoteDistances := [ ] int {
255 ,
nnPeerCount + 1 ,
nnPeerCount ,
nnPeerCount - 1 ,
0 ,
}
expects := [ ] bool {
true ,
true ,
true ,
false ,
false ,
}
// first the unit test on the method that calculates possible receipient using prox
for i , distance := range remoteDistances {
pssMsg := newPssMsg ( & msgParams { } )
pssMsg . To = make ( [ ] byte , len ( localAddr ) )
copy ( pssMsg . To , localAddr )
var byteIdx = distance / 8
pssMsg . To [ byteIdx ] ^ = 1 << uint ( 7 - ( distance % 8 ) )
log . Trace ( fmt . Sprintf ( "addrmatch %v" , bytes . Equal ( pssMsg . To , localAddr ) ) )
if ps . isSelfPossibleRecipient ( pssMsg , true ) != expects [ i ] {
t . Fatalf ( "expected distance %d to be %v" , distance , expects [ i ] )
}
}
// we move up to higher level and test the actual message handler
// for each distance check if we are possible recipient when prox variant is used is set
// this handler will increment a counter for every message that gets passed to the handler
var receives int
rawHandlerFunc := func ( msg [ ] byte , p * p2p . Peer , asymmetric bool , keyid string ) error {
log . Trace ( "in allowraw handler" )
receives ++
return nil
}
// register it marking prox capability
topic := BytesToTopic ( [ ] byte { 0x2a } )
hndlrProxDereg := ps . Register ( & topic , & handler {
f : rawHandlerFunc ,
caps : & handlerCaps {
raw : true ,
prox : true ,
} ,
} )
// test the distances
var prevReceive int
for i , distance := range remoteDistances {
remotePotAddr := pot . RandomAddressAt ( localPotAddr , distance )
remoteAddr := remotePotAddr . Bytes ( )
var data [ 32 ] byte
rand . Read ( data [ : ] )
pssMsg := newPssMsg ( & msgParams { raw : true } )
pssMsg . To = remoteAddr
pssMsg . Expire = uint32 ( time . Now ( ) . Unix ( ) + 4200 )
pssMsg . Payload = & whisper . Envelope {
Topic : whisper . TopicType ( topic ) ,
Data : data [ : ] ,
}
log . Trace ( "withprox addrs" , "local" , localAddr , "remote" , remoteAddr )
ps . handlePssMsg ( context . TODO ( ) , pssMsg )
if ( ! expects [ i ] && prevReceive != receives ) || ( expects [ i ] && prevReceive == receives ) {
t . Fatalf ( "expected distance %d recipient %v when prox is set for handler" , distance , expects [ i ] )
}
prevReceive = receives
}
// now add a non prox-capable handler and test
ps . Register ( & topic , & handler {
f : rawHandlerFunc ,
caps : & handlerCaps {
raw : true ,
} ,
} )
receives = 0
prevReceive = 0
for i , distance := range remoteDistances {
remotePotAddr := pot . RandomAddressAt ( localPotAddr , distance )
remoteAddr := remotePotAddr . Bytes ( )
var data [ 32 ] byte
rand . Read ( data [ : ] )
pssMsg := newPssMsg ( & msgParams { raw : true } )
pssMsg . To = remoteAddr
pssMsg . Expire = uint32 ( time . Now ( ) . Unix ( ) + 4200 )
pssMsg . Payload = & whisper . Envelope {
Topic : whisper . TopicType ( topic ) ,
Data : data [ : ] ,
}
log . Trace ( "withprox addrs" , "local" , localAddr , "remote" , remoteAddr )
ps . handlePssMsg ( context . TODO ( ) , pssMsg )
if ( ! expects [ i ] && prevReceive != receives ) || ( expects [ i ] && prevReceive == receives ) {
t . Fatalf ( "expected distance %d recipient %v when prox is set for handler" , distance , expects [ i ] )
}
prevReceive = receives
}
// now deregister the prox capable handler, now none of the messages will be handled
hndlrProxDereg ( )
receives = 0
for _ , distance := range remoteDistances {
remotePotAddr := pot . RandomAddressAt ( localPotAddr , distance )
remoteAddr := remotePotAddr . Bytes ( )
pssMsg := newPssMsg ( & msgParams { raw : true } )
pssMsg . To = remoteAddr
pssMsg . Expire = uint32 ( time . Now ( ) . Unix ( ) + 4200 )
pssMsg . Payload = & whisper . Envelope {
Topic : whisper . TopicType ( topic ) ,
Data : [ ] byte ( remotePotAddr . String ( ) ) ,
}
log . Trace ( "noprox addrs" , "local" , localAddr , "remote" , remoteAddr )
ps . handlePssMsg ( context . TODO ( ) , pssMsg )
if receives != 0 {
t . Fatalf ( "expected distance %d to not be recipient when prox is not set for handler" , distance )
}
}
}
// verify that message queueing happens when it should, and that expired and corrupt messages are dropped
func TestMessageProcessing ( t * testing . T ) {
2018-09-05 15:36:14 +00:00
t . Skip ( "Disabled due to probable faulty logic for outbox expectations" )
// setup
privkey , err := crypto . GenerateKey ( )
if err != nil {
t . Fatal ( err . Error ( ) )
}
addr := make ( [ ] byte , 32 )
addr [ 0 ] = 0x01
ps := newTestPss ( privkey , network . NewKademlia ( addr , network . NewKadParams ( ) ) , NewPssParams ( ) )
// message should pass
2019-01-14 10:31:28 +00:00
msg := newPssMsg ( & msgParams { } )
msg . To = addr
msg . Expire = uint32 ( time . Now ( ) . Add ( time . Second * 60 ) . Unix ( ) )
msg . Payload = & whisper . Envelope {
Topic : [ 4 ] byte { } ,
Data : [ ] byte { 0x66 , 0x6f , 0x6f } ,
2018-09-05 15:36:14 +00:00
}
if err := ps . handlePssMsg ( context . TODO ( ) , msg ) ; err != nil {
t . Fatal ( err . Error ( ) )
}
tmr := time . NewTimer ( time . Millisecond * 100 )
var outmsg * PssMsg
select {
case outmsg = <- ps . outbox :
case <- tmr . C :
default :
}
if outmsg != nil {
t . Fatalf ( "expected outbox empty after full address on msg, but had message %s" , msg )
}
// message should pass and queue due to partial length
msg . To = addr [ 0 : 1 ]
msg . Payload . Data = [ ] byte { 0x78 , 0x79 , 0x80 , 0x80 , 0x79 }
if err := ps . handlePssMsg ( context . TODO ( ) , msg ) ; err != nil {
t . Fatal ( err . Error ( ) )
}
tmr . Reset ( time . Millisecond * 100 )
outmsg = nil
select {
case outmsg = <- ps . outbox :
case <- tmr . C :
}
if outmsg == nil {
t . Fatal ( "expected message in outbox on encrypt fail, but empty" )
}
outmsg = nil
select {
case outmsg = <- ps . outbox :
default :
}
if outmsg != nil {
t . Fatalf ( "expected only one queued message but also had message %v" , msg )
}
// full address mismatch should put message in queue
msg . To [ 0 ] = 0xff
if err := ps . handlePssMsg ( context . TODO ( ) , msg ) ; err != nil {
t . Fatal ( err . Error ( ) )
}
tmr . Reset ( time . Millisecond * 10 )
outmsg = nil
select {
case outmsg = <- ps . outbox :
case <- tmr . C :
}
if outmsg == nil {
t . Fatal ( "expected message in outbox on address mismatch, but empty" )
}
outmsg = nil
select {
case outmsg = <- ps . outbox :
default :
}
if outmsg != nil {
t . Fatalf ( "expected only one queued message but also had message %v" , msg )
}
// expired message should be dropped
msg . Expire = uint32 ( time . Now ( ) . Add ( - time . Second ) . Unix ( ) )
if err := ps . handlePssMsg ( context . TODO ( ) , msg ) ; err != nil {
t . Fatal ( err . Error ( ) )
}
tmr . Reset ( time . Millisecond * 10 )
outmsg = nil
select {
case outmsg = <- ps . outbox :
case <- tmr . C :
default :
}
if outmsg != nil {
t . Fatalf ( "expected empty queue but have message %v" , msg )
}
// invalid message should return error
fckedupmsg := & struct {
pssMsg * PssMsg
} {
pssMsg : & PssMsg { } ,
}
if err := ps . handlePssMsg ( context . TODO ( ) , fckedupmsg ) ; err == nil {
t . Fatalf ( "expected error from processMsg but error nil" )
}
// outbox full should return error
msg . Expire = uint32 ( time . Now ( ) . Add ( time . Second * 60 ) . Unix ( ) )
for i := 0 ; i < defaultOutboxCapacity ; i ++ {
ps . outbox <- msg
}
msg . Payload . Data = [ ] byte { 0x62 , 0x61 , 0x72 }
err = ps . handlePssMsg ( context . TODO ( ) , msg )
if err == nil {
t . Fatal ( "expected error when mailbox full, but was nil" )
}
}
// 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 , cancel2 := context . WithTimeout ( context . Background ( ) , time . Second )
defer cancel2 ( )
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" ) )
2019-01-16 09:54:01 +00:00
ps . SetPeerPublicKey ( & theirprivkey . PublicKey , topicobj , addr )
outkeyid , err := ps . SetSymmetricKey ( outkey , topicobj , addr , false )
2018-09-05 15:36:14 +00:00
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
2019-01-16 09:54:01 +00:00
inkeyid , err := ps . GenerateSymmetricKey ( topicobj , addr , true )
2018-09-05 15:36:14 +00:00
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 ]
2019-01-16 09:54:01 +00:00
if ! bytes . Equal ( psp . address , addr ) {
t . Fatalf ( "inkey address does not match; %p != %p" , psp . address , addr )
2018-09-05 15:36:14 +00:00
}
}
2019-01-14 10:31:28 +00:00
// check that we can retrieve previously added public key entires per topic and peer
2018-09-05 15:36:14 +00:00
func TestGetPublickeyEntries ( t * testing . T ) {
privkey , err := crypto . GenerateKey ( )
if err != nil {
t . Fatal ( err )
}
ps := newTestPss ( privkey , nil , nil )
peeraddr := network . RandomAddr ( ) . Over ( )
topicaddr := make ( map [ Topic ] PssAddress )
topicaddr [ Topic { 0x13 } ] = peeraddr
topicaddr [ Topic { 0x2a } ] = peeraddr [ : 16 ]
topicaddr [ Topic { 0x02 , 0x9a } ] = [ ] byte { }
remoteprivkey , err := crypto . GenerateKey ( )
if err != nil {
t . Fatal ( err )
}
remotepubkeybytes := crypto . FromECDSAPub ( & remoteprivkey . PublicKey )
remotepubkeyhex := common . ToHex ( remotepubkeybytes )
pssapi := NewAPI ( ps )
for to , a := range topicaddr {
err = pssapi . SetPeerPublicKey ( remotepubkeybytes , to , a )
if err != nil {
t . Fatal ( err )
}
}
intopic , err := pssapi . GetPeerTopics ( remotepubkeyhex )
if err != nil {
t . Fatal ( err )
}
OUTER :
for _ , tnew := range intopic {
for torig , addr := range topicaddr {
if bytes . Equal ( torig [ : ] , tnew [ : ] ) {
inaddr , err := pssapi . GetPeerAddress ( remotepubkeyhex , torig )
if err != nil {
t . Fatal ( err )
}
if ! bytes . Equal ( addr , inaddr ) {
t . Fatalf ( "Address mismatch for topic %x; got %x, expected %x" , torig , inaddr , addr )
}
delete ( topicaddr , torig )
continue OUTER
}
}
t . Fatalf ( "received topic %x did not match any existing topics" , tnew )
}
if len ( topicaddr ) != 0 {
t . Fatalf ( "%d topics were not matched" , len ( topicaddr ) )
}
}
// forwarding should skip peers that do not have matching pss capabilities
2019-01-14 10:31:28 +00:00
func TestPeerCapabilityMismatch ( t * testing . T ) {
2018-09-05 15:36:14 +00:00
// create privkey for forwarder node
privkey , err := crypto . GenerateKey ( )
if err != nil {
t . Fatal ( err )
}
2018-11-21 15:30:00 +00:00
// initialize kad
2018-09-05 15:36:14 +00:00
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 ,
}
2018-11-21 15:30:00 +00:00
nid := enode . ID { 0x01 }
wrongpsspeer := network . NewPeer ( & network . BzzPeer {
Peer : protocols . NewPeer ( p2p . NewPeer ( nid , common . ToHex ( wrongpssaddr . Over ( ) ) , [ ] p2p . Cap { wrongpsscap } ) , rw , nil ) ,
BzzAddr : & network . BzzAddr { OAddr : wrongpssaddr . Over ( ) , UAddr : nil } ,
} , kad )
2018-09-05 15:36:14 +00:00
// one peer doesn't even have pss (boo!)
nopssaddr := network . RandomAddr ( )
nopsscap := p2p . Cap {
Name : "nopss" ,
Version : 1 ,
}
2018-11-21 15:30:00 +00:00
nid = enode . ID { 0x02 }
nopsspeer := network . NewPeer ( & network . BzzPeer {
Peer : protocols . NewPeer ( p2p . NewPeer ( nid , common . ToHex ( nopssaddr . Over ( ) ) , [ ] p2p . Cap { nopsscap } ) , rw , nil ) ,
BzzAddr : & network . BzzAddr { OAddr : nopssaddr . Over ( ) , UAddr : nil } ,
} , kad )
2018-09-05 15:36:14 +00:00
// add peers to kademlia and activate them
// it's safe so don't check errors
2018-11-21 15:30:00 +00:00
kad . Register ( wrongpsspeer . BzzAddr )
2018-09-05 15:36:14 +00:00
kad . On ( wrongpsspeer )
2018-11-21 15:30:00 +00:00
kad . Register ( nopsspeer . BzzAddr )
2018-09-05 15:36:14 +00:00
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 )
}
2019-01-14 10:31:28 +00:00
// verifies that message handlers for raw messages only are invoked when minimum one handler for the topic exists in which raw messages are explicitly allowed
func TestRawAllow ( t * testing . T ) {
// set up pss like so many times before
privKey , err := crypto . GenerateKey ( )
if err != nil {
t . Fatal ( err )
}
baseAddr := network . RandomAddr ( )
kad := network . NewKademlia ( ( baseAddr ) . Over ( ) , network . NewKadParams ( ) )
ps := newTestPss ( privKey , kad , nil )
topic := BytesToTopic ( [ ] byte { 0x2a } )
// create handler innards that increments every time a message hits it
var receives int
rawHandlerFunc := func ( msg [ ] byte , p * p2p . Peer , asymmetric bool , keyid string ) error {
log . Trace ( "in allowraw handler" )
receives ++
return nil
}
// wrap this handler function with a handler without raw capability and register it
hndlrNoRaw := & handler {
f : rawHandlerFunc ,
}
ps . Register ( & topic , hndlrNoRaw )
// test it with a raw message, should be poo-poo
pssMsg := newPssMsg ( & msgParams {
raw : true ,
} )
pssMsg . To = baseAddr . OAddr
pssMsg . Expire = uint32 ( time . Now ( ) . Unix ( ) + 4200 )
pssMsg . Payload = & whisper . Envelope {
Topic : whisper . TopicType ( topic ) ,
}
ps . handlePssMsg ( context . TODO ( ) , pssMsg )
if receives > 0 {
t . Fatalf ( "Expected handler not to be executed with raw cap off" )
}
// now wrap the same handler function with raw capabilities and register it
hndlrRaw := & handler {
f : rawHandlerFunc ,
caps : & handlerCaps {
raw : true ,
} ,
}
deregRawHandler := ps . Register ( & topic , hndlrRaw )
// should work now
pssMsg . Payload . Data = [ ] byte ( "Raw Deal" )
ps . handlePssMsg ( context . TODO ( ) , pssMsg )
if receives == 0 {
t . Fatalf ( "Expected handler to be executed with raw cap on" )
}
// now deregister the raw capable handler
prevReceives := receives
deregRawHandler ( )
// check that raw messages fail again
pssMsg . Payload . Data = [ ] byte ( "Raw Trump" )
ps . handlePssMsg ( context . TODO ( ) , pssMsg )
if receives != prevReceives {
t . Fatalf ( "Expected handler not to be executed when raw handler is retracted" )
}
}
2019-01-16 09:54:01 +00:00
// BELOW HERE ARE TESTS USING THE SIMULATION FRAMEWORK
// tests that the API layer can handle edge case values
func TestApi ( t * testing . T ) {
clients , err := setupNetwork ( 2 , true )
if err != nil {
t . Fatal ( err )
}
topic := "0xdeadbeef"
err = clients [ 0 ] . Call ( nil , "pss_sendRaw" , "0x" , topic , "0x666f6f" )
if err != nil {
t . Fatal ( err )
}
err = clients [ 0 ] . Call ( nil , "pss_sendRaw" , "0xabcdef" , topic , "0x" )
if err == nil {
t . Fatal ( "expected error on empty msg" )
}
overflowAddr := [ 33 ] byte { }
err = clients [ 0 ] . Call ( nil , "pss_sendRaw" , hexutil . Encode ( overflowAddr [ : ] ) , topic , "0x666f6f" )
if err == nil {
t . Fatal ( "expected error on send too big address" )
}
}
2019-01-14 10:31:28 +00:00
// verifies that nodes can send and receive raw (verbatim) messages
2018-09-05 15:36:14 +00:00
func TestSendRaw ( t * testing . T ) {
t . Run ( "32" , testSendRaw )
t . Run ( "8" , testSendRaw )
t . Run ( "0" , testSendRaw )
}
func testSendRaw ( t * testing . T ) {
var addrsize int64
var err error
paramstring := strings . Split ( t . Name ( ) , "/" )
addrsize , _ = strconv . ParseInt ( paramstring [ 1 ] , 10 , 0 )
log . Info ( "raw send test" , "addrsize" , addrsize )
clients , err := setupNetwork ( 2 , true )
if err != nil {
t . Fatal ( err )
}
topic := "0xdeadbeef"
var loaddrhex string
err = clients [ 0 ] . Call ( & loaddrhex , "pss_baseAddr" )
if err != nil {
t . Fatalf ( "rpc get node 1 baseaddr fail: %v" , err )
}
loaddrhex = loaddrhex [ : 2 + ( addrsize * 2 ) ]
var roaddrhex string
err = clients [ 1 ] . Call ( & roaddrhex , "pss_baseAddr" )
if err != nil {
t . Fatalf ( "rpc get node 2 baseaddr fail: %v" , err )
}
roaddrhex = roaddrhex [ : 2 + ( addrsize * 2 ) ]
time . Sleep ( time . Millisecond * 500 )
// at this point we've verified that symkeys are saved and match on each peer
// now try sending symmetrically encrypted message, both directions
lmsgC := make ( chan APIMsg )
lctx , lcancel := context . WithTimeout ( context . Background ( ) , time . Second * 10 )
defer lcancel ( )
2019-01-14 10:31:28 +00:00
lsub , err := clients [ 0 ] . Subscribe ( lctx , "pss" , lmsgC , "receive" , topic , true , false )
2018-09-05 15:36:14 +00:00
log . Trace ( "lsub" , "id" , lsub )
defer lsub . Unsubscribe ( )
rmsgC := make ( chan APIMsg )
rctx , rcancel := context . WithTimeout ( context . Background ( ) , time . Second * 10 )
defer rcancel ( )
2019-01-14 10:31:28 +00:00
rsub , err := clients [ 1 ] . Subscribe ( rctx , "pss" , rmsgC , "receive" , topic , true , false )
2018-09-05 15:36:14 +00:00
log . Trace ( "rsub" , "id" , rsub )
defer rsub . Unsubscribe ( )
// send and verify delivery
lmsg := [ ] byte ( "plugh" )
2019-01-14 10:31:28 +00:00
err = clients [ 1 ] . Call ( nil , "pss_sendRaw" , loaddrhex , topic , hexutil . Encode ( lmsg ) )
2018-09-05 15:36:14 +00:00
if err != nil {
t . Fatal ( err )
}
select {
case recvmsg := <- lmsgC :
if ! bytes . Equal ( recvmsg . Msg , lmsg ) {
t . Fatalf ( "node 1 received payload mismatch: expected %v, got %v" , lmsg , recvmsg )
}
case cerr := <- lctx . Done ( ) :
t . Fatalf ( "test message (left) timed out: %v" , cerr )
}
rmsg := [ ] byte ( "xyzzy" )
2019-01-14 10:31:28 +00:00
err = clients [ 0 ] . Call ( nil , "pss_sendRaw" , roaddrhex , topic , hexutil . Encode ( rmsg ) )
2018-09-05 15:36:14 +00:00
if err != nil {
t . Fatal ( err )
}
select {
case recvmsg := <- rmsgC :
if ! bytes . Equal ( recvmsg . Msg , rmsg ) {
t . Fatalf ( "node 2 received payload mismatch: expected %x, got %v" , rmsg , recvmsg . Msg )
}
case cerr := <- rctx . Done ( ) :
t . Fatalf ( "test message (right) timed out: %v" , cerr )
}
}
// send symmetrically encrypted message between two directly connected peers
func TestSendSym ( t * testing . T ) {
t . Run ( "32" , testSendSym )
t . Run ( "8" , testSendSym )
t . Run ( "0" , testSendSym )
}
func testSendSym ( t * testing . T ) {
// address hint size
var addrsize int64
var err error
paramstring := strings . Split ( t . Name ( ) , "/" )
addrsize , _ = strconv . ParseInt ( paramstring [ 1 ] , 10 , 0 )
log . Info ( "sym send test" , "addrsize" , addrsize )
clients , err := setupNetwork ( 2 , false )
if err != nil {
t . Fatal ( err )
}
var topic string
err = clients [ 0 ] . Call ( & topic , "pss_stringToTopic" , "foo:42" )
if err != nil {
t . Fatal ( err )
}
var loaddrhex string
err = clients [ 0 ] . Call ( & loaddrhex , "pss_baseAddr" )
if err != nil {
t . Fatalf ( "rpc get node 1 baseaddr fail: %v" , err )
}
loaddrhex = loaddrhex [ : 2 + ( addrsize * 2 ) ]
var roaddrhex string
err = clients [ 1 ] . Call ( & roaddrhex , "pss_baseAddr" )
if err != nil {
t . Fatalf ( "rpc get node 2 baseaddr fail: %v" , err )
}
roaddrhex = roaddrhex [ : 2 + ( addrsize * 2 ) ]
// retrieve public key from pss instance
// set this public key reciprocally
var lpubkeyhex string
err = clients [ 0 ] . Call ( & lpubkeyhex , "pss_getPublicKey" )
if err != nil {
t . Fatalf ( "rpc get node 1 pubkey fail: %v" , err )
}
var rpubkeyhex string
err = clients [ 1 ] . Call ( & rpubkeyhex , "pss_getPublicKey" )
if err != nil {
t . Fatalf ( "rpc get node 2 pubkey fail: %v" , err )
}
time . Sleep ( time . Millisecond * 500 )
// at this point we've verified that symkeys are saved and match on each peer
// now try sending symmetrically encrypted message, both directions
lmsgC := make ( chan APIMsg )
lctx , lcancel := context . WithTimeout ( context . Background ( ) , time . Second * 10 )
defer lcancel ( )
2019-01-14 10:31:28 +00:00
lsub , err := clients [ 0 ] . Subscribe ( lctx , "pss" , lmsgC , "receive" , topic , false , false )
2018-09-05 15:36:14 +00:00
log . Trace ( "lsub" , "id" , lsub )
defer lsub . Unsubscribe ( )
rmsgC := make ( chan APIMsg )
rctx , rcancel := context . WithTimeout ( context . Background ( ) , time . Second * 10 )
defer rcancel ( )
2019-01-14 10:31:28 +00:00
rsub , err := clients [ 1 ] . Subscribe ( rctx , "pss" , rmsgC , "receive" , topic , false , false )
2018-09-05 15:36:14 +00:00
log . Trace ( "rsub" , "id" , rsub )
defer rsub . Unsubscribe ( )
lrecvkey := network . RandomAddr ( ) . Over ( )
rrecvkey := network . RandomAddr ( ) . Over ( )
var lkeyids [ 2 ] string
var rkeyids [ 2 ] string
// manually set reciprocal symkeys
err = clients [ 0 ] . Call ( & lkeyids , "psstest_setSymKeys" , rpubkeyhex , lrecvkey , rrecvkey , defaultSymKeySendLimit , topic , roaddrhex )
if err != nil {
t . Fatal ( err )
}
err = clients [ 1 ] . Call ( & rkeyids , "psstest_setSymKeys" , lpubkeyhex , rrecvkey , lrecvkey , defaultSymKeySendLimit , topic , loaddrhex )
if err != nil {
t . Fatal ( err )
}
// send and verify delivery
lmsg := [ ] byte ( "plugh" )
err = clients [ 1 ] . Call ( nil , "pss_sendSym" , rkeyids [ 1 ] , topic , hexutil . Encode ( lmsg ) )
if err != nil {
t . Fatal ( err )
}
select {
case recvmsg := <- lmsgC :
if ! bytes . Equal ( recvmsg . Msg , lmsg ) {
t . Fatalf ( "node 1 received payload mismatch: expected %v, got %v" , lmsg , recvmsg )
}
case cerr := <- lctx . Done ( ) :
t . Fatalf ( "test message timed out: %v" , cerr )
}
rmsg := [ ] byte ( "xyzzy" )
err = clients [ 0 ] . Call ( nil , "pss_sendSym" , lkeyids [ 1 ] , topic , hexutil . Encode ( rmsg ) )
if err != nil {
t . Fatal ( err )
}
select {
case recvmsg := <- rmsgC :
if ! bytes . Equal ( recvmsg . Msg , rmsg ) {
t . Fatalf ( "node 2 received payload mismatch: expected %x, got %v" , rmsg , recvmsg . Msg )
}
case cerr := <- rctx . Done ( ) :
t . Fatalf ( "test message timed out: %v" , cerr )
}
}
// send asymmetrically encrypted message between two directly connected peers
func TestSendAsym ( t * testing . T ) {
t . Run ( "32" , testSendAsym )
t . Run ( "8" , testSendAsym )
t . Run ( "0" , testSendAsym )
}
func testSendAsym ( t * testing . T ) {
// address hint size
var addrsize int64
var err error
paramstring := strings . Split ( t . Name ( ) , "/" )
addrsize , _ = strconv . ParseInt ( paramstring [ 1 ] , 10 , 0 )
log . Info ( "asym send test" , "addrsize" , addrsize )
clients , err := setupNetwork ( 2 , false )
if err != nil {
t . Fatal ( err )
}
var topic string
err = clients [ 0 ] . Call ( & topic , "pss_stringToTopic" , "foo:42" )
if err != nil {
t . Fatal ( err )
}
time . Sleep ( time . Millisecond * 250 )
var loaddrhex string
err = clients [ 0 ] . Call ( & loaddrhex , "pss_baseAddr" )
if err != nil {
t . Fatalf ( "rpc get node 1 baseaddr fail: %v" , err )
}
loaddrhex = loaddrhex [ : 2 + ( addrsize * 2 ) ]
var roaddrhex string
err = clients [ 1 ] . Call ( & roaddrhex , "pss_baseAddr" )
if err != nil {
t . Fatalf ( "rpc get node 2 baseaddr fail: %v" , err )
}
roaddrhex = roaddrhex [ : 2 + ( addrsize * 2 ) ]
// retrieve public key from pss instance
// set this public key reciprocally
var lpubkey string
err = clients [ 0 ] . Call ( & lpubkey , "pss_getPublicKey" )
if err != nil {
t . Fatalf ( "rpc get node 1 pubkey fail: %v" , err )
}
var rpubkey string
err = clients [ 1 ] . Call ( & rpubkey , "pss_getPublicKey" )
if err != nil {
t . Fatalf ( "rpc get node 2 pubkey fail: %v" , err )
}
time . Sleep ( time . Millisecond * 500 ) // replace with hive healthy code
lmsgC := make ( chan APIMsg )
lctx , lcancel := context . WithTimeout ( context . Background ( ) , time . Second * 10 )
defer lcancel ( )
2019-01-14 10:31:28 +00:00
lsub , err := clients [ 0 ] . Subscribe ( lctx , "pss" , lmsgC , "receive" , topic , false , false )
2018-09-05 15:36:14 +00:00
log . Trace ( "lsub" , "id" , lsub )
defer lsub . Unsubscribe ( )
rmsgC := make ( chan APIMsg )
rctx , rcancel := context . WithTimeout ( context . Background ( ) , time . Second * 10 )
defer rcancel ( )
2019-01-14 10:31:28 +00:00
rsub , err := clients [ 1 ] . Subscribe ( rctx , "pss" , rmsgC , "receive" , topic , false , false )
2018-09-05 15:36:14 +00:00
log . Trace ( "rsub" , "id" , rsub )
defer rsub . Unsubscribe ( )
// store reciprocal public keys
err = clients [ 0 ] . Call ( nil , "pss_setPeerPublicKey" , rpubkey , topic , roaddrhex )
if err != nil {
t . Fatal ( err )
}
err = clients [ 1 ] . Call ( nil , "pss_setPeerPublicKey" , lpubkey , topic , loaddrhex )
if err != nil {
t . Fatal ( err )
}
// send and verify delivery
rmsg := [ ] byte ( "xyzzy" )
err = clients [ 0 ] . Call ( nil , "pss_sendAsym" , rpubkey , topic , hexutil . Encode ( rmsg ) )
if err != nil {
t . Fatal ( err )
}
select {
case recvmsg := <- rmsgC :
if ! bytes . Equal ( recvmsg . Msg , rmsg ) {
t . Fatalf ( "node 2 received payload mismatch: expected %v, got %v" , rmsg , recvmsg . Msg )
}
case cerr := <- rctx . Done ( ) :
t . Fatalf ( "test message timed out: %v" , cerr )
}
lmsg := [ ] byte ( "plugh" )
err = clients [ 1 ] . Call ( nil , "pss_sendAsym" , lpubkey , topic , hexutil . Encode ( lmsg ) )
if err != nil {
t . Fatal ( err )
}
select {
case recvmsg := <- lmsgC :
if ! bytes . Equal ( recvmsg . Msg , lmsg ) {
t . Fatalf ( "node 1 received payload mismatch: expected %v, got %v" , lmsg , recvmsg . Msg )
}
case cerr := <- lctx . Done ( ) :
t . Fatalf ( "test message timed out: %v" , cerr )
}
}
type Job struct {
Msg [ ] byte
2018-11-21 15:30:00 +00:00
SendNode enode . ID
RecvNode enode . ID
2018-09-05 15:36:14 +00:00
}
2018-11-21 15:30:00 +00:00
func worker ( id int , jobs <- chan Job , rpcs map [ enode . ID ] * rpc . Client , pubkeys map [ enode . ID ] string , topic string ) {
2018-09-05 15:36:14 +00:00
for j := range jobs {
rpcs [ j . SendNode ] . Call ( nil , "pss_sendAsym" , pubkeys [ j . RecvNode ] , topic , hexutil . Encode ( j . Msg ) )
}
}
func TestNetwork ( t * testing . T ) {
t . Run ( "16/1000/4/sim" , testNetwork )
}
// params in run name:
// nodes/msgs/addrbytes/adaptertype
// if adaptertype is exec uses execadapter, simadapter otherwise
func TestNetwork2000 ( t * testing . T ) {
//enableMetrics()
if ! * longrunning {
t . Skip ( "run with --longrunning flag to run extensive network tests" )
}
t . Run ( "3/2000/4/sim" , testNetwork )
t . Run ( "4/2000/4/sim" , testNetwork )
t . Run ( "8/2000/4/sim" , testNetwork )
t . Run ( "16/2000/4/sim" , testNetwork )
}
func TestNetwork5000 ( t * testing . T ) {
//enableMetrics()
if ! * longrunning {
t . Skip ( "run with --longrunning flag to run extensive network tests" )
}
t . Run ( "3/5000/4/sim" , testNetwork )
t . Run ( "4/5000/4/sim" , testNetwork )
t . Run ( "8/5000/4/sim" , testNetwork )
t . Run ( "16/5000/4/sim" , testNetwork )
}
func TestNetwork10000 ( t * testing . T ) {
//enableMetrics()
if ! * longrunning {
t . Skip ( "run with --longrunning flag to run extensive network tests" )
}
t . Run ( "3/10000/4/sim" , testNetwork )
t . Run ( "4/10000/4/sim" , testNetwork )
t . Run ( "8/10000/4/sim" , testNetwork )
}
func testNetwork ( t * testing . T ) {
paramstring := strings . Split ( t . Name ( ) , "/" )
nodecount , _ := strconv . ParseInt ( paramstring [ 1 ] , 10 , 0 )
msgcount , _ := strconv . ParseInt ( paramstring [ 2 ] , 10 , 0 )
addrsize , _ := strconv . ParseInt ( paramstring [ 3 ] , 10 , 0 )
adapter := paramstring [ 4 ]
log . Info ( "network test" , "nodecount" , nodecount , "msgcount" , msgcount , "addrhintsize" , addrsize )
2018-11-21 15:30:00 +00:00
nodes := make ( [ ] enode . ID , nodecount )
bzzaddrs := make ( map [ enode . ID ] string , nodecount )
rpcs := make ( map [ enode . ID ] * rpc . Client , nodecount )
pubkeys := make ( map [ enode . ID ] string , nodecount )
2018-09-05 15:36:14 +00:00
sentmsgs := make ( [ ] [ ] byte , msgcount )
recvmsgs := make ( [ ] bool , msgcount )
2018-11-21 15:30:00 +00:00
nodemsgcount := make ( map [ enode . ID ] int , nodecount )
2018-09-05 15:36:14 +00:00
2018-11-21 15:30:00 +00:00
trigger := make ( chan enode . ID )
2018-09-05 15:36:14 +00:00
var a adapters . NodeAdapter
if adapter == "exec" {
dirname , err := ioutil . TempDir ( "." , "" )
if err != nil {
t . Fatal ( err )
}
a = adapters . NewExecAdapter ( dirname )
} else if adapter == "tcp" {
a = adapters . NewTCPAdapter ( newServices ( false ) )
} else if adapter == "sim" {
a = adapters . NewSimAdapter ( newServices ( false ) )
}
net := simulations . NewNetwork ( a , & simulations . NetworkConfig {
ID : "0" ,
} )
defer net . Shutdown ( )
f , err := os . Open ( fmt . Sprintf ( "testdata/snapshot_%d.json" , nodecount ) )
if err != nil {
t . Fatal ( err )
}
jsonbyte , err := ioutil . ReadAll ( f )
if err != nil {
t . Fatal ( err )
}
var snap simulations . Snapshot
err = json . Unmarshal ( jsonbyte , & snap )
if err != nil {
t . Fatal ( err )
}
err = net . Load ( & snap )
if err != nil {
//TODO: Fix p2p simulation framework to not crash when loading 32-nodes
//t.Fatal(err)
}
time . Sleep ( 1 * time . Second )
2018-11-21 15:30:00 +00:00
triggerChecks := func ( trigger chan enode . ID , id enode . ID , rpcclient * rpc . Client , topic string ) error {
2018-09-05 15:36:14 +00:00
msgC := make ( chan APIMsg )
ctx , cancel := context . WithTimeout ( context . Background ( ) , 5 * time . Second )
defer cancel ( )
2019-01-14 10:31:28 +00:00
sub , err := rpcclient . Subscribe ( ctx , "pss" , msgC , "receive" , topic , false , false )
2018-09-05 15:36:14 +00:00
if err != nil {
t . Fatal ( err )
}
go func ( ) {
defer sub . Unsubscribe ( )
for {
select {
case recvmsg := <- msgC :
idx , _ := binary . Uvarint ( recvmsg . Msg )
if ! recvmsgs [ idx ] {
log . Debug ( "msg recv" , "idx" , idx , "id" , id )
recvmsgs [ idx ] = true
trigger <- id
}
case <- sub . Err ( ) :
return
}
}
} ( )
return nil
}
var topic string
for i , nod := range net . GetNodes ( ) {
nodes [ i ] = nod . ID ( )
rpcs [ nodes [ i ] ] , err = nod . Client ( )
if err != nil {
t . Fatal ( err )
}
if topic == "" {
err = rpcs [ nodes [ i ] ] . Call ( & topic , "pss_stringToTopic" , "foo:42" )
if err != nil {
t . Fatal ( err )
}
}
var pubkey string
err = rpcs [ nodes [ i ] ] . Call ( & pubkey , "pss_getPublicKey" )
if err != nil {
t . Fatal ( err )
}
pubkeys [ nod . ID ( ) ] = pubkey
var addrhex string
err = rpcs [ nodes [ i ] ] . Call ( & addrhex , "pss_baseAddr" )
if err != nil {
t . Fatal ( err )
}
bzzaddrs [ nodes [ i ] ] = addrhex
err = triggerChecks ( trigger , nodes [ i ] , rpcs [ nodes [ i ] ] , topic )
if err != nil {
t . Fatal ( err )
}
}
time . Sleep ( 1 * time . Second )
// setup workers
jobs := make ( chan Job , 10 )
for w := 1 ; w <= 10 ; w ++ {
go worker ( w , jobs , rpcs , pubkeys , topic )
}
time . Sleep ( 1 * time . Second )
for i := 0 ; i < int ( msgcount ) ; i ++ {
sendnodeidx := rand . Intn ( int ( nodecount ) )
recvnodeidx := rand . Intn ( int ( nodecount - 1 ) )
if recvnodeidx >= sendnodeidx {
recvnodeidx ++
}
nodemsgcount [ nodes [ recvnodeidx ] ] ++
sentmsgs [ i ] = make ( [ ] byte , 8 )
c := binary . PutUvarint ( sentmsgs [ i ] , uint64 ( i ) )
if c == 0 {
t . Fatal ( "0 byte message" )
}
if err != nil {
t . Fatal ( err )
}
err = rpcs [ nodes [ sendnodeidx ] ] . Call ( nil , "pss_setPeerPublicKey" , pubkeys [ nodes [ recvnodeidx ] ] , topic , bzzaddrs [ nodes [ recvnodeidx ] ] )
if err != nil {
t . Fatal ( err )
}
jobs <- Job {
Msg : sentmsgs [ i ] ,
SendNode : nodes [ sendnodeidx ] ,
RecvNode : nodes [ recvnodeidx ] ,
}
}
finalmsgcount := 0
ctx , cancel := context . WithTimeout ( context . Background ( ) , 120 * time . Second )
defer cancel ( )
outer :
for i := 0 ; i < int ( msgcount ) ; i ++ {
select {
case id := <- trigger :
nodemsgcount [ id ] --
finalmsgcount ++
case <- ctx . Done ( ) :
log . Warn ( "timeout" )
break outer
}
}
for i , msg := range recvmsgs {
if ! msg {
log . Debug ( "missing message" , "idx" , i )
}
}
t . Logf ( "%d of %d messages received" , finalmsgcount , msgcount )
if finalmsgcount != int ( msgcount ) {
t . Fatalf ( "%d messages were not received" , int ( msgcount ) - finalmsgcount )
}
}
// check that in a network of a -> b -> c -> a
// a doesn't receive a sent message twice
func TestDeduplication ( t * testing . T ) {
var err error
clients , err := setupNetwork ( 3 , false )
if err != nil {
t . Fatal ( err )
}
var addrsize = 32
var loaddrhex string
err = clients [ 0 ] . Call ( & loaddrhex , "pss_baseAddr" )
if err != nil {
t . Fatalf ( "rpc get node 1 baseaddr fail: %v" , err )
}
loaddrhex = loaddrhex [ : 2 + ( addrsize * 2 ) ]
var roaddrhex string
err = clients [ 1 ] . Call ( & roaddrhex , "pss_baseAddr" )
if err != nil {
t . Fatalf ( "rpc get node 2 baseaddr fail: %v" , err )
}
roaddrhex = roaddrhex [ : 2 + ( addrsize * 2 ) ]
var xoaddrhex string
err = clients [ 2 ] . Call ( & xoaddrhex , "pss_baseAddr" )
if err != nil {
t . Fatalf ( "rpc get node 3 baseaddr fail: %v" , err )
}
xoaddrhex = xoaddrhex [ : 2 + ( addrsize * 2 ) ]
log . Info ( "peer" , "l" , loaddrhex , "r" , roaddrhex , "x" , xoaddrhex )
var topic string
err = clients [ 0 ] . Call ( & topic , "pss_stringToTopic" , "foo:42" )
if err != nil {
t . Fatal ( err )
}
time . Sleep ( time . Millisecond * 250 )
// retrieve public key from pss instance
// set this public key reciprocally
var rpubkey string
err = clients [ 1 ] . Call ( & rpubkey , "pss_getPublicKey" )
if err != nil {
t . Fatalf ( "rpc get receivenode pubkey fail: %v" , err )
}
time . Sleep ( time . Millisecond * 500 ) // replace with hive healthy code
rmsgC := make ( chan APIMsg )
rctx , cancel := context . WithTimeout ( context . Background ( ) , time . Second * 1 )
defer cancel ( )
2019-01-14 10:31:28 +00:00
rsub , err := clients [ 1 ] . Subscribe ( rctx , "pss" , rmsgC , "receive" , topic , false , false )
2018-09-05 15:36:14 +00:00
log . Trace ( "rsub" , "id" , rsub )
defer rsub . Unsubscribe ( )
// store public key for recipient
// zero-length address means forward to all
// we have just two peers, they will be in proxbin, and will both receive
err = clients [ 0 ] . Call ( nil , "pss_setPeerPublicKey" , rpubkey , topic , "0x" )
if err != nil {
t . Fatal ( err )
}
// send and verify delivery
rmsg := [ ] byte ( "xyzzy" )
err = clients [ 0 ] . Call ( nil , "pss_sendAsym" , rpubkey , topic , hexutil . Encode ( rmsg ) )
if err != nil {
t . Fatal ( err )
}
var receivedok bool
OUTER :
for {
select {
case <- rmsgC :
if receivedok {
t . Fatalf ( "duplicate message received" )
}
receivedok = true
case <- rctx . Done ( ) :
break OUTER
}
}
if ! receivedok {
t . Fatalf ( "message did not arrive" )
}
}
// symmetric send performance with varying message sizes
func BenchmarkSymkeySend ( b * testing . B ) {
b . Run ( fmt . Sprintf ( "%d" , 256 ) , benchmarkSymKeySend )
b . Run ( fmt . Sprintf ( "%d" , 1024 ) , benchmarkSymKeySend )
b . Run ( fmt . Sprintf ( "%d" , 1024 * 1024 ) , benchmarkSymKeySend )
b . Run ( fmt . Sprintf ( "%d" , 1024 * 1024 * 10 ) , benchmarkSymKeySend )
b . Run ( fmt . Sprintf ( "%d" , 1024 * 1024 * 100 ) , benchmarkSymKeySend )
}
func benchmarkSymKeySend ( b * testing . B ) {
msgsizestring := strings . Split ( b . Name ( ) , "/" )
if len ( msgsizestring ) != 2 {
b . Fatalf ( "benchmark called without msgsize param" )
}
msgsize , err := strconv . ParseInt ( msgsizestring [ 1 ] , 10 , 0 )
if err != nil {
b . Fatalf ( "benchmark called with invalid msgsize param '%s': %v" , msgsizestring [ 1 ] , err )
}
ctx , cancel := context . WithTimeout ( context . Background ( ) , time . Second )
defer cancel ( )
keys , err := wapi . NewKeyPair ( ctx )
privkey , err := w . GetPrivateKey ( keys )
ps := newTestPss ( privkey , nil , nil )
msg := make ( [ ] byte , msgsize )
rand . Read ( msg )
topic := BytesToTopic ( [ ] byte ( "foo" ) )
to := make ( PssAddress , 32 )
copy ( to [ : ] , network . RandomAddr ( ) . Over ( ) )
2019-01-16 09:54:01 +00:00
symkeyid , err := ps . GenerateSymmetricKey ( topic , to , true )
2018-09-05 15:36:14 +00:00
if err != nil {
b . Fatalf ( "could not generate symkey: %v" , err )
}
symkey , err := ps . w . GetSymKey ( symkeyid )
if err != nil {
b . Fatalf ( "could not retrieve symkey: %v" , err )
}
2019-01-16 09:54:01 +00:00
ps . SetSymmetricKey ( symkey , topic , to , false )
2018-09-05 15:36:14 +00:00
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
ps . SendSym ( symkeyid , topic , msg )
}
}
// asymmetric send performance with varying message sizes
func BenchmarkAsymkeySend ( b * testing . B ) {
b . Run ( fmt . Sprintf ( "%d" , 256 ) , benchmarkAsymKeySend )
b . Run ( fmt . Sprintf ( "%d" , 1024 ) , benchmarkAsymKeySend )
b . Run ( fmt . Sprintf ( "%d" , 1024 * 1024 ) , benchmarkAsymKeySend )
b . Run ( fmt . Sprintf ( "%d" , 1024 * 1024 * 10 ) , benchmarkAsymKeySend )
b . Run ( fmt . Sprintf ( "%d" , 1024 * 1024 * 100 ) , benchmarkAsymKeySend )
}
func benchmarkAsymKeySend ( b * testing . B ) {
msgsizestring := strings . Split ( b . Name ( ) , "/" )
if len ( msgsizestring ) != 2 {
b . Fatalf ( "benchmark called without msgsize param" )
}
msgsize , err := strconv . ParseInt ( msgsizestring [ 1 ] , 10 , 0 )
if err != nil {
b . Fatalf ( "benchmark called with invalid msgsize param '%s': %v" , msgsizestring [ 1 ] , err )
}
ctx , cancel := context . WithTimeout ( context . Background ( ) , time . Second )
defer cancel ( )
keys , err := wapi . NewKeyPair ( ctx )
privkey , err := w . GetPrivateKey ( keys )
ps := newTestPss ( privkey , nil , nil )
msg := make ( [ ] byte , msgsize )
rand . Read ( msg )
topic := BytesToTopic ( [ ] byte ( "foo" ) )
to := make ( PssAddress , 32 )
copy ( to [ : ] , network . RandomAddr ( ) . Over ( ) )
2019-01-16 09:54:01 +00:00
ps . SetPeerPublicKey ( & privkey . PublicKey , topic , to )
2018-09-05 15:36:14 +00:00
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
ps . SendAsym ( common . ToHex ( crypto . FromECDSAPub ( & privkey . PublicKey ) ) , topic , msg )
}
}
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 ( ) )
2019-01-16 09:54:01 +00:00
keyid , err = ps . GenerateSymmetricKey ( topic , to , true )
2018-09-05 15:36:14 +00:00
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 )
}
2019-01-14 10:31:28 +00:00
ps . Register ( & topic , & handler {
f : noopHandlerFunc ,
2018-09-05 15:36:14 +00:00
} )
pssmsgs = append ( pssmsgs , & PssMsg {
To : to ,
Payload : env ,
} )
}
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
2019-01-14 10:31:28 +00:00
if err := ps . process ( pssmsgs [ len ( pssmsgs ) - ( i % len ( pssmsgs ) ) - 1 ] , false , false ) ; err != nil {
2018-09-05 15:36:14 +00:00
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 ( ) )
2019-01-16 09:54:01 +00:00
keyid , err = ps . GenerateSymmetricKey ( topic , addr [ i ] , true )
2018-09-05 15:36:14 +00:00
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 )
}
2019-01-14 10:31:28 +00:00
ps . Register ( & topic , & handler {
f : noopHandlerFunc ,
2018-09-05 15:36:14 +00:00
} )
pssmsg := & PssMsg {
To : addr [ len ( addr ) - 1 ] [ : ] ,
Payload : env ,
}
for i := 0 ; i < b . N ; i ++ {
2019-01-14 10:31:28 +00:00
if err := ps . process ( pssmsg , false , false ) ; err != nil {
2018-09-05 15:36:14 +00:00
b . Fatalf ( "pss processing failed: %v" , err )
}
}
}
// setup simulated network with bzz/discovery and pss services.
// connects nodes in a circle
// if allowRaw is set, omission of builtin pss encryption is enabled (see PssParams)
func setupNetwork ( numnodes int , allowRaw bool ) ( 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 ( newServices ( allowRaw ) )
net := simulations . NewNetwork ( adapter , & simulations . NetworkConfig {
ID : "0" ,
DefaultService : "bzz" ,
} )
for i := 0 ; i < numnodes ; i ++ {
nodeconf := adapters . RandomNodeConfig ( )
nodeconf . Services = [ ] string { "bzz" , pssProtocolName }
nodes [ i ] , err = net . NewNodeWithConfig ( nodeconf )
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 ( allowRaw bool ) adapters . Services {
stateStore := state . NewInmemoryStore ( )
2018-11-21 15:30:00 +00:00
kademlias := make ( map [ enode . ID ] * network . Kademlia )
kademlia := func ( id enode . ID ) * network . Kademlia {
2018-09-05 15:36:14 +00:00
if k , ok := kademlias [ id ] ; ok {
return k
}
params := network . NewKadParams ( )
2019-01-16 09:54:01 +00:00
params . NeighbourhoodSize = 2
2018-09-05 15:36:14 +00:00
params . MaxBinSize = 3
params . MinBinSize = 1
params . MaxRetries = 1000
params . RetryExponent = 2
params . RetryInterval = 1000000
2018-11-21 15:30:00 +00:00
kademlias [ id ] = network . NewKademlia ( id [ : ] , params )
2018-09-05 15:36:14 +00:00
return kademlias [ id ]
}
return adapters . Services {
pssProtocolName : func ( ctx * adapters . ServiceContext ) ( node . Service , error ) {
// 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 ( ) . WithPrivateKey ( privkey )
pssp . AllowRaw = allowRaw
pskad := kademlia ( ctx . Config . ID )
ps , err := NewPss ( pskad , pssp )
if err != nil {
return nil , err
}
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 ( ) )
}
2019-01-14 10:31:28 +00:00
ps . Register ( & PingTopic , & handler {
f : pp . Handle ,
caps : & handlerCaps {
raw : true ,
} ,
} )
2018-09-05 15:36:14 +00:00
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 ) {
2018-11-21 15:30:00 +00:00
addr := network . NewAddr ( ctx . Config . Node ( ) )
2018-09-05 15:36:14 +00:00
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 , nil ) , nil
} ,
}
}
2018-11-21 15:30:00 +00:00
func newTestPss ( privkey * ecdsa . PrivateKey , kad * network . Kademlia , ppextra * PssParams ) * Pss {
nid := enode . PubkeyToIDV4 ( & privkey . PublicKey )
2018-09-05 15:36:14 +00:00
// set up routing if kademlia is not passed to us
2018-11-21 15:30:00 +00:00
if kad == nil {
2018-09-05 15:36:14 +00:00
kp := network . NewKadParams ( )
2019-01-16 09:54:01 +00:00
kp . NeighbourhoodSize = 3
2018-11-21 15:30:00 +00:00
kad = network . NewKademlia ( nid [ : ] , kp )
2018-09-05 15:36:14 +00:00
}
// create pss
pp := NewPssParams ( ) . WithPrivateKey ( privkey )
if ppextra != nil {
pp . SymKeyCacheCapacity = ppextra . SymKeyCacheCapacity
}
2018-11-21 15:30:00 +00:00
ps , err := NewPss ( kad , pp )
2018-09-05 15:36:14 +00:00
if err != nil {
return nil
}
ps . Start ( nil )
return ps
}
// API calls for test/development use
type APITest struct {
* Pss
}
func NewAPITest ( ps * Pss ) * APITest {
return & APITest { Pss : ps }
}
2019-01-16 09:54:01 +00:00
func ( apitest * APITest ) SetSymKeys ( pubkeyid string , recvsymkey [ ] byte , sendsymkey [ ] byte , limit uint16 , topic Topic , to hexutil . Bytes ) ( [ 2 ] string , error ) {
recvsymkeyid , err := apitest . SetSymmetricKey ( recvsymkey , topic , PssAddress ( to ) , true )
2018-09-05 15:36:14 +00:00
if err != nil {
return [ 2 ] string { } , err
}
2019-01-16 09:54:01 +00:00
sendsymkeyid , err := apitest . SetSymmetricKey ( sendsymkey , topic , PssAddress ( to ) , false )
2018-09-05 15:36:14 +00:00
if err != nil {
return [ 2 ] string { } , err
}
return [ 2 ] string { recvsymkeyid , sendsymkeyid } , nil
}
func ( apitest * APITest ) Clean ( ) ( int , error ) {
return apitest . Pss . cleanKeys ( ) , nil
}
// enableMetrics is starting InfluxDB reporter so that we collect stats when running tests locally
func enableMetrics ( ) {
metrics . Enabled = true
go influxdb . InfluxDBWithTags ( metrics . DefaultRegistry , 1 * time . Second , "http://localhost:8086" , "metrics" , "admin" , "admin" , "swarm." , map [ string ] string {
"host" : "test" ,
} )
}