From 9cd713551627a9b48e04a77f64a15ea6f829dcf4 Mon Sep 17 00:00:00 2001 From: gluk256 Date: Sun, 9 Apr 2017 23:49:22 +0200 Subject: 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) --- cmd/wnode/main.go | 195 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 157 insertions(+), 38 deletions(-) (limited to 'cmd') diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go index 82d7eda3c..7431980b5 100644 --- a/cmd/wnode/main.go +++ b/cmd/wnode/main.go @@ -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") - - 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)") + 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)") + 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[:] } -- cgit