github.com/devfans/go-ethereum@v1.5.10-0.20170326212234-7419d0c38291/cmd/wnode/main.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // go-ethereum is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 // This is a simple Whisper node. It could be used as a stand-alone bootstrap node. 18 // Also, could be used for different test and diagnostics purposes. 19 20 package main 21 22 import ( 23 "bufio" 24 "crypto/ecdsa" 25 "crypto/sha512" 26 "encoding/binary" 27 "encoding/hex" 28 "flag" 29 "fmt" 30 "os" 31 "strconv" 32 "strings" 33 "time" 34 35 "github.com/ethereum/go-ethereum/cmd/utils" 36 "github.com/ethereum/go-ethereum/common" 37 "github.com/ethereum/go-ethereum/console" 38 "github.com/ethereum/go-ethereum/crypto" 39 "github.com/ethereum/go-ethereum/log" 40 "github.com/ethereum/go-ethereum/p2p" 41 "github.com/ethereum/go-ethereum/p2p/discover" 42 "github.com/ethereum/go-ethereum/p2p/nat" 43 "github.com/ethereum/go-ethereum/whisper/mailserver" 44 whisper "github.com/ethereum/go-ethereum/whisper/whisperv5" 45 "golang.org/x/crypto/pbkdf2" 46 ) 47 48 const quitCommand = "~Q" 49 const symKeyName = "da919ea33001b04dfc630522e33078ec0df11" 50 51 // singletons 52 var ( 53 server *p2p.Server 54 shh *whisper.Whisper 55 done chan struct{} 56 mailServer mailserver.WMailServer 57 58 input = bufio.NewReader(os.Stdin) 59 ) 60 61 // encryption 62 var ( 63 symKey []byte 64 pub *ecdsa.PublicKey 65 asymKey *ecdsa.PrivateKey 66 nodeid *ecdsa.PrivateKey 67 topic whisper.TopicType 68 filterID string 69 symPass string 70 msPassword string 71 ) 72 73 // cmd arguments 74 var ( 75 echoMode = flag.Bool("e", false, "echo mode: prints some arguments for diagnostics") 76 bootstrapMode = flag.Bool("b", false, "boostrap node: don't actively connect to peers, wait for incoming connections") 77 forwarderMode = flag.Bool("f", false, "forwarder mode: only forward messages, neither send nor decrypt messages") 78 mailServerMode = flag.Bool("s", false, "mail server mode: delivers expired messages on demand") 79 requestMail = flag.Bool("r", false, "request expired messages from the bootstrap server") 80 asymmetricMode = flag.Bool("a", false, "use asymmetric encryption") 81 testMode = flag.Bool("t", false, "use of predefined parameters for diagnostics") 82 generateKey = flag.Bool("k", false, "generate and show the private key") 83 84 argVerbosity = flag.Int("verbosity", int(log.LvlWarn), "log verbosity level") 85 argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds") 86 argWorkTime = flag.Uint("work", 5, "work time in seconds") 87 argPoW = flag.Float64("pow", whisper.MinimumPoW, "PoW for normal messages in float format (e.g. 2.7)") 88 argServerPoW = flag.Float64("mspow", whisper.MinimumPoW, "PoW requirement for Mail Server request") 89 90 argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)") 91 argPub = flag.String("pub", "", "public key for asymmetric encryption") 92 argDBPath = flag.String("dbpath", "", "path to the server's DB directory") 93 argIDFile = flag.String("idfile", "", "file name with node id (private key)") 94 argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)") 95 argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)") 96 ) 97 98 func main() { 99 processArgs() 100 initialize() 101 run() 102 } 103 104 func processArgs() { 105 flag.Parse() 106 107 if len(*argIDFile) > 0 { 108 var err error 109 nodeid, err = crypto.LoadECDSA(*argIDFile) 110 if err != nil { 111 utils.Fatalf("Failed to load file [%s]: %s.", *argIDFile, err) 112 } 113 } 114 115 const enodePrefix = "enode://" 116 if len(*argEnode) > 0 { 117 if (*argEnode)[:len(enodePrefix)] != enodePrefix { 118 *argEnode = enodePrefix + *argEnode 119 } 120 } 121 122 if len(*argTopic) > 0 { 123 x, err := hex.DecodeString(*argTopic) 124 if err != nil { 125 utils.Fatalf("Failed to parse the topic: %s", err) 126 } 127 topic = whisper.BytesToTopic(x) 128 } 129 130 if *asymmetricMode && len(*argPub) > 0 { 131 pub = crypto.ToECDSAPub(common.FromHex(*argPub)) 132 if !isKeyValid(pub) { 133 utils.Fatalf("invalid public key") 134 } 135 } 136 137 if *echoMode { 138 echo() 139 } 140 } 141 142 func echo() { 143 fmt.Printf("ttl = %d \n", *argTTL) 144 fmt.Printf("workTime = %d \n", *argWorkTime) 145 fmt.Printf("pow = %f \n", *argPoW) 146 fmt.Printf("mspow = %f \n", *argServerPoW) 147 fmt.Printf("ip = %s \n", *argIP) 148 fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub))) 149 fmt.Printf("idfile = %s \n", *argIDFile) 150 fmt.Printf("dbpath = %s \n", *argDBPath) 151 fmt.Printf("boot = %s \n", *argEnode) 152 } 153 154 func initialize() { 155 log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*argVerbosity), log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) 156 157 done = make(chan struct{}) 158 var peers []*discover.Node 159 var err error 160 161 if *generateKey { 162 key, err := crypto.GenerateKey() 163 if err != nil { 164 utils.Fatalf("Failed to generate private key: %s", err) 165 } 166 k := hex.EncodeToString(crypto.FromECDSA(key)) 167 fmt.Printf("Random private key: %s \n", k) 168 os.Exit(0) 169 } 170 171 if *testMode { 172 symPass = "wwww" // ascii code: 0x77777777 173 msPassword = "mail server test password" 174 } 175 176 if *bootstrapMode { 177 if len(*argIP) == 0 { 178 argIP = scanLineA("Please enter your IP and port (e.g. 127.0.0.1:30348): ") 179 } 180 } else { 181 if len(*argEnode) == 0 { 182 argEnode = scanLineA("Please enter the peer's enode: ") 183 } 184 peer := discover.MustParseNode(*argEnode) 185 peers = append(peers, peer) 186 } 187 188 if *mailServerMode { 189 if len(msPassword) == 0 { 190 msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ") 191 if err != nil { 192 utils.Fatalf("Failed to read Mail Server password: %s", err) 193 } 194 } 195 shh = whisper.New() 196 shh.RegisterServer(&mailServer) 197 mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW) 198 } else { 199 shh = whisper.New() 200 } 201 202 asymKey = shh.NewIdentity() 203 if nodeid == nil { 204 nodeid = shh.NewIdentity() 205 } 206 207 maxPeers := 80 208 if *bootstrapMode { 209 maxPeers = 800 210 } 211 212 server = &p2p.Server{ 213 Config: p2p.Config{ 214 PrivateKey: nodeid, 215 MaxPeers: maxPeers, 216 Name: common.MakeName("whisper-go", "5.0"), 217 Protocols: shh.Protocols(), 218 ListenAddr: *argIP, 219 NAT: nat.Any(), 220 BootstrapNodes: peers, 221 StaticNodes: peers, 222 TrustedNodes: peers, 223 }, 224 } 225 } 226 227 func startServer() { 228 err := server.Start() 229 if err != nil { 230 utils.Fatalf("Failed to start Whisper peer: %s.", err) 231 } 232 233 fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey))) 234 fmt.Println(server.NodeInfo().Enode) 235 236 if *bootstrapMode { 237 configureNode() 238 fmt.Println("Bootstrap Whisper node started") 239 } else { 240 fmt.Println("Whisper node started") 241 // first see if we can establish connection, then ask for user input 242 waitForConnection(true) 243 configureNode() 244 } 245 246 if !*forwarderMode { 247 fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand) 248 } 249 } 250 251 func isKeyValid(k *ecdsa.PublicKey) bool { 252 return k.X != nil && k.Y != nil 253 } 254 255 func configureNode() { 256 var err error 257 var p2pAccept bool 258 259 if *forwarderMode { 260 return 261 } 262 263 if *asymmetricMode { 264 if len(*argPub) == 0 { 265 s := scanLine("Please enter the peer's public key: ") 266 pub = crypto.ToECDSAPub(common.FromHex(s)) 267 if !isKeyValid(pub) { 268 utils.Fatalf("Error: invalid public key") 269 } 270 } 271 } 272 273 if *requestMail { 274 p2pAccept = true 275 if len(msPassword) == 0 { 276 msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ") 277 if err != nil { 278 utils.Fatalf("Failed to read Mail Server password: %s", err) 279 } 280 } 281 } 282 283 if !*asymmetricMode && !*forwarderMode { 284 if len(symPass) == 0 { 285 symPass, err = console.Stdin.PromptPassword("Please enter the password: ") 286 if err != nil { 287 utils.Fatalf("Failed to read passphrase: %v", err) 288 } 289 } 290 291 shh.AddSymKey(symKeyName, []byte(symPass)) 292 symKey = shh.GetSymKey(symKeyName) 293 if len(*argTopic) == 0 { 294 generateTopic([]byte(symPass)) 295 } 296 } 297 298 if *mailServerMode { 299 if len(*argDBPath) == 0 { 300 argDBPath = scanLineA("Please enter the path to DB file: ") 301 } 302 } 303 304 filter := whisper.Filter{ 305 KeySym: symKey, 306 KeyAsym: asymKey, 307 Topics: []whisper.TopicType{topic}, 308 AcceptP2P: p2pAccept, 309 } 310 filterID, err = shh.Watch(&filter) 311 if err != nil { 312 utils.Fatalf("Failed to install filter: %s", err) 313 } 314 fmt.Printf("Filter is configured for the topic: %x \n", topic) 315 } 316 317 func generateTopic(password []byte) { 318 x := pbkdf2.Key(password, password, 8196, 128, sha512.New) 319 for i := 0; i < len(x); i++ { 320 topic[i%whisper.TopicLength] ^= x[i] 321 } 322 } 323 324 func waitForConnection(timeout bool) { 325 var cnt int 326 var connected bool 327 for !connected { 328 time.Sleep(time.Millisecond * 50) 329 connected = server.PeerCount() > 0 330 if timeout { 331 cnt++ 332 if cnt > 1000 { 333 utils.Fatalf("Timeout expired, failed to connect") 334 } 335 } 336 } 337 338 fmt.Println("Connected to peer.") 339 } 340 341 func run() { 342 defer mailServer.Close() 343 startServer() 344 defer server.Stop() 345 shh.Start(nil) 346 defer shh.Stop() 347 348 if !*forwarderMode { 349 go messageLoop() 350 } 351 352 if *requestMail { 353 requestExpiredMessagesLoop() 354 } else { 355 sendLoop() 356 } 357 } 358 359 func sendLoop() { 360 for { 361 s := scanLine("") 362 if s == quitCommand { 363 fmt.Println("Quit command received") 364 close(done) 365 break 366 } 367 sendMsg([]byte(s)) 368 369 if *asymmetricMode { 370 // print your own message for convenience, 371 // because in asymmetric mode it is impossible to decrypt it 372 timestamp := time.Now().Unix() 373 from := crypto.PubkeyToAddress(asymKey.PublicKey) 374 fmt.Printf("\n%d <%x>: %s\n", timestamp, from, s) 375 } 376 } 377 } 378 379 func scanLine(prompt string) string { 380 if len(prompt) > 0 { 381 fmt.Print(prompt) 382 } 383 txt, err := input.ReadString('\n') 384 if err != nil { 385 utils.Fatalf("input error: %s", err) 386 } 387 txt = strings.TrimRight(txt, "\n\r") 388 return txt 389 } 390 391 func scanLineA(prompt string) *string { 392 s := scanLine(prompt) 393 return &s 394 } 395 396 func scanUint(prompt string) uint32 { 397 s := scanLine(prompt) 398 i, err := strconv.Atoi(s) 399 if err != nil { 400 utils.Fatalf("Fail to parse the lower time limit: %s", err) 401 } 402 return uint32(i) 403 } 404 405 func sendMsg(payload []byte) { 406 params := whisper.MessageParams{ 407 Src: asymKey, 408 Dst: pub, 409 KeySym: symKey, 410 Payload: payload, 411 Topic: topic, 412 TTL: uint32(*argTTL), 413 PoW: *argPoW, 414 WorkTime: uint32(*argWorkTime), 415 } 416 417 msg := whisper.NewSentMessage(¶ms) 418 envelope, err := msg.Wrap(¶ms) 419 if err != nil { 420 fmt.Printf("failed to seal message: %v \n", err) 421 return 422 } 423 424 err = shh.Send(envelope) 425 if err != nil { 426 fmt.Printf("failed to send message: %v \n", err) 427 } 428 } 429 430 func messageLoop() { 431 f := shh.GetFilter(filterID) 432 if f == nil { 433 utils.Fatalf("filter is not installed") 434 } 435 436 ticker := time.NewTicker(time.Millisecond * 50) 437 438 for { 439 select { 440 case <-ticker.C: 441 messages := f.Retrieve() 442 for _, msg := range messages { 443 printMessageInfo(msg) 444 } 445 case <-done: 446 return 447 } 448 } 449 } 450 451 func printMessageInfo(msg *whisper.ReceivedMessage) { 452 timestamp := fmt.Sprintf("%d", msg.Sent) // unix timestamp for diagnostics 453 text := string(msg.Payload) 454 455 var address common.Address 456 if msg.Src != nil { 457 address = crypto.PubkeyToAddress(*msg.Src) 458 } 459 460 if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) { 461 fmt.Printf("\n%s <%x>: %s\n", timestamp, address, text) // message from myself 462 } else { 463 fmt.Printf("\n%s [%x]: %s\n", timestamp, address, text) // message from a peer 464 } 465 } 466 467 func requestExpiredMessagesLoop() { 468 var key, peerID []byte 469 var timeLow, timeUpp uint32 470 var t string 471 var xt, empty whisper.TopicType 472 473 err := shh.AddSymKey(mailserver.MailServerKeyName, []byte(msPassword)) 474 if err != nil { 475 utils.Fatalf("Failed to create symmetric key for mail request: %s", err) 476 } 477 key = shh.GetSymKey(mailserver.MailServerKeyName) 478 peerID = extractIdFromEnode(*argEnode) 479 shh.MarkPeerTrusted(peerID) 480 481 for { 482 timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ") 483 timeUpp = scanUint("Please enter the upper limit of the time range (unix timestamp): ") 484 t = scanLine("Please enter the topic (hexadecimal): ") 485 if len(t) >= whisper.TopicLength*2 { 486 x, err := hex.DecodeString(t) 487 if err != nil { 488 utils.Fatalf("Failed to parse the topic: %s", err) 489 } 490 xt = whisper.BytesToTopic(x) 491 } 492 if timeUpp == 0 { 493 timeUpp = 0xFFFFFFFF 494 } 495 496 data := make([]byte, 8+whisper.TopicLength) 497 binary.BigEndian.PutUint32(data, timeLow) 498 binary.BigEndian.PutUint32(data[4:], timeUpp) 499 copy(data[8:], xt[:]) 500 if xt == empty { 501 data = data[:8] 502 } 503 504 var params whisper.MessageParams 505 params.PoW = *argServerPoW 506 params.Payload = data 507 params.KeySym = key 508 params.Src = nodeid 509 params.WorkTime = 5 510 511 msg := whisper.NewSentMessage(¶ms) 512 env, err := msg.Wrap(¶ms) 513 if err != nil { 514 utils.Fatalf("Wrap failed: %s", err) 515 } 516 517 err = shh.RequestHistoricMessages(peerID, env) 518 if err != nil { 519 utils.Fatalf("Failed to send P2P message: %s", err) 520 } 521 522 time.Sleep(time.Second * 5) 523 } 524 } 525 526 func extractIdFromEnode(s string) []byte { 527 n, err := discover.ParseNode(s) 528 if err != nil { 529 utils.Fatalf("Failed to parse enode: %s", err) 530 return nil 531 } 532 return n.ID[:] 533 }