github.com/beyonderyue/gochain@v2.2.26+incompatible/cmd/wnode/main.go (about) 1 // Copyright 2017 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 "context" 25 "crypto/ecdsa" 26 "crypto/sha512" 27 "encoding/binary" 28 "encoding/hex" 29 "flag" 30 "fmt" 31 "io/ioutil" 32 "os" 33 "path/filepath" 34 "strconv" 35 "strings" 36 "time" 37 38 "github.com/gochain-io/gochain/cmd/utils" 39 "github.com/gochain-io/gochain/common" 40 "github.com/gochain-io/gochain/console" 41 "github.com/gochain-io/gochain/crypto" 42 "github.com/gochain-io/gochain/log" 43 "github.com/gochain-io/gochain/p2p" 44 "github.com/gochain-io/gochain/p2p/discover" 45 "github.com/gochain-io/gochain/p2p/nat" 46 "github.com/gochain-io/gochain/whisper/mailserver" 47 whisper "github.com/gochain-io/gochain/whisper/whisperv6" 48 "golang.org/x/crypto/pbkdf2" 49 ) 50 51 const quitCommand = "~Q" 52 53 // singletons 54 var ( 55 server *p2p.Server 56 shh *whisper.Whisper 57 done chan struct{} 58 mailServer mailserver.WMailServer 59 60 input = bufio.NewReader(os.Stdin) 61 ) 62 63 // encryption 64 var ( 65 symKey []byte 66 pub *ecdsa.PublicKey 67 asymKey *ecdsa.PrivateKey 68 nodeid *ecdsa.PrivateKey 69 topic whisper.TopicType 70 71 asymKeyID string 72 asymFilterID string 73 symFilterID string 74 symPass string 75 msPassword string 76 ) 77 78 // cmd arguments 79 var ( 80 bootstrapMode = flag.Bool("standalone", false, "boostrap node: don't actively connect to peers, wait for incoming connections") 81 forwarderMode = flag.Bool("forwarder", false, "forwarder mode: only forward messages, neither send nor decrypt messages") 82 mailServerMode = flag.Bool("mailserver", false, "mail server mode: delivers expired messages on demand") 83 requestMail = flag.Bool("mailclient", false, "request expired messages from the bootstrap server") 84 asymmetricMode = flag.Bool("asym", false, "use asymmetric encryption") 85 generateKey = flag.Bool("generatekey", false, "generate and show the private key") 86 fileExMode = flag.Bool("fileexchange", false, "file exchange mode") 87 testMode = flag.Bool("test", false, "use of predefined parameters for diagnostics") 88 echoMode = flag.Bool("echo", false, "echo mode: prints some arguments for diagnostics") 89 90 argVerbosity = flag.Int("verbosity", int(log.LvlError), "log verbosity level") 91 argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds") 92 argWorkTime = flag.Uint("work", 5, "work time in seconds") 93 argMaxSize = flag.Uint("maxsize", uint(whisper.DefaultMaxMessageSize), "max size of message") 94 argPoW = flag.Float64("pow", whisper.DefaultMinimumPoW, "PoW for normal messages in float format (e.g. 2.7)") 95 argServerPoW = flag.Float64("mspow", whisper.DefaultMinimumPoW, "PoW requirement for Mail Server request") 96 97 argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)") 98 argPub = flag.String("pub", "", "public key for asymmetric encryption") 99 argDBPath = flag.String("dbpath", "", "path to the server's DB directory") 100 argIDFile = flag.String("idfile", "", "file name with node id (private key)") 101 argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)") 102 argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)") 103 argSaveDir = flag.String("savedir", "", "directory where incoming messages will be saved as files") 104 ) 105 106 func main() { 107 processArgs() 108 initialize() 109 run() 110 } 111 112 func processArgs() { 113 flag.Parse() 114 115 if len(*argIDFile) > 0 { 116 var err error 117 nodeid, err = crypto.LoadECDSA(*argIDFile) 118 if err != nil { 119 utils.Fatalf("Failed to load file [%s]: %s.", *argIDFile, err) 120 } 121 } 122 123 const enodePrefix = "enode://" 124 if len(*argEnode) > 0 { 125 if (*argEnode)[:len(enodePrefix)] != enodePrefix { 126 *argEnode = enodePrefix + *argEnode 127 } 128 } 129 130 if len(*argTopic) > 0 { 131 x, err := hex.DecodeString(*argTopic) 132 if err != nil { 133 utils.Fatalf("Failed to parse the topic: %s", err) 134 } 135 topic = whisper.BytesToTopic(x) 136 } 137 138 if *asymmetricMode && len(*argPub) > 0 { 139 pub = crypto.ToECDSAPub(common.FromHex(*argPub)) 140 if !isKeyValid(pub) { 141 utils.Fatalf("invalid public key") 142 } 143 } 144 145 if len(*argSaveDir) > 0 { 146 if _, err := os.Stat(*argSaveDir); os.IsNotExist(err) { 147 utils.Fatalf("Download directory '%s' does not exist", *argSaveDir) 148 } 149 } else if *fileExMode { 150 utils.Fatalf("Parameter 'savedir' is mandatory for file exchange mode") 151 } 152 153 if *echoMode { 154 echo() 155 } 156 } 157 158 func echo() { 159 fmt.Printf("ttl = %d \n", *argTTL) 160 fmt.Printf("workTime = %d \n", *argWorkTime) 161 fmt.Printf("pow = %f \n", *argPoW) 162 fmt.Printf("mspow = %f \n", *argServerPoW) 163 fmt.Printf("ip = %s \n", *argIP) 164 fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub))) 165 fmt.Printf("idfile = %s \n", *argIDFile) 166 fmt.Printf("dbpath = %s \n", *argDBPath) 167 fmt.Printf("boot = %s \n", *argEnode) 168 } 169 170 func initialize() { 171 log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*argVerbosity), log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) 172 173 done = make(chan struct{}) 174 var peers []*discover.Node 175 var err error 176 177 if *generateKey { 178 key, err := crypto.GenerateKey() 179 if err != nil { 180 utils.Fatalf("Failed to generate private key: %s", err) 181 } 182 k := hex.EncodeToString(crypto.FromECDSA(key)) 183 fmt.Printf("Random private key: %s \n", k) 184 os.Exit(0) 185 } 186 187 if *testMode { 188 symPass = "wwww" // ascii code: 0x77777777 189 msPassword = "wwww" 190 } 191 192 if *bootstrapMode { 193 if len(*argIP) == 0 { 194 argIP = scanLineA("Please enter your IP and port (e.g. 127.0.0.1:30348): ") 195 } 196 } else { 197 if len(*argEnode) == 0 { 198 argEnode = scanLineA("Please enter the peer's enode: ") 199 } 200 peer := discover.MustParseNode(*argEnode) 201 peers = append(peers, peer) 202 } 203 204 cfg := &whisper.Config{ 205 MaxMessageSize: uint32(*argMaxSize), 206 MinimumAcceptedPOW: *argPoW, 207 } 208 209 if *mailServerMode { 210 if len(msPassword) == 0 { 211 msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ") 212 if err != nil { 213 utils.Fatalf("Failed to read Mail Server password: %s", err) 214 } 215 } 216 217 shh = whisper.New(cfg) 218 shh.RegisterServer(&mailServer) 219 mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW) 220 } else { 221 shh = whisper.New(cfg) 222 } 223 224 if *argPoW != whisper.DefaultMinimumPoW { 225 err := shh.SetMinimumPoW(context.Background(), *argPoW) 226 if err != nil { 227 utils.Fatalf("Failed to set PoW: %s", err) 228 } 229 } 230 231 if uint32(*argMaxSize) != whisper.DefaultMaxMessageSize { 232 err := shh.SetMaxMessageSize(uint32(*argMaxSize)) 233 if err != nil { 234 utils.Fatalf("Failed to set max message size: %s", err) 235 } 236 } 237 238 asymKeyID, err = shh.NewKeyPair() 239 if err != nil { 240 utils.Fatalf("Failed to generate a new key pair: %s", err) 241 } 242 243 asymKey, err = shh.GetPrivateKey(asymKeyID) 244 if err != nil { 245 utils.Fatalf("Failed to retrieve a new key pair: %s", err) 246 } 247 248 if nodeid == nil { 249 tmpID, err := shh.NewKeyPair() 250 if err != nil { 251 utils.Fatalf("Failed to generate a new key pair: %s", err) 252 } 253 254 nodeid, err = shh.GetPrivateKey(tmpID) 255 if err != nil { 256 utils.Fatalf("Failed to retrieve a new key pair: %s", err) 257 } 258 } 259 260 maxPeers := 80 261 if *bootstrapMode { 262 maxPeers = 800 263 } 264 265 server = &p2p.Server{ 266 Config: p2p.Config{ 267 PrivateKey: nodeid, 268 MaxPeers: maxPeers, 269 Name: common.MakeName("wnode", "6.0"), 270 Protocols: shh.Protocols(), 271 ListenAddr: *argIP, 272 NAT: nat.Any(), 273 BootstrapNodes: peers, 274 StaticNodes: peers, 275 TrustedNodes: peers, 276 }, 277 } 278 } 279 280 func startServer() { 281 err := server.Start() 282 if err != nil { 283 utils.Fatalf("Failed to start Whisper peer: %s.", err) 284 } 285 286 fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey))) 287 fmt.Println(server.NodeInfo().Enode) 288 289 if *bootstrapMode { 290 configureNode() 291 fmt.Println("Bootstrap Whisper node started") 292 } else { 293 fmt.Println("Whisper node started") 294 // first see if we can establish connection, then ask for user input 295 waitForConnection(true) 296 configureNode() 297 } 298 299 if !*forwarderMode { 300 fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand) 301 } 302 } 303 304 func isKeyValid(k *ecdsa.PublicKey) bool { 305 return k.X != nil && k.Y != nil 306 } 307 308 func configureNode() { 309 var err error 310 var p2pAccept bool 311 312 if *forwarderMode { 313 return 314 } 315 316 if *asymmetricMode { 317 if len(*argPub) == 0 { 318 s := scanLine("Please enter the peer's public key: ") 319 b := common.FromHex(s) 320 if b == nil { 321 utils.Fatalf("Error: can not convert hexadecimal string") 322 } 323 pub = crypto.ToECDSAPub(b) 324 if !isKeyValid(pub) { 325 utils.Fatalf("Error: invalid public key") 326 } 327 } 328 } 329 330 if *requestMail { 331 p2pAccept = true 332 if len(msPassword) == 0 { 333 msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ") 334 if err != nil { 335 utils.Fatalf("Failed to read Mail Server password: %s", err) 336 } 337 } 338 } 339 340 if !*asymmetricMode && !*forwarderMode { 341 if len(symPass) == 0 { 342 symPass, err = console.Stdin.PromptPassword("Please enter the password for symmetric encryption: ") 343 if err != nil { 344 utils.Fatalf("Failed to read passphrase: %v", err) 345 } 346 } 347 348 symKeyID, err := shh.AddSymKeyFromPassword(symPass) 349 if err != nil { 350 utils.Fatalf("Failed to create symmetric key: %s", err) 351 } 352 symKey, err = shh.GetSymKey(symKeyID) 353 if err != nil { 354 utils.Fatalf("Failed to save symmetric key: %s", err) 355 } 356 if len(*argTopic) == 0 { 357 generateTopic([]byte(symPass)) 358 } 359 360 fmt.Printf("Filter is configured for the topic: %x \n", topic) 361 } 362 363 if *mailServerMode { 364 if len(*argDBPath) == 0 { 365 argDBPath = scanLineA("Please enter the path to DB file: ") 366 } 367 } 368 369 symFilter := whisper.Filter{ 370 KeySym: symKey, 371 Topics: [][]byte{topic[:]}, 372 AllowP2P: p2pAccept, 373 } 374 symFilterID, err = shh.Subscribe(&symFilter) 375 if err != nil { 376 utils.Fatalf("Failed to install filter: %s", err) 377 } 378 379 asymFilter := whisper.Filter{ 380 KeyAsym: asymKey, 381 Topics: [][]byte{topic[:]}, 382 AllowP2P: p2pAccept, 383 } 384 asymFilterID, err = shh.Subscribe(&asymFilter) 385 if err != nil { 386 utils.Fatalf("Failed to install filter: %s", err) 387 } 388 } 389 390 func generateTopic(password []byte) { 391 x := pbkdf2.Key(password, password, 4096, 128, sha512.New) 392 for i := 0; i < len(x); i++ { 393 topic[i%whisper.TopicLength] ^= x[i] 394 } 395 } 396 397 func waitForConnection(timeout bool) { 398 var cnt int 399 var connected bool 400 for !connected { 401 time.Sleep(time.Millisecond * 50) 402 connected = server.PeerCount() > 0 403 if timeout { 404 cnt++ 405 if cnt > 1000 { 406 utils.Fatalf("Timeout expired, failed to connect") 407 } 408 } 409 } 410 411 fmt.Println("Connected to peer.") 412 } 413 414 func run() { 415 defer mailServer.Close() 416 startServer() 417 defer server.Stop() 418 shh.Start(nil) 419 defer shh.Stop() 420 421 if !*forwarderMode { 422 go messageLoop() 423 } 424 425 if *requestMail { 426 requestExpiredMessagesLoop() 427 } else if *fileExMode { 428 sendFilesLoop() 429 } else { 430 sendLoop() 431 } 432 } 433 434 func sendLoop() { 435 for { 436 s := scanLine("") 437 if s == quitCommand { 438 fmt.Println("Quit command received") 439 close(done) 440 break 441 } 442 sendMsg([]byte(s)) 443 444 if *asymmetricMode { 445 // print your own message for convenience, 446 // because in asymmetric mode it is impossible to decrypt it 447 timestamp := time.Now().Unix() 448 from := crypto.PubkeyToAddress(asymKey.PublicKey) 449 fmt.Printf("\n%d <%x>: %s\n", timestamp, from, s) 450 } 451 } 452 } 453 454 func sendFilesLoop() { 455 for { 456 s := scanLine("") 457 if s == quitCommand { 458 fmt.Println("Quit command received") 459 close(done) 460 break 461 } 462 b, err := ioutil.ReadFile(s) 463 if err != nil { 464 fmt.Printf(">>> Error: %s \n", err) 465 continue 466 } else { 467 h := sendMsg(b) 468 if (h == common.Hash{}) { 469 fmt.Printf(">>> Error: message was not sent \n") 470 } else { 471 timestamp := time.Now().Unix() 472 from := crypto.PubkeyToAddress(asymKey.PublicKey) 473 fmt.Printf("\n%d <%x>: sent message with hash %x\n", timestamp, from, h) 474 } 475 } 476 } 477 } 478 479 func scanLine(prompt string) string { 480 if len(prompt) > 0 { 481 fmt.Print(prompt) 482 } 483 txt, err := input.ReadString('\n') 484 if err != nil { 485 utils.Fatalf("input error: %s", err) 486 } 487 txt = strings.TrimRight(txt, "\n\r") 488 return txt 489 } 490 491 func scanLineA(prompt string) *string { 492 s := scanLine(prompt) 493 return &s 494 } 495 496 func scanUint(prompt string) uint32 { 497 s := scanLine(prompt) 498 i, err := strconv.Atoi(s) 499 if err != nil { 500 utils.Fatalf("Fail to parse the lower time limit: %s", err) 501 } 502 return uint32(i) 503 } 504 505 func sendMsg(payload []byte) common.Hash { 506 params := whisper.MessageParams{ 507 Src: asymKey, 508 Dst: pub, 509 KeySym: symKey, 510 Payload: payload, 511 Topic: topic, 512 TTL: uint32(*argTTL), 513 PoW: *argPoW, 514 WorkTime: uint32(*argWorkTime), 515 } 516 517 msg, err := whisper.NewSentMessage(¶ms) 518 if err != nil { 519 utils.Fatalf("failed to create new message: %s", err) 520 } 521 envelope, err := msg.Wrap(¶ms) 522 if err != nil { 523 fmt.Printf("failed to seal message: %v \n", err) 524 return common.Hash{} 525 } 526 527 err = shh.Send(envelope) 528 if err != nil { 529 fmt.Printf("failed to send message: %v \n", err) 530 return common.Hash{} 531 } 532 533 return envelope.Hash() 534 } 535 536 func messageLoop() { 537 sf := shh.GetFilter(symFilterID) 538 if sf == nil { 539 utils.Fatalf("symmetric filter is not installed") 540 } 541 542 af := shh.GetFilter(asymFilterID) 543 if af == nil { 544 utils.Fatalf("asymmetric filter is not installed") 545 } 546 547 ticker := time.NewTicker(time.Millisecond * 50) 548 549 for { 550 select { 551 case <-ticker.C: 552 messages := sf.Retrieve() 553 for _, msg := range messages { 554 if *fileExMode || len(msg.Payload) > 2048 { 555 writeMessageToFile(*argSaveDir, msg) 556 } else { 557 printMessageInfo(msg) 558 } 559 } 560 561 messages = af.Retrieve() 562 for _, msg := range messages { 563 if *fileExMode || len(msg.Payload) > 2048 { 564 writeMessageToFile(*argSaveDir, msg) 565 } else { 566 printMessageInfo(msg) 567 } 568 } 569 case <-done: 570 return 571 } 572 } 573 } 574 575 func printMessageInfo(msg *whisper.ReceivedMessage) { 576 timestamp := fmt.Sprintf("%d", msg.Sent) // unix timestamp for diagnostics 577 text := string(msg.Payload) 578 579 var address common.Address 580 if msg.Src != nil { 581 address = crypto.PubkeyToAddress(*msg.Src) 582 } 583 584 if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) { 585 fmt.Printf("\n%s <%x>: %s\n", timestamp, address, text) // message from myself 586 } else { 587 fmt.Printf("\n%s [%x]: %s\n", timestamp, address, text) // message from a peer 588 } 589 } 590 591 func writeMessageToFile(dir string, msg *whisper.ReceivedMessage) { 592 timestamp := fmt.Sprintf("%d", msg.Sent) 593 name := fmt.Sprintf("%x", msg.EnvelopeHash) 594 595 var address common.Address 596 if msg.Src != nil { 597 address = crypto.PubkeyToAddress(*msg.Src) 598 } 599 600 if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) { 601 // message from myself: don't save, only report 602 fmt.Printf("\n%s <%x>: message received: '%s'\n", timestamp, address, name) 603 } else if len(dir) > 0 { 604 fullpath := filepath.Join(dir, name) 605 err := ioutil.WriteFile(fullpath, msg.Payload, 0644) 606 if err != nil { 607 fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err) 608 } else { 609 fmt.Printf("\n%s {%x}: message received and saved as '%s' (%d bytes)\n", timestamp, address, name, len(msg.Payload)) 610 } 611 } else { 612 fmt.Printf("\n%s {%x}: big message received (%d bytes), but not saved: %s\n", timestamp, address, len(msg.Payload), name) 613 } 614 } 615 616 func requestExpiredMessagesLoop() { 617 var key, peerID []byte 618 var timeLow, timeUpp uint32 619 var t string 620 var xt, empty whisper.TopicType 621 622 keyID, err := shh.AddSymKeyFromPassword(msPassword) 623 if err != nil { 624 utils.Fatalf("Failed to create symmetric key for mail request: %s", err) 625 } 626 key, err = shh.GetSymKey(keyID) 627 if err != nil { 628 utils.Fatalf("Failed to save symmetric key for mail request: %s", err) 629 } 630 peerID = extractIDFromEnode(*argEnode) 631 shh.AllowP2PMessagesFromPeer(peerID) 632 633 for { 634 timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ") 635 timeUpp = scanUint("Please enter the upper limit of the time range (unix timestamp): ") 636 t = scanLine("Please enter the topic (hexadecimal): ") 637 if len(t) >= whisper.TopicLength*2 { 638 x, err := hex.DecodeString(t) 639 if err != nil { 640 utils.Fatalf("Failed to parse the topic: %s", err) 641 } 642 xt = whisper.BytesToTopic(x) 643 } 644 if timeUpp == 0 { 645 timeUpp = 0xFFFFFFFF 646 } 647 648 data := make([]byte, 8+whisper.TopicLength) 649 binary.BigEndian.PutUint32(data, timeLow) 650 binary.BigEndian.PutUint32(data[4:], timeUpp) 651 copy(data[8:], xt[:]) 652 if xt == empty { 653 data = data[:8] 654 } 655 656 var params whisper.MessageParams 657 params.PoW = *argServerPoW 658 params.Payload = data 659 params.KeySym = key 660 params.Src = asymKey 661 params.WorkTime = 5 662 663 msg, err := whisper.NewSentMessage(¶ms) 664 if err != nil { 665 utils.Fatalf("failed to create new message: %s", err) 666 } 667 env, err := msg.Wrap(¶ms) 668 if err != nil { 669 utils.Fatalf("Wrap failed: %s", err) 670 } 671 672 err = shh.RequestHistoricMessages(context.Background(), peerID, env) 673 if err != nil { 674 utils.Fatalf("Failed to send P2P message: %s", err) 675 } 676 677 time.Sleep(time.Second * 5) 678 } 679 } 680 681 func extractIDFromEnode(s string) []byte { 682 n, err := discover.ParseNode(s) 683 if err != nil { 684 utils.Fatalf("Failed to parse enode: %s", err) 685 } 686 return n.ID[:] 687 }