From c67703048942f48226e4af3ccc1e97191f90c63c Mon Sep 17 00:00:00 2001 From: Vlad Date: Mon, 26 Dec 2016 00:39:36 +0100 Subject: [PATCH 01/13] whisper: command-line tool (whisper node) introduced --- whisper/wnode/main.go | 477 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 477 insertions(+) create mode 100644 whisper/wnode/main.go diff --git a/whisper/wnode/main.go b/whisper/wnode/main.go new file mode 100644 index 0000000000..21876eefba --- /dev/null +++ b/whisper/wnode/main.go @@ -0,0 +1,477 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// This is a simple Whisper node. +// It also implements a command line chat between the peers sharing the same credentials. + +package main + +import ( + "bufio" + "crypto/ecdsa" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "encoding/hex" + "fmt" + "os" + "strconv" + "syscall" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/p2p/nat" + whisper "github.com/ethereum/go-ethereum/whisper/whisperv5" + "golang.org/x/crypto/pbkdf2" + "golang.org/x/crypto/ssh/terminal" +) + +var input *bufio.Reader +var done chan struct{} +var server p2p.Server +var shh *whisper.Whisper +var asymKey *ecdsa.PrivateKey // used for asymmetric decryption and signing +var pub *ecdsa.PublicKey // used for asymmetric encryption +var symKey []byte // used for symmetric encryption +var filter whisper.Filter +var filterID uint32 +var topic whisper.TopicType +var pow float64 = whisper.MinimumPoW +var ttl uint32 = 30 +var workTime uint32 = 5 +var ipAddress, enode, salt, topicStr, pubStr string + +var bootstrapNode bool // does not actively connect to peers, wait for incoming connections +var daemonMode bool // only forward messages, neither send nor track +var testMode bool // predefined password, topic, ip and port +var echoMode bool // shows params, etc. +var isAsymmetric bool + +var argNameHelp string = "-h" +var argNameBootstrap string = "-b" +var argNameDaemon string = "-d" +var argNameAsymmetric string = "-a" +var argNameTest string = "-test" +var argNameEcho string = "-echo" +var argNameWorkTime string = "-work" +var argNameTTL string = "-ttl" +var argNameIP string = "-ip" +var argNameTopic string = "-topic" +var argNamePoW string = "-pow" +var argNameSalt string = "-salt" +var argNamePub string = "-pub" +var argNameEnode string = "-enode" +var quitCommand string = "/q/" + +func printHelp() { + fmt.Println("wchat is a stand-alone whisper node with command-line interface.") + fmt.Println("wchat also allows to set up a chat using either symmetric or asymmetric encryption.\n") + fmt.Println("usage: wchat [arguments]") + fmt.Printf(" %s\t\t boostrap node: don't actively connect to peers, wait for incoming connections\n", argNameBootstrap) + fmt.Printf(" %s\t\t daemon mode: only forward messages, neither send nor decrypt messages \n", argNameDaemon) + fmt.Printf(" %s\t\t use asymmetric encryption \n", argNameAsymmetric) + fmt.Printf(" %s\t\t test mode: predefined password, topic, ip and port \n", argNameTest) + fmt.Printf(" %s\t\t echo mode: shows arguments \n", argNameEcho) + fmt.Printf(" %s\t\t work time in seconds (e.g. -w=12) \n", argNameWorkTime) + fmt.Printf(" %s\t\t time-to-live for messages in seconds (e.g. -l=30) \n", argNameTTL) + fmt.Printf(" %s\t\t IP address and port of this node (e.g. 127.0.0.1:30303) \n", argNameIP) + fmt.Printf(" %s\t\t topic in hexadecimal format (e.g. 0f45beef) \n", argNameTopic) + fmt.Printf(" %s\t\t public key for asymmetric encryption \n", argNamePub) + fmt.Printf(" %s\t\t PoW in integer format \n", argNamePoW) + fmt.Printf(" %s\t\t salt \n", argNameSalt) + fmt.Printf(" %s\t\t enode \n", argNameEnode) + fmt.Printf(" %s\t\t help \n", argNameHelp) +} + +func main() { + parseArgs() + initialize() + run() +} + +func parseArgs() { + var p uint32 + help1 := checkMode(argNameHelp) + help2 := checkMode("-help") + help3 := checkMode("--help") + if help1 || help2 || help3 { + printHelp() + os.Exit(0) + } + + bootstrapNode = checkMode(argNameBootstrap) + daemonMode = checkMode(argNameDaemon) + testMode = checkMode(argNameTest) + echoMode = checkMode(argNameEcho) + isAsymmetric = checkMode(argNameAsymmetric) + + checkIntArg(argNameTTL, &ttl) + checkIntArg(argNameWorkTime, &workTime) + checkIntArg(argNamePoW, &p) + checkStringArg(argNameIP, &ipAddress) + checkStringArg(argNameEnode, &enode) + checkStringArg(argNameSalt, &salt) + checkStringArg(argNameTopic, &topicStr) + checkStringArg(argNamePub, &pubStr) + + if p > 0 { + pow = float64(p) + } + + if len(topicStr) > 0 { + var x []byte + x, err := hex.DecodeString(topicStr) + if err != nil { + fmt.Printf("Failed to parse the topic: %s \n", err) + os.Exit(0) + } + topic = whisper.BytesToTopic(x) + } + + if isAsymmetric && len(pubStr) > 0 { + pub = crypto.ToECDSAPub(common.FromHex(pubStr)) + if !isKeyValid(pub) { + fmt.Println("Error: invalid public key") + os.Exit(0) + } + } + + if echoMode { + fmt.Printf("ttl = %d \n", ttl) + fmt.Printf("workTime = %d \n", workTime) + fmt.Printf("pow = %f \n", pow) + fmt.Printf("ip = %s \n", ipAddress) + fmt.Printf("salt = %s \n", salt) + fmt.Printf("topic = %x \n", topic) + fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub))) + fmt.Printf("enode = %s \n", enode) + } +} + +func checkIntArg(pattern string, dst *uint32) { + pattern += "=" + sz := len(pattern) + for _, arg := range os.Args { + if len(arg) < sz { + continue + } + + prefix := arg[:sz] + if prefix == pattern { + s := arg[sz:] + i, err := strconv.ParseUint(s, 10, 0) + if err != nil { + fmt.Printf("Failed to parse argument %s: %s \n", pattern, err) + os.Exit(0) + } + if err == nil && i > 0 { + *dst = uint32(i) + } + return + } + } +} + +func checkStringArg(pattern string, dst *string) { + pattern += "=" + sz := len(pattern) + for _, arg := range os.Args { + if len(arg) < sz { + continue + } + + prefix := arg[:sz] + if prefix == pattern { + s := arg[sz:] + if len(s) > 0 { + *dst = s + } + return + } + } +} + +func checkMode(pattern string) bool { + for _, arg := range os.Args { + if arg == pattern { + return true + } + } + return false +} + +func initialize() { + glog.SetV(logger.Warn) + glog.SetToStderr(true) + + done = make(chan struct{}) + input = bufio.NewReader(os.Stdin) + var peers []*discover.Node + + if testMode { + password := []byte("this is a test password for symmetric encryption") + salt := []byte("this is a test salt for symmetric encryption") + symKey = pbkdf2.Key(password, salt, 64, 32, sha256.New) + topic = whisper.TopicType{0xFF, 0xFF, 0xFF, 0xFF} + } + + if bootstrapNode { + if len(ipAddress) == 0 { + if testMode { + ipAddress = "127.0.0.1:30303" + } else { + fmt.Printf("Please enter your IP and port (e.g. 127.0.0.1:30303): ") + fmt.Scanln(&ipAddress) + } + } + } else { + if len(enode) == 0 { + fmt.Printf("Please enter the peer's enode: ") + fmt.Scanln(&enode) + } + peer := discover.MustParseNode(enode) + peers = append(peers, peer) + } + + shh = whisper.NewWhisper(nil) + myNodeId := shh.NewIdentity() + asymKey = shh.NewIdentity() + + server = p2p.Server{ + Config: p2p.Config{ + PrivateKey: myNodeId, + MaxPeers: 48, + Name: common.MakeName("whisper-go", "5.0"), + Protocols: shh.Protocols(), + ListenAddr: ipAddress, + NAT: nat.Any(), + BootstrapNodes: peers, + StaticNodes: peers, + TrustedNodes: peers, + }, + } +} + +func startServer() { + err := server.Start() + if err != nil { + fmt.Printf("Failed to start Whsiper peer: %s.\n", err) + os.Exit(0) + } + + fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey))) + fmt.Println(server.NodeInfo().Enode) + + if bootstrapNode { + configureChat() + fmt.Println("Bootstrap Whisper node started") + waitForConnection(false) + } else { + fmt.Println("Whisper node started") + // first see if we can establish connection, then require futher user input + waitForConnection(true) + configureChat() + } + + if !daemonMode { + fmt.Printf("Chat is enabled. Please type the message. To quit type: '%s'\n", quitCommand) + } +} + +func isKeyValid(k *ecdsa.PublicKey) bool { + return k.X != nil && k.Y != nil +} + +func configureChat() { + if daemonMode { + return + } + + if isAsymmetric && len(pubStr) == 0 { + fmt.Printf("Please enter the peer's public key: ") + pubStr = scanLine() + pub = crypto.ToECDSAPub(common.FromHex(pubStr)) + if !isKeyValid(pub) { + fmt.Println("Error: invalid public key") + os.Exit(0) + } + } + + if !isAsymmetric && !testMode { + fmt.Printf("Please enter the password: ") + pass, err := terminal.ReadPassword(syscall.Stdin) + fmt.Println() + if err != nil { + fmt.Printf("Error: %s \n", err) + os.Exit(0) + } + + if len(salt) == 0 { + fmt.Printf("Please enter the salt: ") + salt = scanLine() + } + + symKey = pbkdf2.Key(pass, []byte(salt), 65356, 32, sha256.New) + + if len(topicStr) == 0 { + generateTopic(pass, []byte(salt)) + } + } + + filter = whisper.Filter{KeySym: symKey, KeyAsym: asymKey, Topics: []whisper.TopicType{topic}} + filterID = shh.Watch(&filter) +} + +func generateTopic(password, salt []byte) { + const rounds = 4000 + const size = 128 + x1 := pbkdf2.Key(password, salt, rounds, size, sha512.New) + x2 := pbkdf2.Key(password, salt, rounds, size, sha1.New) + x3 := pbkdf2.Key(x1, x2, rounds, size, sha256.New) + + for i := 0; i < size; i++ { + topic[i%whisper.TopicLength] ^= x3[i] + } +} + +func waitForConnection(timeout bool) { + var cnt int + var connected bool + for !connected { + time.Sleep(time.Millisecond * 50) + connected = server.PeerCount() > 0 + if timeout { + cnt++ + if cnt > 1000 { + fmt.Println("Timeout expired, failed to connect") + os.Exit(0) + } + } + } + + fmt.Println("Connected to peer.") +} + +func run() { + startServer() + defer server.Stop() + shh.Start(nil) + defer shh.Stop() + + if !daemonMode { + go messageLoop() + } + + for { + s := scanLine() + if s == quitCommand { + fmt.Println("Quit command received") + close(done) + break + } + sendMsg([]byte(s)) + + if isAsymmetric { + // print your own message for convenience, + // because in asymmetric mode it is impossible to decrypt it + hour, min, sec := time.Now().Clock() + from := crypto.PubkeyToAddress(asymKey.PublicKey) + fmt.Printf("\n%02d:%02d:%02d <%x>: %s\n", hour, min, sec, from, s) + } + } +} + +func scanLine() string { + txt, err := input.ReadString('\n') + if err != nil { + fmt.Printf("input error: %s \n", err) + os.Exit(0) + } + last := len(txt) - 1 + if txt[last] == '\n' { + return txt[:last] // without the trailing newline + } + return txt +} + +func sendMsg(payload []byte) { + params := whisper.MessageParams{ + Src: asymKey, + Dst: pub, + KeySym: symKey, + Payload: payload, + Topic: topic, + TTL: ttl, + PoW: pow, + WorkTime: workTime, + } + + msg := whisper.NewSentMessage(¶ms) + envelope, err := msg.Wrap(¶ms) + if err != nil { + fmt.Printf("failed to seal message: %v \n", err) + return + } + + err = shh.Send(envelope) + if err != nil { + fmt.Printf("failed to send message: %v \n", err) + } +} + +func messageLoop() { + f := shh.GetFilter(filterID) + if f == nil { + fmt.Println("error: filter is not installed!") + os.Exit(0) + } + + ticker := time.NewTicker(time.Millisecond * 50) + + for { + select { + case <-ticker.C: + messages := f.Retrieve() + for _, msg := range messages { + printMessageInfo(msg) + } + case <-done: + return + } + } +} + +func printMessageInfo(msg *whisper.ReceivedMessage) { + hour, min, sec := time.Now().Clock() + timestamp := fmt.Sprintf("%02d:%02d:%02d", hour, min, sec) + text := string(msg.Payload) + + var address common.Address + if msg.Src != nil { + address = crypto.PubkeyToAddress(*msg.Src) + } + + if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) { + fmt.Printf("\n%s <%x>: %s\n", timestamp, address, text) // message from myself + } else { + fmt.Printf("\n%s [%x]: %s\n", timestamp, address, text) // message from a peer + } +} From bfc59eaf8b9d391383140030f33e88b61e0a584c Mon Sep 17 00:00:00 2001 From: Vlad Date: Tue, 27 Dec 2016 18:29:02 +0100 Subject: [PATCH 02/13] whisper: parameter fixed --- whisper/wnode/main.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/whisper/wnode/main.go b/whisper/wnode/main.go index 21876eefba..5ed4f3a726 100644 --- a/whisper/wnode/main.go +++ b/whisper/wnode/main.go @@ -132,6 +132,13 @@ func parseArgs() { checkStringArg(argNameTopic, &topicStr) checkStringArg(argNamePub, &pubStr) + if len(enode) > 0 { + prefix := "enode://" + if enode[:len(prefix)] != prefix { + enode = prefix + enode + } + } + if p > 0 { pow = float64(p) } @@ -317,7 +324,7 @@ func configureChat() { if !isAsymmetric && !testMode { fmt.Printf("Please enter the password: ") - pass, err := terminal.ReadPassword(syscall.Stdin) + pass, err := terminal.ReadPassword(int(syscall.Stdin)) fmt.Println() if err != nil { fmt.Printf("Error: %s \n", err) From 9d5cdd28a063ad7da3fe084d05c0a1105d053ae1 Mon Sep 17 00:00:00 2001 From: Vlad Date: Thu, 29 Dec 2016 21:01:26 +0100 Subject: [PATCH 03/13] whisper: node id persistance introduced --- whisper/wnode/main.go | 97 +++++++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/whisper/wnode/main.go b/whisper/wnode/main.go index 5ed4f3a726..680d7a4f19 100644 --- a/whisper/wnode/main.go +++ b/whisper/wnode/main.go @@ -48,16 +48,16 @@ var input *bufio.Reader var done chan struct{} var server p2p.Server var shh *whisper.Whisper -var asymKey *ecdsa.PrivateKey // used for asymmetric decryption and signing -var pub *ecdsa.PublicKey // used for asymmetric encryption -var symKey []byte // used for symmetric encryption +var asymKey, nodeid *ecdsa.PrivateKey // used for asymmetric decryption and signing +var pub *ecdsa.PublicKey // used for asymmetric encryption +var symKey []byte // used for symmetric encryption var filter whisper.Filter var filterID uint32 var topic whisper.TopicType var pow float64 = whisper.MinimumPoW var ttl uint32 = 30 var workTime uint32 = 5 -var ipAddress, enode, salt, topicStr, pubStr string +var ipAddress, enode, salt, topicStr, pubStr, NodeIdFile string var bootstrapNode bool // does not actively connect to peers, wait for incoming connections var daemonMode bool // only forward messages, neither send nor track @@ -65,40 +65,52 @@ var testMode bool // predefined password, topic, ip and port var echoMode bool // shows params, etc. var isAsymmetric bool -var argNameHelp string = "-h" -var argNameBootstrap string = "-b" -var argNameDaemon string = "-d" -var argNameAsymmetric string = "-a" -var argNameTest string = "-test" -var argNameEcho string = "-echo" -var argNameWorkTime string = "-work" -var argNameTTL string = "-ttl" -var argNameIP string = "-ip" -var argNameTopic string = "-topic" -var argNamePoW string = "-pow" -var argNameSalt string = "-salt" -var argNamePub string = "-pub" -var argNameEnode string = "-enode" -var quitCommand string = "/q/" +var ( + argNameHelp = "-h" + argNameBootstrap = "-b" + argNameDaemon = "-d" + argNameAsymmetric = "-a" + argNameTest = "-test" + argNameEcho = "-echo" + argNameWorkTime = "-work" + argNameTTL = "-ttl" + argNameIP = "-ip" + argNameTopic = "-topic" + argNamePoW = "-pow" + argNameSalt = "-salt" + argNamePub = "-pub" + argNameEnode = "-enode" + argNameIdSrc = "-idfile" + quitCommand = "~q" +) + +func padRight(str string) string { + res := str + for len(res) < 9 { + res += " " + } + return res +} func printHelp() { fmt.Println("wchat is a stand-alone whisper node with command-line interface.") fmt.Println("wchat also allows to set up a chat using either symmetric or asymmetric encryption.\n") fmt.Println("usage: wchat [arguments]") - fmt.Printf(" %s\t\t boostrap node: don't actively connect to peers, wait for incoming connections\n", argNameBootstrap) - fmt.Printf(" %s\t\t daemon mode: only forward messages, neither send nor decrypt messages \n", argNameDaemon) - fmt.Printf(" %s\t\t use asymmetric encryption \n", argNameAsymmetric) - fmt.Printf(" %s\t\t test mode: predefined password, topic, ip and port \n", argNameTest) - fmt.Printf(" %s\t\t echo mode: shows arguments \n", argNameEcho) - fmt.Printf(" %s\t\t work time in seconds (e.g. -w=12) \n", argNameWorkTime) - fmt.Printf(" %s\t\t time-to-live for messages in seconds (e.g. -l=30) \n", argNameTTL) - fmt.Printf(" %s\t\t IP address and port of this node (e.g. 127.0.0.1:30303) \n", argNameIP) - fmt.Printf(" %s\t\t topic in hexadecimal format (e.g. 0f45beef) \n", argNameTopic) - fmt.Printf(" %s\t\t public key for asymmetric encryption \n", argNamePub) - fmt.Printf(" %s\t\t PoW in integer format \n", argNamePoW) - fmt.Printf(" %s\t\t salt \n", argNameSalt) - fmt.Printf(" %s\t\t enode \n", argNameEnode) - fmt.Printf(" %s\t\t help \n", argNameHelp) + fmt.Printf(" %s print this help and exit \n", padRight(argNameHelp)) + fmt.Printf(" %s boostrap node: don't actively connect to peers, wait for incoming connections\n", padRight(argNameBootstrap)) + fmt.Printf(" %s daemon mode: only forward messages, neither send nor decrypt messages \n", padRight(argNameDaemon)) + fmt.Printf(" %s use asymmetric encryption \n", padRight(argNameAsymmetric)) + fmt.Printf(" %s test mode: predefined password, salt, topic, ip and port \n", padRight(argNameTest)) + fmt.Printf(" %s echo mode: prints arguments (diagnostic) \n", padRight(argNameEcho)) + fmt.Printf(" %s IP address and port of this node (e.g. %s=127.0.0.1:30303) \n", padRight(argNameIP), argNameIP) + fmt.Printf(" %s time-to-live for messages in seconds (e.g. %s=30) \n", padRight(argNameTTL), argNameTTL) + fmt.Printf(" %s PoW in integer format \n", padRight(argNamePoW)) + fmt.Printf(" %s work time in seconds (e.g. %s=12) \n", padRight(argNameWorkTime), argNameWorkTime) + fmt.Printf(" %s salt (for topic and key derivation) \n", padRight(argNameSalt)) + fmt.Printf(" %s topic in hexadecimal format (e.g. %s=70a4beef) \n", padRight(argNameTopic), argNameTopic) + fmt.Printf(" %s public key for asymmetric encryption \n", padRight(argNamePub)) + fmt.Printf(" %s file name with node id (private key) \n", padRight(argNameIdSrc)) + fmt.Printf(" %s enode \n", padRight(argNameEnode)) } func main() { @@ -108,11 +120,13 @@ func main() { } func parseArgs() { + var err error var p uint32 help1 := checkMode(argNameHelp) help2 := checkMode("-help") help3 := checkMode("--help") - if help1 || help2 || help3 { + help4 := checkMode("-?") + if help1 || help2 || help3 || help4 { printHelp() os.Exit(0) } @@ -131,6 +145,15 @@ func parseArgs() { checkStringArg(argNameSalt, &salt) checkStringArg(argNameTopic, &topicStr) checkStringArg(argNamePub, &pubStr) + checkStringArg(argNameIdSrc, &NodeIdFile) + + if len(NodeIdFile) > 0 { + nodeid, err = crypto.LoadECDSA(NodeIdFile) + if err != nil { + fmt.Printf("Failed to load file [%s]: %s.\n", NodeIdFile, err) + os.Exit(-1) + } + } if len(enode) > 0 { prefix := "enode://" @@ -259,12 +282,14 @@ func initialize() { } shh = whisper.NewWhisper(nil) - myNodeId := shh.NewIdentity() asymKey = shh.NewIdentity() + if nodeid == nil { + nodeid = shh.NewIdentity() + } server = p2p.Server{ Config: p2p.Config{ - PrivateKey: myNodeId, + PrivateKey: nodeid, MaxPeers: 48, Name: common.MakeName("whisper-go", "5.0"), Protocols: shh.Protocols(), From ac273604ede7555d204e4700148bad00962ff3a1 Mon Sep 17 00:00:00 2001 From: Vlad Date: Fri, 30 Dec 2016 16:11:37 +0100 Subject: [PATCH 04/13] whisper: refactored input/output functions --- whisper/wnode/main.go | 63 +++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/whisper/wnode/main.go b/whisper/wnode/main.go index 680d7a4f19..80f709c3a6 100644 --- a/whisper/wnode/main.go +++ b/whisper/wnode/main.go @@ -20,7 +20,6 @@ package main import ( - "bufio" "crypto/ecdsa" "crypto/sha1" "crypto/sha256" @@ -29,10 +28,11 @@ import ( "fmt" "os" "strconv" - "syscall" "time" + "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/console" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" @@ -41,10 +41,8 @@ import ( "github.com/ethereum/go-ethereum/p2p/nat" whisper "github.com/ethereum/go-ethereum/whisper/whisperv5" "golang.org/x/crypto/pbkdf2" - "golang.org/x/crypto/ssh/terminal" ) -var input *bufio.Reader var done chan struct{} var server p2p.Server var shh *whisper.Whisper @@ -81,7 +79,7 @@ var ( argNamePub = "-pub" argNameEnode = "-enode" argNameIdSrc = "-idfile" - quitCommand = "~q" + quitCommand = "~Q" ) func padRight(str string) string { @@ -150,8 +148,7 @@ func parseArgs() { if len(NodeIdFile) > 0 { nodeid, err = crypto.LoadECDSA(NodeIdFile) if err != nil { - fmt.Printf("Failed to load file [%s]: %s.\n", NodeIdFile, err) - os.Exit(-1) + utils.Fatalf("Failed to load file [%s]: %s.", NodeIdFile, err) } } @@ -170,8 +167,7 @@ func parseArgs() { var x []byte x, err := hex.DecodeString(topicStr) if err != nil { - fmt.Printf("Failed to parse the topic: %s \n", err) - os.Exit(0) + utils.Fatalf("Failed to parse the topic: %s", err) } topic = whisper.BytesToTopic(x) } @@ -179,8 +175,7 @@ func parseArgs() { if isAsymmetric && len(pubStr) > 0 { pub = crypto.ToECDSAPub(common.FromHex(pubStr)) if !isKeyValid(pub) { - fmt.Println("Error: invalid public key") - os.Exit(0) + utils.Fatalf("invalid public key") } } @@ -209,8 +204,7 @@ func checkIntArg(pattern string, dst *uint32) { s := arg[sz:] i, err := strconv.ParseUint(s, 10, 0) if err != nil { - fmt.Printf("Failed to parse argument %s: %s \n", pattern, err) - os.Exit(0) + utils.Fatalf("Failed to parse argument %s: %s", pattern, err) } if err == nil && i > 0 { *dst = uint32(i) @@ -253,7 +247,6 @@ func initialize() { glog.SetToStderr(true) done = make(chan struct{}) - input = bufio.NewReader(os.Stdin) var peers []*discover.Node if testMode { @@ -305,8 +298,7 @@ func initialize() { func startServer() { err := server.Start() if err != nil { - fmt.Printf("Failed to start Whsiper peer: %s.\n", err) - os.Exit(0) + utils.Fatalf("Failed to start Whsiper peer: %s.", err) } fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey))) @@ -338,33 +330,27 @@ func configureChat() { } if isAsymmetric && len(pubStr) == 0 { - fmt.Printf("Please enter the peer's public key: ") - pubStr = scanLine() + pubStr = scanLine("Please enter the peer's public key: ") pub = crypto.ToECDSAPub(common.FromHex(pubStr)) if !isKeyValid(pub) { - fmt.Println("Error: invalid public key") - os.Exit(0) + utils.Fatalf("Error: invalid public key") } } if !isAsymmetric && !testMode { - fmt.Printf("Please enter the password: ") - pass, err := terminal.ReadPassword(int(syscall.Stdin)) - fmt.Println() + pass, err := console.Stdin.PromptPassword("Please enter the password: ") if err != nil { - fmt.Printf("Error: %s \n", err) - os.Exit(0) + utils.Fatalf("Failed to read passphrase: %v", err) } if len(salt) == 0 { - fmt.Printf("Please enter the salt: ") - salt = scanLine() + salt = scanLine("Please enter the salt: ") } - symKey = pbkdf2.Key(pass, []byte(salt), 65356, 32, sha256.New) + symKey = pbkdf2.Key([]byte(pass), []byte(salt), 65356, 32, sha256.New) if len(topicStr) == 0 { - generateTopic(pass, []byte(salt)) + generateTopic([]byte(pass), []byte(salt)) } } @@ -393,8 +379,7 @@ func waitForConnection(timeout bool) { if timeout { cnt++ if cnt > 1000 { - fmt.Println("Timeout expired, failed to connect") - os.Exit(0) + utils.Fatalf("Timeout expired, failed to connect") } } } @@ -413,7 +398,7 @@ func run() { } for { - s := scanLine() + s := scanLine("") if s == quitCommand { fmt.Println("Quit command received") close(done) @@ -431,15 +416,10 @@ func run() { } } -func scanLine() string { - txt, err := input.ReadString('\n') +func scanLine(prompt string) string { + txt, err := console.Stdin.PromptInput(prompt) if err != nil { - fmt.Printf("input error: %s \n", err) - os.Exit(0) - } - last := len(txt) - 1 - if txt[last] == '\n' { - return txt[:last] // without the trailing newline + utils.Fatalf("input error: %s", err) } return txt } @@ -472,8 +452,7 @@ func sendMsg(payload []byte) { func messageLoop() { f := shh.GetFilter(filterID) if f == nil { - fmt.Println("error: filter is not installed!") - os.Exit(0) + utils.Fatalf("filter is not installed") } ticker := time.NewTicker(time.Millisecond * 50) From 50205216f219190a9c6229cadb34ca86e13e503e Mon Sep 17 00:00:00 2001 From: Vlad Date: Fri, 30 Dec 2016 17:53:14 +0100 Subject: [PATCH 05/13] whisper: number of peers changed --- whisper/wnode/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/whisper/wnode/main.go b/whisper/wnode/main.go index 80f709c3a6..8e6d92cea4 100644 --- a/whisper/wnode/main.go +++ b/whisper/wnode/main.go @@ -283,7 +283,7 @@ func initialize() { server = p2p.Server{ Config: p2p.Config{ PrivateKey: nodeid, - MaxPeers: 48, + MaxPeers: 128, Name: common.MakeName("whisper-go", "5.0"), Protocols: shh.Protocols(), ListenAddr: ipAddress, From 697a50385b4c4ac24a00a9c8411c1f096d65a27a Mon Sep 17 00:00:00 2001 From: Vlad Date: Sat, 31 Dec 2016 14:49:33 +0100 Subject: [PATCH 06/13] whisper: fixed the build errors --- whisper/wnode/main.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/whisper/wnode/main.go b/whisper/wnode/main.go index 8e6d92cea4..308a4a4d22 100644 --- a/whisper/wnode/main.go +++ b/whisper/wnode/main.go @@ -44,12 +44,11 @@ import ( ) var done chan struct{} -var server p2p.Server +var server *p2p.Server var shh *whisper.Whisper var asymKey, nodeid *ecdsa.PrivateKey // used for asymmetric decryption and signing var pub *ecdsa.PublicKey // used for asymmetric encryption var symKey []byte // used for symmetric encryption -var filter whisper.Filter var filterID uint32 var topic whisper.TopicType var pow float64 = whisper.MinimumPoW @@ -92,7 +91,8 @@ func padRight(str string) string { func printHelp() { fmt.Println("wchat is a stand-alone whisper node with command-line interface.") - fmt.Println("wchat also allows to set up a chat using either symmetric or asymmetric encryption.\n") + fmt.Println("wchat also allows to set up a chat using either symmetric or asymmetric encryption.") + fmt.Println() fmt.Println("usage: wchat [arguments]") fmt.Printf(" %s print this help and exit \n", padRight(argNameHelp)) fmt.Printf(" %s boostrap node: don't actively connect to peers, wait for incoming connections\n", padRight(argNameBootstrap)) @@ -280,7 +280,7 @@ func initialize() { nodeid = shh.NewIdentity() } - server = p2p.Server{ + server = &p2p.Server{ Config: p2p.Config{ PrivateKey: nodeid, MaxPeers: 128, @@ -354,8 +354,8 @@ func configureChat() { } } - filter = whisper.Filter{KeySym: symKey, KeyAsym: asymKey, Topics: []whisper.TopicType{topic}} - filterID = shh.Watch(&filter) + filter := &whisper.Filter{KeySym: symKey, KeyAsym: asymKey, Topics: []whisper.TopicType{topic}} + filterID = shh.Watch(filter) } func generateTopic(password, salt []byte) { From 5b682d648547350865e0301cc431aeff66a16e41 Mon Sep 17 00:00:00 2001 From: Vlad Date: Wed, 4 Jan 2017 23:14:29 +0100 Subject: [PATCH 07/13] whisper: input method changed --- whisper/wnode/main.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/whisper/wnode/main.go b/whisper/wnode/main.go index 308a4a4d22..dd484cb286 100644 --- a/whisper/wnode/main.go +++ b/whisper/wnode/main.go @@ -20,6 +20,7 @@ package main import ( + "bufio" "crypto/ecdsa" "crypto/sha1" "crypto/sha256" @@ -28,6 +29,7 @@ import ( "fmt" "os" "strconv" + "strings" "time" "github.com/ethereum/go-ethereum/cmd/utils" @@ -55,6 +57,7 @@ var pow float64 = whisper.MinimumPoW var ttl uint32 = 30 var workTime uint32 = 5 var ipAddress, enode, salt, topicStr, pubStr, NodeIdFile string +var input *bufio.Reader = bufio.NewReader(os.Stdin) var bootstrapNode bool // does not actively connect to peers, wait for incoming connections var daemonMode bool // only forward messages, neither send nor track @@ -417,11 +420,15 @@ func run() { } func scanLine(prompt string) string { - txt, err := console.Stdin.PromptInput(prompt) + if len(prompt) > 0 { + fmt.Print(prompt) + } + //txt, err := console.Stdin.PromptInput(prompt) // todo: delete + txt, err := input.ReadString('\n') if err != nil { utils.Fatalf("input error: %s", err) } - return txt + return strings.TrimRight(txt, "\n\r") } func sendMsg(payload []byte) { From 03ef1219198020034c825e35b2124dff27aa5d69 Mon Sep 17 00:00:00 2001 From: Vlad Date: Wed, 11 Jan 2017 12:36:37 +0100 Subject: [PATCH 08/13] whisper: new test added (rlp encode/decode) --- whisper/whisperv5/envelope.go | 4 ++++ whisper/whisperv5/message_test.go | 33 +++++++++++++++++++++++++++++++ whisper/whisperv5/peer.go | 5 +++++ 3 files changed, 42 insertions(+) diff --git a/whisper/whisperv5/envelope.go b/whisper/whisperv5/envelope.go index 1b976705d4..3c94ffbdaa 100644 --- a/whisper/whisperv5/envelope.go +++ b/whisper/whisperv5/envelope.go @@ -172,6 +172,10 @@ func (e *Envelope) DecodeRLP(s *rlp.Stream) error { if err != nil { return err } + return e.DecodeBytes(raw) +} + +func (e *Envelope) DecodeBytes(raw []byte) error { // The decoding of Envelope uses the struct fields but also needs // to compute the hash of the whole RLP-encoded envelope. This // type has the same structure as Envelope but is not an diff --git a/whisper/whisperv5/message_test.go b/whisper/whisperv5/message_test.go index 5cbc9182f2..280670880e 100644 --- a/whisper/whisperv5/message_test.go +++ b/whisper/whisperv5/message_test.go @@ -22,6 +22,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" ) func copyFromBuf(dst []byte, src []byte, beg int) int { @@ -315,3 +316,35 @@ func TestEncryptWithZeroKey(t *testing.T) { t.Fatalf("wrapped with nil key, seed: %d.", seed) } } + +func TestRlpEncode(t *testing.T) { + InitSingleTest() + + 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("wrapped with zero key, seed: %d.", seed) + } + + rlpEncoded, err := rlp.EncodeToBytes(env) + if err != nil { + t.Fatalf("RLP encode failed: %s.", err) + } + + var decoded Envelope + err = decoded.DecodeBytes(rlpEncoded) + if err != nil { + t.Fatalf("RLP decode failed: %s.", err) + } + + he := env.Hash() + hd := decoded.Hash() + + if he != hd { + t.Fatalf("Hashes are not equal: %x vs. %x", he, hd) + } +} diff --git a/whisper/whisperv5/peer.go b/whisper/whisperv5/peer.go index 4273cfce1f..f0bda0a638 100644 --- a/whisper/whisperv5/peer.go +++ b/whisper/whisperv5/peer.go @@ -175,3 +175,8 @@ func (p *Peer) broadcast() error { glog.V(logger.Detail).Infoln(p.peer, "broadcasted", len(transmit), "message(s)") return nil } + +func (p *Peer) ID() []byte { + id := p.peer.ID() + return id[:] +} From 556992d503c040155ffbb6eba144ae11375bf9bd Mon Sep 17 00:00:00 2001 From: Vlad Date: Fri, 13 Jan 2017 16:19:44 +0100 Subject: [PATCH 09/13] whisper: rlp test updated --- whisper/whisperv5/message_test.go | 4 ++-- whisper/whisperv5/whisper.go | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/whisper/whisperv5/message_test.go b/whisper/whisperv5/message_test.go index 280670880e..66c969b5e4 100644 --- a/whisper/whisperv5/message_test.go +++ b/whisper/whisperv5/message_test.go @@ -330,13 +330,13 @@ func TestRlpEncode(t *testing.T) { t.Fatalf("wrapped with zero key, seed: %d.", seed) } - rlpEncoded, err := rlp.EncodeToBytes(env) + raw, err := rlp.EncodeToBytes(env) if err != nil { t.Fatalf("RLP encode failed: %s.", err) } var decoded Envelope - err = decoded.DecodeBytes(rlpEncoded) + rlp.DecodeBytes(raw, &decoded) if err != nil { t.Fatalf("RLP decode failed: %s.", err) } diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go index 789adbdb3e..5641c7b9ad 100644 --- a/whisper/whisperv5/whisper.go +++ b/whisper/whisperv5/whisper.go @@ -142,6 +142,10 @@ func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error { return p2p.Send(p.ws, p2pCode, envelope) } +func (w *Whisper) SendP2PDirect(peer *Peer, envelope *Envelope) error { + return p2p.Send(peer.ws, p2pCode, envelope) +} + // NewIdentity generates a new cryptographic identity for the client, and injects // it into the known identities for message decryption. func (w *Whisper) NewIdentity() *ecdsa.PrivateKey { From fab165ead95920e6d65b5c41f1098b0569374ee5 Mon Sep 17 00:00:00 2001 From: Vlad Date: Fri, 13 Jan 2017 19:41:18 +0100 Subject: [PATCH 10/13] whisper: grouped variables, added a server stub --- whisper/wnode/main.go | 298 +++++++++++++++++++++++++++++++++--------- 1 file changed, 239 insertions(+), 59 deletions(-) diff --git a/whisper/wnode/main.go b/whisper/wnode/main.go index dd484cb286..0d61dc686b 100644 --- a/whisper/wnode/main.go +++ b/whisper/wnode/main.go @@ -1,21 +1,21 @@ // Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. +// This file is part of go-ethereum. // -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // -// The go-ethereum library is distributed in the hope that it will be useful, +// go-ethereum is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// GNU General Public License for more details. // -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see . -// This is a simple Whisper node. -// It also implements a command line chat between the peers sharing the same credentials. +// This is a simple Whisper node. It could be used as a stand-alone bootstrap node. +// Also, could be used for different test and diagnostics purposes. package main @@ -25,6 +25,7 @@ import ( "crypto/sha1" "crypto/sha256" "crypto/sha512" + "encoding/binary" "encoding/hex" "fmt" "os" @@ -41,35 +42,62 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/rlp" whisper "github.com/ethereum/go-ethereum/whisper/whisperv5" "golang.org/x/crypto/pbkdf2" ) -var done chan struct{} -var server *p2p.Server -var shh *whisper.Whisper -var asymKey, nodeid *ecdsa.PrivateKey // used for asymmetric decryption and signing -var pub *ecdsa.PublicKey // used for asymmetric encryption -var symKey []byte // used for symmetric encryption -var filterID uint32 -var topic whisper.TopicType -var pow float64 = whisper.MinimumPoW -var ttl uint32 = 30 -var workTime uint32 = 5 -var ipAddress, enode, salt, topicStr, pubStr, NodeIdFile string -var input *bufio.Reader = bufio.NewReader(os.Stdin) +const sizeOfInt = 4 -var bootstrapNode bool // does not actively connect to peers, wait for incoming connections -var daemonMode bool // only forward messages, neither send nor track -var testMode bool // predefined password, topic, ip and port -var echoMode bool // shows params, etc. -var isAsymmetric bool +// singletons +var ( + server *p2p.Server + shh *whisper.Whisper + mailServer WMailServer + + done chan struct{} + input *bufio.Reader = bufio.NewReader(os.Stdin) +) + +// encryption/decryption +var ( + pub *ecdsa.PublicKey + asymKey *ecdsa.PrivateKey + nodeid *ecdsa.PrivateKey + symKey []byte +) + +// parameters +var ( + filterID uint32 + topic whisper.TopicType + ttl uint32 = 30 + workTime uint32 = 5 + msPoW float64 + timeLow uint32 + timeUpp uint32 + pow float64 = whisper.MinimumPoW + + ipAddress, enode, salt, topicStr, pubStr, NodeIdFile, dbPath, msPassword string +) + +var ( + bootstrapMode bool // does not actively connect to peers, wait for incoming connections + forwarderMode bool // only forward messages, neither send nor track + mailServerMode bool // delivers expired messages on demand + testMode bool // predefined password, topic, ip and port + echoMode bool // shows params, etc. + isAsymmetric bool + requestMail bool +) var ( argNameHelp = "-h" - argNameBootstrap = "-b" - argNameDaemon = "-d" + argBootstrapMode = "-b" + argNameForwarder = "-f" argNameAsymmetric = "-a" + argMailServerMode = "-m" + argRequestMail = "-r" argNameTest = "-test" argNameEcho = "-echo" argNameWorkTime = "-work" @@ -79,11 +107,23 @@ var ( argNamePoW = "-pow" argNameSalt = "-salt" argNamePub = "-pub" - argNameEnode = "-enode" + argNameBoot = "-boot" argNameIdSrc = "-idfile" - quitCommand = "~Q" + argNameDbPath = "-dbpath" + argNameMSPoW = "-mspow" + + quitCommand = "~Q" + enodePrefix = "enode://" ) +// this is a temporary stub, will be expanded later +type WMailServer struct{} + +func (s *WMailServer) Archive(env *whisper.Envelope) {} +func (s *WMailServer) DeliverMail(peer *whisper.Peer, data []byte) {} +func (s *WMailServer) Init(w *whisper.Whisper, path string, password string, pow float64) {} +func (s *WMailServer) Close() {} + func padRight(str string) string { res := str for len(res) < 9 { @@ -93,14 +133,14 @@ func padRight(str string) string { } func printHelp() { - fmt.Println("wchat is a stand-alone whisper node with command-line interface.") - fmt.Println("wchat also allows to set up a chat using either symmetric or asymmetric encryption.") + fmt.Println("wnode is a stand-alone whisper node with command-line interface.") fmt.Println() - fmt.Println("usage: wchat [arguments]") + fmt.Println("usage: wnode [arguments]") fmt.Printf(" %s print this help and exit \n", padRight(argNameHelp)) - fmt.Printf(" %s boostrap node: don't actively connect to peers, wait for incoming connections\n", padRight(argNameBootstrap)) - fmt.Printf(" %s daemon mode: only forward messages, neither send nor decrypt messages \n", padRight(argNameDaemon)) + fmt.Printf(" %s boostrap node: don't actively connect to peers, wait for incoming connections\n", padRight(argBootstrapMode)) + fmt.Printf(" %s forwarder mode: only forward messages, neither send nor decrypt messages \n", padRight(argNameForwarder)) fmt.Printf(" %s use asymmetric encryption \n", padRight(argNameAsymmetric)) + fmt.Printf(" %s mail server mode: delivers expired messages on demand \n", padRight(argMailServerMode)) fmt.Printf(" %s test mode: predefined password, salt, topic, ip and port \n", padRight(argNameTest)) fmt.Printf(" %s echo mode: prints arguments (diagnostic) \n", padRight(argNameEcho)) fmt.Printf(" %s IP address and port of this node (e.g. %s=127.0.0.1:30303) \n", padRight(argNameIP), argNameIP) @@ -111,7 +151,10 @@ func printHelp() { fmt.Printf(" %s topic in hexadecimal format (e.g. %s=70a4beef) \n", padRight(argNameTopic), argNameTopic) fmt.Printf(" %s public key for asymmetric encryption \n", padRight(argNamePub)) fmt.Printf(" %s file name with node id (private key) \n", padRight(argNameIdSrc)) - fmt.Printf(" %s enode \n", padRight(argNameEnode)) + fmt.Printf(" %s path to the DB directory \n", padRight(argNameDbPath)) + fmt.Printf(" %s PoW requirement for Mail Server (int) \n", padRight(argNameMSPoW)) + fmt.Printf(" %s request old (expired) messages from the bootstrap server \n", padRight(argRequestMail)) + fmt.Printf(" %s bootstrap node you want to connect to (e.g. %s=enode://e454......08d50@52.176.211.200:16428) \n", padRight(argNameBoot), argNameBoot) } func main() { @@ -122,7 +165,7 @@ func main() { func parseArgs() { var err error - var p uint32 + var p, x uint32 help1 := checkMode(argNameHelp) help2 := checkMode("-help") help3 := checkMode("--help") @@ -132,21 +175,25 @@ func parseArgs() { os.Exit(0) } - bootstrapNode = checkMode(argNameBootstrap) - daemonMode = checkMode(argNameDaemon) + bootstrapMode = checkMode(argBootstrapMode) + forwarderMode = checkMode(argNameForwarder) + mailServerMode = checkMode(argMailServerMode) testMode = checkMode(argNameTest) echoMode = checkMode(argNameEcho) isAsymmetric = checkMode(argNameAsymmetric) + requestMail = checkMode(argRequestMail) checkIntArg(argNameTTL, &ttl) checkIntArg(argNameWorkTime, &workTime) checkIntArg(argNamePoW, &p) + checkIntArg(argNameMSPoW, &x) checkStringArg(argNameIP, &ipAddress) - checkStringArg(argNameEnode, &enode) + checkStringArg(argNameBoot, &enode) checkStringArg(argNameSalt, &salt) checkStringArg(argNameTopic, &topicStr) checkStringArg(argNamePub, &pubStr) checkStringArg(argNameIdSrc, &NodeIdFile) + checkStringArg(argNameDbPath, &dbPath) if len(NodeIdFile) > 0 { nodeid, err = crypto.LoadECDSA(NodeIdFile) @@ -156,9 +203,8 @@ func parseArgs() { } if len(enode) > 0 { - prefix := "enode://" - if enode[:len(prefix)] != prefix { - enode = prefix + enode + if enode[:len(enodePrefix)] != enodePrefix { + enode = enodePrefix + enode } } @@ -166,6 +212,10 @@ func parseArgs() { pow = float64(p) } + if x > 0 { + msPoW = float64(x) + } + if len(topicStr) > 0 { var x []byte x, err := hex.DecodeString(topicStr) @@ -190,7 +240,7 @@ func parseArgs() { fmt.Printf("salt = %s \n", salt) fmt.Printf("topic = %x \n", topic) fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub))) - fmt.Printf("enode = %s \n", enode) + fmt.Printf("boot = %s \n", enode) } } @@ -251,6 +301,7 @@ func initialize() { done = make(chan struct{}) var peers []*discover.Node + var err error if testMode { password := []byte("this is a test password for symmetric encryption") @@ -259,7 +310,7 @@ func initialize() { topic = whisper.TopicType{0xFF, 0xFF, 0xFF, 0xFF} } - if bootstrapNode { + if bootstrapMode { if len(ipAddress) == 0 { if testMode { ipAddress = "127.0.0.1:30303" @@ -277,7 +328,18 @@ func initialize() { peers = append(peers, peer) } - shh = whisper.NewWhisper(nil) + if mailServerMode { + msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ") + if err != nil { + utils.Fatalf("Failed to read Mail Server password: %s", err) + } + + shh = whisper.NewWhisper(&mailServer) + mailServer.Init(shh, dbPath, msPassword, msPoW) + } else { + shh = whisper.NewWhisper(nil) + } + asymKey = shh.NewIdentity() if nodeid == nil { nodeid = shh.NewIdentity() @@ -307,19 +369,19 @@ func startServer() { fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey))) fmt.Println(server.NodeInfo().Enode) - if bootstrapNode { - configureChat() + if bootstrapMode { + configureNode() fmt.Println("Bootstrap Whisper node started") - waitForConnection(false) + //waitForConnection(false) // todo: review } else { fmt.Println("Whisper node started") // first see if we can establish connection, then require futher user input waitForConnection(true) - configureChat() + configureNode() } - if !daemonMode { - fmt.Printf("Chat is enabled. Please type the message. To quit type: '%s'\n", quitCommand) + if !forwarderMode { + fmt.Printf("Connection is established. Please type the message. To quit type: '%s'\n", quitCommand) } } @@ -327,8 +389,12 @@ func isKeyValid(k *ecdsa.PublicKey) bool { return k.X != nil && k.Y != nil } -func configureChat() { - if daemonMode { +func configureNode() { + var i int + var s string + var err error + + if forwarderMode { return } @@ -340,7 +406,7 @@ func configureChat() { } } - if !isAsymmetric && !testMode { + if !isAsymmetric && !testMode && !forwarderMode { pass, err := console.Stdin.PromptPassword("Please enter the password: ") if err != nil { utils.Fatalf("Failed to read passphrase: %v", err) @@ -357,6 +423,28 @@ func configureChat() { } } + if mailServerMode { + if len(dbPath) == 0 { + dbPath = scanLine("Please enter the path to DB file: ") + } + + if msPoW == 0.0 { + s = scanLine("Please enter the PoW requirement for the Mail Server (int): ") + i, err = strconv.Atoi(s) + if err != nil { + utils.Fatalf("Fail to parse the PoW: %s", err) + } + msPoW = float64(i) + } + } + + if requestMail { + msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ") + if err != nil { + utils.Fatalf("Failed to read Mail Server password: %s", err) + } + } + filter := &whisper.Filter{KeySym: symKey, KeyAsym: asymKey, Topics: []whisper.TopicType{topic}} filterID = shh.Watch(filter) } @@ -391,15 +479,24 @@ func waitForConnection(timeout bool) { } func run() { + defer mailServer.Close() startServer() defer server.Stop() shh.Start(nil) defer shh.Stop() - if !daemonMode { + if !forwarderMode { go messageLoop() } + if requestMail { + requestExpiredMessagesLoop() + } else { + sendLoop() + } +} + +func sendLoop() { for { s := scanLine("") if s == quitCommand { @@ -478,8 +575,7 @@ func messageLoop() { } func printMessageInfo(msg *whisper.ReceivedMessage) { - hour, min, sec := time.Now().Clock() - timestamp := fmt.Sprintf("%02d:%02d:%02d", hour, min, sec) + timestamp := fmt.Sprintf("%d", msg.Sent) // unix timestamp for diagnostics text := string(msg.Payload) var address common.Address @@ -493,3 +589,87 @@ func printMessageInfo(msg *whisper.ReceivedMessage) { fmt.Printf("\n%s [%x]: %s\n", timestamp, address, text) // message from a peer } } + +func requestExpiredMessagesLoop() { + var key, peerID []byte + err := shh.AddSymKey(argRequestMail, []byte(msPassword)) + if err != nil { + utils.Fatalf("Failed to create symmetric key for mail request: %s", err) + } + key = shh.GetSymKey(argRequestMail) + peerID = extractIdFromEnode(enode) + shh.MarkPeerTrusted(peerID) + + for { + s := scanLine("Please enter the lower limit for the time range (unix timestamp): ") + i, err := strconv.Atoi(s) + if err != nil { + utils.Fatalf("Fail to parse the lower time limit: %s", err) + } + timeLow = uint32(i) + + s = scanLine("Please enter the upper limit for the time range (unix timestamp): ") + i, err = strconv.Atoi(s) + if err != nil { + utils.Fatalf("Fail to parse the upper time limit: %s", err) + } + timeUpp = uint32(i) + if timeUpp == 0 { + timeUpp = 0xFFFFFFFF + } + + data := make([]byte, sizeOfInt*2) + binary.BigEndian.PutUint32(data, timeLow) + binary.BigEndian.PutUint32(data[sizeOfInt:], timeUpp) + // todo: add topic + + var params whisper.MessageParams + params.PoW = msPoW + params.Payload = data + params.KeySym = key + params.Src = nodeid + params.WorkTime = 5 + + msg := whisper.NewSentMessage(¶ms) + env, err := msg.Wrap(¶ms) + if err != nil { + utils.Fatalf("Wrap failed: %s", err) + } + + encoded, err := rlp.EncodeToBytes(env) + if err != nil { + utils.Fatalf("RLP encoding failed: %s", err) + } + + err = shh.RequestHistoricMessages(peerID, encoded) + if err != nil { + utils.Fatalf("Failed to send P2P message: %s", err) + } + + time.Sleep(time.Second * 5) + } +} + +func extractIdFromEnode(s string) []byte { + if len(s) == 0 { + return nil + } + + p := len(enodePrefix) + if s[:p] == enodePrefix { + s = s[p:] + } + + i := strings.Index(s, "@") + if i > 0 { + s = s[:i] + } + + b, err := hex.DecodeString(s) + if err != nil { + utils.Fatalf("Failed to decode enode: %s", err) + return nil + } + + return b +} From c8eff7fde051c39190ff30d091853051ff1e4bc5 Mon Sep 17 00:00:00 2001 From: Vlad Date: Sun, 15 Jan 2017 00:04:29 +0100 Subject: [PATCH 11/13] whisper: fixed flags, etc. --- whisper/wnode/main.go | 417 ++++++++++++++---------------------------- 1 file changed, 139 insertions(+), 278 deletions(-) diff --git a/whisper/wnode/main.go b/whisper/wnode/main.go index 0d61dc686b..019f261165 100644 --- a/whisper/wnode/main.go +++ b/whisper/wnode/main.go @@ -27,6 +27,7 @@ import ( "crypto/sha512" "encoding/binary" "encoding/hex" + "flag" "fmt" "os" "strconv" @@ -49,250 +50,111 @@ import ( const sizeOfInt = 4 +var ( + quitCommand = "~Q" + enodePrefix = "enode://" + mailServerKeyName = "fd8c027e29852ef" +) + // singletons var ( server *p2p.Server shh *whisper.Whisper + done chan struct{} + input *bufio.Reader = bufio.NewReader(os.Stdin) mailServer WMailServer - - done chan struct{} - input *bufio.Reader = bufio.NewReader(os.Stdin) ) -// encryption/decryption +// encryption var ( - pub *ecdsa.PublicKey - asymKey *ecdsa.PrivateKey - nodeid *ecdsa.PrivateKey - symKey []byte + symKey []byte + pub *ecdsa.PublicKey + asymKey *ecdsa.PrivateKey + nodeid *ecdsa.PrivateKey + topic whisper.TopicType + filterID uint32 + msPassword string ) -// parameters +// cmd arguments var ( - filterID uint32 - topic whisper.TopicType - ttl uint32 = 30 - workTime uint32 = 5 - msPoW float64 - timeLow uint32 - timeUpp uint32 - pow float64 = whisper.MinimumPoW + 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("m", 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") - ipAddress, enode, salt, topicStr, pubStr, NodeIdFile, dbPath, msPassword string + 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)") + argSalt = flag.String("salt", "", "salt (for topic and key derivation)") + 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)") ) -var ( - bootstrapMode bool // does not actively connect to peers, wait for incoming connections - forwarderMode bool // only forward messages, neither send nor track - mailServerMode bool // delivers expired messages on demand - testMode bool // predefined password, topic, ip and port - echoMode bool // shows params, etc. - isAsymmetric bool - requestMail bool -) - -var ( - argNameHelp = "-h" - argBootstrapMode = "-b" - argNameForwarder = "-f" - argNameAsymmetric = "-a" - argMailServerMode = "-m" - argRequestMail = "-r" - argNameTest = "-test" - argNameEcho = "-echo" - argNameWorkTime = "-work" - argNameTTL = "-ttl" - argNameIP = "-ip" - argNameTopic = "-topic" - argNamePoW = "-pow" - argNameSalt = "-salt" - argNamePub = "-pub" - argNameBoot = "-boot" - argNameIdSrc = "-idfile" - argNameDbPath = "-dbpath" - argNameMSPoW = "-mspow" - - quitCommand = "~Q" - enodePrefix = "enode://" -) - -// this is a temporary stub, will be expanded later -type WMailServer struct{} - -func (s *WMailServer) Archive(env *whisper.Envelope) {} -func (s *WMailServer) DeliverMail(peer *whisper.Peer, data []byte) {} -func (s *WMailServer) Init(w *whisper.Whisper, path string, password string, pow float64) {} -func (s *WMailServer) Close() {} - -func padRight(str string) string { - res := str - for len(res) < 9 { - res += " " - } - return res -} - -func printHelp() { - fmt.Println("wnode is a stand-alone whisper node with command-line interface.") - fmt.Println() - fmt.Println("usage: wnode [arguments]") - fmt.Printf(" %s print this help and exit \n", padRight(argNameHelp)) - fmt.Printf(" %s boostrap node: don't actively connect to peers, wait for incoming connections\n", padRight(argBootstrapMode)) - fmt.Printf(" %s forwarder mode: only forward messages, neither send nor decrypt messages \n", padRight(argNameForwarder)) - fmt.Printf(" %s use asymmetric encryption \n", padRight(argNameAsymmetric)) - fmt.Printf(" %s mail server mode: delivers expired messages on demand \n", padRight(argMailServerMode)) - fmt.Printf(" %s test mode: predefined password, salt, topic, ip and port \n", padRight(argNameTest)) - fmt.Printf(" %s echo mode: prints arguments (diagnostic) \n", padRight(argNameEcho)) - fmt.Printf(" %s IP address and port of this node (e.g. %s=127.0.0.1:30303) \n", padRight(argNameIP), argNameIP) - fmt.Printf(" %s time-to-live for messages in seconds (e.g. %s=30) \n", padRight(argNameTTL), argNameTTL) - fmt.Printf(" %s PoW in integer format \n", padRight(argNamePoW)) - fmt.Printf(" %s work time in seconds (e.g. %s=12) \n", padRight(argNameWorkTime), argNameWorkTime) - fmt.Printf(" %s salt (for topic and key derivation) \n", padRight(argNameSalt)) - fmt.Printf(" %s topic in hexadecimal format (e.g. %s=70a4beef) \n", padRight(argNameTopic), argNameTopic) - fmt.Printf(" %s public key for asymmetric encryption \n", padRight(argNamePub)) - fmt.Printf(" %s file name with node id (private key) \n", padRight(argNameIdSrc)) - fmt.Printf(" %s path to the DB directory \n", padRight(argNameDbPath)) - fmt.Printf(" %s PoW requirement for Mail Server (int) \n", padRight(argNameMSPoW)) - fmt.Printf(" %s request old (expired) messages from the bootstrap server \n", padRight(argRequestMail)) - fmt.Printf(" %s bootstrap node you want to connect to (e.g. %s=enode://e454......08d50@52.176.211.200:16428) \n", padRight(argNameBoot), argNameBoot) -} - func main() { - parseArgs() + processArgs() initialize() run() } -func parseArgs() { - var err error - var p, x uint32 - help1 := checkMode(argNameHelp) - help2 := checkMode("-help") - help3 := checkMode("--help") - help4 := checkMode("-?") - if help1 || help2 || help3 || help4 { - printHelp() - os.Exit(0) - } +func processArgs() { + flag.Parse() - bootstrapMode = checkMode(argBootstrapMode) - forwarderMode = checkMode(argNameForwarder) - mailServerMode = checkMode(argMailServerMode) - testMode = checkMode(argNameTest) - echoMode = checkMode(argNameEcho) - isAsymmetric = checkMode(argNameAsymmetric) - requestMail = checkMode(argRequestMail) - - checkIntArg(argNameTTL, &ttl) - checkIntArg(argNameWorkTime, &workTime) - checkIntArg(argNamePoW, &p) - checkIntArg(argNameMSPoW, &x) - checkStringArg(argNameIP, &ipAddress) - checkStringArg(argNameBoot, &enode) - checkStringArg(argNameSalt, &salt) - checkStringArg(argNameTopic, &topicStr) - checkStringArg(argNamePub, &pubStr) - checkStringArg(argNameIdSrc, &NodeIdFile) - checkStringArg(argNameDbPath, &dbPath) - - if len(NodeIdFile) > 0 { - nodeid, err = crypto.LoadECDSA(NodeIdFile) + if len(*argIDFile) > 0 { + var err error + nodeid, err = crypto.LoadECDSA(*argIDFile) if err != nil { - utils.Fatalf("Failed to load file [%s]: %s.", NodeIdFile, err) + utils.Fatalf("Failed to load file [%s]: %s.", *argIDFile, err) } } - if len(enode) > 0 { - if enode[:len(enodePrefix)] != enodePrefix { - enode = enodePrefix + enode + if len(*argEnode) > 0 { + if (*argEnode)[:len(enodePrefix)] != enodePrefix { + *argEnode = enodePrefix + *argEnode } } - if p > 0 { - pow = float64(p) - } - - if x > 0 { - msPoW = float64(x) - } - - if len(topicStr) > 0 { - var x []byte - x, err := hex.DecodeString(topicStr) + if len(*argTopic) > 0 { + x, err := hex.DecodeString(*argTopic) if err != nil { utils.Fatalf("Failed to parse the topic: %s", err) } topic = whisper.BytesToTopic(x) } - if isAsymmetric && len(pubStr) > 0 { - pub = crypto.ToECDSAPub(common.FromHex(pubStr)) + if *asymmetricMode && len(*argPub) > 0 { + pub = crypto.ToECDSAPub(common.FromHex(*argPub)) if !isKeyValid(pub) { utils.Fatalf("invalid public key") } } - if echoMode { - fmt.Printf("ttl = %d \n", ttl) - fmt.Printf("workTime = %d \n", workTime) - fmt.Printf("pow = %f \n", pow) - fmt.Printf("ip = %s \n", ipAddress) - fmt.Printf("salt = %s \n", salt) - fmt.Printf("topic = %x \n", topic) - fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub))) - fmt.Printf("boot = %s \n", enode) + if *echoMode { + echo() } } -func checkIntArg(pattern string, dst *uint32) { - pattern += "=" - sz := len(pattern) - for _, arg := range os.Args { - if len(arg) < sz { - continue - } - - prefix := arg[:sz] - if prefix == pattern { - s := arg[sz:] - i, err := strconv.ParseUint(s, 10, 0) - if err != nil { - utils.Fatalf("Failed to parse argument %s: %s", pattern, err) - } - if err == nil && i > 0 { - *dst = uint32(i) - } - return - } - } -} - -func checkStringArg(pattern string, dst *string) { - pattern += "=" - sz := len(pattern) - for _, arg := range os.Args { - if len(arg) < sz { - continue - } - - prefix := arg[:sz] - if prefix == pattern { - s := arg[sz:] - if len(s) > 0 { - *dst = s - } - return - } - } -} - -func checkMode(pattern string) bool { - for _, arg := range os.Args { - if arg == pattern { - return true - } - } - return false +func echo() { + fmt.Printf("ttl = %d \n", *argTTL) + fmt.Printf("workTime = %d \n", *argWorkTime) + fmt.Printf("pow = %f \n", *argPoW) + fmt.Printf("mspow = %f \n", *argServerPoW) + fmt.Printf("ip = %s \n", *argIP) + fmt.Printf("salt = %s \n", *argSalt) + fmt.Printf("topic = %x \n", topic) + fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub))) + fmt.Printf("idfile = %s \n", *argIDFile) + fmt.Printf("dbpath = %s \n", *argDBPath) + fmt.Printf("boot = %s \n", *argEnode) } func initialize() { @@ -303,39 +165,34 @@ func initialize() { var peers []*discover.Node var err error - if testMode { - password := []byte("this is a test password for symmetric encryption") - salt := []byte("this is a test salt for symmetric encryption") + if *testMode { + password := []byte("test password for symmetric encryption") + salt := []byte("test salt for symmetric encryption") symKey = pbkdf2.Key(password, salt, 64, 32, sha256.New) topic = whisper.TopicType{0xFF, 0xFF, 0xFF, 0xFF} + msPassword = "mail server test password" } - if bootstrapMode { - if len(ipAddress) == 0 { - if testMode { - ipAddress = "127.0.0.1:30303" - } else { - fmt.Printf("Please enter your IP and port (e.g. 127.0.0.1:30303): ") - fmt.Scanln(&ipAddress) - } + if *bootstrapMode { + if len(*argIP) == 0 { + argIP = scanLineA("Please enter your IP and port (e.g. 127.0.0.1:30303): ") } } else { - if len(enode) == 0 { - fmt.Printf("Please enter the peer's enode: ") - fmt.Scanln(&enode) + if len(*argEnode) == 0 { + argEnode = scanLineA("Please enter the peer's enode: ") } - peer := discover.MustParseNode(enode) + peer := discover.MustParseNode(*argEnode) peers = append(peers, peer) } - if mailServerMode { + if *mailServerMode && len(msPassword) == 0 { msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ") if err != nil { utils.Fatalf("Failed to read Mail Server password: %s", err) } shh = whisper.NewWhisper(&mailServer) - mailServer.Init(shh, dbPath, msPassword, msPoW) + mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW) } else { shh = whisper.NewWhisper(nil) } @@ -351,7 +208,7 @@ func initialize() { MaxPeers: 128, Name: common.MakeName("whisper-go", "5.0"), Protocols: shh.Protocols(), - ListenAddr: ipAddress, + ListenAddr: *argIP, NAT: nat.Any(), BootstrapNodes: peers, StaticNodes: peers, @@ -369,19 +226,18 @@ func startServer() { fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey))) fmt.Println(server.NodeInfo().Enode) - if bootstrapMode { + if *bootstrapMode { configureNode() fmt.Println("Bootstrap Whisper node started") - //waitForConnection(false) // todo: review } else { fmt.Println("Whisper node started") - // first see if we can establish connection, then require futher user input + // first see if we can establish connection, then ask for user input waitForConnection(true) configureNode() } - if !forwarderMode { - fmt.Printf("Connection is established. Please type the message. To quit type: '%s'\n", quitCommand) + if !*forwarderMode { + fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand) } } @@ -390,55 +246,44 @@ func isKeyValid(k *ecdsa.PublicKey) bool { } func configureNode() { - var i int - var s string var err error - if forwarderMode { + if *forwarderMode { return } - if isAsymmetric && len(pubStr) == 0 { - pubStr = scanLine("Please enter the peer's public key: ") - pub = crypto.ToECDSAPub(common.FromHex(pubStr)) + if *asymmetricMode && len(*argPub) == 0 { + s := scanLine("Please enter the peer's public key: ") + pub = crypto.ToECDSAPub(common.FromHex(s)) if !isKeyValid(pub) { utils.Fatalf("Error: invalid public key") } } - if !isAsymmetric && !testMode && !forwarderMode { + if !*asymmetricMode && !*forwarderMode && !*testMode { pass, err := console.Stdin.PromptPassword("Please enter the password: ") if err != nil { utils.Fatalf("Failed to read passphrase: %v", err) } - if len(salt) == 0 { - salt = scanLine("Please enter the salt: ") + if len(*argSalt) == 0 { + argSalt = scanLineA("Please enter the salt: ") } - symKey = pbkdf2.Key([]byte(pass), []byte(salt), 65356, 32, sha256.New) + symKey = pbkdf2.Key([]byte(pass), []byte(*argSalt), 65356, 32, sha256.New) - if len(topicStr) == 0 { - generateTopic([]byte(pass), []byte(salt)) + if len(*argTopic) == 0 { + generateTopic([]byte(pass), []byte(*argSalt)) } } - if mailServerMode { - if len(dbPath) == 0 { - dbPath = scanLine("Please enter the path to DB file: ") - } - - if msPoW == 0.0 { - s = scanLine("Please enter the PoW requirement for the Mail Server (int): ") - i, err = strconv.Atoi(s) - if err != nil { - utils.Fatalf("Fail to parse the PoW: %s", err) - } - msPoW = float64(i) + if *mailServerMode { + if len(*argDBPath) == 0 { + argDBPath = scanLineA("Please enter the path to DB file: ") } } - if requestMail { + if *requestMail && len(msPassword) == 0 { msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ") if err != nil { utils.Fatalf("Failed to read Mail Server password: %s", err) @@ -485,11 +330,11 @@ func run() { shh.Start(nil) defer shh.Stop() - if !forwarderMode { + if !*forwarderMode { go messageLoop() } - if requestMail { + if *requestMail { requestExpiredMessagesLoop() } else { sendLoop() @@ -506,7 +351,7 @@ func sendLoop() { } sendMsg([]byte(s)) - if isAsymmetric { + if *asymmetricMode { // print your own message for convenience, // because in asymmetric mode it is impossible to decrypt it hour, min, sec := time.Now().Clock() @@ -520,12 +365,26 @@ func scanLine(prompt string) string { if len(prompt) > 0 { fmt.Print(prompt) } - //txt, err := console.Stdin.PromptInput(prompt) // todo: delete txt, err := input.ReadString('\n') if err != nil { utils.Fatalf("input error: %s", err) } - return strings.TrimRight(txt, "\n\r") + txt = strings.TrimRight(txt, "\n\r") + return txt +} + +func scanLineA(prompt string) *string { + s := scanLine(prompt) + return &s +} + +func scanUint(prompt string) uint32 { + s := scanLine(prompt) + i, err := strconv.Atoi(s) + if err != nil { + utils.Fatalf("Fail to parse the lower time limit: %s", err) + } + return uint32(i) } func sendMsg(payload []byte) { @@ -535,9 +394,9 @@ func sendMsg(payload []byte) { KeySym: symKey, Payload: payload, Topic: topic, - TTL: ttl, - PoW: pow, - WorkTime: workTime, + TTL: uint32(*argTTL), + PoW: *argPoW, + WorkTime: uint32(*argWorkTime), } msg := whisper.NewSentMessage(¶ms) @@ -592,39 +451,33 @@ func printMessageInfo(msg *whisper.ReceivedMessage) { func requestExpiredMessagesLoop() { var key, peerID []byte - err := shh.AddSymKey(argRequestMail, []byte(msPassword)) + var timeLow, timeUpp, t uint32 + err := shh.AddSymKey(mailServerKeyName, []byte(msPassword)) if err != nil { utils.Fatalf("Failed to create symmetric key for mail request: %s", err) } - key = shh.GetSymKey(argRequestMail) - peerID = extractIdFromEnode(enode) + key = shh.GetSymKey(mailServerKeyName) + peerID = extractIdFromEnode(*argEnode) shh.MarkPeerTrusted(peerID) for { - s := scanLine("Please enter the lower limit for the time range (unix timestamp): ") - i, err := strconv.Atoi(s) - if err != nil { - utils.Fatalf("Fail to parse the lower time limit: %s", err) - } - timeLow = uint32(i) - - s = scanLine("Please enter the upper limit for the time range (unix timestamp): ") - i, err = strconv.Atoi(s) - if err != nil { - utils.Fatalf("Fail to parse the upper time limit: %s", err) - } - timeUpp = uint32(i) + timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ") + timeUpp = scanUint("Please enter the upper limit of the time range (unix timestamp): ") + t = scanUint("Please enter the topic: ") if timeUpp == 0 { timeUpp = 0xFFFFFFFF } - data := make([]byte, sizeOfInt*2) + data := make([]byte, sizeOfInt*3) binary.BigEndian.PutUint32(data, timeLow) binary.BigEndian.PutUint32(data[sizeOfInt:], timeUpp) - // todo: add topic + binary.BigEndian.PutUint32(data[sizeOfInt*2:], t) + if t == 0 { + data = data[:sizeOfInt*2] + } var params whisper.MessageParams - params.PoW = msPoW + params.PoW = *argServerPoW params.Payload = data params.KeySym = key params.Src = nodeid @@ -673,3 +526,11 @@ func extractIdFromEnode(s string) []byte { return b } + +// this is a temporary stub, will be expanded later +type WMailServer struct{} + +func (s *WMailServer) Archive(env *whisper.Envelope) {} +func (s *WMailServer) DeliverMail(peer *whisper.Peer, data []byte) {} +func (s *WMailServer) Init(w *whisper.Whisper, path string, password string, pow float64) {} +func (s *WMailServer) Close() {} From 2772532e1015473c3f5a3092b11adafda8b1518e Mon Sep 17 00:00:00 2001 From: Vlad Date: Sun, 15 Jan 2017 00:16:16 +0100 Subject: [PATCH 12/13] whisper: moved to cmd --- {whisper => cmd}/wnode/main.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {whisper => cmd}/wnode/main.go (100%) diff --git a/whisper/wnode/main.go b/cmd/wnode/main.go similarity index 100% rename from whisper/wnode/main.go rename to cmd/wnode/main.go From b392986c5caf79797e997feb132f6066161043bc Mon Sep 17 00:00:00 2001 From: Vlad Date: Sun, 15 Jan 2017 00:39:11 +0100 Subject: [PATCH 13/13] whisper: grammar fix --- cmd/wnode/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go index 019f261165..d83df46260 100644 --- a/cmd/wnode/main.go +++ b/cmd/wnode/main.go @@ -220,7 +220,7 @@ func initialize() { func startServer() { err := server.Start() if err != nil { - utils.Fatalf("Failed to start Whsiper peer: %s.", err) + utils.Fatalf("Failed to start Whisper peer: %s.", err) } fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey)))