github.com/bcskill/bcschain/v3@v3.4.9-beta2/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/bcskill/bcschain/v3/console/prompt" 39 40 "github.com/bcskill/bcschain/v3/cmd/utils" 41 "github.com/bcskill/bcschain/v3/common" 42 "github.com/bcskill/bcschain/v3/crypto" 43 "github.com/bcskill/bcschain/v3/log" 44 "github.com/bcskill/bcschain/v3/p2p" 45 "github.com/bcskill/bcschain/v3/p2p/discover" 46 "github.com/bcskill/bcschain/v3/p2p/nat" 47 "github.com/bcskill/bcschain/v3/whisper/mailserver" 48 whisper "github.com/bcskill/bcschain/v3/whisper/whisperv6" 49 "golang.org/x/crypto/pbkdf2" 50 ) 51 52 const quitCommand = "~Q" 53 54 // singletons 55 var ( 56 server *p2p.Server 57 shh *whisper.Whisper 58 done chan struct{} 59 mailServer mailserver.WMailServer 60 61 input = bufio.NewReader(os.Stdin) 62 ) 63 64 // encryption 65 var ( 66 symKey []byte 67 pub *ecdsa.PublicKey 68 asymKey *ecdsa.PrivateKey 69 nodeid *ecdsa.PrivateKey 70 topic whisper.TopicType 71 72 asymKeyID string 73 asymFilterID string 74 symFilterID string 75 symPass string 76 msPassword string 77 ) 78 79 // cmd arguments 80 var ( 81 bootstrapMode = flag.Bool("standalone", false, "boostrap node: don't actively connect to peers, wait for incoming connections") 82 forwarderMode = flag.Bool("forwarder", false, "forwarder mode: only forward messages, neither send nor decrypt messages") 83 mailServerMode = flag.Bool("mailserver", false, "mail server mode: delivers expired messages on demand") 84 requestMail = flag.Bool("mailclient", false, "request expired messages from the bootstrap server") 85 asymmetricMode = flag.Bool("asym", false, "use asymmetric encryption") 86 generateKey = flag.Bool("generatekey", false, "generate and show the private key") 87 fileExMode = flag.Bool("fileexchange", false, "file exchange mode") 88 testMode = flag.Bool("test", false, "use of predefined parameters for diagnostics") 89 echoMode = flag.Bool("echo", false, "echo mode: prints some arguments for diagnostics") 90 91 argVerbosity = flag.Int("verbosity", int(log.LvlError), "log verbosity level") 92 argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds") 93 argWorkTime = flag.Uint("work", 5, "work time in seconds") 94 argMaxSize = flag.Uint("maxsize", uint(whisper.DefaultMaxMessageSize), "max size of message") 95 argPoW = flag.Float64("pow", whisper.DefaultMinimumPoW, "PoW for normal messages in float format (e.g. 2.7)") 96 argServerPoW = flag.Float64("mspow", whisper.DefaultMinimumPoW, "PoW requirement for Mail Server request") 97 98 argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)") 99 argPub = flag.String("pub", "", "public key for asymmetric encryption") 100 argDBPath = flag.String("dbpath", "", "path to the server's DB directory") 101 argIDFile = flag.String("idfile", "", "file name with node id (private key)") 102 argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)") 103 argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)") 104 argSaveDir = flag.String("savedir", "", "directory where incoming messages will be saved as files") 105 ) 106 107 func main() { 108 processArgs() 109 initialize() 110 run() 111 } 112 113 func processArgs() { 114 flag.Parse() 115 116 if len(*argIDFile) > 0 { 117 var err error 118 nodeid, err = crypto.LoadECDSA(*argIDFile) 119 if err != nil { 120 utils.Fatalf("Failed to load file [%s]: %s.", *argIDFile, err) 121 } 122 } 123 124 const enodePrefix = "enode://" 125 if len(*argEnode) > 0 { 126 if (*argEnode)[:len(enodePrefix)] != enodePrefix { 127 *argEnode = enodePrefix + *argEnode 128 } 129 } 130 131 if len(*argTopic) > 0 { 132 x, err := hex.DecodeString(*argTopic) 133 if err != nil { 134 utils.Fatalf("Failed to parse the topic: %s", err) 135 } 136 topic = whisper.BytesToTopic(x) 137 } 138 139 if *asymmetricMode && len(*argPub) > 0 { 140 var err error 141 if pub, err = crypto.UnmarshalPubkey(common.FromHex(*argPub)); err != nil { 142 utils.Fatalf("invalid public key") 143 } 144 } 145 146 if len(*argSaveDir) > 0 { 147 if _, err := os.Stat(*argSaveDir); os.IsNotExist(err) { 148 utils.Fatalf("Download directory '%s' does not exist", *argSaveDir) 149 } 150 } else if *fileExMode { 151 utils.Fatalf("Parameter 'savedir' is mandatory for file exchange mode") 152 } 153 154 if *echoMode { 155 echo() 156 } 157 } 158 159 func echo() { 160 fmt.Printf("ttl = %d \n", *argTTL) 161 fmt.Printf("workTime = %d \n", *argWorkTime) 162 fmt.Printf("pow = %f \n", *argPoW) 163 fmt.Printf("mspow = %f \n", *argServerPoW) 164 fmt.Printf("ip = %s \n", *argIP) 165 fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub))) 166 fmt.Printf("idfile = %s \n", *argIDFile) 167 fmt.Printf("dbpath = %s \n", *argDBPath) 168 fmt.Printf("boot = %s \n", *argEnode) 169 } 170 171 func initialize() { 172 log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*argVerbosity), log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) 173 174 done = make(chan struct{}) 175 var peers []*discover.Node 176 var err error 177 178 if *generateKey { 179 key, err := crypto.GenerateKey() 180 if err != nil { 181 utils.Fatalf("Failed to generate private key: %s", err) 182 } 183 k := hex.EncodeToString(crypto.FromECDSA(key)) 184 fmt.Printf("Random private key: %s \n", k) 185 os.Exit(0) 186 } 187 188 if *testMode { 189 symPass = "wwww" // ascii code: 0x77777777 190 msPassword = "wwww" 191 } 192 193 if *bootstrapMode { 194 if len(*argIP) == 0 { 195 argIP = scanLineA("Please enter your IP and port (e.g. 127.0.0.1:30348): ") 196 } 197 } else { 198 if len(*argEnode) == 0 { 199 argEnode = scanLineA("Please enter the peer's enode: ") 200 } 201 peer := discover.MustParseNode(*argEnode) 202 peers = append(peers, peer) 203 } 204 205 cfg := &whisper.Config{ 206 MaxMessageSize: uint32(*argMaxSize), 207 MinimumAcceptedPOW: *argPoW, 208 } 209 210 if *mailServerMode { 211 if len(msPassword) == 0 { 212 msPassword, err = prompt.Stdin.PromptPassword("Please enter the Mail Server password: ") 213 if err != nil { 214 utils.Fatalf("Failed to read Mail Server password: %s", err) 215 } 216 } 217 218 shh = whisper.New(cfg) 219 shh.RegisterServer(&mailServer) 220 mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW) 221 } else { 222 shh = whisper.New(cfg) 223 } 224 225 if *argPoW != whisper.DefaultMinimumPoW { 226 err := shh.SetMinimumPoW(context.Background(), *argPoW) 227 if err != nil { 228 utils.Fatalf("Failed to set PoW: %s", err) 229 } 230 } 231 232 if uint32(*argMaxSize) != whisper.DefaultMaxMessageSize { 233 err := shh.SetMaxMessageSize(uint32(*argMaxSize)) 234 if err != nil { 235 utils.Fatalf("Failed to set max message size: %s", err) 236 } 237 } 238 239 asymKeyID, err = shh.NewKeyPair() 240 if err != nil { 241 utils.Fatalf("Failed to generate a new key pair: %s", err) 242 } 243 244 asymKey, err = shh.GetPrivateKey(asymKeyID) 245 if err != nil { 246 utils.Fatalf("Failed to retrieve a new key pair: %s", err) 247 } 248 249 if nodeid == nil { 250 tmpID, err := shh.NewKeyPair() 251 if err != nil { 252 utils.Fatalf("Failed to generate a new key pair: %s", err) 253 } 254 255 nodeid, err = shh.GetPrivateKey(tmpID) 256 if err != nil { 257 utils.Fatalf("Failed to retrieve a new key pair: %s", err) 258 } 259 } 260 261 maxPeers := 80 262 if *bootstrapMode { 263 maxPeers = 800 264 } 265 266 server = &p2p.Server{ 267 Config: p2p.Config{ 268 PrivateKey: nodeid, 269 MaxPeers: maxPeers, 270 Name: common.MakeName("wnode", "6.0"), 271 Protocols: shh.Protocols(), 272 ListenAddr: *argIP, 273 NAT: nat.Any(), 274 BootstrapNodes: peers, 275 StaticNodes: peers, 276 TrustedNodes: peers, 277 }, 278 } 279 } 280 281 func startServer() { 282 err := server.Start() 283 if err != nil { 284 utils.Fatalf("Failed to start Whisper peer: %s.", err) 285 } 286 287 fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey))) 288 fmt.Println(server.NodeInfo().Enode) 289 290 if *bootstrapMode { 291 configureNode() 292 fmt.Println("Bootstrap Whisper node started") 293 } else { 294 fmt.Println("Whisper node started") 295 // first see if we can establish connection, then ask for user input 296 waitForConnection(true) 297 configureNode() 298 } 299 300 if !*forwarderMode { 301 fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand) 302 } 303 } 304 305 func isKeyValid(k *ecdsa.PublicKey) bool { 306 return k.X != nil && k.Y != nil 307 } 308 309 func configureNode() { 310 var err error 311 var p2pAccept bool 312 313 if *forwarderMode { 314 return 315 } 316 317 if *asymmetricMode { 318 if len(*argPub) == 0 { 319 s := scanLine("Please enter the peer's public key: ") 320 b := common.FromHex(s) 321 if b == nil { 322 utils.Fatalf("Error: can not convert hexadecimal string") 323 } 324 if pub, err = crypto.UnmarshalPubkey(b); err != nil { 325 utils.Fatalf("Error: invalid peer public key") 326 } 327 } 328 } 329 330 if *requestMail { 331 p2pAccept = true 332 if len(msPassword) == 0 { 333 msPassword, err = prompt.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 = prompt.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 }