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:
@ -27,7 +27,9 @@ import (
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -46,7 +48,6 @@ import (
|
||||
)
|
||||
|
||||
const quitCommand = "~Q"
|
||||
const symKeyName = "da919ea33001b04dfc630522e33078ec0df11"
|
||||
|
||||
// singletons
|
||||
var (
|
||||
@ -64,7 +65,8 @@ var (
|
||||
pub *ecdsa.PublicKey
|
||||
asymKey *ecdsa.PrivateKey
|
||||
nodeid *ecdsa.PrivateKey
|
||||
topic whisper.TopicType
|
||||
topic []byte
|
||||
asymKeyID string
|
||||
filterID string
|
||||
symPass string
|
||||
msPassword string
|
||||
@ -72,27 +74,30 @@ var (
|
||||
|
||||
// cmd arguments
|
||||
var (
|
||||
echoMode = flag.Bool("e", false, "echo mode: prints some arguments for diagnostics")
|
||||
bootstrapMode = flag.Bool("b", false, "boostrap node: don't actively connect to peers, wait for incoming connections")
|
||||
forwarderMode = flag.Bool("f", false, "forwarder mode: only forward messages, neither send nor decrypt messages")
|
||||
mailServerMode = flag.Bool("s", false, "mail server mode: delivers expired messages on demand")
|
||||
requestMail = flag.Bool("r", false, "request expired messages from the bootstrap server")
|
||||
asymmetricMode = flag.Bool("a", false, "use asymmetric encryption")
|
||||
testMode = flag.Bool("t", false, "use of predefined parameters for diagnostics")
|
||||
generateKey = flag.Bool("k", false, "generate and show the private key")
|
||||
bootstrapMode = flag.Bool("standalone", 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")
|
||||
mailServerMode = flag.Bool("mailserver", false, "mail server mode: delivers expired messages on demand")
|
||||
requestMail = flag.Bool("mailclient", false, "request expired messages from the bootstrap server")
|
||||
asymmetricMode = flag.Bool("asym", false, "use asymmetric encryption")
|
||||
generateKey = flag.Bool("generatekey", false, "generate and show the private key")
|
||||
fileExMode = flag.Bool("fileexchange", false, "file exchange mode")
|
||||
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")
|
||||
argTTL = flag.Uint("ttl", 30, "time-to-live for messages 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)")
|
||||
argServerPoW = flag.Float64("mspow", whisper.MinimumPoW, "PoW requirement for Mail Server request")
|
||||
argMaxSize = flag.Int("maxsize", whisper.DefaultMaxMessageLength, "max size of message")
|
||||
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)")
|
||||
argPub = flag.String("pub", "", "public key for asymmetric encryption")
|
||||
argDBPath = flag.String("dbpath", "", "path to the server's DB directory")
|
||||
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)")
|
||||
argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)")
|
||||
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")
|
||||
argDBPath = flag.String("dbpath", "", "path to the server's DB directory")
|
||||
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)")
|
||||
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() {
|
||||
@ -124,7 +129,7 @@ func processArgs() {
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to parse the topic: %s", err)
|
||||
}
|
||||
topic = whisper.BytesToTopic(x)
|
||||
topic = x
|
||||
}
|
||||
|
||||
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 {
|
||||
echo()
|
||||
}
|
||||
@ -199,9 +212,40 @@ func initialize() {
|
||||
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 {
|
||||
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
|
||||
@ -213,7 +257,8 @@ func initialize() {
|
||||
Config: p2p.Config{
|
||||
PrivateKey: nodeid,
|
||||
MaxPeers: maxPeers,
|
||||
Name: common.MakeName("whisper-go", "5.0"),
|
||||
Discovery: true,
|
||||
Name: common.MakeName("wnode", "5.0"),
|
||||
Protocols: shh.Protocols(),
|
||||
ListenAddr: *argIP,
|
||||
NAT: nat.Any(),
|
||||
@ -288,8 +333,14 @@ func configureNode() {
|
||||
}
|
||||
}
|
||||
|
||||
shh.AddSymKey(symKeyName, []byte(symPass))
|
||||
symKey = shh.GetSymKey(symKeyName)
|
||||
symKeyID, err := shh.AddSymKeyFromPassword(symPass)
|
||||
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 {
|
||||
generateTopic([]byte(symPass))
|
||||
}
|
||||
@ -302,12 +353,12 @@ func configureNode() {
|
||||
}
|
||||
|
||||
filter := whisper.Filter{
|
||||
KeySym: symKey,
|
||||
KeyAsym: asymKey,
|
||||
Topics: []whisper.TopicType{topic},
|
||||
AcceptP2P: p2pAccept,
|
||||
KeySym: symKey,
|
||||
KeyAsym: asymKey,
|
||||
Topics: [][]byte{topic},
|
||||
AllowP2P: p2pAccept,
|
||||
}
|
||||
filterID, err = shh.Watch(&filter)
|
||||
filterID, err = shh.Subscribe(&filter)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to install filter: %s", err)
|
||||
}
|
||||
@ -351,6 +402,8 @@ func run() {
|
||||
|
||||
if *requestMail {
|
||||
requestExpiredMessagesLoop()
|
||||
} else if *fileExMode {
|
||||
sendFilesLoop()
|
||||
} else {
|
||||
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 {
|
||||
if len(prompt) > 0 {
|
||||
fmt.Print(prompt)
|
||||
@ -402,29 +480,36 @@ func scanUint(prompt string) uint32 {
|
||||
return uint32(i)
|
||||
}
|
||||
|
||||
func sendMsg(payload []byte) {
|
||||
func sendMsg(payload []byte) common.Hash {
|
||||
params := whisper.MessageParams{
|
||||
Src: asymKey,
|
||||
Dst: pub,
|
||||
KeySym: symKey,
|
||||
Payload: payload,
|
||||
Topic: topic,
|
||||
Topic: whisper.BytesToTopic(topic),
|
||||
TTL: uint32(*argTTL),
|
||||
PoW: *argPoW,
|
||||
WorkTime: uint32(*argWorkTime),
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to seal message: %v \n", err)
|
||||
return
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
err = shh.Send(envelope)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to send message: %v \n", err)
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
return envelope.Hash()
|
||||
}
|
||||
|
||||
func messageLoop() {
|
||||
@ -440,7 +525,11 @@ func messageLoop() {
|
||||
case <-ticker.C:
|
||||
messages := f.Retrieve()
|
||||
for _, msg := range messages {
|
||||
printMessageInfo(msg)
|
||||
if *fileExMode || len(msg.Payload) > 2048 {
|
||||
writeMessageToFile(*argSaveDir, msg)
|
||||
} else {
|
||||
printMessageInfo(msg)
|
||||
}
|
||||
}
|
||||
case <-done:
|
||||
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() {
|
||||
var key, peerID []byte
|
||||
var timeLow, timeUpp uint32
|
||||
var t string
|
||||
var xt, empty whisper.TopicType
|
||||
|
||||
err := shh.AddSymKey(mailserver.MailServerKeyName, []byte(msPassword))
|
||||
keyID, err := shh.AddSymKeyFromPassword(msPassword)
|
||||
if err != nil {
|
||||
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)
|
||||
shh.MarkPeerTrusted(peerID)
|
||||
shh.AllowP2PMessagesFromPeer(peerID)
|
||||
|
||||
for {
|
||||
timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ")
|
||||
@ -509,6 +626,9 @@ func requestExpiredMessagesLoop() {
|
||||
params.WorkTime = 5
|
||||
|
||||
msg := whisper.NewSentMessage(¶ms)
|
||||
if msg == nil {
|
||||
utils.Fatalf("failed to create new message (OS level error)")
|
||||
}
|
||||
env, err := msg.Wrap(¶ms)
|
||||
if err != nil {
|
||||
utils.Fatalf("Wrap failed: %s", err)
|
||||
@ -527,7 +647,6 @@ func extractIdFromEnode(s string) []byte {
|
||||
n, err := discover.ParseNode(s)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to parse enode: %s", err)
|
||||
return nil
|
||||
}
|
||||
return n.ID[:]
|
||||
}
|
||||
|
Reference in New Issue
Block a user