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