mirror of https://github.com/status-im/op-geth.git
whisper: big refactoring (#13852)
* whisper: GetMessages fixed; size restriction updated * whisper: made PoW and MaxMsgSize customizable * whisper: test added * whisper: sym key management changed * whisper: identity management refactored * whisper: API refactoring (Post and Filter) * whisper: big refactoring complete * whisper: spelling fix * whisper: variable topic size allowed for a filter * whisper: final update * whisper: formatting * whisper: file exchange introduced in wnode * whisper: bugfix * whisper: API updated + new tests * whisper: statistics updated * whisper: wnode server updated * whisper: allowed filtering for variable topic size * whisper: tests added * whisper: resolving merge conflicts * whisper: refactoring (documenting mostly) * whsiper: tests fixed * whisper: down cased error messages * whisper: documenting the API functions * whisper: logging fixed * whisper: fixed wnode parameters * whisper: logs fixed (typos)
This commit is contained in:
parent
8570ef19eb
commit
9cd7135516
|
@ -27,7 +27,9 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -46,7 +48,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const quitCommand = "~Q"
|
const quitCommand = "~Q"
|
||||||
const symKeyName = "da919ea33001b04dfc630522e33078ec0df11"
|
|
||||||
|
|
||||||
// singletons
|
// singletons
|
||||||
var (
|
var (
|
||||||
|
@ -64,7 +65,8 @@ var (
|
||||||
pub *ecdsa.PublicKey
|
pub *ecdsa.PublicKey
|
||||||
asymKey *ecdsa.PrivateKey
|
asymKey *ecdsa.PrivateKey
|
||||||
nodeid *ecdsa.PrivateKey
|
nodeid *ecdsa.PrivateKey
|
||||||
topic whisper.TopicType
|
topic []byte
|
||||||
|
asymKeyID string
|
||||||
filterID string
|
filterID string
|
||||||
symPass string
|
symPass string
|
||||||
msPassword string
|
msPassword string
|
||||||
|
@ -72,27 +74,30 @@ var (
|
||||||
|
|
||||||
// cmd arguments
|
// cmd arguments
|
||||||
var (
|
var (
|
||||||
echoMode = flag.Bool("e", false, "echo mode: prints some arguments for diagnostics")
|
bootstrapMode = flag.Bool("standalone", false, "boostrap node: don't actively connect to peers, wait for incoming connections")
|
||||||
bootstrapMode = flag.Bool("b", false, "boostrap node: don't actively connect to peers, wait for incoming connections")
|
forwarderMode = flag.Bool("forwarder", false, "forwarder mode: only forward messages, neither send nor decrypt messages")
|
||||||
forwarderMode = flag.Bool("f", false, "forwarder mode: only forward messages, neither send nor decrypt messages")
|
mailServerMode = flag.Bool("mailserver", false, "mail server mode: delivers expired messages on demand")
|
||||||
mailServerMode = flag.Bool("s", false, "mail server mode: delivers expired messages on demand")
|
requestMail = flag.Bool("mailclient", false, "request expired messages from the bootstrap server")
|
||||||
requestMail = flag.Bool("r", false, "request expired messages from the bootstrap server")
|
asymmetricMode = flag.Bool("asym", false, "use asymmetric encryption")
|
||||||
asymmetricMode = flag.Bool("a", false, "use asymmetric encryption")
|
generateKey = flag.Bool("generatekey", false, "generate and show the private key")
|
||||||
testMode = flag.Bool("t", false, "use of predefined parameters for diagnostics")
|
fileExMode = flag.Bool("fileexchange", false, "file exchange mode")
|
||||||
generateKey = flag.Bool("k", false, "generate and show the private key")
|
testMode = flag.Bool("test", false, "use of predefined parameters for diagnostics")
|
||||||
|
echoMode = flag.Bool("echo", false, "echo mode: prints some arguments for diagnostics")
|
||||||
|
|
||||||
argVerbosity = flag.Int("verbosity", int(log.LvlWarn), "log verbosity level")
|
argVerbosity = flag.Int("verbosity", int(log.LvlWarn), "log verbosity level")
|
||||||
argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds")
|
argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds")
|
||||||
argWorkTime = flag.Uint("work", 5, "work time in seconds")
|
argWorkTime = flag.Uint("work", 5, "work time in seconds")
|
||||||
argPoW = flag.Float64("pow", whisper.MinimumPoW, "PoW for normal messages in float format (e.g. 2.7)")
|
argMaxSize = flag.Int("maxsize", whisper.DefaultMaxMessageLength, "max size of message")
|
||||||
argServerPoW = flag.Float64("mspow", whisper.MinimumPoW, "PoW requirement for Mail Server request")
|
argPoW = flag.Float64("pow", whisper.DefaultMinimumPoW, "PoW for normal messages in float format (e.g. 2.7)")
|
||||||
|
argServerPoW = flag.Float64("mspow", whisper.DefaultMinimumPoW, "PoW requirement for Mail Server request")
|
||||||
|
|
||||||
argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)")
|
argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)")
|
||||||
argPub = flag.String("pub", "", "public key for asymmetric encryption")
|
argPub = flag.String("pub", "", "public key for asymmetric encryption")
|
||||||
argDBPath = flag.String("dbpath", "", "path to the server's DB directory")
|
argDBPath = flag.String("dbpath", "", "path to the server's DB directory")
|
||||||
argIDFile = flag.String("idfile", "", "file name with node id (private key)")
|
argIDFile = flag.String("idfile", "", "file name with node id (private key)")
|
||||||
argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)")
|
argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)")
|
||||||
argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)")
|
argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)")
|
||||||
|
argSaveDir = flag.String("savedir", "", "directory where incoming messages will be saved as files")
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -124,7 +129,7 @@ func processArgs() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to parse the topic: %s", err)
|
utils.Fatalf("Failed to parse the topic: %s", err)
|
||||||
}
|
}
|
||||||
topic = whisper.BytesToTopic(x)
|
topic = x
|
||||||
}
|
}
|
||||||
|
|
||||||
if *asymmetricMode && len(*argPub) > 0 {
|
if *asymmetricMode && len(*argPub) > 0 {
|
||||||
|
@ -134,6 +139,14 @@ func processArgs() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(*argSaveDir) > 0 {
|
||||||
|
if _, err := os.Stat(*argSaveDir); os.IsNotExist(err) {
|
||||||
|
utils.Fatalf("Download directory '%s' does not exist", *argSaveDir)
|
||||||
|
}
|
||||||
|
} else if *fileExMode {
|
||||||
|
utils.Fatalf("Parameter 'savedir' is mandatory for file exchange mode")
|
||||||
|
}
|
||||||
|
|
||||||
if *echoMode {
|
if *echoMode {
|
||||||
echo()
|
echo()
|
||||||
}
|
}
|
||||||
|
@ -199,9 +212,40 @@ func initialize() {
|
||||||
shh = whisper.New()
|
shh = whisper.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
asymKey = shh.NewIdentity()
|
if *argPoW != whisper.DefaultMinimumPoW {
|
||||||
|
err := shh.SetMinimumPoW(*argPoW)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to set PoW: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *argMaxSize != whisper.DefaultMaxMessageLength {
|
||||||
|
err := shh.SetMaxMessageLength(*argMaxSize)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to set max message size: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
asymKeyID, err = shh.NewKeyPair()
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to generate a new key pair: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
asymKey, err = shh.GetPrivateKey(asymKeyID)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to retrieve a new key pair: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
if nodeid == nil {
|
if nodeid == nil {
|
||||||
nodeid = shh.NewIdentity()
|
tmpID, err := shh.NewKeyPair()
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to generate a new key pair: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeid, err = shh.GetPrivateKey(tmpID)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to retrieve a new key pair: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
maxPeers := 80
|
maxPeers := 80
|
||||||
|
@ -213,7 +257,8 @@ func initialize() {
|
||||||
Config: p2p.Config{
|
Config: p2p.Config{
|
||||||
PrivateKey: nodeid,
|
PrivateKey: nodeid,
|
||||||
MaxPeers: maxPeers,
|
MaxPeers: maxPeers,
|
||||||
Name: common.MakeName("whisper-go", "5.0"),
|
Discovery: true,
|
||||||
|
Name: common.MakeName("wnode", "5.0"),
|
||||||
Protocols: shh.Protocols(),
|
Protocols: shh.Protocols(),
|
||||||
ListenAddr: *argIP,
|
ListenAddr: *argIP,
|
||||||
NAT: nat.Any(),
|
NAT: nat.Any(),
|
||||||
|
@ -288,8 +333,14 @@ func configureNode() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shh.AddSymKey(symKeyName, []byte(symPass))
|
symKeyID, err := shh.AddSymKeyFromPassword(symPass)
|
||||||
symKey = shh.GetSymKey(symKeyName)
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to create symmetric key: %s", err)
|
||||||
|
}
|
||||||
|
symKey, err = shh.GetSymKey(symKeyID)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to save symmetric key: %s", err)
|
||||||
|
}
|
||||||
if len(*argTopic) == 0 {
|
if len(*argTopic) == 0 {
|
||||||
generateTopic([]byte(symPass))
|
generateTopic([]byte(symPass))
|
||||||
}
|
}
|
||||||
|
@ -302,12 +353,12 @@ func configureNode() {
|
||||||
}
|
}
|
||||||
|
|
||||||
filter := whisper.Filter{
|
filter := whisper.Filter{
|
||||||
KeySym: symKey,
|
KeySym: symKey,
|
||||||
KeyAsym: asymKey,
|
KeyAsym: asymKey,
|
||||||
Topics: []whisper.TopicType{topic},
|
Topics: [][]byte{topic},
|
||||||
AcceptP2P: p2pAccept,
|
AllowP2P: p2pAccept,
|
||||||
}
|
}
|
||||||
filterID, err = shh.Watch(&filter)
|
filterID, err = shh.Subscribe(&filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to install filter: %s", err)
|
utils.Fatalf("Failed to install filter: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -351,6 +402,8 @@ func run() {
|
||||||
|
|
||||||
if *requestMail {
|
if *requestMail {
|
||||||
requestExpiredMessagesLoop()
|
requestExpiredMessagesLoop()
|
||||||
|
} else if *fileExMode {
|
||||||
|
sendFilesLoop()
|
||||||
} else {
|
} else {
|
||||||
sendLoop()
|
sendLoop()
|
||||||
}
|
}
|
||||||
|
@ -376,6 +429,31 @@ func sendLoop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sendFilesLoop() {
|
||||||
|
for {
|
||||||
|
s := scanLine("")
|
||||||
|
if s == quitCommand {
|
||||||
|
fmt.Println("Quit command received")
|
||||||
|
close(done)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
b, err := ioutil.ReadFile(s)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(">>> Error: %s \n", err)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
h := sendMsg(b)
|
||||||
|
if (h == common.Hash{}) {
|
||||||
|
fmt.Printf(">>> Error: message was not sent \n")
|
||||||
|
} else {
|
||||||
|
timestamp := time.Now().Unix()
|
||||||
|
from := crypto.PubkeyToAddress(asymKey.PublicKey)
|
||||||
|
fmt.Printf("\n%d <%x>: sent message with hash %x\n", timestamp, from, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func scanLine(prompt string) string {
|
func scanLine(prompt string) string {
|
||||||
if len(prompt) > 0 {
|
if len(prompt) > 0 {
|
||||||
fmt.Print(prompt)
|
fmt.Print(prompt)
|
||||||
|
@ -402,29 +480,36 @@ func scanUint(prompt string) uint32 {
|
||||||
return uint32(i)
|
return uint32(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendMsg(payload []byte) {
|
func sendMsg(payload []byte) common.Hash {
|
||||||
params := whisper.MessageParams{
|
params := whisper.MessageParams{
|
||||||
Src: asymKey,
|
Src: asymKey,
|
||||||
Dst: pub,
|
Dst: pub,
|
||||||
KeySym: symKey,
|
KeySym: symKey,
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
Topic: topic,
|
Topic: whisper.BytesToTopic(topic),
|
||||||
TTL: uint32(*argTTL),
|
TTL: uint32(*argTTL),
|
||||||
PoW: *argPoW,
|
PoW: *argPoW,
|
||||||
WorkTime: uint32(*argWorkTime),
|
WorkTime: uint32(*argWorkTime),
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := whisper.NewSentMessage(¶ms)
|
msg := whisper.NewSentMessage(¶ms)
|
||||||
|
if msg == nil {
|
||||||
|
fmt.Printf("failed to create new message (OS level error)")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
envelope, err := msg.Wrap(¶ms)
|
envelope, err := msg.Wrap(¶ms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("failed to seal message: %v \n", err)
|
fmt.Printf("failed to seal message: %v \n", err)
|
||||||
return
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = shh.Send(envelope)
|
err = shh.Send(envelope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("failed to send message: %v \n", err)
|
fmt.Printf("failed to send message: %v \n", err)
|
||||||
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return envelope.Hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
func messageLoop() {
|
func messageLoop() {
|
||||||
|
@ -440,7 +525,11 @@ func messageLoop() {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
messages := f.Retrieve()
|
messages := f.Retrieve()
|
||||||
for _, msg := range messages {
|
for _, msg := range messages {
|
||||||
printMessageInfo(msg)
|
if *fileExMode || len(msg.Payload) > 2048 {
|
||||||
|
writeMessageToFile(*argSaveDir, msg)
|
||||||
|
} else {
|
||||||
|
printMessageInfo(msg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case <-done:
|
case <-done:
|
||||||
return
|
return
|
||||||
|
@ -464,19 +553,47 @@ func printMessageInfo(msg *whisper.ReceivedMessage) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeMessageToFile(dir string, msg *whisper.ReceivedMessage) {
|
||||||
|
timestamp := fmt.Sprintf("%d", msg.Sent)
|
||||||
|
name := fmt.Sprintf("%x", msg.EnvelopeHash)
|
||||||
|
|
||||||
|
var address common.Address
|
||||||
|
if msg.Src != nil {
|
||||||
|
address = crypto.PubkeyToAddress(*msg.Src)
|
||||||
|
}
|
||||||
|
|
||||||
|
if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) {
|
||||||
|
// message from myself: don't save, only report
|
||||||
|
fmt.Printf("\n%s <%x>: message received: '%s'\n", timestamp, address, name)
|
||||||
|
} else if len(dir) > 0 {
|
||||||
|
fullpath := filepath.Join(dir, name)
|
||||||
|
err := ioutil.WriteFile(fullpath, msg.Payload, 0644)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("\n%s {%x}: message received and saved as '%s' (%d bytes)\n", timestamp, address, name, len(msg.Payload))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("\n%s {%x}: big message received (%d bytes), but not saved: %s\n", timestamp, address, len(msg.Payload), name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func requestExpiredMessagesLoop() {
|
func requestExpiredMessagesLoop() {
|
||||||
var key, peerID []byte
|
var key, peerID []byte
|
||||||
var timeLow, timeUpp uint32
|
var timeLow, timeUpp uint32
|
||||||
var t string
|
var t string
|
||||||
var xt, empty whisper.TopicType
|
var xt, empty whisper.TopicType
|
||||||
|
|
||||||
err := shh.AddSymKey(mailserver.MailServerKeyName, []byte(msPassword))
|
keyID, err := shh.AddSymKeyFromPassword(msPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to create symmetric key for mail request: %s", err)
|
utils.Fatalf("Failed to create symmetric key for mail request: %s", err)
|
||||||
}
|
}
|
||||||
key = shh.GetSymKey(mailserver.MailServerKeyName)
|
key, err = shh.GetSymKey(keyID)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to save symmetric key for mail request: %s", err)
|
||||||
|
}
|
||||||
peerID = extractIdFromEnode(*argEnode)
|
peerID = extractIdFromEnode(*argEnode)
|
||||||
shh.MarkPeerTrusted(peerID)
|
shh.AllowP2PMessagesFromPeer(peerID)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ")
|
timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ")
|
||||||
|
@ -509,6 +626,9 @@ func requestExpiredMessagesLoop() {
|
||||||
params.WorkTime = 5
|
params.WorkTime = 5
|
||||||
|
|
||||||
msg := whisper.NewSentMessage(¶ms)
|
msg := whisper.NewSentMessage(¶ms)
|
||||||
|
if msg == nil {
|
||||||
|
utils.Fatalf("failed to create new message (OS level error)")
|
||||||
|
}
|
||||||
env, err := msg.Wrap(¶ms)
|
env, err := msg.Wrap(¶ms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Wrap failed: %s", err)
|
utils.Fatalf("Wrap failed: %s", err)
|
||||||
|
@ -527,7 +647,6 @@ func extractIdFromEnode(s string) []byte {
|
||||||
n, err := discover.ParseNode(s)
|
n, err := discover.ParseNode(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to parse enode: %s", err)
|
utils.Fatalf("Failed to parse enode: %s", err)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return n.ID[:]
|
return n.ID[:]
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,6 @@ import (
|
||||||
"github.com/syndtr/goleveldb/leveldb/util"
|
"github.com/syndtr/goleveldb/leveldb/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const MailServerKeyName = "958e04ab302fb36ad2616a352cbac79d"
|
|
||||||
|
|
||||||
type WMailServer struct {
|
type WMailServer struct {
|
||||||
db *leveldb.DB
|
db *leveldb.DB
|
||||||
w *whisper.Whisper
|
w *whisper.Whisper
|
||||||
|
@ -75,11 +73,14 @@ func (s *WMailServer) Init(shh *whisper.Whisper, path string, password string, p
|
||||||
s.w = shh
|
s.w = shh
|
||||||
s.pow = pow
|
s.pow = pow
|
||||||
|
|
||||||
err = s.w.AddSymKey(MailServerKeyName, []byte(password))
|
MailServerKeyID, err := s.w.AddSymKeyFromPassword(password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to create symmetric key for MailServer: %s", err)
|
utils.Fatalf("Failed to create symmetric key for MailServer: %s", err)
|
||||||
}
|
}
|
||||||
s.key = s.w.GetSymKey(MailServerKeyName)
|
s.key, err = s.w.GetSymKey(MailServerKeyID)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to save symmetric key for MailServer")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WMailServer) Close() {
|
func (s *WMailServer) Close() {
|
||||||
|
|
|
@ -30,8 +30,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const powRequirement = 0.00001
|
const powRequirement = 0.00001
|
||||||
const keyName = "6d604bac5401ce9a6b995f1b45a4ab"
|
|
||||||
|
|
||||||
|
var keyID string
|
||||||
var shh *whisper.Whisper
|
var shh *whisper.Whisper
|
||||||
var seed = time.Now().Unix()
|
var seed = time.Now().Unix()
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ func TestMailServer(t *testing.T) {
|
||||||
server.Init(shh, dir, password, powRequirement)
|
server.Init(shh, dir, password, powRequirement)
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
err = shh.AddSymKey(keyName, []byte(password))
|
keyID, err = shh.AddSymKeyFromPassword(password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create symmetric key for mail request: %s", err)
|
t.Fatalf("Failed to create symmetric key for mail request: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,14 @@ func TestMailServer(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func deliverTest(t *testing.T, server *WMailServer, env *whisper.Envelope) {
|
func deliverTest(t *testing.T, server *WMailServer, env *whisper.Envelope) {
|
||||||
testPeerID := shh.NewIdentity()
|
id, err := shh.NewKeyPair()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate new key pair with seed %d: %s.", seed, err)
|
||||||
|
}
|
||||||
|
testPeerID, err := shh.GetPrivateKey(id)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to retrieve new key pair with seed %d: %s.", seed, err)
|
||||||
|
}
|
||||||
birth := env.Expiry - env.TTL
|
birth := env.Expiry - env.TTL
|
||||||
p := &ServerTestParams{
|
p := &ServerTestParams{
|
||||||
topic: env.Topic,
|
topic: env.Topic,
|
||||||
|
@ -167,8 +174,13 @@ func createRequest(t *testing.T, p *ServerTestParams) *whisper.Envelope {
|
||||||
binary.BigEndian.PutUint32(data[4:], p.upp)
|
binary.BigEndian.PutUint32(data[4:], p.upp)
|
||||||
copy(data[8:], p.topic[:])
|
copy(data[8:], p.topic[:])
|
||||||
|
|
||||||
|
key, err := shh.GetSymKey(keyID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to retrieve sym key with seed %d: %s.", seed, err)
|
||||||
|
}
|
||||||
|
|
||||||
params := &whisper.MessageParams{
|
params := &whisper.MessageParams{
|
||||||
KeySym: shh.GetSymKey(keyName),
|
KeySym: key,
|
||||||
Topic: p.topic,
|
Topic: p.topic,
|
||||||
Payload: data,
|
Payload: data,
|
||||||
PoW: powRequirement * 2,
|
PoW: powRequirement * 2,
|
||||||
|
|
|
@ -20,15 +20,14 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
mathrand "math/rand"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
)
|
)
|
||||||
|
|
||||||
var whisperOffLineErr = errors.New("whisper is offline")
|
var whisperOfflineErr = errors.New("whisper is offline")
|
||||||
|
|
||||||
// PublicWhisperAPI provides the whisper RPC service.
|
// PublicWhisperAPI provides the whisper RPC service.
|
||||||
type PublicWhisperAPI struct {
|
type PublicWhisperAPI struct {
|
||||||
|
@ -43,7 +42,7 @@ func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI {
|
||||||
// Start starts the Whisper worker threads.
|
// Start starts the Whisper worker threads.
|
||||||
func (api *PublicWhisperAPI) Start() error {
|
func (api *PublicWhisperAPI) Start() error {
|
||||||
if api.whisper == nil {
|
if api.whisper == nil {
|
||||||
return whisperOffLineErr
|
return whisperOfflineErr
|
||||||
}
|
}
|
||||||
return api.whisper.Start(nil)
|
return api.whisper.Start(nil)
|
||||||
}
|
}
|
||||||
|
@ -51,7 +50,7 @@ func (api *PublicWhisperAPI) Start() error {
|
||||||
// Stop stops the Whisper worker threads.
|
// Stop stops the Whisper worker threads.
|
||||||
func (api *PublicWhisperAPI) Stop() error {
|
func (api *PublicWhisperAPI) Stop() error {
|
||||||
if api.whisper == nil {
|
if api.whisper == nil {
|
||||||
return whisperOffLineErr
|
return whisperOfflineErr
|
||||||
}
|
}
|
||||||
return api.whisper.Stop()
|
return api.whisper.Stop()
|
||||||
}
|
}
|
||||||
|
@ -59,179 +58,219 @@ func (api *PublicWhisperAPI) Stop() error {
|
||||||
// Version returns the Whisper version this node offers.
|
// Version returns the Whisper version this node offers.
|
||||||
func (api *PublicWhisperAPI) Version() (hexutil.Uint, error) {
|
func (api *PublicWhisperAPI) Version() (hexutil.Uint, error) {
|
||||||
if api.whisper == nil {
|
if api.whisper == nil {
|
||||||
return 0, whisperOffLineErr
|
return 0, whisperOfflineErr
|
||||||
}
|
}
|
||||||
return hexutil.Uint(api.whisper.Version()), nil
|
return hexutil.Uint(api.whisper.Version()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stats returns the Whisper statistics for diagnostics.
|
// Info returns the Whisper statistics for diagnostics.
|
||||||
func (api *PublicWhisperAPI) Stats() (string, error) {
|
func (api *PublicWhisperAPI) Info() (string, error) {
|
||||||
if api.whisper == nil {
|
if api.whisper == nil {
|
||||||
return "", whisperOffLineErr
|
return "", whisperOfflineErr
|
||||||
}
|
}
|
||||||
return api.whisper.Stats(), nil
|
return api.whisper.Stats(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarkPeerTrusted marks specific peer trusted, which will allow it
|
// SetMaxMessageLength sets the maximal message length allowed by this node
|
||||||
|
func (api *PublicWhisperAPI) SetMaxMessageLength(val int) error {
|
||||||
|
if api.whisper == nil {
|
||||||
|
return whisperOfflineErr
|
||||||
|
}
|
||||||
|
return api.whisper.SetMaxMessageLength(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMinimumPoW sets the minimal PoW required by this node
|
||||||
|
func (api *PublicWhisperAPI) SetMinimumPoW(val float64) error {
|
||||||
|
if api.whisper == nil {
|
||||||
|
return whisperOfflineErr
|
||||||
|
}
|
||||||
|
return api.whisper.SetMinimumPoW(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowP2PMessagesFromPeer marks specific peer trusted, which will allow it
|
||||||
// to send historic (expired) messages.
|
// to send historic (expired) messages.
|
||||||
func (api *PublicWhisperAPI) MarkPeerTrusted(peerID hexutil.Bytes) error {
|
func (api *PublicWhisperAPI) AllowP2PMessagesFromPeer(enode string) error {
|
||||||
if api.whisper == nil {
|
if api.whisper == nil {
|
||||||
return whisperOffLineErr
|
return whisperOfflineErr
|
||||||
}
|
}
|
||||||
return api.whisper.MarkPeerTrusted(peerID)
|
n, err := discover.ParseNode(enode)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("failed to parse enode of trusted peer: " + err.Error())
|
||||||
|
}
|
||||||
|
return api.whisper.AllowP2PMessagesFromPeer(n.ID[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequestHistoricMessages requests the peer to deliver the old (expired) messages.
|
// HasKeyPair checks if the whisper node is configured with the private key
|
||||||
// data contains parameters (time frame, payment details, etc.), required
|
|
||||||
// by the remote email-like server. Whisper is not aware about the data format,
|
|
||||||
// it will just forward the raw data to the server.
|
|
||||||
//func (api *PublicWhisperAPI) RequestHistoricMessages(peerID hexutil.Bytes, data hexutil.Bytes) error {
|
|
||||||
// if api.whisper == nil {
|
|
||||||
// return whisperOffLineErr
|
|
||||||
// }
|
|
||||||
// return api.whisper.RequestHistoricMessages(peerID, data)
|
|
||||||
//}
|
|
||||||
|
|
||||||
// HasIdentity checks if the whisper node is configured with the private key
|
|
||||||
// of the specified public pair.
|
// of the specified public pair.
|
||||||
func (api *PublicWhisperAPI) HasIdentity(identity string) (bool, error) {
|
func (api *PublicWhisperAPI) HasKeyPair(id string) (bool, error) {
|
||||||
if api.whisper == nil {
|
if api.whisper == nil {
|
||||||
return false, whisperOffLineErr
|
return false, whisperOfflineErr
|
||||||
}
|
}
|
||||||
return api.whisper.HasIdentity(identity), nil
|
return api.whisper.HasKeyPair(id), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteIdentity deletes the specifies key if it exists.
|
// DeleteKeyPair deletes the specifies key if it exists.
|
||||||
func (api *PublicWhisperAPI) DeleteIdentity(identity string) error {
|
func (api *PublicWhisperAPI) DeleteKeyPair(id string) (bool, error) {
|
||||||
if api.whisper == nil {
|
if api.whisper == nil {
|
||||||
return whisperOffLineErr
|
return false, whisperOfflineErr
|
||||||
}
|
}
|
||||||
api.whisper.DeleteIdentity(identity)
|
return api.whisper.DeleteKeyPair(id), nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIdentity generates a new cryptographic identity for the client, and injects
|
// NewKeyPair generates a new cryptographic identity for the client, and injects
|
||||||
// it into the known identities for message decryption.
|
// it into the known identities for message decryption.
|
||||||
func (api *PublicWhisperAPI) NewIdentity() (string, error) {
|
func (api *PublicWhisperAPI) NewKeyPair() (string, error) {
|
||||||
if api.whisper == nil {
|
if api.whisper == nil {
|
||||||
return "", whisperOffLineErr
|
return "", whisperOfflineErr
|
||||||
}
|
}
|
||||||
identity := api.whisper.NewIdentity()
|
return api.whisper.NewKeyPair()
|
||||||
return common.ToHex(crypto.FromECDSAPub(&identity.PublicKey)), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateSymKey generates a random symmetric key and stores it under
|
// GetPublicKey returns the public key for identity id
|
||||||
// the 'name' id. Will be used in the future for session key exchange.
|
func (api *PublicWhisperAPI) GetPublicKey(id string) (hexutil.Bytes, error) {
|
||||||
func (api *PublicWhisperAPI) GenerateSymKey(name string) error {
|
|
||||||
if api.whisper == nil {
|
if api.whisper == nil {
|
||||||
return whisperOffLineErr
|
return nil, whisperOfflineErr
|
||||||
}
|
}
|
||||||
return api.whisper.GenerateSymKey(name)
|
key, err := api.whisper.GetPrivateKey(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return crypto.FromECDSAPub(&key.PublicKey), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSymKey stores the key under the 'name' id.
|
// GetPrivateKey returns the private key for identity id
|
||||||
func (api *PublicWhisperAPI) AddSymKey(name string, key hexutil.Bytes) error {
|
func (api *PublicWhisperAPI) GetPrivateKey(id string) (string, error) {
|
||||||
if api.whisper == nil {
|
if api.whisper == nil {
|
||||||
return whisperOffLineErr
|
return "", whisperOfflineErr
|
||||||
}
|
}
|
||||||
return api.whisper.AddSymKey(name, key)
|
key, err := api.whisper.GetPrivateKey(id)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return common.ToHex(crypto.FromECDSA(key)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasSymKey returns true if there is a key associated with the name string.
|
// GenerateSymmetricKey generates a random symmetric key and stores it under id,
|
||||||
|
// which is then returned. Will be used in the future for session key exchange.
|
||||||
|
func (api *PublicWhisperAPI) GenerateSymmetricKey() (string, error) {
|
||||||
|
if api.whisper == nil {
|
||||||
|
return "", whisperOfflineErr
|
||||||
|
}
|
||||||
|
return api.whisper.GenerateSymKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSymmetricKeyDirect stores the key, and returns its id.
|
||||||
|
func (api *PublicWhisperAPI) AddSymmetricKeyDirect(key hexutil.Bytes) (string, error) {
|
||||||
|
if api.whisper == nil {
|
||||||
|
return "", whisperOfflineErr
|
||||||
|
}
|
||||||
|
return api.whisper.AddSymKeyDirect(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSymmetricKeyFromPassword generates the key from password, stores it, and returns its id.
|
||||||
|
func (api *PublicWhisperAPI) AddSymmetricKeyFromPassword(password string) (string, error) {
|
||||||
|
if api.whisper == nil {
|
||||||
|
return "", whisperOfflineErr
|
||||||
|
}
|
||||||
|
return api.whisper.AddSymKeyFromPassword(password)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasSymmetricKey returns true if there is a key associated with the given id.
|
||||||
// Otherwise returns false.
|
// Otherwise returns false.
|
||||||
func (api *PublicWhisperAPI) HasSymKey(name string) (bool, error) {
|
func (api *PublicWhisperAPI) HasSymmetricKey(id string) (bool, error) {
|
||||||
if api.whisper == nil {
|
if api.whisper == nil {
|
||||||
return false, whisperOffLineErr
|
return false, whisperOfflineErr
|
||||||
}
|
}
|
||||||
res := api.whisper.HasSymKey(name)
|
res := api.whisper.HasSymKey(id)
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteSymKey deletes the key associated with the name string if it exists.
|
// GetSymmetricKey returns the symmetric key associated with the given id.
|
||||||
func (api *PublicWhisperAPI) DeleteSymKey(name string) error {
|
func (api *PublicWhisperAPI) GetSymmetricKey(name string) (hexutil.Bytes, error) {
|
||||||
if api.whisper == nil {
|
if api.whisper == nil {
|
||||||
return whisperOffLineErr
|
return nil, whisperOfflineErr
|
||||||
}
|
}
|
||||||
api.whisper.DeleteSymKey(name)
|
b, err := api.whisper.GetSymKey(name)
|
||||||
return nil
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWhisperFilter creates and registers a new message filter to watch for inbound whisper messages.
|
// DeleteSymmetricKey deletes the key associated with the name string if it exists.
|
||||||
// Returns the ID of the newly created Filter.
|
func (api *PublicWhisperAPI) DeleteSymmetricKey(name string) (bool, error) {
|
||||||
func (api *PublicWhisperAPI) NewFilter(args WhisperFilterArgs) (string, error) {
|
|
||||||
if api.whisper == nil {
|
if api.whisper == nil {
|
||||||
return "", whisperOffLineErr
|
return false, whisperOfflineErr
|
||||||
|
}
|
||||||
|
res := api.whisper.DeleteSymKey(name)
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe creates and registers a new filter to watch for inbound whisper messages.
|
||||||
|
// Returns the ID of the newly created filter.
|
||||||
|
func (api *PublicWhisperAPI) Subscribe(args WhisperFilterArgs) (string, error) {
|
||||||
|
if api.whisper == nil {
|
||||||
|
return "", whisperOfflineErr
|
||||||
}
|
}
|
||||||
|
|
||||||
filter := Filter{
|
filter := Filter{
|
||||||
Src: crypto.ToECDSAPub(common.FromHex(args.From)),
|
Src: crypto.ToECDSAPub(common.FromHex(args.SignedWith)),
|
||||||
KeySym: api.whisper.GetSymKey(args.KeyName),
|
PoW: args.MinPoW,
|
||||||
PoW: args.PoW,
|
Messages: make(map[common.Hash]*ReceivedMessage),
|
||||||
Messages: make(map[common.Hash]*ReceivedMessage),
|
AllowP2P: args.AllowP2P,
|
||||||
AcceptP2P: args.AcceptP2P,
|
|
||||||
}
|
|
||||||
if len(filter.KeySym) > 0 {
|
|
||||||
filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym)
|
|
||||||
}
|
|
||||||
filter.Topics = append(filter.Topics, args.Topics...)
|
|
||||||
|
|
||||||
if len(args.Topics) == 0 && len(args.KeyName) != 0 {
|
|
||||||
info := "NewFilter: at least one topic must be specified"
|
|
||||||
log.Error(fmt.Sprintf(info))
|
|
||||||
return "", errors.New(info)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(args.KeyName) != 0 && len(filter.KeySym) == 0 {
|
var err error
|
||||||
info := "NewFilter: key was not found by name: " + args.KeyName
|
for i, bt := range args.Topics {
|
||||||
log.Error(fmt.Sprintf(info))
|
if len(bt) == 0 || len(bt) > 4 {
|
||||||
return "", errors.New(info)
|
return "", errors.New(fmt.Sprintf("subscribe: topic %d has wrong size: %d", i, len(bt)))
|
||||||
}
|
|
||||||
|
|
||||||
if len(args.To) == 0 && len(filter.KeySym) == 0 {
|
|
||||||
info := "NewFilter: filter must contain either symmetric or asymmetric key"
|
|
||||||
log.Error(fmt.Sprintf(info))
|
|
||||||
return "", errors.New(info)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args.To) != 0 && len(filter.KeySym) != 0 {
|
|
||||||
info := "NewFilter: filter must not contain both symmetric and asymmetric key"
|
|
||||||
log.Error(fmt.Sprintf(info))
|
|
||||||
return "", errors.New(info)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args.To) > 0 {
|
|
||||||
dst := crypto.ToECDSAPub(common.FromHex(args.To))
|
|
||||||
if !ValidatePublicKey(dst) {
|
|
||||||
info := "NewFilter: Invalid 'To' address"
|
|
||||||
log.Error(fmt.Sprintf(info))
|
|
||||||
return "", errors.New(info)
|
|
||||||
}
|
|
||||||
filter.KeyAsym = api.whisper.GetIdentity(string(args.To))
|
|
||||||
if filter.KeyAsym == nil {
|
|
||||||
info := "NewFilter: non-existent identity provided"
|
|
||||||
log.Error(fmt.Sprintf(info))
|
|
||||||
return "", errors.New(info)
|
|
||||||
}
|
}
|
||||||
|
filter.Topics = append(filter.Topics, bt)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(args.From) > 0 {
|
if err = ValidateKeyID(args.Key); err != nil {
|
||||||
|
return "", errors.New("subscribe: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args.SignedWith) > 0 {
|
||||||
if !ValidatePublicKey(filter.Src) {
|
if !ValidatePublicKey(filter.Src) {
|
||||||
info := "NewFilter: Invalid 'From' address"
|
return "", errors.New("subscribe: invalid 'SignedWith' field")
|
||||||
log.Error(fmt.Sprintf(info))
|
|
||||||
return "", errors.New(info)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return api.whisper.Watch(&filter)
|
if args.Symmetric {
|
||||||
|
if len(args.Topics) == 0 {
|
||||||
|
return "", errors.New("subscribe: at least one topic must be specified with symmetric encryption")
|
||||||
|
}
|
||||||
|
symKey, err := api.whisper.GetSymKey(args.Key)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.New("subscribe: invalid key ID")
|
||||||
|
}
|
||||||
|
if !validateSymmetricKey(symKey) {
|
||||||
|
return "", errors.New("subscribe: retrieved key is invalid")
|
||||||
|
}
|
||||||
|
filter.KeySym = symKey
|
||||||
|
filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym)
|
||||||
|
} else {
|
||||||
|
filter.KeyAsym, err = api.whisper.GetPrivateKey(args.Key)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.New("subscribe: invalid key ID")
|
||||||
|
}
|
||||||
|
if filter.KeyAsym == nil {
|
||||||
|
return "", errors.New("subscribe: non-existent identity provided")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.whisper.Subscribe(&filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UninstallFilter disables and removes an existing filter.
|
// Unsubscribe disables and removes an existing filter.
|
||||||
func (api *PublicWhisperAPI) UninstallFilter(filterId string) {
|
func (api *PublicWhisperAPI) Unsubscribe(id string) {
|
||||||
api.whisper.Unwatch(filterId)
|
api.whisper.Unsubscribe(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFilterChanges retrieves all the new messages matched by a filter since the last retrieval.
|
// GetSubscriptionMessages retrieves all the new messages matched by a filter since the last retrieval.
|
||||||
func (api *PublicWhisperAPI) GetFilterChanges(filterId string) []*WhisperMessage {
|
func (api *PublicWhisperAPI) GetSubscriptionMessages(filterId string) []*WhisperMessage {
|
||||||
f := api.whisper.GetFilter(filterId)
|
f := api.whisper.GetFilter(filterId)
|
||||||
if f != nil {
|
if f != nil {
|
||||||
newMail := f.Retrieve()
|
newMail := f.Retrieve()
|
||||||
|
@ -240,7 +279,8 @@ func (api *PublicWhisperAPI) GetFilterChanges(filterId string) []*WhisperMessage
|
||||||
return toWhisperMessages(nil)
|
return toWhisperMessages(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMessages retrieves all the known messages that match a specific filter.
|
// GetMessages retrieves all the floating messages that match a specific filter.
|
||||||
|
// It is likely to be called once per session, right after Subscribe call.
|
||||||
func (api *PublicWhisperAPI) GetMessages(filterId string) []*WhisperMessage {
|
func (api *PublicWhisperAPI) GetMessages(filterId string) []*WhisperMessage {
|
||||||
all := api.whisper.Messages(filterId)
|
all := api.whisper.Messages(filterId)
|
||||||
return toWhisperMessages(all)
|
return toWhisperMessages(all)
|
||||||
|
@ -258,139 +298,107 @@ func toWhisperMessages(messages []*ReceivedMessage) []*WhisperMessage {
|
||||||
// Post creates a whisper message and injects it into the network for distribution.
|
// Post creates a whisper message and injects it into the network for distribution.
|
||||||
func (api *PublicWhisperAPI) Post(args PostArgs) error {
|
func (api *PublicWhisperAPI) Post(args PostArgs) error {
|
||||||
if api.whisper == nil {
|
if api.whisper == nil {
|
||||||
return whisperOffLineErr
|
return whisperOfflineErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
params := MessageParams{
|
params := MessageParams{
|
||||||
TTL: args.TTL,
|
TTL: args.TTL,
|
||||||
Dst: crypto.ToECDSAPub(common.FromHex(args.To)),
|
WorkTime: args.PowTime,
|
||||||
KeySym: api.whisper.GetSymKey(args.KeyName),
|
PoW: args.PowTarget,
|
||||||
Topic: args.Topic,
|
|
||||||
Payload: args.Payload,
|
Payload: args.Payload,
|
||||||
Padding: args.Padding,
|
Padding: args.Padding,
|
||||||
WorkTime: args.WorkTime,
|
|
||||||
PoW: args.PoW,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(args.From) > 0 {
|
if len(args.Key) == 0 {
|
||||||
pub := crypto.ToECDSAPub(common.FromHex(args.From))
|
return errors.New("post: key is missing")
|
||||||
if !ValidatePublicKey(pub) {
|
}
|
||||||
info := "Post: Invalid 'From' address"
|
|
||||||
log.Error(fmt.Sprintf(info))
|
if len(args.SignWith) > 0 {
|
||||||
return errors.New(info)
|
params.Src, err = api.whisper.GetPrivateKey(args.SignWith)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
params.Src = api.whisper.GetIdentity(string(args.From))
|
|
||||||
if params.Src == nil {
|
if params.Src == nil {
|
||||||
info := "Post: non-existent identity provided"
|
return errors.New("post: empty identity")
|
||||||
log.Error(fmt.Sprintf(info))
|
|
||||||
return errors.New(info)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filter := api.whisper.GetFilter(args.FilterID)
|
if len(args.Topic) == TopicLength {
|
||||||
if filter == nil && len(args.FilterID) > 0 {
|
params.Topic = BytesToTopic(args.Topic)
|
||||||
info := fmt.Sprintf("Post: wrong filter id %s", args.FilterID)
|
} else if len(args.Topic) != 0 {
|
||||||
log.Error(fmt.Sprintf(info))
|
return errors.New(fmt.Sprintf("post: wrong topic size %d", len(args.Topic)))
|
||||||
return errors.New(info)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if filter != nil {
|
if args.Type == "sym" {
|
||||||
// get the missing fields from the filter
|
if err = ValidateKeyID(args.Key); err != nil {
|
||||||
if params.KeySym == nil && filter.KeySym != nil {
|
return err
|
||||||
params.KeySym = filter.KeySym
|
|
||||||
}
|
}
|
||||||
if params.Src == nil && filter.Src != nil {
|
params.KeySym, err = api.whisper.GetSymKey(args.Key)
|
||||||
params.Src = filter.KeyAsym
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if (params.Topic == TopicType{}) {
|
if !validateSymmetricKey(params.KeySym) {
|
||||||
sz := len(filter.Topics)
|
return errors.New("post: key for symmetric encryption is invalid")
|
||||||
if sz < 1 {
|
|
||||||
info := fmt.Sprintf("Post: no topics in filter # %s", args.FilterID)
|
|
||||||
log.Error(fmt.Sprintf(info))
|
|
||||||
return errors.New(info)
|
|
||||||
} else if sz == 1 {
|
|
||||||
params.Topic = filter.Topics[0]
|
|
||||||
} else {
|
|
||||||
// choose randomly
|
|
||||||
rnd := mathrand.Intn(sz)
|
|
||||||
params.Topic = filter.Topics[rnd]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
if len(params.Topic) == 0 {
|
||||||
|
return errors.New("post: topic is missing for symmetric encryption")
|
||||||
// validate
|
}
|
||||||
if len(args.KeyName) != 0 && len(params.KeySym) == 0 {
|
} else if args.Type == "asym" {
|
||||||
info := "Post: key was not found by name: " + args.KeyName
|
params.Dst = crypto.ToECDSAPub(common.FromHex(args.Key))
|
||||||
log.Error(fmt.Sprintf(info))
|
|
||||||
return errors.New(info)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args.To) == 0 && len(params.KeySym) == 0 {
|
|
||||||
info := "Post: message must be encrypted either symmetrically or asymmetrically"
|
|
||||||
log.Error(fmt.Sprintf(info))
|
|
||||||
return errors.New(info)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args.To) != 0 && len(params.KeySym) != 0 {
|
|
||||||
info := "Post: ambigous encryption method requested"
|
|
||||||
log.Error(fmt.Sprintf(info))
|
|
||||||
return errors.New(info)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args.To) > 0 {
|
|
||||||
if !ValidatePublicKey(params.Dst) {
|
if !ValidatePublicKey(params.Dst) {
|
||||||
info := "Post: Invalid 'To' address"
|
return errors.New("post: public key for asymmetric encryption is invalid")
|
||||||
log.Error(fmt.Sprintf(info))
|
|
||||||
return errors.New(info)
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return errors.New("post: wrong type (sym/asym)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypt and send
|
// encrypt and send
|
||||||
message := NewSentMessage(¶ms)
|
message := NewSentMessage(¶ms)
|
||||||
|
if message == nil {
|
||||||
|
return errors.New("post: failed create new message, probably due to failed rand function (OS level)")
|
||||||
|
}
|
||||||
envelope, err := message.Wrap(¶ms)
|
envelope, err := message.Wrap(¶ms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(fmt.Sprintf(err.Error()))
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(envelope.Data) > MaxMessageLength {
|
if envelope.size() > api.whisper.maxMsgLength {
|
||||||
info := "Post: message is too big"
|
return errors.New("post: message is too big")
|
||||||
log.Error(fmt.Sprintf(info))
|
|
||||||
return errors.New(info)
|
|
||||||
}
|
|
||||||
if (envelope.Topic == TopicType{} && envelope.IsSymmetric()) {
|
|
||||||
info := "Post: topic is missing for symmetric encryption"
|
|
||||||
log.Error(fmt.Sprintf(info))
|
|
||||||
return errors.New(info)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.PeerID != nil {
|
if len(args.TargetPeer) != 0 {
|
||||||
return api.whisper.SendP2PMessage(args.PeerID, envelope)
|
n, err := discover.ParseNode(args.TargetPeer)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("post: failed to parse enode of target peer: " + err.Error())
|
||||||
|
}
|
||||||
|
return api.whisper.SendP2PMessage(n.ID[:], envelope)
|
||||||
|
} else if args.PowTarget < api.whisper.minPoW {
|
||||||
|
return errors.New("post: target PoW is less than minimum PoW, the message can not be sent")
|
||||||
}
|
}
|
||||||
|
|
||||||
return api.whisper.Send(envelope)
|
return api.whisper.Send(envelope)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostArgs struct {
|
type PostArgs struct {
|
||||||
TTL uint32 `json:"ttl"`
|
Type string `json:"type"` // "sym"/"asym" (symmetric or asymmetric)
|
||||||
From string `json:"from"`
|
TTL uint32 `json:"ttl"` // time-to-live in seconds
|
||||||
To string `json:"to"`
|
SignWith string `json:"signWith"` // id of the signing key
|
||||||
KeyName string `json:"keyname"`
|
Key string `json:"key"` // id of encryption key
|
||||||
Topic TopicType `json:"topic"`
|
Topic hexutil.Bytes `json:"topic"` // topic (4 bytes)
|
||||||
Padding hexutil.Bytes `json:"padding"`
|
Padding hexutil.Bytes `json:"padding"` // optional padding bytes
|
||||||
Payload hexutil.Bytes `json:"payload"`
|
Payload hexutil.Bytes `json:"payload"` // payload to be encrypted
|
||||||
WorkTime uint32 `json:"worktime"`
|
PowTime uint32 `json:"powTime"` // maximal time in seconds to be spent on PoW
|
||||||
PoW float64 `json:"pow"`
|
PowTarget float64 `json:"powTarget"` // minimal PoW required for this message
|
||||||
FilterID string `json:"filterID"`
|
TargetPeer string `json:"targetPeer"` // peer id (for p2p message only)
|
||||||
PeerID hexutil.Bytes `json:"peerID"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type WhisperFilterArgs struct {
|
type WhisperFilterArgs struct {
|
||||||
To string `json:"to"`
|
Symmetric bool // encryption type
|
||||||
From string `json:"from"`
|
Key string // id of the key to be used for decryption
|
||||||
KeyName string `json:"keyname"`
|
SignedWith string // public key of the sender to be verified
|
||||||
PoW float64 `json:"pow"`
|
MinPoW float64 // minimal PoW requirement
|
||||||
Topics []TopicType `json:"topics"`
|
Topics [][]byte // list of topics (up to 4 bytes each) to match
|
||||||
AcceptP2P bool `json:"p2p"`
|
AllowP2P bool // indicates wheather direct p2p messages are allowed for this filter
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a
|
// UnmarshalJSON implements the json.Unmarshaler interface, invoked to convert a
|
||||||
|
@ -398,22 +406,30 @@ type WhisperFilterArgs struct {
|
||||||
func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
|
func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
|
||||||
// Unmarshal the JSON message and sanity check
|
// Unmarshal the JSON message and sanity check
|
||||||
var obj struct {
|
var obj struct {
|
||||||
To string `json:"to"`
|
Type string `json:"type"`
|
||||||
From string `json:"from"`
|
Key string `json:"key"`
|
||||||
KeyName string `json:"keyname"`
|
SignedWith string `json:"signedWith"`
|
||||||
PoW float64 `json:"pow"`
|
MinPoW float64 `json:"minPoW"`
|
||||||
Topics []interface{} `json:"topics"`
|
Topics []interface{} `json:"topics"`
|
||||||
AcceptP2P bool `json:"p2p"`
|
AllowP2P bool `json:"allowP2P"`
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(b, &obj); err != nil {
|
if err := json.Unmarshal(b, &obj); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
args.To = obj.To
|
switch obj.Type {
|
||||||
args.From = obj.From
|
case "sym":
|
||||||
args.KeyName = obj.KeyName
|
args.Symmetric = true
|
||||||
args.PoW = obj.PoW
|
case "asym":
|
||||||
args.AcceptP2P = obj.AcceptP2P
|
args.Symmetric = false
|
||||||
|
default:
|
||||||
|
return errors.New("wrong type (sym/asym)")
|
||||||
|
}
|
||||||
|
|
||||||
|
args.Key = obj.Key
|
||||||
|
args.SignedWith = obj.SignedWith
|
||||||
|
args.MinPoW = obj.MinPoW
|
||||||
|
args.AllowP2P = obj.AllowP2P
|
||||||
|
|
||||||
// Construct the topic array
|
// Construct the topic array
|
||||||
if obj.Topics != nil {
|
if obj.Topics != nil {
|
||||||
|
@ -428,13 +444,13 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
|
||||||
return fmt.Errorf("topic[%d] is not a string", i)
|
return fmt.Errorf("topic[%d] is not a string", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
topicsDecoded := make([]TopicType, len(topics))
|
topicsDecoded := make([][]byte, len(topics))
|
||||||
for j, s := range topics {
|
for j, s := range topics {
|
||||||
x := common.FromHex(s)
|
x := common.FromHex(s)
|
||||||
if x == nil || len(x) != TopicLength {
|
if x == nil || len(x) > TopicLength {
|
||||||
return fmt.Errorf("topic[%d] is invalid", j)
|
return fmt.Errorf("topic[%d] is invalid", j)
|
||||||
}
|
}
|
||||||
topicsDecoded[j] = BytesToTopic(x)
|
topicsDecoded[j] = x
|
||||||
}
|
}
|
||||||
args.Topics = topicsDecoded
|
args.Topics = topicsDecoded
|
||||||
}
|
}
|
||||||
|
@ -444,34 +460,34 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
|
||||||
|
|
||||||
// WhisperMessage is the RPC representation of a whisper message.
|
// WhisperMessage is the RPC representation of a whisper message.
|
||||||
type WhisperMessage struct {
|
type WhisperMessage struct {
|
||||||
Topic string `json:"topic"`
|
Topic string `json:"topic"`
|
||||||
Payload string `json:"payload"`
|
Payload string `json:"payload"`
|
||||||
Padding string `json:"padding"`
|
Padding string `json:"padding"`
|
||||||
From string `json:"from"`
|
Src string `json:"signedWith"`
|
||||||
To string `json:"to"`
|
Dst string `json:"recipientPublicKey"`
|
||||||
Sent uint32 `json:"sent"`
|
Timestamp uint32 `json:"timestamp"`
|
||||||
TTL uint32 `json:"ttl"`
|
TTL uint32 `json:"ttl"`
|
||||||
PoW float64 `json:"pow"`
|
PoW float64 `json:"pow"`
|
||||||
Hash string `json:"hash"`
|
Hash string `json:"hash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWhisperMessage converts an internal message into an API version.
|
// NewWhisperMessage converts an internal message into an API version.
|
||||||
func NewWhisperMessage(message *ReceivedMessage) *WhisperMessage {
|
func NewWhisperMessage(message *ReceivedMessage) *WhisperMessage {
|
||||||
msg := WhisperMessage{
|
msg := WhisperMessage{
|
||||||
Topic: common.ToHex(message.Topic[:]),
|
Topic: common.ToHex(message.Topic[:]),
|
||||||
Payload: common.ToHex(message.Payload),
|
Payload: common.ToHex(message.Payload),
|
||||||
Padding: common.ToHex(message.Padding),
|
Padding: common.ToHex(message.Padding),
|
||||||
Sent: message.Sent,
|
Timestamp: message.Sent,
|
||||||
TTL: message.TTL,
|
TTL: message.TTL,
|
||||||
PoW: message.PoW,
|
PoW: message.PoW,
|
||||||
Hash: common.ToHex(message.EnvelopeHash.Bytes()),
|
Hash: common.ToHex(message.EnvelopeHash.Bytes()),
|
||||||
}
|
}
|
||||||
|
|
||||||
if message.Dst != nil {
|
if message.Dst != nil {
|
||||||
msg.To = common.ToHex(crypto.FromECDSAPub(message.Dst))
|
msg.Dst = common.ToHex(crypto.FromECDSAPub(message.Dst))
|
||||||
}
|
}
|
||||||
if isMessageSigned(message.Raw[0]) {
|
if isMessageSigned(message.Raw[0]) {
|
||||||
msg.From = common.ToHex(crypto.FromECDSAPub(message.SigToPubKey()))
|
msg.Src = common.ToHex(crypto.FromECDSAPub(message.SigToPubKey()))
|
||||||
}
|
}
|
||||||
return &msg
|
return &msg
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBasic(t *testing.T) {
|
func TestBasic(t *testing.T) {
|
||||||
|
@ -42,12 +43,12 @@ func TestBasic(t *testing.T) {
|
||||||
t.Fatalf("wrong version: %d.", ver)
|
t.Fatalf("wrong version: %d.", ver)
|
||||||
}
|
}
|
||||||
|
|
||||||
mail := api.GetFilterChanges("non-existent-id")
|
mail := api.GetSubscriptionMessages("non-existent-id")
|
||||||
if len(mail) != 0 {
|
if len(mail) != 0 {
|
||||||
t.Fatalf("failed GetFilterChanges: premature result")
|
t.Fatalf("failed GetFilterChanges: premature result")
|
||||||
}
|
}
|
||||||
|
|
||||||
exist, err := api.HasIdentity(id)
|
exist, err := api.HasKeyPair(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed initial HasIdentity: %s.", err)
|
t.Fatalf("failed initial HasIdentity: %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -55,12 +56,15 @@ func TestBasic(t *testing.T) {
|
||||||
t.Fatalf("failed initial HasIdentity: false positive.")
|
t.Fatalf("failed initial HasIdentity: false positive.")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = api.DeleteIdentity(id)
|
success, err := api.DeleteKeyPair(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed DeleteIdentity: %s.", err)
|
t.Fatalf("failed DeleteIdentity: %s.", err)
|
||||||
}
|
}
|
||||||
|
if success {
|
||||||
|
t.Fatalf("deleted non-existing identity: false positive.")
|
||||||
|
}
|
||||||
|
|
||||||
pub, err := api.NewIdentity()
|
pub, err := api.NewKeyPair()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed NewIdentity: %s.", err)
|
t.Fatalf("failed NewIdentity: %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -68,7 +72,7 @@ func TestBasic(t *testing.T) {
|
||||||
t.Fatalf("failed NewIdentity: empty")
|
t.Fatalf("failed NewIdentity: empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
exist, err = api.HasIdentity(pub)
|
exist, err = api.HasKeyPair(pub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed HasIdentity: %s.", err)
|
t.Fatalf("failed HasIdentity: %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -76,12 +80,15 @@ func TestBasic(t *testing.T) {
|
||||||
t.Fatalf("failed HasIdentity: false negative.")
|
t.Fatalf("failed HasIdentity: false negative.")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = api.DeleteIdentity(pub)
|
success, err = api.DeleteKeyPair(pub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to delete second identity: %s.", err)
|
t.Fatalf("failed to delete second identity: %s.", err)
|
||||||
}
|
}
|
||||||
|
if !success {
|
||||||
|
t.Fatalf("failed to delete second identity.")
|
||||||
|
}
|
||||||
|
|
||||||
exist, err = api.HasIdentity(pub)
|
exist, err = api.HasKeyPair(pub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed HasIdentity(): %s.", err)
|
t.Fatalf("failed HasIdentity(): %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -92,7 +99,7 @@ func TestBasic(t *testing.T) {
|
||||||
id = "arbitrary text"
|
id = "arbitrary text"
|
||||||
id2 := "another arbitrary string"
|
id2 := "another arbitrary string"
|
||||||
|
|
||||||
exist, err = api.HasSymKey(id)
|
exist, err = api.HasSymmetricKey(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed HasSymKey: %s.", err)
|
t.Fatalf("failed HasSymKey: %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -100,12 +107,12 @@ func TestBasic(t *testing.T) {
|
||||||
t.Fatalf("failed HasSymKey: false positive.")
|
t.Fatalf("failed HasSymKey: false positive.")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = api.GenerateSymKey(id)
|
id, err = api.GenerateSymmetricKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed GenerateSymKey: %s.", err)
|
t.Fatalf("failed GenerateSymKey: %s.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
exist, err = api.HasSymKey(id)
|
exist, err = api.HasSymmetricKey(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed HasSymKey(): %s.", err)
|
t.Fatalf("failed HasSymKey(): %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -113,17 +120,18 @@ func TestBasic(t *testing.T) {
|
||||||
t.Fatalf("failed HasSymKey(): false negative.")
|
t.Fatalf("failed HasSymKey(): false negative.")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = api.AddSymKey(id, []byte("some stuff here"))
|
const password = "some stuff here"
|
||||||
if err == nil {
|
id, err = api.AddSymmetricKeyFromPassword(password)
|
||||||
t.Fatalf("failed AddSymKey: %s.", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = api.AddSymKey(id2, []byte("some stuff here"))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed AddSymKey: %s.", err)
|
t.Fatalf("failed AddSymKey: %s.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
exist, err = api.HasSymKey(id2)
|
id2, err = api.AddSymmetricKeyFromPassword(password)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed AddSymKey: %s.", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
exist, err = api.HasSymmetricKey(id2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed HasSymKey(id2): %s.", err)
|
t.Fatalf("failed HasSymKey(id2): %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -131,12 +139,28 @@ func TestBasic(t *testing.T) {
|
||||||
t.Fatalf("failed HasSymKey(id2): false negative.")
|
t.Fatalf("failed HasSymKey(id2): false negative.")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = api.DeleteSymKey(id)
|
k1, err := api.GetSymmetricKey(id)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed GetSymKey(id): %s.", err)
|
||||||
|
}
|
||||||
|
k2, err := api.GetSymmetricKey(id2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed GetSymKey(id2): %s.", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(k1, k2) {
|
||||||
|
t.Fatalf("installed keys are not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
exist, err = api.DeleteSymmetricKey(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed DeleteSymKey(id): %s.", err)
|
t.Fatalf("failed DeleteSymKey(id): %s.", err)
|
||||||
}
|
}
|
||||||
|
if !exist {
|
||||||
|
t.Fatalf("failed DeleteSymKey(id): false negative.")
|
||||||
|
}
|
||||||
|
|
||||||
exist, err = api.HasSymKey(id)
|
exist, err = api.HasSymmetricKey(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed HasSymKey(id): %s.", err)
|
t.Fatalf("failed HasSymKey(id): %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -147,12 +171,12 @@ func TestBasic(t *testing.T) {
|
||||||
|
|
||||||
func TestUnmarshalFilterArgs(t *testing.T) {
|
func TestUnmarshalFilterArgs(t *testing.T) {
|
||||||
s := []byte(`{
|
s := []byte(`{
|
||||||
"to":"0x70c87d191324e6712a591f304b4eedef6ad9bb9d",
|
"type":"sym",
|
||||||
"from":"0x9b2055d370f73ec7d8a03e965129118dc8f5bf83",
|
"key":"0x70c87d191324e6712a591f304b4eedef6ad9bb9d",
|
||||||
"keyname":"testname",
|
"signedWith":"0x9b2055d370f73ec7d8a03e965129118dc8f5bf83",
|
||||||
"pow":2.34,
|
"minPoW":2.34,
|
||||||
"topics":["0x00000000", "0x007f80ff", "0xff807f00", "0xf26e7779"],
|
"topics":["0x00000000", "0x007f80ff", "0xff807f00", "0xf26e7779"],
|
||||||
"p2p":true
|
"allowP2P":true
|
||||||
}`)
|
}`)
|
||||||
|
|
||||||
var f WhisperFilterArgs
|
var f WhisperFilterArgs
|
||||||
|
@ -161,59 +185,58 @@ func TestUnmarshalFilterArgs(t *testing.T) {
|
||||||
t.Fatalf("failed UnmarshalJSON: %s.", err)
|
t.Fatalf("failed UnmarshalJSON: %s.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.To != "0x70c87d191324e6712a591f304b4eedef6ad9bb9d" {
|
if !f.Symmetric {
|
||||||
t.Fatalf("wrong To: %x.", f.To)
|
t.Fatalf("wrong type.")
|
||||||
}
|
}
|
||||||
if f.From != "0x9b2055d370f73ec7d8a03e965129118dc8f5bf83" {
|
if f.Key != "0x70c87d191324e6712a591f304b4eedef6ad9bb9d" {
|
||||||
t.Fatalf("wrong From: %x.", f.To)
|
t.Fatalf("wrong key: %s.", f.Key)
|
||||||
}
|
}
|
||||||
if f.KeyName != "testname" {
|
if f.SignedWith != "0x9b2055d370f73ec7d8a03e965129118dc8f5bf83" {
|
||||||
t.Fatalf("wrong KeyName: %s.", f.KeyName)
|
t.Fatalf("wrong SignedWith: %s.", f.SignedWith)
|
||||||
}
|
}
|
||||||
if f.PoW != 2.34 {
|
if f.MinPoW != 2.34 {
|
||||||
t.Fatalf("wrong pow: %f.", f.PoW)
|
t.Fatalf("wrong MinPoW: %f.", f.MinPoW)
|
||||||
}
|
}
|
||||||
if !f.AcceptP2P {
|
if !f.AllowP2P {
|
||||||
t.Fatalf("wrong AcceptP2P: %v.", f.AcceptP2P)
|
t.Fatalf("wrong AllowP2P.")
|
||||||
}
|
}
|
||||||
if len(f.Topics) != 4 {
|
if len(f.Topics) != 4 {
|
||||||
t.Fatalf("wrong topics number: %d.", len(f.Topics))
|
t.Fatalf("wrong topics number: %d.", len(f.Topics))
|
||||||
}
|
}
|
||||||
|
|
||||||
i := 0
|
i := 0
|
||||||
if f.Topics[i] != (TopicType{0x00, 0x00, 0x00, 0x00}) {
|
if !bytes.Equal(f.Topics[i], []byte{0x00, 0x00, 0x00, 0x00}) {
|
||||||
t.Fatalf("wrong topic[%d]: %x.", i, f.Topics[i])
|
t.Fatalf("wrong topic[%d]: %x.", i, f.Topics[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
i++
|
i++
|
||||||
if f.Topics[i] != (TopicType{0x00, 0x7f, 0x80, 0xff}) {
|
if !bytes.Equal(f.Topics[i], []byte{0x00, 0x7f, 0x80, 0xff}) {
|
||||||
t.Fatalf("wrong topic[%d]: %x.", i, f.Topics[i])
|
t.Fatalf("wrong topic[%d]: %x.", i, f.Topics[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
i++
|
i++
|
||||||
if f.Topics[i] != (TopicType{0xff, 0x80, 0x7f, 0x00}) {
|
if !bytes.Equal(f.Topics[i], []byte{0xff, 0x80, 0x7f, 0x00}) {
|
||||||
t.Fatalf("wrong topic[%d]: %x.", i, f.Topics[i])
|
t.Fatalf("wrong topic[%d]: %x.", i, f.Topics[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
i++
|
i++
|
||||||
if f.Topics[i] != (TopicType{0xf2, 0x6e, 0x77, 0x79}) {
|
if !bytes.Equal(f.Topics[i], []byte{0xf2, 0x6e, 0x77, 0x79}) {
|
||||||
t.Fatalf("wrong topic[%d]: %x.", i, f.Topics[i])
|
t.Fatalf("wrong topic[%d]: %x.", i, f.Topics[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalPostArgs(t *testing.T) {
|
func TestUnmarshalPostArgs(t *testing.T) {
|
||||||
s := []byte(`{
|
s := []byte(`{
|
||||||
|
"type":"sym",
|
||||||
"ttl":12345,
|
"ttl":12345,
|
||||||
"from":"0x70c87d191324e6712a591f304b4eedef6ad9bb9d",
|
"signWith":"0x70c87d191324e6712a591f304b4eedef6ad9bb9d",
|
||||||
"to":"0x9b2055d370f73ec7d8a03e965129118dc8f5bf83",
|
"key":"0x9b2055d370f73ec7d8a03e965129118dc8f5bf83",
|
||||||
"keyname":"shh_test",
|
|
||||||
"topic":"0xf26e7779",
|
"topic":"0xf26e7779",
|
||||||
"padding":"0x74686973206973206D79207465737420737472696E67",
|
"padding":"0x74686973206973206D79207465737420737472696E67",
|
||||||
"payload":"0x7061796C6F61642073686F756C642062652070736575646F72616E646F6D",
|
"payload":"0x7061796C6F61642073686F756C642062652070736575646F72616E646F6D",
|
||||||
"worktime":777,
|
"powTime":777,
|
||||||
"pow":3.1416,
|
"powTarget":3.1416,
|
||||||
"filterid":"test-filter-id",
|
"targetPeer":"enode://915533f667b1369793ebb9bda022416b1295235a1420799cd87a969467372546d808ebf59c5c9ce23f103d59b61b97df8af91f0908552485975397181b993461@127.0.0.1:12345"
|
||||||
"peerid":"0xf26e7779"
|
|
||||||
}`)
|
}`)
|
||||||
|
|
||||||
var a PostArgs
|
var a PostArgs
|
||||||
|
@ -222,19 +245,20 @@ func TestUnmarshalPostArgs(t *testing.T) {
|
||||||
t.Fatalf("failed UnmarshalJSON: %s.", err)
|
t.Fatalf("failed UnmarshalJSON: %s.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if a.Type != "sym" {
|
||||||
|
t.Fatalf("wrong Type: %s.", a.Type)
|
||||||
|
}
|
||||||
if a.TTL != 12345 {
|
if a.TTL != 12345 {
|
||||||
t.Fatalf("wrong ttl: %d.", a.TTL)
|
t.Fatalf("wrong ttl: %d.", a.TTL)
|
||||||
}
|
}
|
||||||
if a.From != "0x70c87d191324e6712a591f304b4eedef6ad9bb9d" {
|
if a.SignWith != "0x70c87d191324e6712a591f304b4eedef6ad9bb9d" {
|
||||||
t.Fatalf("wrong From: %x.", a.To)
|
t.Fatalf("wrong From: %s.", a.SignWith)
|
||||||
}
|
}
|
||||||
if a.To != "0x9b2055d370f73ec7d8a03e965129118dc8f5bf83" {
|
if a.Key != "0x9b2055d370f73ec7d8a03e965129118dc8f5bf83" {
|
||||||
t.Fatalf("wrong To: %x.", a.To)
|
t.Fatalf("wrong Key: %s.", a.Key)
|
||||||
}
|
}
|
||||||
if a.KeyName != "shh_test" {
|
|
||||||
t.Fatalf("wrong KeyName: %s.", a.KeyName)
|
if BytesToTopic(a.Topic) != (TopicType{0xf2, 0x6e, 0x77, 0x79}) {
|
||||||
}
|
|
||||||
if a.Topic != (TopicType{0xf2, 0x6e, 0x77, 0x79}) {
|
|
||||||
t.Fatalf("wrong topic: %x.", a.Topic)
|
t.Fatalf("wrong topic: %x.", a.Topic)
|
||||||
}
|
}
|
||||||
if string(a.Padding) != "this is my test string" {
|
if string(a.Padding) != "this is my test string" {
|
||||||
|
@ -243,31 +267,34 @@ func TestUnmarshalPostArgs(t *testing.T) {
|
||||||
if string(a.Payload) != "payload should be pseudorandom" {
|
if string(a.Payload) != "payload should be pseudorandom" {
|
||||||
t.Fatalf("wrong Payload: %s.", string(a.Payload))
|
t.Fatalf("wrong Payload: %s.", string(a.Payload))
|
||||||
}
|
}
|
||||||
if a.WorkTime != 777 {
|
if a.PowTime != 777 {
|
||||||
t.Fatalf("wrong WorkTime: %d.", a.WorkTime)
|
t.Fatalf("wrong PowTime: %d.", a.PowTime)
|
||||||
}
|
}
|
||||||
if a.PoW != 3.1416 {
|
if a.PowTarget != 3.1416 {
|
||||||
t.Fatalf("wrong pow: %f.", a.PoW)
|
t.Fatalf("wrong PowTarget: %f.", a.PowTarget)
|
||||||
}
|
}
|
||||||
if a.FilterID != "test-filter-id" {
|
if a.TargetPeer != "enode://915533f667b1369793ebb9bda022416b1295235a1420799cd87a969467372546d808ebf59c5c9ce23f103d59b61b97df8af91f0908552485975397181b993461@127.0.0.1:12345" {
|
||||||
t.Fatalf("wrong FilterID: %s.", a.FilterID)
|
t.Fatalf("wrong PeerID: %s.", a.TargetPeer)
|
||||||
}
|
|
||||||
if !bytes.Equal(a.PeerID[:], a.Topic[:]) {
|
|
||||||
t.Fatalf("wrong PeerID: %x.", a.PeerID)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitForMessage(api *PublicWhisperAPI, id string, target int) bool {
|
func waitForMessages(api *PublicWhisperAPI, id string, target int) []*WhisperMessage {
|
||||||
for i := 0; i < 64; i++ {
|
// timeout: 2 seconds
|
||||||
all := api.GetMessages(id)
|
result := make([]*WhisperMessage, 0, target)
|
||||||
if len(all) >= target {
|
for i := 0; i < 100; i++ {
|
||||||
return true
|
mail := api.GetSubscriptionMessages(id)
|
||||||
|
if len(mail) > 0 {
|
||||||
|
for _, m := range mail {
|
||||||
|
result = append(result, m)
|
||||||
|
}
|
||||||
|
if len(result) >= target {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
time.Sleep(time.Millisecond * 16)
|
time.Sleep(time.Millisecond * 20)
|
||||||
}
|
}
|
||||||
|
|
||||||
// timeout 1024 milliseconds
|
return result
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIntegrationAsym(t *testing.T) {
|
func TestIntegrationAsym(t *testing.T) {
|
||||||
|
@ -280,7 +307,7 @@ func TestIntegrationAsym(t *testing.T) {
|
||||||
api.Start()
|
api.Start()
|
||||||
defer api.Stop()
|
defer api.Stop()
|
||||||
|
|
||||||
sig, err := api.NewIdentity()
|
sig, err := api.NewKeyPair()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed NewIdentity: %s.", err)
|
t.Fatalf("failed NewIdentity: %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -288,7 +315,7 @@ func TestIntegrationAsym(t *testing.T) {
|
||||||
t.Fatalf("wrong signature")
|
t.Fatalf("wrong signature")
|
||||||
}
|
}
|
||||||
|
|
||||||
exist, err := api.HasIdentity(sig)
|
exist, err := api.HasKeyPair(sig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed HasIdentity: %s.", err)
|
t.Fatalf("failed HasIdentity: %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -296,7 +323,12 @@ func TestIntegrationAsym(t *testing.T) {
|
||||||
t.Fatalf("failed HasIdentity: false negative.")
|
t.Fatalf("failed HasIdentity: false negative.")
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := api.NewIdentity()
|
sigPubKey, err := api.GetPublicKey(sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed GetPublicKey: %s.", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := api.NewKeyPair()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed NewIdentity(): %s.", err)
|
t.Fatalf("failed NewIdentity(): %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -304,42 +336,46 @@ func TestIntegrationAsym(t *testing.T) {
|
||||||
t.Fatalf("wrong key")
|
t.Fatalf("wrong key")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dstPubKey, err := api.GetPublicKey(key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed GetPublicKey: %s.", err)
|
||||||
|
}
|
||||||
|
|
||||||
var topics [2]TopicType
|
var topics [2]TopicType
|
||||||
topics[0] = TopicType{0x00, 0x64, 0x00, 0xff}
|
topics[0] = TopicType{0x00, 0x64, 0x00, 0xff}
|
||||||
topics[1] = TopicType{0xf2, 0x6e, 0x77, 0x79}
|
topics[1] = TopicType{0xf2, 0x6e, 0x77, 0x79}
|
||||||
var f WhisperFilterArgs
|
var f WhisperFilterArgs
|
||||||
f.To = key
|
f.Symmetric = false
|
||||||
f.From = sig
|
f.Key = key
|
||||||
f.Topics = topics[:]
|
f.SignedWith = sigPubKey.String()
|
||||||
f.PoW = MinimumPoW / 2
|
f.Topics = make([][]byte, 2)
|
||||||
f.AcceptP2P = true
|
f.Topics[0] = topics[0][:]
|
||||||
|
f.Topics[1] = topics[1][:]
|
||||||
|
f.MinPoW = DefaultMinimumPoW / 2
|
||||||
|
f.AllowP2P = true
|
||||||
|
|
||||||
id, err := api.NewFilter(f)
|
id, err := api.Subscribe(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new filter: %s.", err)
|
t.Fatalf("failed to create new filter: %s.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var p PostArgs
|
var p PostArgs
|
||||||
|
p.Type = "asym"
|
||||||
p.TTL = 2
|
p.TTL = 2
|
||||||
p.From = f.From
|
p.SignWith = sig
|
||||||
p.To = f.To
|
p.Key = dstPubKey.String()
|
||||||
p.Padding = []byte("test string")
|
p.Padding = []byte("test string")
|
||||||
p.Payload = []byte("extended test string")
|
p.Payload = []byte("extended test string")
|
||||||
p.PoW = MinimumPoW
|
p.PowTarget = DefaultMinimumPoW
|
||||||
p.Topic = TopicType{0xf2, 0x6e, 0x77, 0x79}
|
p.PowTime = 2
|
||||||
p.WorkTime = 2
|
p.Topic = hexutil.Bytes{0xf2, 0x6e, 0x77, 0x79} // topics[1]
|
||||||
|
|
||||||
err = api.Post(p)
|
err = api.Post(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to post message: %s.", err)
|
t.Errorf("failed to post message: %s.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ok := waitForMessage(api, id, 1)
|
mail := waitForMessages(api, id, 1)
|
||||||
if !ok {
|
|
||||||
t.Fatalf("failed to receive first message: timeout.")
|
|
||||||
}
|
|
||||||
|
|
||||||
mail := api.GetFilterChanges(id)
|
|
||||||
if len(mail) != 1 {
|
if len(mail) != 1 {
|
||||||
t.Fatalf("failed to GetFilterChanges: got %d messages.", len(mail))
|
t.Fatalf("failed to GetFilterChanges: got %d messages.", len(mail))
|
||||||
}
|
}
|
||||||
|
@ -356,12 +392,7 @@ func TestIntegrationAsym(t *testing.T) {
|
||||||
t.Fatalf("failed to post next message: %s.", err)
|
t.Fatalf("failed to post next message: %s.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = waitForMessage(api, id, 2)
|
mail = waitForMessages(api, id, 1)
|
||||||
if !ok {
|
|
||||||
t.Fatalf("failed to receive second message: timeout.")
|
|
||||||
}
|
|
||||||
|
|
||||||
mail = api.GetFilterChanges(id)
|
|
||||||
if len(mail) != 1 {
|
if len(mail) != 1 {
|
||||||
t.Fatalf("failed to GetFilterChanges: got %d messages.", len(mail))
|
t.Fatalf("failed to GetFilterChanges: got %d messages.", len(mail))
|
||||||
}
|
}
|
||||||
|
@ -382,21 +413,25 @@ func TestIntegrationSym(t *testing.T) {
|
||||||
api.Start()
|
api.Start()
|
||||||
defer api.Stop()
|
defer api.Stop()
|
||||||
|
|
||||||
keyname := "schluessel"
|
symKeyID, err := api.GenerateSymmetricKey()
|
||||||
err := api.GenerateSymKey(keyname)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed GenerateSymKey: %s.", err)
|
t.Fatalf("failed GenerateSymKey: %s.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig, err := api.NewIdentity()
|
sig, err := api.NewKeyPair()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed NewIdentity: %s.", err)
|
t.Fatalf("failed NewKeyPair: %s.", err)
|
||||||
}
|
}
|
||||||
if len(sig) == 0 {
|
if len(sig) == 0 {
|
||||||
t.Fatalf("wrong signature")
|
t.Fatalf("wrong signature")
|
||||||
}
|
}
|
||||||
|
|
||||||
exist, err := api.HasIdentity(sig)
|
sigPubKey, err := api.GetPublicKey(sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed GetPublicKey: %s.", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
exist, err := api.HasKeyPair(sig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed HasIdentity: %s.", err)
|
t.Fatalf("failed HasIdentity: %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -408,38 +443,37 @@ func TestIntegrationSym(t *testing.T) {
|
||||||
topics[0] = TopicType{0x00, 0x7f, 0x80, 0xff}
|
topics[0] = TopicType{0x00, 0x7f, 0x80, 0xff}
|
||||||
topics[1] = TopicType{0xf2, 0x6e, 0x77, 0x79}
|
topics[1] = TopicType{0xf2, 0x6e, 0x77, 0x79}
|
||||||
var f WhisperFilterArgs
|
var f WhisperFilterArgs
|
||||||
f.KeyName = keyname
|
f.Symmetric = true
|
||||||
f.Topics = topics[:]
|
f.Key = symKeyID
|
||||||
f.PoW = 0.324
|
f.Topics = make([][]byte, 2)
|
||||||
f.From = sig
|
f.Topics[0] = topics[0][:]
|
||||||
f.AcceptP2P = false
|
f.Topics[1] = topics[1][:]
|
||||||
|
f.MinPoW = 0.324
|
||||||
|
f.SignedWith = sigPubKey.String()
|
||||||
|
f.AllowP2P = false
|
||||||
|
|
||||||
id, err := api.NewFilter(f)
|
id, err := api.Subscribe(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new filter: %s.", err)
|
t.Fatalf("failed to create new filter: %s.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var p PostArgs
|
var p PostArgs
|
||||||
|
p.Type = "sym"
|
||||||
p.TTL = 1
|
p.TTL = 1
|
||||||
p.KeyName = keyname
|
p.Key = symKeyID
|
||||||
p.From = f.From
|
p.SignWith = sig
|
||||||
p.Padding = []byte("test string")
|
p.Padding = []byte("test string")
|
||||||
p.Payload = []byte("extended test string")
|
p.Payload = []byte("extended test string")
|
||||||
p.PoW = MinimumPoW
|
p.PowTarget = DefaultMinimumPoW
|
||||||
p.Topic = TopicType{0xf2, 0x6e, 0x77, 0x79}
|
p.PowTime = 2
|
||||||
p.WorkTime = 2
|
p.Topic = hexutil.Bytes{0xf2, 0x6e, 0x77, 0x79}
|
||||||
|
|
||||||
err = api.Post(p)
|
err = api.Post(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to post first message: %s.", err)
|
t.Fatalf("failed to post first message: %s.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ok := waitForMessage(api, id, 1)
|
mail := waitForMessages(api, id, 1)
|
||||||
if !ok {
|
|
||||||
t.Fatalf("failed to receive first message: timeout.")
|
|
||||||
}
|
|
||||||
|
|
||||||
mail := api.GetFilterChanges(id)
|
|
||||||
if len(mail) != 1 {
|
if len(mail) != 1 {
|
||||||
t.Fatalf("failed GetFilterChanges: got %d messages.", len(mail))
|
t.Fatalf("failed GetFilterChanges: got %d messages.", len(mail))
|
||||||
}
|
}
|
||||||
|
@ -456,12 +490,7 @@ func TestIntegrationSym(t *testing.T) {
|
||||||
t.Fatalf("failed to post second message: %s.", err)
|
t.Fatalf("failed to post second message: %s.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = waitForMessage(api, id, 2)
|
mail = waitForMessages(api, id, 1)
|
||||||
if !ok {
|
|
||||||
t.Fatalf("failed to receive second message: timeout.")
|
|
||||||
}
|
|
||||||
|
|
||||||
mail = api.GetFilterChanges(id)
|
|
||||||
if len(mail) != 1 {
|
if len(mail) != 1 {
|
||||||
t.Fatalf("failed second GetFilterChanges: got %d messages.", len(mail))
|
t.Fatalf("failed second GetFilterChanges: got %d messages.", len(mail))
|
||||||
}
|
}
|
||||||
|
@ -482,21 +511,20 @@ func TestIntegrationSymWithFilter(t *testing.T) {
|
||||||
api.Start()
|
api.Start()
|
||||||
defer api.Stop()
|
defer api.Stop()
|
||||||
|
|
||||||
keyname := "schluessel"
|
symKeyID, err := api.GenerateSymmetricKey()
|
||||||
err := api.GenerateSymKey(keyname)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to GenerateSymKey: %s.", err)
|
t.Fatalf("failed to GenerateSymKey: %s.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig, err := api.NewIdentity()
|
sigKeyID, err := api.NewKeyPair()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed NewIdentity: %s.", err)
|
t.Fatalf("failed NewIdentity: %s.", err)
|
||||||
}
|
}
|
||||||
if len(sig) == 0 {
|
if len(sigKeyID) == 0 {
|
||||||
t.Fatalf("wrong signature.")
|
t.Fatalf("wrong signature.")
|
||||||
}
|
}
|
||||||
|
|
||||||
exist, err := api.HasIdentity(sig)
|
exist, err := api.HasKeyPair(sigKeyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed HasIdentity: %s.", err)
|
t.Fatalf("failed HasIdentity: %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -504,42 +532,46 @@ func TestIntegrationSymWithFilter(t *testing.T) {
|
||||||
t.Fatalf("failed HasIdentity: does not exist.")
|
t.Fatalf("failed HasIdentity: does not exist.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sigPubKey, err := api.GetPublicKey(sigKeyID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed GetPublicKey: %s.", err)
|
||||||
|
}
|
||||||
|
|
||||||
var topics [2]TopicType
|
var topics [2]TopicType
|
||||||
topics[0] = TopicType{0x00, 0x7f, 0x80, 0xff}
|
topics[0] = TopicType{0x00, 0x7f, 0x80, 0xff}
|
||||||
topics[1] = TopicType{0xf2, 0x6e, 0x77, 0x79}
|
topics[1] = TopicType{0xf2, 0x6e, 0x77, 0x79}
|
||||||
var f WhisperFilterArgs
|
var f WhisperFilterArgs
|
||||||
f.KeyName = keyname
|
f.Symmetric = true
|
||||||
f.Topics = topics[:]
|
f.Key = symKeyID
|
||||||
f.PoW = 0.324
|
f.Topics = make([][]byte, 2)
|
||||||
f.From = sig
|
f.Topics[0] = topics[0][:]
|
||||||
f.AcceptP2P = false
|
f.Topics[1] = topics[1][:]
|
||||||
|
f.MinPoW = 0.324
|
||||||
|
f.SignedWith = sigPubKey.String()
|
||||||
|
f.AllowP2P = false
|
||||||
|
|
||||||
id, err := api.NewFilter(f)
|
id, err := api.Subscribe(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new filter: %s.", err)
|
t.Fatalf("failed to create new filter: %s.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var p PostArgs
|
var p PostArgs
|
||||||
|
p.Type = "sym"
|
||||||
p.TTL = 1
|
p.TTL = 1
|
||||||
p.FilterID = id
|
p.Key = symKeyID
|
||||||
p.From = sig
|
p.SignWith = sigKeyID
|
||||||
p.Padding = []byte("test string")
|
p.Padding = []byte("test string")
|
||||||
p.Payload = []byte("extended test string")
|
p.Payload = []byte("extended test string")
|
||||||
p.PoW = MinimumPoW
|
p.PowTarget = DefaultMinimumPoW
|
||||||
p.Topic = TopicType{0xf2, 0x6e, 0x77, 0x79}
|
p.PowTime = 2
|
||||||
p.WorkTime = 2
|
p.Topic = hexutil.Bytes{0xf2, 0x6e, 0x77, 0x79}
|
||||||
|
|
||||||
err = api.Post(p)
|
err = api.Post(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to post message: %s.", err)
|
t.Fatalf("failed to post message: %s.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ok := waitForMessage(api, id, 1)
|
mail := waitForMessages(api, id, 1)
|
||||||
if !ok {
|
|
||||||
t.Fatalf("failed to receive first message: timeout.")
|
|
||||||
}
|
|
||||||
|
|
||||||
mail := api.GetFilterChanges(id)
|
|
||||||
if len(mail) != 1 {
|
if len(mail) != 1 {
|
||||||
t.Fatalf("failed to GetFilterChanges: got %d messages.", len(mail))
|
t.Fatalf("failed to GetFilterChanges: got %d messages.", len(mail))
|
||||||
}
|
}
|
||||||
|
@ -556,12 +588,7 @@ func TestIntegrationSymWithFilter(t *testing.T) {
|
||||||
t.Fatalf("failed to post next message: %s.", err)
|
t.Fatalf("failed to post next message: %s.", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = waitForMessage(api, id, 2)
|
mail = waitForMessages(api, id, 1)
|
||||||
if !ok {
|
|
||||||
t.Fatalf("failed to receive second message: timeout.")
|
|
||||||
}
|
|
||||||
|
|
||||||
mail = api.GetFilterChanges(id)
|
|
||||||
if len(mail) != 1 {
|
if len(mail) != 1 {
|
||||||
t.Fatalf("failed to GetFilterChanges: got %d messages.", len(mail))
|
t.Fatalf("failed to GetFilterChanges: got %d messages.", len(mail))
|
||||||
}
|
}
|
||||||
|
@ -571,3 +598,83 @@ func TestIntegrationSymWithFilter(t *testing.T) {
|
||||||
t.Fatalf("failed to decrypt second message: %s.", text)
|
t.Fatalf("failed to decrypt second message: %s.", text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestKey(t *testing.T) {
|
||||||
|
w := New()
|
||||||
|
api := NewPublicWhisperAPI(w)
|
||||||
|
if api == nil {
|
||||||
|
t.Fatalf("failed to create API.")
|
||||||
|
}
|
||||||
|
|
||||||
|
k, err := api.AddSymmetricKeyFromPassword("wwww")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create key: %s.", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := api.GetSymmetricKey(k)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get sym key: %s.", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
k2, err := api.AddSymmetricKeyDirect(s)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to add sym key: %s.", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s2, err := api.GetSymmetricKey(k2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get sym key: %s.", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.String() != "0x448652d595bd6ec00b2a9ea220ad6c26592d9bf4cf79023d3c1b30cb681e6e07" {
|
||||||
|
t.Fatalf("wrong key from password: %s", s.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(s, s2) {
|
||||||
|
t.Fatalf("wrong key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubscribe(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
var s string
|
||||||
|
|
||||||
|
w := New()
|
||||||
|
api := NewPublicWhisperAPI(w)
|
||||||
|
if api == nil {
|
||||||
|
t.Fatalf("failed to create API.")
|
||||||
|
}
|
||||||
|
|
||||||
|
symKeyID, err := api.GenerateSymmetricKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to GenerateSymKey: %s.", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var f WhisperFilterArgs
|
||||||
|
f.Symmetric = true
|
||||||
|
f.Key = symKeyID
|
||||||
|
f.Topics = make([][]byte, 5)
|
||||||
|
f.Topics[0] = []byte{0x21}
|
||||||
|
f.Topics[1] = []byte{0xd2, 0xe3}
|
||||||
|
f.Topics[2] = []byte{0x64, 0x75, 0x76}
|
||||||
|
f.Topics[3] = []byte{0xf8, 0xe9, 0xa0, 0xba}
|
||||||
|
f.Topics[4] = []byte{0xcb, 0x3c, 0xdd, 0xee, 0xff}
|
||||||
|
|
||||||
|
s, err = api.Subscribe(f)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Subscribe: false positive.")
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Topics[4] = []byte{}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Subscribe: false positive again.")
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Topics[4] = []byte{0x00}
|
||||||
|
s, err = api.Subscribe(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to subscribe: %s.", err)
|
||||||
|
} else {
|
||||||
|
api.Unsubscribe(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -54,9 +54,10 @@ const (
|
||||||
aesKeyLength = 32
|
aesKeyLength = 32
|
||||||
saltLength = 12
|
saltLength = 12
|
||||||
AESNonceMaxLength = 12
|
AESNonceMaxLength = 12
|
||||||
|
keyIdSize = 32
|
||||||
|
|
||||||
MaxMessageLength = 0x0FFFFF // todo: remove this restriction after testing. this should be regulated by PoW.
|
DefaultMaxMessageLength = 1024 * 1024
|
||||||
MinimumPoW = 10.0 // todo: review after testing.
|
DefaultMinimumPoW = 1.0 // todo: review after testing.
|
||||||
|
|
||||||
padSizeLimitLower = 128 // it can not be less - we don't want to reveal the absence of signature
|
padSizeLimitLower = 128 // it can not be less - we don't want to reveal the absence of signature
|
||||||
padSizeLimitUpper = 256 // just an arbitrary number, could be changed without losing compatibility
|
padSizeLimitUpper = 256 // just an arbitrary number, could be changed without losing compatibility
|
||||||
|
|
|
@ -21,7 +21,6 @@ package whisperv5
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
gmath "math"
|
gmath "math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
@ -83,7 +82,7 @@ func (e *Envelope) isAsymmetric() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Envelope) Ver() uint64 {
|
func (e *Envelope) Ver() uint64 {
|
||||||
return bytesToIntLittleEndian(e.Version)
|
return bytesToUintLittleEndian(e.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seal closes the envelope by spending the requested amount of time as a proof
|
// Seal closes the envelope by spending the requested amount of time as a proof
|
||||||
|
@ -95,6 +94,9 @@ func (e *Envelope) Seal(options *MessageParams) error {
|
||||||
e.Expiry += options.WorkTime
|
e.Expiry += options.WorkTime
|
||||||
} else {
|
} else {
|
||||||
target = e.powToFirstBit(options.PoW)
|
target = e.powToFirstBit(options.PoW)
|
||||||
|
if target < 1 {
|
||||||
|
target = 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := make([]byte, 64)
|
buf := make([]byte, 64)
|
||||||
|
@ -118,7 +120,7 @@ func (e *Envelope) Seal(options *MessageParams) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if target > 0 && bestBit < target {
|
if target > 0 && bestBit < target {
|
||||||
return errors.New("Failed to reach the PoW target, insufficient work time")
|
return fmt.Errorf("failed to reach the PoW target, specified pow time (%d seconds) was insufficient", options.WorkTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -18,7 +18,6 @@ package whisperv5
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
crand "crypto/rand"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -30,9 +29,9 @@ type Filter struct {
|
||||||
Src *ecdsa.PublicKey // Sender of the message
|
Src *ecdsa.PublicKey // Sender of the message
|
||||||
KeyAsym *ecdsa.PrivateKey // Private Key of recipient
|
KeyAsym *ecdsa.PrivateKey // Private Key of recipient
|
||||||
KeySym []byte // Key associated with the Topic
|
KeySym []byte // Key associated with the Topic
|
||||||
Topics []TopicType // Topics to filter messages with
|
Topics [][]byte // Topics to filter messages with
|
||||||
PoW float64 // Proof of work as described in the Whisper spec
|
PoW float64 // Proof of work as described in the Whisper spec
|
||||||
AcceptP2P bool // Indicates whether this filter is interested in direct peer-to-peer messages
|
AllowP2P bool // Indicates whether this filter is interested in direct peer-to-peer messages
|
||||||
SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization
|
SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization
|
||||||
|
|
||||||
Messages map[common.Hash]*ReceivedMessage
|
Messages map[common.Hash]*ReceivedMessage
|
||||||
|
@ -52,47 +51,35 @@ func NewFilters(w *Whisper) *Filters {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *Filters) generateRandomID() (id string, err error) {
|
|
||||||
buf := make([]byte, 20)
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
_, err = crand.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !validateSymmetricKey(buf) {
|
|
||||||
err = fmt.Errorf("error in generateRandomID: crypto/rand failed to generate random data")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
id = common.Bytes2Hex(buf)
|
|
||||||
if fs.watchers[id] != nil {
|
|
||||||
err = fmt.Errorf("error in generateRandomID: generated same ID twice")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return id, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *Filters) Install(watcher *Filter) (string, error) {
|
func (fs *Filters) Install(watcher *Filter) (string, error) {
|
||||||
if watcher.Messages == nil {
|
if watcher.Messages == nil {
|
||||||
watcher.Messages = make(map[common.Hash]*ReceivedMessage)
|
watcher.Messages = make(map[common.Hash]*ReceivedMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id, err := GenerateRandomID()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
fs.mutex.Lock()
|
fs.mutex.Lock()
|
||||||
defer fs.mutex.Unlock()
|
defer fs.mutex.Unlock()
|
||||||
|
|
||||||
id, err := fs.generateRandomID()
|
if fs.watchers[id] != nil {
|
||||||
if err == nil {
|
return "", fmt.Errorf("failed to generate unique ID")
|
||||||
fs.watchers[id] = watcher
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs.watchers[id] = watcher
|
||||||
return id, err
|
return id, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *Filters) Uninstall(id string) {
|
func (fs *Filters) Uninstall(id string) bool {
|
||||||
fs.mutex.Lock()
|
fs.mutex.Lock()
|
||||||
defer fs.mutex.Unlock()
|
defer fs.mutex.Unlock()
|
||||||
delete(fs.watchers, id)
|
if fs.watchers[id] != nil {
|
||||||
|
delete(fs.watchers, id)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *Filters) Get(id string) *Filter {
|
func (fs *Filters) Get(id string) *Filter {
|
||||||
|
@ -102,11 +89,16 @@ func (fs *Filters) Get(id string) *Filter {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
|
func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
|
||||||
fs.mutex.RLock()
|
|
||||||
var msg *ReceivedMessage
|
var msg *ReceivedMessage
|
||||||
for j, watcher := range fs.watchers {
|
|
||||||
if p2pMessage && !watcher.AcceptP2P {
|
fs.mutex.RLock()
|
||||||
log.Trace(fmt.Sprintf("msg [%x], filter [%s]: p2p messages are not allowed", env.Hash(), j))
|
defer fs.mutex.RUnlock()
|
||||||
|
|
||||||
|
i := -1 // only used for logging info
|
||||||
|
for _, watcher := range fs.watchers {
|
||||||
|
i++
|
||||||
|
if p2pMessage && !watcher.AllowP2P {
|
||||||
|
log.Trace(fmt.Sprintf("msg [%x], filter [%d]: p2p messages are not allowed", env.Hash(), i))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,22 +110,32 @@ func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) {
|
||||||
if match {
|
if match {
|
||||||
msg = env.Open(watcher)
|
msg = env.Open(watcher)
|
||||||
if msg == nil {
|
if msg == nil {
|
||||||
log.Trace(fmt.Sprintf("msg [%x], filter [%s]: failed to open", env.Hash(), j))
|
log.Trace("processing message: failed to open", "message", env.Hash().Hex(), "filter", i)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Trace(fmt.Sprintf("msg [%x], filter [%s]: does not match", env.Hash(), j))
|
log.Trace("processing message: does not match", "message", env.Hash().Hex(), "filter", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if match && msg != nil {
|
if match && msg != nil {
|
||||||
|
log.Trace("processing message: decrypted", "hash", env.Hash().Hex())
|
||||||
watcher.Trigger(msg)
|
watcher.Trigger(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fs.mutex.RUnlock() // we need to unlock before calling addDecryptedMessage
|
}
|
||||||
|
|
||||||
if msg != nil {
|
func (f *Filter) processEnvelope(env *Envelope) *ReceivedMessage {
|
||||||
fs.whisper.addDecryptedMessage(msg)
|
if f.MatchEnvelope(env) {
|
||||||
|
msg := env.Open(f)
|
||||||
|
if msg != nil {
|
||||||
|
return msg
|
||||||
|
} else {
|
||||||
|
log.Trace("processing envelope: failed to open", "hash", env.Hash().Hex())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Trace("processing envelope: does not match", "hash", env.Hash().Hex())
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Filter) expectsAsymmetricEncryption() bool {
|
func (f *Filter) expectsAsymmetricEncryption() bool {
|
||||||
|
@ -200,20 +202,33 @@ func (f *Filter) MatchTopic(topic TopicType) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range f.Topics {
|
for _, bt := range f.Topics {
|
||||||
if t == topic {
|
if matchSingleTopic(topic, bt) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func matchSingleTopic(topic TopicType, bt []byte) bool {
|
||||||
|
if len(bt) > 4 {
|
||||||
|
bt = bt[:4]
|
||||||
|
}
|
||||||
|
|
||||||
|
for j, b := range bt {
|
||||||
|
if topic[j] != b {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool {
|
func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool {
|
||||||
if !ValidatePublicKey(a) {
|
if !ValidatePublicKey(a) {
|
||||||
return false
|
return false
|
||||||
} else if !ValidatePublicKey(b) {
|
} else if !ValidatePublicKey(b) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// the Curve is always the same, just compare the points
|
// the curve is always the same, just compare the points
|
||||||
return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0
|
return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,8 +53,9 @@ func generateFilter(t *testing.T, symmetric bool) (*Filter, error) {
|
||||||
f.Messages = make(map[common.Hash]*ReceivedMessage)
|
f.Messages = make(map[common.Hash]*ReceivedMessage)
|
||||||
|
|
||||||
const topicNum = 8
|
const topicNum = 8
|
||||||
f.Topics = make([]TopicType, topicNum)
|
f.Topics = make([][]byte, topicNum)
|
||||||
for i := 0; i < topicNum; i++ {
|
for i := 0; i < topicNum; i++ {
|
||||||
|
f.Topics[i] = make([]byte, 4)
|
||||||
mrand.Read(f.Topics[i][:])
|
mrand.Read(f.Topics[i][:])
|
||||||
f.Topics[i][0] = 0x01
|
f.Topics[i][0] = 0x01
|
||||||
}
|
}
|
||||||
|
@ -108,7 +109,7 @@ func TestInstallFilters(t *testing.T) {
|
||||||
t.Fatalf("seed %d: failed to install filter: %s", seed, err)
|
t.Fatalf("seed %d: failed to install filter: %s", seed, err)
|
||||||
}
|
}
|
||||||
tst[i].id = j
|
tst[i].id = j
|
||||||
if len(j) != 40 {
|
if len(j) != keyIdSize*2 {
|
||||||
t.Fatalf("seed %d: wrong filter id size [%d]", seed, len(j))
|
t.Fatalf("seed %d: wrong filter id size [%d]", seed, len(j))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,8 +195,8 @@ func TestMatchEnvelope(t *testing.T) {
|
||||||
|
|
||||||
// encrypt symmetrically
|
// encrypt symmetrically
|
||||||
i := mrand.Int() % 4
|
i := mrand.Int() % 4
|
||||||
fsym.Topics[i] = params.Topic
|
fsym.Topics[i] = params.Topic[:]
|
||||||
fasym.Topics[i] = params.Topic
|
fasym.Topics[i] = params.Topic[:]
|
||||||
msg = NewSentMessage(params)
|
msg = NewSentMessage(params)
|
||||||
env, err = msg.Wrap(params)
|
env, err = msg.Wrap(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -320,7 +321,7 @@ func TestMatchMessageSym(t *testing.T) {
|
||||||
|
|
||||||
const index = 1
|
const index = 1
|
||||||
params.KeySym = f.KeySym
|
params.KeySym = f.KeySym
|
||||||
params.Topic = f.Topics[index]
|
params.Topic = BytesToTopic(f.Topics[index])
|
||||||
|
|
||||||
sentMessage := NewSentMessage(params)
|
sentMessage := NewSentMessage(params)
|
||||||
env, err := sentMessage.Wrap(params)
|
env, err := sentMessage.Wrap(params)
|
||||||
|
@ -413,7 +414,7 @@ func TestMatchMessageAsym(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = 1
|
const index = 1
|
||||||
params.Topic = f.Topics[index]
|
params.Topic = BytesToTopic(f.Topics[index])
|
||||||
params.Dst = &f.KeyAsym.PublicKey
|
params.Dst = &f.KeyAsym.PublicKey
|
||||||
keySymOrig := params.KeySym
|
keySymOrig := params.KeySym
|
||||||
params.KeySym = nil
|
params.KeySym = nil
|
||||||
|
@ -491,7 +492,7 @@ func cloneFilter(orig *Filter) *Filter {
|
||||||
clone.KeySym = orig.KeySym
|
clone.KeySym = orig.KeySym
|
||||||
clone.Topics = orig.Topics
|
clone.Topics = orig.Topics
|
||||||
clone.PoW = orig.PoW
|
clone.PoW = orig.PoW
|
||||||
clone.AcceptP2P = orig.AcceptP2P
|
clone.AllowP2P = orig.AllowP2P
|
||||||
clone.SymKeyHash = orig.SymKeyHash
|
clone.SymKeyHash = orig.SymKeyHash
|
||||||
return &clone
|
return &clone
|
||||||
}
|
}
|
||||||
|
@ -504,7 +505,7 @@ func generateCompatibeEnvelope(t *testing.T, f *Filter) *Envelope {
|
||||||
}
|
}
|
||||||
|
|
||||||
params.KeySym = f.KeySym
|
params.KeySym = f.KeySym
|
||||||
params.Topic = f.Topics[2]
|
params.Topic = BytesToTopic(f.Topics[2])
|
||||||
sentMessage := NewSentMessage(params)
|
sentMessage := NewSentMessage(params)
|
||||||
env, err := sentMessage.Wrap(params)
|
env, err := sentMessage.Wrap(params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -655,7 +656,7 @@ func TestWatchers(t *testing.T) {
|
||||||
if f == nil {
|
if f == nil {
|
||||||
t.Fatalf("failed to get the filter with seed %d.", seed)
|
t.Fatalf("failed to get the filter with seed %d.", seed)
|
||||||
}
|
}
|
||||||
f.AcceptP2P = true
|
f.AllowP2P = true
|
||||||
total = 0
|
total = 0
|
||||||
filters.NotifyWatchers(envelopes[0], true)
|
filters.NotifyWatchers(envelopes[0], true)
|
||||||
|
|
||||||
|
@ -668,3 +669,40 @@ func TestWatchers(t *testing.T) {
|
||||||
t.Fatalf("failed with seed %d: total: got %d, want 1.", seed, total)
|
t.Fatalf("failed with seed %d: total: got %d, want 1.", seed, total)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVariableTopics(t *testing.T) {
|
||||||
|
InitSingleTest()
|
||||||
|
|
||||||
|
var match bool
|
||||||
|
params, err := generateMessageParams()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
||||||
|
}
|
||||||
|
msg := NewSentMessage(params)
|
||||||
|
env, err := msg.Wrap(params)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := generateFilter(t, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
arr := make([]byte, i+1, 4)
|
||||||
|
copy(arr, env.Topic[:i+1])
|
||||||
|
|
||||||
|
f.Topics[4] = arr
|
||||||
|
match = f.MatchEnvelope(env)
|
||||||
|
if !match {
|
||||||
|
t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Topics[4][i]++
|
||||||
|
match = f.MatchEnvelope(env)
|
||||||
|
if match {
|
||||||
|
t.Fatalf("MatchEnvelope symmetric with seed %d, step %d: false positive.", seed, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -25,8 +25,6 @@ import (
|
||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
mrand "math/rand"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
@ -102,14 +100,18 @@ func NewSentMessage(params *MessageParams) *SentMessage {
|
||||||
msg := SentMessage{}
|
msg := SentMessage{}
|
||||||
msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Payload)+signatureLength+padSizeLimitUpper)
|
msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Payload)+signatureLength+padSizeLimitUpper)
|
||||||
msg.Raw[0] = 0 // set all the flags to zero
|
msg.Raw[0] = 0 // set all the flags to zero
|
||||||
msg.appendPadding(params)
|
err := msg.appendPadding(params)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to create NewSentMessage", "err", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
msg.Raw = append(msg.Raw, params.Payload...)
|
msg.Raw = append(msg.Raw, params.Payload...)
|
||||||
return &msg
|
return &msg
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendPadding appends the pseudorandom padding bytes and sets the padding flag.
|
// appendPadding appends the pseudorandom padding bytes and sets the padding flag.
|
||||||
// The last byte contains the size of padding (thus, its size must not exceed 256).
|
// The last byte contains the size of padding (thus, its size must not exceed 256).
|
||||||
func (msg *SentMessage) appendPadding(params *MessageParams) {
|
func (msg *SentMessage) appendPadding(params *MessageParams) error {
|
||||||
total := len(params.Payload) + 1
|
total := len(params.Payload) + 1
|
||||||
if params.Src != nil {
|
if params.Src != nil {
|
||||||
total += signatureLength
|
total += signatureLength
|
||||||
|
@ -128,7 +130,10 @@ func (msg *SentMessage) appendPadding(params *MessageParams) {
|
||||||
panic("please fix the padding algorithm before releasing new version")
|
panic("please fix the padding algorithm before releasing new version")
|
||||||
}
|
}
|
||||||
buf := make([]byte, padSize)
|
buf := make([]byte, padSize)
|
||||||
mrand.Read(buf[1:])
|
_, err := crand.Read(buf[1:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
buf[0] = byte(padSize)
|
buf[0] = byte(padSize)
|
||||||
if params.Padding != nil {
|
if params.Padding != nil {
|
||||||
copy(buf[1:], params.Padding)
|
copy(buf[1:], params.Padding)
|
||||||
|
@ -136,6 +141,7 @@ func (msg *SentMessage) appendPadding(params *MessageParams) {
|
||||||
msg.Raw = append(msg.Raw, buf...)
|
msg.Raw = append(msg.Raw, buf...)
|
||||||
msg.Raw[0] |= byte(0x1) // number of bytes indicating the padding size
|
msg.Raw[0] |= byte(0x1) // number of bytes indicating the padding size
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// sign calculates and sets the cryptographic signature for the message,
|
// sign calculates and sets the cryptographic signature for the message,
|
||||||
|
@ -143,7 +149,7 @@ func (msg *SentMessage) appendPadding(params *MessageParams) {
|
||||||
func (msg *SentMessage) sign(key *ecdsa.PrivateKey) error {
|
func (msg *SentMessage) sign(key *ecdsa.PrivateKey) error {
|
||||||
if isMessageSigned(msg.Raw[0]) {
|
if isMessageSigned(msg.Raw[0]) {
|
||||||
// this should not happen, but no reason to panic
|
// this should not happen, but no reason to panic
|
||||||
log.Error(fmt.Sprintf("Trying to sign a message which was already signed"))
|
log.Error("failed to sign the message: already signed")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +167,7 @@ func (msg *SentMessage) sign(key *ecdsa.PrivateKey) error {
|
||||||
// encryptAsymmetric encrypts a message with a public key.
|
// encryptAsymmetric encrypts a message with a public key.
|
||||||
func (msg *SentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error {
|
func (msg *SentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error {
|
||||||
if !ValidatePublicKey(key) {
|
if !ValidatePublicKey(key) {
|
||||||
return fmt.Errorf("Invalid public key provided for asymmetric encryption")
|
return errors.New("invalid public key provided for asymmetric encryption")
|
||||||
}
|
}
|
||||||
encrypted, err := ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(key), msg.Raw, nil, nil)
|
encrypted, err := ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(key), msg.Raw, nil, nil)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -215,17 +221,6 @@ func (msg *SentMessage) encryptSymmetric(key []byte) (salt []byte, nonce []byte,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap bundles the message into an Envelope to transmit over the network.
|
// Wrap bundles the message into an Envelope to transmit over the network.
|
||||||
//
|
|
||||||
// pow (Proof Of Work) controls how much time to spend on hashing the message,
|
|
||||||
// inherently controlling its priority through the network (smaller hash, bigger
|
|
||||||
// priority).
|
|
||||||
//
|
|
||||||
// The user can control the amount of identity, privacy and encryption through
|
|
||||||
// the options parameter as follows:
|
|
||||||
// - options.From == nil && options.To == nil: anonymous broadcast
|
|
||||||
// - options.From != nil && options.To == nil: signed broadcast (known sender)
|
|
||||||
// - options.From == nil && options.To != nil: encrypted anonymous message
|
|
||||||
// - options.From != nil && options.To != nil: encrypted signed message
|
|
||||||
func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err error) {
|
func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err error) {
|
||||||
if options.TTL == 0 {
|
if options.TTL == 0 {
|
||||||
options.TTL = DefaultTTL
|
options.TTL = DefaultTTL
|
||||||
|
@ -236,17 +231,13 @@ func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(msg.Raw) > MaxMessageLength {
|
|
||||||
log.Error(fmt.Sprintf("Message size must not exceed %d bytes", MaxMessageLength))
|
|
||||||
return nil, errors.New("Oversized message")
|
|
||||||
}
|
|
||||||
var salt, nonce []byte
|
var salt, nonce []byte
|
||||||
if options.Dst != nil {
|
if options.Dst != nil {
|
||||||
err = msg.encryptAsymmetric(options.Dst)
|
err = msg.encryptAsymmetric(options.Dst)
|
||||||
} else if options.KeySym != nil {
|
} else if options.KeySym != nil {
|
||||||
salt, nonce, err = msg.encryptSymmetric(options.KeySym)
|
salt, nonce, err = msg.encryptSymmetric(options.KeySym)
|
||||||
} else {
|
} else {
|
||||||
err = errors.New("Unable to encrypt the message: neither Dst nor Key")
|
err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -258,7 +249,6 @@ func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return envelope, nil
|
return envelope, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,9 +269,8 @@ func (msg *ReceivedMessage) decryptSymmetric(key []byte, salt []byte, nonce []by
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(nonce) != aesgcm.NonceSize() {
|
if len(nonce) != aesgcm.NonceSize() {
|
||||||
info := fmt.Sprintf("Wrong AES nonce size - want: %d, got: %d", len(nonce), aesgcm.NonceSize())
|
log.Error("decrypting the message", "AES nonce size", len(nonce))
|
||||||
log.Error(fmt.Sprintf(info))
|
return errors.New("wrong AES nonce size")
|
||||||
return errors.New(info)
|
|
||||||
}
|
}
|
||||||
decrypted, err := aesgcm.Open(nil, nonce, msg.Raw, nil)
|
decrypted, err := aesgcm.Open(nil, nonce, msg.Raw, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -336,7 +325,7 @@ func (msg *ReceivedMessage) extractPadding(end int) (int, bool) {
|
||||||
paddingSize := 0
|
paddingSize := 0
|
||||||
sz := int(msg.Raw[0] & paddingMask) // number of bytes containing the entire size of padding, could be zero
|
sz := int(msg.Raw[0] & paddingMask) // number of bytes containing the entire size of padding, could be zero
|
||||||
if sz != 0 {
|
if sz != 0 {
|
||||||
paddingSize = int(bytesToIntLittleEndian(msg.Raw[1 : 1+sz]))
|
paddingSize = int(bytesToUintLittleEndian(msg.Raw[1 : 1+sz]))
|
||||||
if paddingSize < sz || paddingSize+1 > end {
|
if paddingSize < sz || paddingSize+1 > end {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
@ -351,7 +340,7 @@ func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey {
|
||||||
|
|
||||||
pub, err := crypto.SigToPub(msg.hash(), msg.Signature)
|
pub, err := crypto.SigToPub(msg.hash(), msg.Signature)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(fmt.Sprintf("Could not get public key from signature: %v", err))
|
log.Error("failed to recover public key from signature", "err", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return pub
|
return pub
|
||||||
|
|
|
@ -55,13 +55,13 @@ func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer {
|
||||||
// into the network.
|
// into the network.
|
||||||
func (p *Peer) start() {
|
func (p *Peer) start() {
|
||||||
go p.update()
|
go p.update()
|
||||||
log.Debug(fmt.Sprintf("%v: whisper started", p.peer))
|
log.Trace("start", "peer", p.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop terminates the peer updater, stopping message forwarding to it.
|
// stop terminates the peer updater, stopping message forwarding to it.
|
||||||
func (p *Peer) stop() {
|
func (p *Peer) stop() {
|
||||||
close(p.quit)
|
close(p.quit)
|
||||||
log.Debug(fmt.Sprintf("%v: whisper stopped", p.peer))
|
log.Trace("stop", "peer", p.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
// handshake sends the protocol initiation status message to the remote peer and
|
// handshake sends the protocol initiation status message to the remote peer and
|
||||||
|
@ -78,19 +78,19 @@ func (p *Peer) handshake() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if packet.Code != statusCode {
|
if packet.Code != statusCode {
|
||||||
return fmt.Errorf("peer sent %x before status packet", packet.Code)
|
return fmt.Errorf("peer [%x] sent packet %x before status packet", p.ID(), packet.Code)
|
||||||
}
|
}
|
||||||
s := rlp.NewStream(packet.Payload, uint64(packet.Size))
|
s := rlp.NewStream(packet.Payload, uint64(packet.Size))
|
||||||
peerVersion, err := s.Uint()
|
peerVersion, err := s.Uint()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("bad status message: %v", err)
|
return fmt.Errorf("peer [%x] sent bad status message: %v", p.ID(), err)
|
||||||
}
|
}
|
||||||
if peerVersion != ProtocolVersion {
|
if peerVersion != ProtocolVersion {
|
||||||
return fmt.Errorf("protocol version mismatch %d != %d", peerVersion, ProtocolVersion)
|
return fmt.Errorf("peer [%x]: protocol version mismatch %d != %d", p.ID(), peerVersion, ProtocolVersion)
|
||||||
}
|
}
|
||||||
// Wait until out own status is consumed too
|
// Wait until out own status is consumed too
|
||||||
if err := <-errc; err != nil {
|
if err := <-errc; err != nil {
|
||||||
return fmt.Errorf("failed to send status packet: %v", err)
|
return fmt.Errorf("peer [%x] failed to send status packet: %v", p.ID(), err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ func (p *Peer) update() {
|
||||||
|
|
||||||
case <-transmit.C:
|
case <-transmit.C:
|
||||||
if err := p.broadcast(); err != nil {
|
if err := p.broadcast(); err != nil {
|
||||||
log.Info(fmt.Sprintf("%v: broadcast failed: %v", p.peer, err))
|
log.Trace("broadcast failed", "reason", err, "peer", p.ID())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ func (p *Peer) broadcast() error {
|
||||||
if err := p2p.Send(p.ws, messagesCode, transmit); err != nil {
|
if err := p2p.Send(p.ws, messagesCode, transmit); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Trace(fmt.Sprint(p.peer, "broadcasted", len(transmit), "message(s)"))
|
log.Trace("broadcast", "num. messages", len(transmit))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,12 +114,13 @@ func initialize(t *testing.T) {
|
||||||
for i := 0; i < NumNodes; i++ {
|
for i := 0; i < NumNodes; i++ {
|
||||||
var node TestNode
|
var node TestNode
|
||||||
node.shh = New()
|
node.shh = New()
|
||||||
node.shh.test = true
|
node.shh.SetMinimumPoW(0.00000001)
|
||||||
node.shh.Start(nil)
|
node.shh.Start(nil)
|
||||||
topics := make([]TopicType, 0)
|
topics := make([]TopicType, 0)
|
||||||
topics = append(topics, sharedTopic)
|
topics = append(topics, sharedTopic)
|
||||||
f := Filter{KeySym: sharedKey, Topics: topics}
|
f := Filter{KeySym: sharedKey}
|
||||||
node.filerId, err = node.shh.Watch(&f)
|
f.Topics = [][]byte{topics[0][:]}
|
||||||
|
node.filerId, err = node.shh.Subscribe(&f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to install the filter: %s.", err)
|
t.Fatalf("failed to install the filter: %s.", err)
|
||||||
}
|
}
|
||||||
|
@ -166,7 +167,7 @@ func stopServers() {
|
||||||
for i := 0; i < NumNodes; i++ {
|
for i := 0; i < NumNodes; i++ {
|
||||||
n := nodes[i]
|
n := nodes[i]
|
||||||
if n != nil {
|
if n != nil {
|
||||||
n.shh.Unwatch(n.filerId)
|
n.shh.Unsubscribe(n.filerId)
|
||||||
n.shh.Stop()
|
n.shh.Stop()
|
||||||
n.server.Stop()
|
n.server.Stop()
|
||||||
}
|
}
|
||||||
|
@ -257,7 +258,7 @@ func sendMsg(t *testing.T, expected bool, id int) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
opt := MessageParams{KeySym: sharedKey, Topic: sharedTopic, Payload: expectedMessage, PoW: 0.00000001}
|
opt := MessageParams{KeySym: sharedKey, Topic: sharedTopic, Payload: expectedMessage, PoW: 0.00000001, WorkTime: 1}
|
||||||
if !expected {
|
if !expected {
|
||||||
opt.KeySym[0]++
|
opt.KeySym[0]++
|
||||||
opt.Topic[0]++
|
opt.Topic[0]++
|
||||||
|
@ -267,12 +268,12 @@ func sendMsg(t *testing.T, expected bool, id int) {
|
||||||
msg := NewSentMessage(&opt)
|
msg := NewSentMessage(&opt)
|
||||||
envelope, err := msg.Wrap(&opt)
|
envelope, err := msg.Wrap(&opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to seal message.")
|
t.Fatalf("failed to seal message: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = nodes[id].shh.Send(envelope)
|
err = nodes[id].shh.Send(envelope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to send message.")
|
t.Fatalf("failed to send message: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,59 +31,62 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/errors"
|
||||||
"golang.org/x/crypto/pbkdf2"
|
"golang.org/x/crypto/pbkdf2"
|
||||||
set "gopkg.in/fatih/set.v0"
|
set "gopkg.in/fatih/set.v0"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Statistics struct {
|
type Statistics struct {
|
||||||
messagesCleared int
|
messagesCleared int
|
||||||
memoryCleared int
|
memoryCleared int
|
||||||
totalMemoryUsed int
|
memoryUsed int
|
||||||
|
cycles int
|
||||||
|
totalMessagesCleared int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whisper represents a dark communication interface through the Ethereum
|
// Whisper represents a dark communication interface through the Ethereum
|
||||||
// network, using its very own P2P communication layer.
|
// network, using its very own P2P communication layer.
|
||||||
type Whisper struct {
|
type Whisper struct {
|
||||||
protocol p2p.Protocol
|
protocol p2p.Protocol // Protocol description and parameters
|
||||||
filters *Filters
|
filters *Filters // Message filters installed with Subscribe function
|
||||||
|
|
||||||
privateKeys map[string]*ecdsa.PrivateKey
|
privateKeys map[string]*ecdsa.PrivateKey // Private key storage
|
||||||
symKeys map[string][]byte
|
symKeys map[string][]byte // Symmetric key storage
|
||||||
keyMu sync.RWMutex
|
keyMu sync.RWMutex // Mutex associated with key storages
|
||||||
|
|
||||||
envelopes map[common.Hash]*Envelope // Pool of envelopes currently tracked by this node
|
envelopes map[common.Hash]*Envelope // Pool of envelopes currently tracked by this node
|
||||||
messages map[common.Hash]*ReceivedMessage // Pool of successfully decrypted messages, which are not expired yet
|
expirations map[uint32]*set.SetNonTS // Message expiration pool
|
||||||
expirations map[uint32]*set.SetNonTS // Message expiration pool
|
poolMu sync.RWMutex // Mutex to sync the message and expiration pools
|
||||||
poolMu sync.RWMutex // Mutex to sync the message and expiration pools
|
|
||||||
|
|
||||||
peers map[*Peer]struct{} // Set of currently active peers
|
peers map[*Peer]struct{} // Set of currently active peers
|
||||||
peerMu sync.RWMutex // Mutex to sync the active peer set
|
peerMu sync.RWMutex // Mutex to sync the active peer set
|
||||||
|
|
||||||
mailServer MailServer
|
messageQueue chan *Envelope // Message queue for normal whisper messages
|
||||||
|
p2pMsgQueue chan *Envelope // Message queue for peer-to-peer messages (not to be forwarded any further)
|
||||||
|
quit chan struct{} // Channel used for graceful exit
|
||||||
|
|
||||||
messageQueue chan *Envelope
|
minPoW float64 // Minimal PoW required by the whisper node
|
||||||
p2pMsgQueue chan *Envelope
|
maxMsgLength int // Maximal message length allowed by the whisper node
|
||||||
quit chan struct{}
|
overflow bool // Indicator of message queue overflow
|
||||||
|
|
||||||
stats Statistics
|
stats Statistics // Statistics of whisper node
|
||||||
|
|
||||||
overflow bool
|
mailServer MailServer // MailServer interface
|
||||||
test bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a Whisper client ready to communicate through the Ethereum P2P network.
|
// New creates a Whisper client ready to communicate through the Ethereum P2P network.
|
||||||
// Param s should be passed if you want to implement mail server, otherwise nil.
|
|
||||||
func New() *Whisper {
|
func New() *Whisper {
|
||||||
whisper := &Whisper{
|
whisper := &Whisper{
|
||||||
privateKeys: make(map[string]*ecdsa.PrivateKey),
|
privateKeys: make(map[string]*ecdsa.PrivateKey),
|
||||||
symKeys: make(map[string][]byte),
|
symKeys: make(map[string][]byte),
|
||||||
envelopes: make(map[common.Hash]*Envelope),
|
envelopes: make(map[common.Hash]*Envelope),
|
||||||
messages: make(map[common.Hash]*ReceivedMessage),
|
|
||||||
expirations: make(map[uint32]*set.SetNonTS),
|
expirations: make(map[uint32]*set.SetNonTS),
|
||||||
peers: make(map[*Peer]struct{}),
|
peers: make(map[*Peer]struct{}),
|
||||||
messageQueue: make(chan *Envelope, messageQueueLimit),
|
messageQueue: make(chan *Envelope, messageQueueLimit),
|
||||||
p2pMsgQueue: make(chan *Envelope, messageQueueLimit),
|
p2pMsgQueue: make(chan *Envelope, messageQueueLimit),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
|
minPoW: DefaultMinimumPoW,
|
||||||
|
maxMsgLength: DefaultMaxMessageLength,
|
||||||
}
|
}
|
||||||
whisper.filters = NewFilters(whisper)
|
whisper.filters = NewFilters(whisper)
|
||||||
|
|
||||||
|
@ -110,6 +113,8 @@ func (w *Whisper) APIs() []rpc.API {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterServer registers MailServer interface.
|
||||||
|
// MailServer will process all the incoming messages with p2pRequestCode.
|
||||||
func (w *Whisper) RegisterServer(server MailServer) {
|
func (w *Whisper) RegisterServer(server MailServer) {
|
||||||
w.mailServer = server
|
w.mailServer = server
|
||||||
}
|
}
|
||||||
|
@ -124,6 +129,25 @@ func (w *Whisper) Version() uint {
|
||||||
return w.protocol.Version
|
return w.protocol.Version
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetMaxMessageLength sets the maximal message length allowed by this node
|
||||||
|
func (w *Whisper) SetMaxMessageLength(val int) error {
|
||||||
|
if val <= 0 {
|
||||||
|
return fmt.Errorf("invalid message length: %d", val)
|
||||||
|
}
|
||||||
|
w.maxMsgLength = val
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMinimumPoW sets the minimal PoW required by this node
|
||||||
|
func (w *Whisper) SetMinimumPoW(val float64) error {
|
||||||
|
if val <= 0.0 {
|
||||||
|
return fmt.Errorf("invalid PoW: %f", val)
|
||||||
|
}
|
||||||
|
w.minPoW = val
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPeer retrieves peer by ID
|
||||||
func (w *Whisper) getPeer(peerID []byte) (*Peer, error) {
|
func (w *Whisper) getPeer(peerID []byte) (*Peer, error) {
|
||||||
w.peerMu.Lock()
|
w.peerMu.Lock()
|
||||||
defer w.peerMu.Unlock()
|
defer w.peerMu.Unlock()
|
||||||
|
@ -136,9 +160,9 @@ func (w *Whisper) getPeer(peerID []byte) (*Peer, error) {
|
||||||
return nil, fmt.Errorf("Could not find peer with ID: %x", peerID)
|
return nil, fmt.Errorf("Could not find peer with ID: %x", peerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarkPeerTrusted marks specific peer trusted, which will allow it
|
// AllowP2PMessagesFromPeer marks specific peer trusted,
|
||||||
// to send historic (expired) messages.
|
// which will allow it to send historic (expired) messages.
|
||||||
func (w *Whisper) MarkPeerTrusted(peerID []byte) error {
|
func (w *Whisper) AllowP2PMessagesFromPeer(peerID []byte) error {
|
||||||
p, err := w.getPeer(peerID)
|
p, err := w.getPeer(peerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -147,6 +171,11 @@ func (w *Whisper) MarkPeerTrusted(peerID []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequestHistoricMessages sends a message with p2pRequestCode to a specific peer,
|
||||||
|
// which is known to implement MailServer interface, and is supposed to process this
|
||||||
|
// request and respond with a number of peer-to-peer messages (possibly expired),
|
||||||
|
// which are not supposed to be forwarded any further.
|
||||||
|
// The whisper protocol is agnostic of the format and contents of envelope.
|
||||||
func (w *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) error {
|
func (w *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) error {
|
||||||
p, err := w.getPeer(peerID)
|
p, err := w.getPeer(peerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -156,153 +185,226 @@ func (w *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) err
|
||||||
return p2p.Send(p.ws, p2pRequestCode, envelope)
|
return p2p.Send(p.ws, p2pRequestCode, envelope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendP2PMessage sends a peer-to-peer message to a specific peer.
|
||||||
func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error {
|
func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error {
|
||||||
p, err := w.getPeer(peerID)
|
p, err := w.getPeer(peerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return p2p.Send(p.ws, p2pCode, envelope)
|
return w.SendP2PDirect(p, envelope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendP2PDirect sends a peer-to-peer message to a specific peer.
|
||||||
func (w *Whisper) SendP2PDirect(peer *Peer, envelope *Envelope) error {
|
func (w *Whisper) SendP2PDirect(peer *Peer, envelope *Envelope) error {
|
||||||
return p2p.Send(peer.ws, p2pCode, envelope)
|
return p2p.Send(peer.ws, p2pCode, envelope)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIdentity generates a new cryptographic identity for the client, and injects
|
// NewKeyPair generates a new cryptographic identity for the client, and injects
|
||||||
// it into the known identities for message decryption.
|
// it into the known identities for message decryption. Returns ID of the new key pair.
|
||||||
func (w *Whisper) NewIdentity() *ecdsa.PrivateKey {
|
func (w *Whisper) NewKeyPair() (string, error) {
|
||||||
key, err := crypto.GenerateKey()
|
key, err := crypto.GenerateKey()
|
||||||
if err != nil || !validatePrivateKey(key) {
|
if err != nil || !validatePrivateKey(key) {
|
||||||
key, err = crypto.GenerateKey() // retry once
|
key, err = crypto.GenerateKey() // retry once
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return "", err
|
||||||
}
|
}
|
||||||
if !validatePrivateKey(key) {
|
if !validatePrivateKey(key) {
|
||||||
panic("Failed to generate valid key")
|
return "", fmt.Errorf("failed to generate valid key")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id, err := GenerateRandomID()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to generate ID: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
w.keyMu.Lock()
|
w.keyMu.Lock()
|
||||||
defer w.keyMu.Unlock()
|
defer w.keyMu.Unlock()
|
||||||
w.privateKeys[common.ToHex(crypto.FromECDSAPub(&key.PublicKey))] = key
|
|
||||||
return key
|
if w.privateKeys[id] != nil {
|
||||||
|
return "", fmt.Errorf("failed to generate unique ID")
|
||||||
|
}
|
||||||
|
w.privateKeys[id] = key
|
||||||
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteIdentity deletes the specified key if it exists.
|
// DeleteKeyPair deletes the specified key if it exists.
|
||||||
func (w *Whisper) DeleteIdentity(key string) {
|
func (w *Whisper) DeleteKeyPair(key string) bool {
|
||||||
w.keyMu.Lock()
|
w.keyMu.Lock()
|
||||||
defer w.keyMu.Unlock()
|
defer w.keyMu.Unlock()
|
||||||
delete(w.privateKeys, key)
|
|
||||||
|
if w.privateKeys[key] != nil {
|
||||||
|
delete(w.privateKeys, key)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasIdentity checks if the the whisper node is configured with the private key
|
// HasKeyPair checks if the the whisper node is configured with the private key
|
||||||
// of the specified public pair.
|
// of the specified public pair.
|
||||||
func (w *Whisper) HasIdentity(pubKey string) bool {
|
func (w *Whisper) HasKeyPair(id string) bool {
|
||||||
w.keyMu.RLock()
|
w.keyMu.RLock()
|
||||||
defer w.keyMu.RUnlock()
|
defer w.keyMu.RUnlock()
|
||||||
return w.privateKeys[pubKey] != nil
|
return w.privateKeys[id] != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetIdentity retrieves the private key of the specified public identity.
|
// GetPrivateKey retrieves the private key of the specified identity.
|
||||||
func (w *Whisper) GetIdentity(pubKey string) *ecdsa.PrivateKey {
|
func (w *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) {
|
||||||
w.keyMu.RLock()
|
w.keyMu.RLock()
|
||||||
defer w.keyMu.RUnlock()
|
defer w.keyMu.RUnlock()
|
||||||
return w.privateKeys[pubKey]
|
key := w.privateKeys[id]
|
||||||
|
if key == nil {
|
||||||
|
return nil, fmt.Errorf("invalid id")
|
||||||
|
}
|
||||||
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Whisper) GenerateSymKey(name string) error {
|
// GenerateSymKey generates a random symmetric key and stores it under id,
|
||||||
|
// which is then returned. Will be used in the future for session key exchange.
|
||||||
|
func (w *Whisper) GenerateSymKey() (string, error) {
|
||||||
const size = aesKeyLength * 2
|
const size = aesKeyLength * 2
|
||||||
buf := make([]byte, size)
|
buf := make([]byte, size)
|
||||||
_, err := crand.Read(buf)
|
_, err := crand.Read(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
} else if !validateSymmetricKey(buf) {
|
} else if !validateSymmetricKey(buf) {
|
||||||
return fmt.Errorf("error in GenerateSymKey: crypto/rand failed to generate random data")
|
return "", fmt.Errorf("error in GenerateSymKey: crypto/rand failed to generate random data")
|
||||||
}
|
}
|
||||||
|
|
||||||
key := buf[:aesKeyLength]
|
key := buf[:aesKeyLength]
|
||||||
salt := buf[aesKeyLength:]
|
salt := buf[aesKeyLength:]
|
||||||
derived, err := DeriveOneTimeKey(key, salt, EnvelopeVersion)
|
derived, err := DeriveOneTimeKey(key, salt, EnvelopeVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
} else if !validateSymmetricKey(derived) {
|
} else if !validateSymmetricKey(derived) {
|
||||||
return fmt.Errorf("failed to derive valid key")
|
return "", fmt.Errorf("failed to derive valid key")
|
||||||
}
|
}
|
||||||
|
|
||||||
w.keyMu.Lock()
|
id, err := GenerateRandomID()
|
||||||
defer w.keyMu.Unlock()
|
|
||||||
|
|
||||||
if w.symKeys[name] != nil {
|
|
||||||
return fmt.Errorf("Key with name [%s] already exists", name)
|
|
||||||
}
|
|
||||||
w.symKeys[name] = derived
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Whisper) AddSymKey(name string, key []byte) error {
|
|
||||||
if w.HasSymKey(name) {
|
|
||||||
return fmt.Errorf("Key with name [%s] already exists", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
derived, err := deriveKeyMaterial(key, EnvelopeVersion)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", fmt.Errorf("failed to generate ID: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.keyMu.Lock()
|
w.keyMu.Lock()
|
||||||
defer w.keyMu.Unlock()
|
defer w.keyMu.Unlock()
|
||||||
|
|
||||||
// double check is necessary, because deriveKeyMaterial() is slow
|
if w.symKeys[id] != nil {
|
||||||
if w.symKeys[name] != nil {
|
return "", fmt.Errorf("failed to generate unique ID")
|
||||||
return fmt.Errorf("Key with name [%s] already exists", name)
|
|
||||||
}
|
}
|
||||||
w.symKeys[name] = derived
|
w.symKeys[id] = derived
|
||||||
return nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Whisper) HasSymKey(name string) bool {
|
// AddSymKeyDirect stores the key, and returns its id.
|
||||||
w.keyMu.RLock()
|
func (w *Whisper) AddSymKeyDirect(key []byte) (string, error) {
|
||||||
defer w.keyMu.RUnlock()
|
if len(key) != aesKeyLength {
|
||||||
return w.symKeys[name] != nil
|
return "", fmt.Errorf("wrong key size: %d", len(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id, err := GenerateRandomID()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to generate ID: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Whisper) DeleteSymKey(name string) {
|
|
||||||
w.keyMu.Lock()
|
w.keyMu.Lock()
|
||||||
defer w.keyMu.Unlock()
|
defer w.keyMu.Unlock()
|
||||||
delete(w.symKeys, name)
|
|
||||||
|
if w.symKeys[id] != nil {
|
||||||
|
return "", fmt.Errorf("failed to generate unique ID")
|
||||||
|
}
|
||||||
|
w.symKeys[id] = key
|
||||||
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Whisper) GetSymKey(name string) []byte {
|
// AddSymKeyFromPassword generates the key from password, stores it, and returns its id.
|
||||||
|
func (w *Whisper) AddSymKeyFromPassword(password string) (string, error) {
|
||||||
|
id, err := GenerateRandomID()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to generate ID: %s", err)
|
||||||
|
}
|
||||||
|
if w.HasSymKey(id) {
|
||||||
|
return "", fmt.Errorf("failed to generate unique ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
derived, err := deriveKeyMaterial([]byte(password), EnvelopeVersion)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.keyMu.Lock()
|
||||||
|
defer w.keyMu.Unlock()
|
||||||
|
|
||||||
|
// double check is necessary, because deriveKeyMaterial() is very slow
|
||||||
|
if w.symKeys[id] != nil {
|
||||||
|
return "", fmt.Errorf("critical error: failed to generate unique ID")
|
||||||
|
}
|
||||||
|
w.symKeys[id] = derived
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasSymKey returns true if there is a key associated with the given id.
|
||||||
|
// Otherwise returns false.
|
||||||
|
func (w *Whisper) HasSymKey(id string) bool {
|
||||||
w.keyMu.RLock()
|
w.keyMu.RLock()
|
||||||
defer w.keyMu.RUnlock()
|
defer w.keyMu.RUnlock()
|
||||||
return w.symKeys[name]
|
return w.symKeys[id] != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch installs a new message handler to run in case a matching packet arrives
|
// DeleteSymKey deletes the key associated with the name string if it exists.
|
||||||
// from the whisper network.
|
func (w *Whisper) DeleteSymKey(id string) bool {
|
||||||
func (w *Whisper) Watch(f *Filter) (string, error) {
|
w.keyMu.Lock()
|
||||||
|
defer w.keyMu.Unlock()
|
||||||
|
if w.symKeys[id] != nil {
|
||||||
|
delete(w.symKeys, id)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSymKey returns the symmetric key associated with the given id.
|
||||||
|
func (w *Whisper) GetSymKey(id string) ([]byte, error) {
|
||||||
|
w.keyMu.RLock()
|
||||||
|
defer w.keyMu.RUnlock()
|
||||||
|
if w.symKeys[id] != nil {
|
||||||
|
return w.symKeys[id], nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("non-existent key ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe installs a new message handler used for filtering, decrypting
|
||||||
|
// and subsequent storing of incoming messages.
|
||||||
|
func (w *Whisper) Subscribe(f *Filter) (string, error) {
|
||||||
return w.filters.Install(f)
|
return w.filters.Install(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetFilter returns the filter by id.
|
||||||
func (w *Whisper) GetFilter(id string) *Filter {
|
func (w *Whisper) GetFilter(id string) *Filter {
|
||||||
return w.filters.Get(id)
|
return w.filters.Get(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unwatch removes an installed message handler.
|
// Unsubscribe removes an installed message handler.
|
||||||
func (w *Whisper) Unwatch(id string) {
|
func (w *Whisper) Unsubscribe(id string) error {
|
||||||
w.filters.Uninstall(id)
|
ok := w.filters.Uninstall(id)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Unsubscribe: Invalid ID")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send injects a message into the whisper send queue, to be distributed in the
|
// Send injects a message into the whisper send queue, to be distributed in the
|
||||||
// network in the coming cycles.
|
// network in the coming cycles.
|
||||||
func (w *Whisper) Send(envelope *Envelope) error {
|
func (w *Whisper) Send(envelope *Envelope) error {
|
||||||
_, err := w.add(envelope)
|
ok, err := w.add(envelope)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("failed to add envelope")
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start implements node.Service, starting the background data propagation thread
|
// Start implements node.Service, starting the background data propagation thread
|
||||||
// of the Whisper protocol.
|
// of the Whisper protocol.
|
||||||
func (w *Whisper) Start(*p2p.Server) error {
|
func (w *Whisper) Start(*p2p.Server) error {
|
||||||
log.Info(fmt.Sprint("Whisper started"))
|
log.Info("started whisper v." + ProtocolVersionStr)
|
||||||
go w.update()
|
go w.update()
|
||||||
|
|
||||||
numCPU := runtime.NumCPU()
|
numCPU := runtime.NumCPU()
|
||||||
|
@ -317,11 +419,11 @@ func (w *Whisper) Start(*p2p.Server) error {
|
||||||
// of the Whisper protocol.
|
// of the Whisper protocol.
|
||||||
func (w *Whisper) Stop() error {
|
func (w *Whisper) Stop() error {
|
||||||
close(w.quit)
|
close(w.quit)
|
||||||
log.Info(fmt.Sprint("Whisper stopped"))
|
log.Info("whisper stopped")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlePeer is called by the underlying P2P layer when the whisper sub-protocol
|
// HandlePeer is called by the underlying P2P layer when the whisper sub-protocol
|
||||||
// connection is negotiated.
|
// connection is negotiated.
|
||||||
func (wh *Whisper) HandlePeer(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
|
func (wh *Whisper) HandlePeer(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||||
// Create the new peer and start tracking it
|
// Create the new peer and start tracking it
|
||||||
|
@ -353,26 +455,31 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
|
||||||
// fetch the next packet
|
// fetch the next packet
|
||||||
packet, err := rw.ReadMsg()
|
packet, err := rw.ReadMsg()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Warn("message loop", "peer", p.peer.ID(), "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if packet.Size > uint32(wh.maxMsgLength) {
|
||||||
|
log.Warn("oversized message received", "peer", p.peer.ID())
|
||||||
|
return errors.New("oversized message received")
|
||||||
|
}
|
||||||
|
|
||||||
switch packet.Code {
|
switch packet.Code {
|
||||||
case statusCode:
|
case statusCode:
|
||||||
// this should not happen, but no need to panic; just ignore this message.
|
// this should not happen, but no need to panic; just ignore this message.
|
||||||
log.Warn(fmt.Sprintf("%v: unxepected status message received", p.peer))
|
log.Warn("unxepected status message received", "peer", p.peer.ID())
|
||||||
case messagesCode:
|
case messagesCode:
|
||||||
// decode the contained envelopes
|
// decode the contained envelopes
|
||||||
var envelopes []*Envelope
|
var envelopes []*Envelope
|
||||||
if err := packet.Decode(&envelopes); err != nil {
|
if err := packet.Decode(&envelopes); err != nil {
|
||||||
log.Warn(fmt.Sprintf("%v: failed to decode envelope: [%v], peer will be disconnected", p.peer, err))
|
log.Warn("failed to decode envelope, peer will be disconnected", "peer", p.peer.ID(), "err", err)
|
||||||
return fmt.Errorf("garbage received")
|
return errors.New("invalid envelope")
|
||||||
}
|
}
|
||||||
// inject all envelopes into the internal pool
|
// inject all envelopes into the internal pool
|
||||||
for _, envelope := range envelopes {
|
for _, envelope := range envelopes {
|
||||||
cached, err := wh.add(envelope)
|
cached, err := wh.add(envelope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn(fmt.Sprintf("%v: bad envelope received: [%v], peer will be disconnected", p.peer, err))
|
log.Warn("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err)
|
||||||
return fmt.Errorf("invalid envelope")
|
return errors.New("invalid envelope")
|
||||||
}
|
}
|
||||||
if cached {
|
if cached {
|
||||||
p.mark(envelope)
|
p.mark(envelope)
|
||||||
|
@ -386,8 +493,8 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
|
||||||
if p.trusted {
|
if p.trusted {
|
||||||
var envelope Envelope
|
var envelope Envelope
|
||||||
if err := packet.Decode(&envelope); err != nil {
|
if err := packet.Decode(&envelope); err != nil {
|
||||||
log.Warn(fmt.Sprintf("%v: failed to decode direct message: [%v], peer will be disconnected", p.peer, err))
|
log.Warn("failed to decode direct message, peer will be disconnected", "peer", p.peer.ID(), "err", err)
|
||||||
return fmt.Errorf("garbage received (directMessage)")
|
return errors.New("invalid direct message")
|
||||||
}
|
}
|
||||||
wh.postEvent(&envelope, true)
|
wh.postEvent(&envelope, true)
|
||||||
}
|
}
|
||||||
|
@ -396,8 +503,8 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
|
||||||
if wh.mailServer != nil {
|
if wh.mailServer != nil {
|
||||||
var request Envelope
|
var request Envelope
|
||||||
if err := packet.Decode(&request); err != nil {
|
if err := packet.Decode(&request); err != nil {
|
||||||
log.Warn(fmt.Sprintf("%v: failed to decode p2p request message: [%v], peer will be disconnected", p.peer, err))
|
log.Warn("failed to decode p2p request message, peer will be disconnected", "peer", p.peer.ID(), "err", err)
|
||||||
return fmt.Errorf("garbage received (p2p request)")
|
return errors.New("invalid p2p request")
|
||||||
}
|
}
|
||||||
wh.mailServer.DeliverMail(p, &request)
|
wh.mailServer.DeliverMail(p, &request)
|
||||||
}
|
}
|
||||||
|
@ -430,12 +537,12 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) {
|
||||||
if envelope.Expiry+SynchAllowance*2 < now {
|
if envelope.Expiry+SynchAllowance*2 < now {
|
||||||
return false, fmt.Errorf("very old message")
|
return false, fmt.Errorf("very old message")
|
||||||
} else {
|
} else {
|
||||||
log.Debug(fmt.Sprintf("expired envelope dropped [%x]", envelope.Hash()))
|
log.Debug("expired envelope dropped", "hash", envelope.Hash().Hex())
|
||||||
return false, nil // drop envelope without error
|
return false, nil // drop envelope without error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(envelope.Data) > MaxMessageLength {
|
if envelope.size() > wh.maxMsgLength {
|
||||||
return false, fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash())
|
return false, fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,8 +560,8 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) {
|
||||||
return false, fmt.Errorf("oversized salt [%x]", envelope.Hash())
|
return false, fmt.Errorf("oversized salt [%x]", envelope.Hash())
|
||||||
}
|
}
|
||||||
|
|
||||||
if envelope.PoW() < MinimumPoW && !wh.test {
|
if envelope.PoW() < wh.minPoW {
|
||||||
log.Debug(fmt.Sprintf("envelope with low PoW dropped: %f [%x]", envelope.PoW(), envelope.Hash()))
|
log.Debug("envelope with low PoW dropped", "PoW", envelope.PoW(), "hash", envelope.Hash().Hex())
|
||||||
return false, nil // drop envelope without error
|
return false, nil // drop envelope without error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -474,10 +581,10 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) {
|
||||||
wh.poolMu.Unlock()
|
wh.poolMu.Unlock()
|
||||||
|
|
||||||
if alreadyCached {
|
if alreadyCached {
|
||||||
log.Trace(fmt.Sprintf("whisper envelope already cached [%x]\n", envelope.Hash()))
|
log.Trace("whisper envelope already cached", "hash", envelope.Hash().Hex())
|
||||||
} else {
|
} else {
|
||||||
log.Trace(fmt.Sprintf("cached whisper envelope [%x]: %v\n", envelope.Hash(), envelope))
|
log.Trace("cached whisper envelope", "hash", envelope.Hash().Hex())
|
||||||
wh.stats.totalMemoryUsed += envelope.size()
|
wh.stats.memoryUsed += envelope.size()
|
||||||
wh.postEvent(envelope, false) // notify the local node about the new message
|
wh.postEvent(envelope, false) // notify the local node about the new message
|
||||||
if wh.mailServer != nil {
|
if wh.mailServer != nil {
|
||||||
wh.mailServer.Archive(envelope)
|
wh.mailServer.Archive(envelope)
|
||||||
|
@ -508,11 +615,12 @@ func (w *Whisper) checkOverflow() {
|
||||||
if queueSize == messageQueueLimit {
|
if queueSize == messageQueueLimit {
|
||||||
if !w.overflow {
|
if !w.overflow {
|
||||||
w.overflow = true
|
w.overflow = true
|
||||||
log.Warn(fmt.Sprint("message queue overflow"))
|
log.Warn("message queue overflow")
|
||||||
}
|
}
|
||||||
} else if queueSize <= messageQueueLimit/2 {
|
} else if queueSize <= messageQueueLimit/2 {
|
||||||
if w.overflow {
|
if w.overflow {
|
||||||
w.overflow = false
|
w.overflow = false
|
||||||
|
log.Warn("message queue overflow fixed (back to normal)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -558,19 +666,17 @@ func (w *Whisper) expire() {
|
||||||
w.poolMu.Lock()
|
w.poolMu.Lock()
|
||||||
defer w.poolMu.Unlock()
|
defer w.poolMu.Unlock()
|
||||||
|
|
||||||
w.stats.clear()
|
w.stats.reset()
|
||||||
now := uint32(time.Now().Unix())
|
now := uint32(time.Now().Unix())
|
||||||
for expiry, hashSet := range w.expirations {
|
for expiry, hashSet := range w.expirations {
|
||||||
if expiry < now {
|
if expiry < now {
|
||||||
w.stats.messagesCleared++
|
|
||||||
|
|
||||||
// Dump all expired messages and remove timestamp
|
// Dump all expired messages and remove timestamp
|
||||||
hashSet.Each(func(v interface{}) bool {
|
hashSet.Each(func(v interface{}) bool {
|
||||||
sz := w.envelopes[v.(common.Hash)].size()
|
sz := w.envelopes[v.(common.Hash)].size()
|
||||||
w.stats.memoryCleared += sz
|
|
||||||
w.stats.totalMemoryUsed -= sz
|
|
||||||
delete(w.envelopes, v.(common.Hash))
|
delete(w.envelopes, v.(common.Hash))
|
||||||
delete(w.messages, v.(common.Hash))
|
w.stats.messagesCleared++
|
||||||
|
w.stats.memoryCleared += sz
|
||||||
|
w.stats.memoryUsed -= sz
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
w.expirations[expiry].Clear()
|
w.expirations[expiry].Clear()
|
||||||
|
@ -579,12 +685,21 @@ func (w *Whisper) expire() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stats returns the whisper node statistics.
|
||||||
func (w *Whisper) Stats() string {
|
func (w *Whisper) Stats() string {
|
||||||
return fmt.Sprintf("Latest expiry cycle cleared %d messages (%d bytes). Memory usage: %d bytes.",
|
result := fmt.Sprintf("Memory usage: %d bytes. Average messages cleared per expiry cycle: %d. Total messages cleared: %d.",
|
||||||
w.stats.messagesCleared, w.stats.memoryCleared, w.stats.totalMemoryUsed)
|
w.stats.memoryUsed, w.stats.totalMessagesCleared/w.stats.cycles, w.stats.totalMessagesCleared)
|
||||||
|
if w.stats.messagesCleared > 0 {
|
||||||
|
result += fmt.Sprintf(" Latest expiry cycle cleared %d messages (%d bytes).",
|
||||||
|
w.stats.messagesCleared, w.stats.memoryCleared)
|
||||||
|
}
|
||||||
|
if w.overflow {
|
||||||
|
result += " Message queue state: overflow."
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// envelopes retrieves all the messages currently pooled by the node.
|
// Envelopes retrieves all the messages currently pooled by the node.
|
||||||
func (w *Whisper) Envelopes() []*Envelope {
|
func (w *Whisper) Envelopes() []*Envelope {
|
||||||
w.poolMu.RLock()
|
w.poolMu.RLock()
|
||||||
defer w.poolMu.RUnlock()
|
defer w.poolMu.RUnlock()
|
||||||
|
@ -596,15 +711,17 @@ func (w *Whisper) Envelopes() []*Envelope {
|
||||||
return all
|
return all
|
||||||
}
|
}
|
||||||
|
|
||||||
// Messages retrieves all the decrypted messages matching a filter id.
|
// Messages iterates through all currently floating envelopes
|
||||||
|
// and retrieves all the messages, that this filter could decrypt.
|
||||||
func (w *Whisper) Messages(id string) []*ReceivedMessage {
|
func (w *Whisper) Messages(id string) []*ReceivedMessage {
|
||||||
result := make([]*ReceivedMessage, 0)
|
result := make([]*ReceivedMessage, 0)
|
||||||
w.poolMu.RLock()
|
w.poolMu.RLock()
|
||||||
defer w.poolMu.RUnlock()
|
defer w.poolMu.RUnlock()
|
||||||
|
|
||||||
if filter := w.filters.Get(id); filter != nil {
|
if filter := w.filters.Get(id); filter != nil {
|
||||||
for _, msg := range w.messages {
|
for _, env := range w.envelopes {
|
||||||
if filter.MatchMessage(msg) {
|
msg := filter.processEnvelope(env)
|
||||||
|
if msg != nil {
|
||||||
result = append(result, msg)
|
result = append(result, msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -612,6 +729,7 @@ func (w *Whisper) Messages(id string) []*ReceivedMessage {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isEnvelopeCached checks if envelope with specific hash has already been received and cached.
|
||||||
func (w *Whisper) isEnvelopeCached(hash common.Hash) bool {
|
func (w *Whisper) isEnvelopeCached(hash common.Hash) bool {
|
||||||
w.poolMu.Lock()
|
w.poolMu.Lock()
|
||||||
defer w.poolMu.Unlock()
|
defer w.poolMu.Unlock()
|
||||||
|
@ -620,22 +738,30 @@ func (w *Whisper) isEnvelopeCached(hash common.Hash) bool {
|
||||||
return exist
|
return exist
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Whisper) addDecryptedMessage(msg *ReceivedMessage) {
|
// reset resets the node's statistics after each expiry cycle.
|
||||||
w.poolMu.Lock()
|
func (s *Statistics) reset() {
|
||||||
defer w.poolMu.Unlock()
|
s.cycles++
|
||||||
|
s.totalMessagesCleared += s.messagesCleared
|
||||||
|
|
||||||
w.messages[msg.EnvelopeHash] = msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Statistics) clear() {
|
|
||||||
s.memoryCleared = 0
|
s.memoryCleared = 0
|
||||||
s.messagesCleared = 0
|
s.messagesCleared = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateKeyID checks the format of key id.
|
||||||
|
func ValidateKeyID(id string) error {
|
||||||
|
const target = keyIdSize * 2
|
||||||
|
if len(id) != target {
|
||||||
|
return fmt.Errorf("wrong size of key ID (expected %d bytes, got %d)", target, len(id))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidatePublicKey checks the format of the given public key.
|
||||||
func ValidatePublicKey(k *ecdsa.PublicKey) bool {
|
func ValidatePublicKey(k *ecdsa.PublicKey) bool {
|
||||||
return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0
|
return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validatePrivateKey checks the format of the given private key.
|
||||||
func validatePrivateKey(k *ecdsa.PrivateKey) bool {
|
func validatePrivateKey(k *ecdsa.PrivateKey) bool {
|
||||||
if k == nil || k.D == nil || k.D.Sign() == 0 {
|
if k == nil || k.D == nil || k.D.Sign() == 0 {
|
||||||
return false
|
return false
|
||||||
|
@ -648,6 +774,7 @@ func validateSymmetricKey(k []byte) bool {
|
||||||
return len(k) > 0 && !containsOnlyZeros(k)
|
return len(k) > 0 && !containsOnlyZeros(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// containsOnlyZeros checks if the data contain only zeros.
|
||||||
func containsOnlyZeros(data []byte) bool {
|
func containsOnlyZeros(data []byte) bool {
|
||||||
for _, b := range data {
|
for _, b := range data {
|
||||||
if b != 0 {
|
if b != 0 {
|
||||||
|
@ -657,7 +784,8 @@ func containsOnlyZeros(data []byte) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func bytesToIntLittleEndian(b []byte) (res uint64) {
|
// bytesToUintLittleEndian converts the slice to 64-bit unsigned integer.
|
||||||
|
func bytesToUintLittleEndian(b []byte) (res uint64) {
|
||||||
mul := uint64(1)
|
mul := uint64(1)
|
||||||
for i := 0; i < len(b); i++ {
|
for i := 0; i < len(b); i++ {
|
||||||
res += uint64(b[i]) * mul
|
res += uint64(b[i]) * mul
|
||||||
|
@ -666,7 +794,8 @@ func bytesToIntLittleEndian(b []byte) (res uint64) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func BytesToIntBigEndian(b []byte) (res uint64) {
|
// BytesToUintBigEndian converts the slice to 64-bit unsigned integer.
|
||||||
|
func BytesToUintBigEndian(b []byte) (res uint64) {
|
||||||
for i := 0; i < len(b); i++ {
|
for i := 0; i < len(b); i++ {
|
||||||
res *= 256
|
res *= 256
|
||||||
res += uint64(b[i])
|
res += uint64(b[i])
|
||||||
|
@ -674,7 +803,7 @@ func BytesToIntBigEndian(b []byte) (res uint64) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeriveSymmetricKey derives symmetric key material from the key or password.
|
// deriveKeyMaterial derives symmetric key material from the key or password.
|
||||||
// pbkdf2 is used for security, in case people use password instead of randomly generated keys.
|
// pbkdf2 is used for security, in case people use password instead of randomly generated keys.
|
||||||
func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error) {
|
func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error) {
|
||||||
if version == 0 {
|
if version == 0 {
|
||||||
|
@ -686,3 +815,17 @@ func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error
|
||||||
return nil, unknownVersionError(version)
|
return nil, unknownVersionError(version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateRandomID generates a random string, which is then returned to be used as a key id
|
||||||
|
func GenerateRandomID() (id string, err error) {
|
||||||
|
buf := make([]byte, keyIdSize)
|
||||||
|
_, err = crand.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !validateSymmetricKey(buf) {
|
||||||
|
return "", fmt.Errorf("error in generateRandomID: crypto/rand failed to generate random data")
|
||||||
|
}
|
||||||
|
id = common.Bytes2Hex(buf)
|
||||||
|
return id, err
|
||||||
|
}
|
||||||
|
|
|
@ -21,9 +21,6 @@ import (
|
||||||
mrand "math/rand"
|
mrand "math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWhisperBasic(t *testing.T) {
|
func TestWhisperBasic(t *testing.T) {
|
||||||
|
@ -55,16 +52,19 @@ func TestWhisperBasic(t *testing.T) {
|
||||||
if peer != nil {
|
if peer != nil {
|
||||||
t.Fatal("found peer for random key.")
|
t.Fatal("found peer for random key.")
|
||||||
}
|
}
|
||||||
if err := w.MarkPeerTrusted(peerID); err == nil {
|
if err := w.AllowP2PMessagesFromPeer(peerID); err == nil {
|
||||||
t.Fatalf("failed MarkPeerTrusted.")
|
t.Fatalf("failed MarkPeerTrusted.")
|
||||||
}
|
}
|
||||||
exist := w.HasSymKey("non-existing")
|
exist := w.HasSymKey("non-existing")
|
||||||
if exist {
|
if exist {
|
||||||
t.Fatalf("failed HasSymKey.")
|
t.Fatalf("failed HasSymKey.")
|
||||||
}
|
}
|
||||||
key := w.GetSymKey("non-existing")
|
key, err := w.GetSymKey("non-existing")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("failed GetSymKey(non-existing): false positive.")
|
||||||
|
}
|
||||||
if key != nil {
|
if key != nil {
|
||||||
t.Fatalf("failed GetSymKey.")
|
t.Fatalf("failed GetSymKey: false positive.")
|
||||||
}
|
}
|
||||||
mail := w.Envelopes()
|
mail := w.Envelopes()
|
||||||
if len(mail) != 0 {
|
if len(mail) != 0 {
|
||||||
|
@ -80,7 +80,7 @@ func TestWhisperBasic(t *testing.T) {
|
||||||
if _, err := deriveKeyMaterial(peerID, ver); err != unknownVersionError(ver) {
|
if _, err := deriveKeyMaterial(peerID, ver); err != unknownVersionError(ver) {
|
||||||
t.Fatalf("failed deriveKeyMaterial with param = %v: %s.", peerID, err)
|
t.Fatalf("failed deriveKeyMaterial with param = %v: %s.", peerID, err)
|
||||||
}
|
}
|
||||||
derived, err := deriveKeyMaterial(peerID, 0)
|
derived, err = deriveKeyMaterial(peerID, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed second deriveKeyMaterial with param = %v: %s.", peerID, err)
|
t.Fatalf("failed second deriveKeyMaterial with param = %v: %s.", peerID, err)
|
||||||
}
|
}
|
||||||
|
@ -92,8 +92,8 @@ func TestWhisperBasic(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := []byte{0xFF, 0xE5, 0x80, 0x2, 0}
|
buf := []byte{0xFF, 0xE5, 0x80, 0x2, 0}
|
||||||
le := bytesToIntLittleEndian(buf)
|
le := bytesToUintLittleEndian(buf)
|
||||||
be := BytesToIntBigEndian(buf)
|
be := BytesToUintBigEndian(buf)
|
||||||
if le != uint64(0x280e5ff) {
|
if le != uint64(0x280e5ff) {
|
||||||
t.Fatalf("failed bytesToIntLittleEndian: %d.", le)
|
t.Fatalf("failed bytesToIntLittleEndian: %d.", le)
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,14 @@ func TestWhisperBasic(t *testing.T) {
|
||||||
t.Fatalf("failed BytesToIntBigEndian: %d.", be)
|
t.Fatalf("failed BytesToIntBigEndian: %d.", be)
|
||||||
}
|
}
|
||||||
|
|
||||||
pk := w.NewIdentity()
|
id, err := w.NewKeyPair()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate new key pair: %s.", err)
|
||||||
|
}
|
||||||
|
pk, err := w.GetPrivateKey(id)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to retrieve new key pair: %s.", err)
|
||||||
|
}
|
||||||
if !validatePrivateKey(pk) {
|
if !validatePrivateKey(pk) {
|
||||||
t.Fatalf("failed validatePrivateKey: %v.", pk)
|
t.Fatalf("failed validatePrivateKey: %v.", pk)
|
||||||
}
|
}
|
||||||
|
@ -112,67 +119,112 @@ func TestWhisperBasic(t *testing.T) {
|
||||||
|
|
||||||
func TestWhisperIdentityManagement(t *testing.T) {
|
func TestWhisperIdentityManagement(t *testing.T) {
|
||||||
w := New()
|
w := New()
|
||||||
id1 := w.NewIdentity()
|
id1, err := w.NewKeyPair()
|
||||||
id2 := w.NewIdentity()
|
if err != nil {
|
||||||
pub1 := common.ToHex(crypto.FromECDSAPub(&id1.PublicKey))
|
t.Fatalf("failed to generate new key pair: %s.", err)
|
||||||
pub2 := common.ToHex(crypto.FromECDSAPub(&id2.PublicKey))
|
|
||||||
pk1 := w.GetIdentity(pub1)
|
|
||||||
pk2 := w.GetIdentity(pub2)
|
|
||||||
if !w.HasIdentity(pub1) {
|
|
||||||
t.Fatalf("failed HasIdentity(pub1).")
|
|
||||||
}
|
}
|
||||||
if !w.HasIdentity(pub2) {
|
id2, err := w.NewKeyPair()
|
||||||
t.Fatalf("failed HasIdentity(pub2).")
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate new key pair: %s.", err)
|
||||||
}
|
}
|
||||||
if pk1 != id1 {
|
pk1, err := w.GetPrivateKey(id1)
|
||||||
t.Fatalf("failed GetIdentity(pub1).")
|
if err != nil {
|
||||||
|
t.Fatalf("failed to retrieve the key pair: %s.", err)
|
||||||
}
|
}
|
||||||
if pk2 != id2 {
|
pk2, err := w.GetPrivateKey(id2)
|
||||||
t.Fatalf("failed GetIdentity(pub2).")
|
if err != nil {
|
||||||
|
t.Fatalf("failed to retrieve the key pair: %s.", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !w.HasKeyPair(id1) {
|
||||||
|
t.Fatalf("failed HasIdentity(pk1).")
|
||||||
|
}
|
||||||
|
if !w.HasKeyPair(id2) {
|
||||||
|
t.Fatalf("failed HasIdentity(pk2).")
|
||||||
|
}
|
||||||
|
if pk1 == nil {
|
||||||
|
t.Fatalf("failed GetIdentity(pk1).")
|
||||||
|
}
|
||||||
|
if pk2 == nil {
|
||||||
|
t.Fatalf("failed GetIdentity(pk2).")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !validatePrivateKey(pk1) {
|
||||||
|
t.Fatalf("pk1 is invalid.")
|
||||||
|
}
|
||||||
|
if !validatePrivateKey(pk2) {
|
||||||
|
t.Fatalf("pk2 is invalid.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete one identity
|
// Delete one identity
|
||||||
w.DeleteIdentity(pub1)
|
done := w.DeleteKeyPair(id1)
|
||||||
pk1 = w.GetIdentity(pub1)
|
if !done {
|
||||||
pk2 = w.GetIdentity(pub2)
|
t.Fatalf("failed to delete id1.")
|
||||||
if w.HasIdentity(pub1) {
|
}
|
||||||
|
pk1, err = w.GetPrivateKey(id1)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("retrieve the key pair: false positive.")
|
||||||
|
}
|
||||||
|
pk2, err = w.GetPrivateKey(id2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to retrieve the key pair: %s.", err)
|
||||||
|
}
|
||||||
|
if w.HasKeyPair(id1) {
|
||||||
t.Fatalf("failed DeleteIdentity(pub1): still exist.")
|
t.Fatalf("failed DeleteIdentity(pub1): still exist.")
|
||||||
}
|
}
|
||||||
if !w.HasIdentity(pub2) {
|
if !w.HasKeyPair(id2) {
|
||||||
t.Fatalf("failed DeleteIdentity(pub1): pub2 does not exist.")
|
t.Fatalf("failed DeleteIdentity(pub1): pub2 does not exist.")
|
||||||
}
|
}
|
||||||
if pk1 != nil {
|
if pk1 != nil {
|
||||||
t.Fatalf("failed DeleteIdentity(pub1): first key still exist.")
|
t.Fatalf("failed DeleteIdentity(pub1): first key still exist.")
|
||||||
}
|
}
|
||||||
if pk2 != id2 {
|
if pk2 == nil {
|
||||||
t.Fatalf("failed DeleteIdentity(pub1): second key does not exist.")
|
t.Fatalf("failed DeleteIdentity(pub1): second key does not exist.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete again non-existing identity
|
// Delete again non-existing identity
|
||||||
w.DeleteIdentity(pub1)
|
done = w.DeleteKeyPair(id1)
|
||||||
pk1 = w.GetIdentity(pub1)
|
if done {
|
||||||
pk2 = w.GetIdentity(pub2)
|
t.Fatalf("delete id1: false positive.")
|
||||||
if w.HasIdentity(pub1) {
|
}
|
||||||
|
pk1, err = w.GetPrivateKey(id1)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("retrieve the key pair: false positive.")
|
||||||
|
}
|
||||||
|
pk2, err = w.GetPrivateKey(id2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to retrieve the key pair: %s.", err)
|
||||||
|
}
|
||||||
|
if w.HasKeyPair(id1) {
|
||||||
t.Fatalf("failed delete non-existing identity: exist.")
|
t.Fatalf("failed delete non-existing identity: exist.")
|
||||||
}
|
}
|
||||||
if !w.HasIdentity(pub2) {
|
if !w.HasKeyPair(id2) {
|
||||||
t.Fatalf("failed delete non-existing identity: pub2 does not exist.")
|
t.Fatalf("failed delete non-existing identity: pub2 does not exist.")
|
||||||
}
|
}
|
||||||
if pk1 != nil {
|
if pk1 != nil {
|
||||||
t.Fatalf("failed delete non-existing identity: first key exist.")
|
t.Fatalf("failed delete non-existing identity: first key exist.")
|
||||||
}
|
}
|
||||||
if pk2 != id2 {
|
if pk2 == nil {
|
||||||
t.Fatalf("failed delete non-existing identity: second key does not exist.")
|
t.Fatalf("failed delete non-existing identity: second key does not exist.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete second identity
|
// Delete second identity
|
||||||
w.DeleteIdentity(pub2)
|
done = w.DeleteKeyPair(id2)
|
||||||
pk1 = w.GetIdentity(pub1)
|
if !done {
|
||||||
pk2 = w.GetIdentity(pub2)
|
t.Fatalf("failed to delete id2.")
|
||||||
if w.HasIdentity(pub1) {
|
}
|
||||||
|
pk1, err = w.GetPrivateKey(id1)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("retrieve the key pair: false positive.")
|
||||||
|
}
|
||||||
|
pk2, err = w.GetPrivateKey(id2)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("retrieve the key pair: false positive.")
|
||||||
|
}
|
||||||
|
if w.HasKeyPair(id1) {
|
||||||
t.Fatalf("failed delete second identity: first identity exist.")
|
t.Fatalf("failed delete second identity: first identity exist.")
|
||||||
}
|
}
|
||||||
if w.HasIdentity(pub2) {
|
if w.HasKeyPair(id2) {
|
||||||
t.Fatalf("failed delete second identity: still exist.")
|
t.Fatalf("failed delete second identity: still exist.")
|
||||||
}
|
}
|
||||||
if pk1 != nil {
|
if pk1 != nil {
|
||||||
|
@ -186,23 +238,30 @@ func TestWhisperIdentityManagement(t *testing.T) {
|
||||||
func TestWhisperSymKeyManagement(t *testing.T) {
|
func TestWhisperSymKeyManagement(t *testing.T) {
|
||||||
InitSingleTest()
|
InitSingleTest()
|
||||||
|
|
||||||
|
var err error
|
||||||
var k1, k2 []byte
|
var k1, k2 []byte
|
||||||
w := New()
|
w := New()
|
||||||
id1 := string("arbitrary-string-1")
|
id1 := string("arbitrary-string-1")
|
||||||
id2 := string("arbitrary-string-2")
|
id2 := string("arbitrary-string-2")
|
||||||
|
|
||||||
err := w.GenerateSymKey(id1)
|
id1, err = w.GenerateSymKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed GenerateSymKey with seed %d: %s.", seed, err)
|
t.Fatalf("failed GenerateSymKey with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
k1 = w.GetSymKey(id1)
|
k1, err = w.GetSymKey(id1)
|
||||||
k2 = w.GetSymKey(id2)
|
if err != nil {
|
||||||
|
t.Fatalf("failed GetSymKey(id1).")
|
||||||
|
}
|
||||||
|
k2, err = w.GetSymKey(id2)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("failed GetSymKey(id2): false positive.")
|
||||||
|
}
|
||||||
if !w.HasSymKey(id1) {
|
if !w.HasSymKey(id1) {
|
||||||
t.Fatalf("failed HasSymKey(id1).")
|
t.Fatalf("failed HasSymKey(id1).")
|
||||||
}
|
}
|
||||||
if w.HasSymKey(id2) {
|
if w.HasSymKey(id2) {
|
||||||
t.Fatalf("failed HasSymKey(id2).")
|
t.Fatalf("failed HasSymKey(id2): false positive.")
|
||||||
}
|
}
|
||||||
if k1 == nil {
|
if k1 == nil {
|
||||||
t.Fatalf("first key does not exist.")
|
t.Fatalf("first key does not exist.")
|
||||||
|
@ -212,37 +271,49 @@ func TestWhisperSymKeyManagement(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// add existing id, nothing should change
|
// add existing id, nothing should change
|
||||||
randomKey := make([]byte, 16)
|
randomKey := make([]byte, aesKeyLength)
|
||||||
mrand.Read(randomKey)
|
mrand.Read(randomKey)
|
||||||
err = w.AddSymKey(id1, randomKey)
|
id1, err = w.AddSymKeyDirect(randomKey)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
t.Fatalf("failed AddSymKey with seed %d.", seed)
|
t.Fatalf("failed AddSymKey with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
k1 = w.GetSymKey(id1)
|
k1, err = w.GetSymKey(id1)
|
||||||
k2 = w.GetSymKey(id2)
|
if err != nil {
|
||||||
|
t.Fatalf("failed w.GetSymKey(id1).")
|
||||||
|
}
|
||||||
|
k2, err = w.GetSymKey(id2)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("failed w.GetSymKey(id2): false positive.")
|
||||||
|
}
|
||||||
if !w.HasSymKey(id1) {
|
if !w.HasSymKey(id1) {
|
||||||
t.Fatalf("failed w.HasSymKey(id1).")
|
t.Fatalf("failed w.HasSymKey(id1).")
|
||||||
}
|
}
|
||||||
if w.HasSymKey(id2) {
|
if w.HasSymKey(id2) {
|
||||||
t.Fatalf("failed w.HasSymKey(id2).")
|
t.Fatalf("failed w.HasSymKey(id2): false positive.")
|
||||||
}
|
}
|
||||||
if k1 == nil {
|
if k1 == nil {
|
||||||
t.Fatalf("first key does not exist.")
|
t.Fatalf("first key does not exist.")
|
||||||
}
|
}
|
||||||
if bytes.Equal(k1, randomKey) {
|
if !bytes.Equal(k1, randomKey) {
|
||||||
t.Fatalf("k1 == randomKey.")
|
t.Fatalf("k1 != randomKey.")
|
||||||
}
|
}
|
||||||
if k2 != nil {
|
if k2 != nil {
|
||||||
t.Fatalf("second key already exist.")
|
t.Fatalf("second key already exist.")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = w.AddSymKey(id2, randomKey) // add non-existing (yet)
|
id2, err = w.AddSymKeyDirect(randomKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed AddSymKey(id2) with seed %d: %s.", seed, err)
|
t.Fatalf("failed AddSymKey(id2) with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
k1 = w.GetSymKey(id1)
|
k1, err = w.GetSymKey(id1)
|
||||||
k2 = w.GetSymKey(id2)
|
if err != nil {
|
||||||
|
t.Fatalf("failed w.GetSymKey(id1).")
|
||||||
|
}
|
||||||
|
k2, err = w.GetSymKey(id2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed w.GetSymKey(id2).")
|
||||||
|
}
|
||||||
if !w.HasSymKey(id1) {
|
if !w.HasSymKey(id1) {
|
||||||
t.Fatalf("HasSymKey(id1) failed.")
|
t.Fatalf("HasSymKey(id1) failed.")
|
||||||
}
|
}
|
||||||
|
@ -255,11 +326,11 @@ func TestWhisperSymKeyManagement(t *testing.T) {
|
||||||
if k2 == nil {
|
if k2 == nil {
|
||||||
t.Fatalf("k2 does not exist.")
|
t.Fatalf("k2 does not exist.")
|
||||||
}
|
}
|
||||||
if bytes.Equal(k1, k2) {
|
if !bytes.Equal(k1, k2) {
|
||||||
t.Fatalf("k1 == k2.")
|
t.Fatalf("k1 != k2.")
|
||||||
}
|
}
|
||||||
if bytes.Equal(k1, randomKey) {
|
if !bytes.Equal(k1, randomKey) {
|
||||||
t.Fatalf("k1 == randomKey.")
|
t.Fatalf("k1 != randomKey.")
|
||||||
}
|
}
|
||||||
if len(k1) != aesKeyLength {
|
if len(k1) != aesKeyLength {
|
||||||
t.Fatalf("wrong length of k1.")
|
t.Fatalf("wrong length of k1.")
|
||||||
|
@ -269,8 +340,17 @@ func TestWhisperSymKeyManagement(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
w.DeleteSymKey(id1)
|
w.DeleteSymKey(id1)
|
||||||
k1 = w.GetSymKey(id1)
|
k1, err = w.GetSymKey(id1)
|
||||||
k2 = w.GetSymKey(id2)
|
if err == nil {
|
||||||
|
t.Fatalf("failed w.GetSymKey(id1): false positive.")
|
||||||
|
}
|
||||||
|
if k1 != nil {
|
||||||
|
t.Fatalf("failed GetSymKey(id1): false positive.")
|
||||||
|
}
|
||||||
|
k2, err = w.GetSymKey(id2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed w.GetSymKey(id2).")
|
||||||
|
}
|
||||||
if w.HasSymKey(id1) {
|
if w.HasSymKey(id1) {
|
||||||
t.Fatalf("failed to delete first key: still exist.")
|
t.Fatalf("failed to delete first key: still exist.")
|
||||||
}
|
}
|
||||||
|
@ -286,8 +366,17 @@ func TestWhisperSymKeyManagement(t *testing.T) {
|
||||||
|
|
||||||
w.DeleteSymKey(id1)
|
w.DeleteSymKey(id1)
|
||||||
w.DeleteSymKey(id2)
|
w.DeleteSymKey(id2)
|
||||||
k1 = w.GetSymKey(id1)
|
k1, err = w.GetSymKey(id1)
|
||||||
k2 = w.GetSymKey(id2)
|
if err == nil {
|
||||||
|
t.Fatalf("failed w.GetSymKey(id1): false positive.")
|
||||||
|
}
|
||||||
|
k2, err = w.GetSymKey(id2)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("failed w.GetSymKey(id2): false positive.")
|
||||||
|
}
|
||||||
|
if k1 != nil || k2 != nil {
|
||||||
|
t.Fatalf("k1 or k2 is not nil")
|
||||||
|
}
|
||||||
if w.HasSymKey(id1) {
|
if w.HasSymKey(id1) {
|
||||||
t.Fatalf("failed to delete second key: first key exist.")
|
t.Fatalf("failed to delete second key: first key exist.")
|
||||||
}
|
}
|
||||||
|
@ -300,13 +389,63 @@ func TestWhisperSymKeyManagement(t *testing.T) {
|
||||||
if k2 != nil {
|
if k2 != nil {
|
||||||
t.Fatalf("failed to delete second key: second key is not nil.")
|
t.Fatalf("failed to delete second key: second key is not nil.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
randomKey = make([]byte, aesKeyLength+1)
|
||||||
|
mrand.Read(randomKey)
|
||||||
|
id1, err = w.AddSymKeyDirect(randomKey)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("added the key with wrong size, seed %d.", seed)
|
||||||
|
}
|
||||||
|
|
||||||
|
const password = "arbitrary data here"
|
||||||
|
id1, err = w.AddSymKeyFromPassword(password)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed AddSymKeyFromPassword(id1) with seed %d: %s.", seed, err)
|
||||||
|
}
|
||||||
|
id2, err = w.AddSymKeyFromPassword(password)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed AddSymKeyFromPassword(id2) with seed %d: %s.", seed, err)
|
||||||
|
}
|
||||||
|
k1, err = w.GetSymKey(id1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed w.GetSymKey(id1).")
|
||||||
|
}
|
||||||
|
k2, err = w.GetSymKey(id2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed w.GetSymKey(id2).")
|
||||||
|
}
|
||||||
|
if !w.HasSymKey(id1) {
|
||||||
|
t.Fatalf("HasSymKey(id1) failed.")
|
||||||
|
}
|
||||||
|
if !w.HasSymKey(id2) {
|
||||||
|
t.Fatalf("HasSymKey(id2) failed.")
|
||||||
|
}
|
||||||
|
if k1 == nil {
|
||||||
|
t.Fatalf("k1 does not exist.")
|
||||||
|
}
|
||||||
|
if k2 == nil {
|
||||||
|
t.Fatalf("k2 does not exist.")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(k1, k2) {
|
||||||
|
t.Fatalf("k1 != k2.")
|
||||||
|
}
|
||||||
|
if len(k1) != aesKeyLength {
|
||||||
|
t.Fatalf("wrong length of k1.")
|
||||||
|
}
|
||||||
|
if len(k2) != aesKeyLength {
|
||||||
|
t.Fatalf("wrong length of k2.")
|
||||||
|
}
|
||||||
|
if !validateSymmetricKey(k2) {
|
||||||
|
t.Fatalf("key validation failed.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExpiry(t *testing.T) {
|
func TestExpiry(t *testing.T) {
|
||||||
InitSingleTest()
|
InitSingleTest()
|
||||||
|
|
||||||
w := New()
|
w := New()
|
||||||
w.test = true
|
w.SetMinimumPoW(0.0000001)
|
||||||
|
defer w.SetMinimumPoW(DefaultMinimumPoW)
|
||||||
w.Start(nil)
|
w.Start(nil)
|
||||||
defer w.Stop()
|
defer w.Stop()
|
||||||
|
|
||||||
|
@ -354,3 +493,87 @@ func TestExpiry(t *testing.T) {
|
||||||
t.Fatalf("expire failed, seed: %d.", seed)
|
t.Fatalf("expire failed, seed: %d.", seed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCustomization(t *testing.T) {
|
||||||
|
InitSingleTest()
|
||||||
|
|
||||||
|
w := New()
|
||||||
|
defer w.SetMinimumPoW(DefaultMinimumPoW)
|
||||||
|
defer w.SetMaxMessageLength(DefaultMaxMessageLength)
|
||||||
|
w.Start(nil)
|
||||||
|
defer w.Stop()
|
||||||
|
|
||||||
|
const smallPoW = 0.00001
|
||||||
|
|
||||||
|
f, err := generateFilter(t, true)
|
||||||
|
params, err := generateMessageParams()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
params.KeySym = f.KeySym
|
||||||
|
params.Topic = BytesToTopic(f.Topics[2])
|
||||||
|
params.PoW = smallPoW
|
||||||
|
params.TTL = 3600 * 24 // one day
|
||||||
|
msg := NewSentMessage(params)
|
||||||
|
env, err := msg.Wrap(params)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = w.Send(env)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("successfully sent envelope with PoW %.06f, false positive (seed %d).", env.PoW(), seed)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.SetMinimumPoW(smallPoW / 2)
|
||||||
|
err = w.Send(env)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to send envelope with seed %d: %s.", seed, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
params.TTL++
|
||||||
|
msg = NewSentMessage(params)
|
||||||
|
env, err = msg.Wrap(params)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||||||
|
}
|
||||||
|
w.SetMaxMessageLength(env.size() - 1)
|
||||||
|
err = w.Send(env)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("successfully sent oversized envelope (seed %d): false positive.", seed)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.SetMaxMessageLength(DefaultMaxMessageLength)
|
||||||
|
err = w.Send(env)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to send second envelope with seed %d: %s.", seed, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait till received or timeout
|
||||||
|
var received bool
|
||||||
|
for j := 0; j < 20; j++ {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
if len(w.Envelopes()) > 1 {
|
||||||
|
received = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !received {
|
||||||
|
t.Fatalf("did not receive the sent envelope, seed: %d.", seed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check w.messages()
|
||||||
|
id, err := w.Subscribe(f)
|
||||||
|
time.Sleep(5 * time.Millisecond)
|
||||||
|
mail := f.Retrieve()
|
||||||
|
if len(mail) > 0 {
|
||||||
|
t.Fatalf("received premature mail")
|
||||||
|
}
|
||||||
|
|
||||||
|
mail = w.Messages(id)
|
||||||
|
if len(mail) != 2 {
|
||||||
|
t.Fatalf("failed to get whisper messages")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue