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