github.com/luckypickle/go-ethereum-vet@v1.14.2/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/luckypickle/go-ethereum-vet/cmd/utils" 39 "github.com/luckypickle/go-ethereum-vet/common" 40 "github.com/luckypickle/go-ethereum-vet/console" 41 "github.com/luckypickle/go-ethereum-vet/crypto" 42 "github.com/luckypickle/go-ethereum-vet/log" 43 "github.com/luckypickle/go-ethereum-vet/p2p" 44 "github.com/luckypickle/go-ethereum-vet/p2p/discover" 45 "github.com/luckypickle/go-ethereum-vet/p2p/nat" 46 "github.com/luckypickle/go-ethereum-vet/whisper/mailserver" 47 whisper "github.com/luckypickle/go-ethereum-vet/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 var err error 144 if pub, err = crypto.UnmarshalPubkey(common.FromHex(*argPub)); err != nil { 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 if err := mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW); err != nil { 275 utils.Fatalf("Failed to init MailServer: %s", err) 276 } 277 } 278 279 server = &p2p.Server{ 280 Config: p2p.Config{ 281 PrivateKey: nodeid, 282 MaxPeers: maxPeers, 283 Name: common.MakeName("wnode", "6.0"), 284 Protocols: shh.Protocols(), 285 ListenAddr: *argIP, 286 NAT: nat.Any(), 287 BootstrapNodes: peers, 288 StaticNodes: peers, 289 TrustedNodes: peers, 290 }, 291 } 292 } 293 294 func startServer() error { 295 err := server.Start() 296 if err != nil { 297 fmt.Printf("Failed to start Whisper peer: %s.", err) 298 return err 299 } 300 301 fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey))) 302 fmt.Println(server.NodeInfo().Enode) 303 304 if *bootstrapMode { 305 configureNode() 306 fmt.Println("Bootstrap Whisper node started") 307 } else { 308 fmt.Println("Whisper node started") 309 // first see if we can establish connection, then ask for user input 310 waitForConnection(true) 311 configureNode() 312 } 313 314 if *fileExMode { 315 fmt.Printf("Please type the file name to be send. To quit type: '%s'\n", quitCommand) 316 } else if *fileReader { 317 fmt.Printf("Please type the file name to be decrypted. To quit type: '%s'\n", quitCommand) 318 } else if !*forwarderMode { 319 fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand) 320 } 321 return nil 322 } 323 324 func configureNode() { 325 var err error 326 var p2pAccept bool 327 328 if *forwarderMode { 329 return 330 } 331 332 if *asymmetricMode { 333 if len(*argPub) == 0 { 334 s := scanLine("Please enter the peer's public key: ") 335 b := common.FromHex(s) 336 if b == nil { 337 utils.Fatalf("Error: can not convert hexadecimal string") 338 } 339 if pub, err = crypto.UnmarshalPubkey(b); err != nil { 340 utils.Fatalf("Error: invalid peer public key") 341 } 342 } 343 } 344 345 if *requestMail { 346 p2pAccept = true 347 if len(msPassword) == 0 { 348 msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ") 349 if err != nil { 350 utils.Fatalf("Failed to read Mail Server password: %s", err) 351 } 352 } 353 } 354 355 if !*asymmetricMode && !*forwarderMode { 356 if len(symPass) == 0 { 357 symPass, err = console.Stdin.PromptPassword("Please enter the password for symmetric encryption: ") 358 if err != nil { 359 utils.Fatalf("Failed to read passphrase: %v", err) 360 } 361 } 362 363 symKeyID, err := shh.AddSymKeyFromPassword(symPass) 364 if err != nil { 365 utils.Fatalf("Failed to create symmetric key: %s", err) 366 } 367 symKey, err = shh.GetSymKey(symKeyID) 368 if err != nil { 369 utils.Fatalf("Failed to save symmetric key: %s", err) 370 } 371 if len(*argTopic) == 0 { 372 generateTopic([]byte(symPass)) 373 } 374 375 fmt.Printf("Filter is configured for the topic: %x \n", topic) 376 } 377 378 if *mailServerMode { 379 if len(*argDBPath) == 0 { 380 argDBPath = scanLineA("Please enter the path to DB file: ") 381 } 382 } 383 384 symFilter := whisper.Filter{ 385 KeySym: symKey, 386 Topics: [][]byte{topic[:]}, 387 AllowP2P: p2pAccept, 388 } 389 symFilterID, err = shh.Subscribe(&symFilter) 390 if err != nil { 391 utils.Fatalf("Failed to install filter: %s", err) 392 } 393 394 asymFilter := whisper.Filter{ 395 KeyAsym: asymKey, 396 Topics: [][]byte{topic[:]}, 397 AllowP2P: p2pAccept, 398 } 399 asymFilterID, err = shh.Subscribe(&asymFilter) 400 if err != nil { 401 utils.Fatalf("Failed to install filter: %s", err) 402 } 403 } 404 405 func generateTopic(password []byte) { 406 x := pbkdf2.Key(password, password, 4096, 128, sha512.New) 407 for i := 0; i < len(x); i++ { 408 topic[i%whisper.TopicLength] ^= x[i] 409 } 410 } 411 412 func waitForConnection(timeout bool) { 413 var cnt int 414 var connected bool 415 for !connected { 416 time.Sleep(time.Millisecond * 50) 417 connected = server.PeerCount() > 0 418 if timeout { 419 cnt++ 420 if cnt > 1000 { 421 utils.Fatalf("Timeout expired, failed to connect") 422 } 423 } 424 } 425 426 fmt.Println("Connected to peer.") 427 } 428 429 func run() { 430 err := startServer() 431 if err != nil { 432 return 433 } 434 defer server.Stop() 435 shh.Start(nil) 436 defer shh.Stop() 437 438 if !*forwarderMode { 439 go messageLoop() 440 } 441 442 if *requestMail { 443 requestExpiredMessagesLoop() 444 } else if *fileExMode { 445 sendFilesLoop() 446 } else if *fileReader { 447 fileReaderLoop() 448 } else { 449 sendLoop() 450 } 451 } 452 453 func shutdown() { 454 close(done) 455 mailServer.Close() 456 } 457 458 func sendLoop() { 459 for { 460 s := scanLine("") 461 if s == quitCommand { 462 fmt.Println("Quit command received") 463 return 464 } 465 sendMsg([]byte(s)) 466 if *asymmetricMode { 467 // print your own message for convenience, 468 // because in asymmetric mode it is impossible to decrypt it 469 timestamp := time.Now().Unix() 470 from := crypto.PubkeyToAddress(asymKey.PublicKey) 471 fmt.Printf("\n%d <%x>: %s\n", timestamp, from, s) 472 } 473 } 474 } 475 476 func sendFilesLoop() { 477 for { 478 s := scanLine("") 479 if s == quitCommand { 480 fmt.Println("Quit command received") 481 return 482 } 483 b, err := ioutil.ReadFile(s) 484 if err != nil { 485 fmt.Printf(">>> Error: %s \n", err) 486 } else { 487 h := sendMsg(b) 488 if (h == common.Hash{}) { 489 fmt.Printf(">>> Error: message was not sent \n") 490 } else { 491 timestamp := time.Now().Unix() 492 from := crypto.PubkeyToAddress(asymKey.PublicKey) 493 fmt.Printf("\n%d <%x>: sent message with hash %x\n", timestamp, from, h) 494 } 495 } 496 } 497 } 498 499 func fileReaderLoop() { 500 watcher1 := shh.GetFilter(symFilterID) 501 watcher2 := shh.GetFilter(asymFilterID) 502 if watcher1 == nil && watcher2 == nil { 503 fmt.Println("Error: neither symmetric nor asymmetric filter is installed") 504 return 505 } 506 507 for { 508 s := scanLine("") 509 if s == quitCommand { 510 fmt.Println("Quit command received") 511 return 512 } 513 raw, err := ioutil.ReadFile(s) 514 if err != nil { 515 fmt.Printf(">>> Error: %s \n", err) 516 } else { 517 env := whisper.Envelope{Data: raw} // the topic is zero 518 msg := env.Open(watcher1) // force-open envelope regardless of the topic 519 if msg == nil { 520 msg = env.Open(watcher2) 521 } 522 if msg == nil { 523 fmt.Printf(">>> Error: failed to decrypt the message \n") 524 } else { 525 printMessageInfo(msg) 526 } 527 } 528 } 529 } 530 531 func scanLine(prompt string) string { 532 if len(prompt) > 0 { 533 fmt.Print(prompt) 534 } 535 txt, err := input.ReadString('\n') 536 if err != nil { 537 utils.Fatalf("input error: %s", err) 538 } 539 txt = strings.TrimRight(txt, "\n\r") 540 return txt 541 } 542 543 func scanLineA(prompt string) *string { 544 s := scanLine(prompt) 545 return &s 546 } 547 548 func scanUint(prompt string) uint32 { 549 s := scanLine(prompt) 550 i, err := strconv.Atoi(s) 551 if err != nil { 552 utils.Fatalf("Fail to parse the lower time limit: %s", err) 553 } 554 return uint32(i) 555 } 556 557 func sendMsg(payload []byte) common.Hash { 558 params := whisper.MessageParams{ 559 Src: asymKey, 560 Dst: pub, 561 KeySym: symKey, 562 Payload: payload, 563 Topic: topic, 564 TTL: uint32(*argTTL), 565 PoW: *argPoW, 566 WorkTime: uint32(*argWorkTime), 567 } 568 569 msg, err := whisper.NewSentMessage(¶ms) 570 if err != nil { 571 utils.Fatalf("failed to create new message: %s", err) 572 } 573 574 envelope, err := msg.Wrap(¶ms) 575 if err != nil { 576 fmt.Printf("failed to seal message: %v \n", err) 577 return common.Hash{} 578 } 579 580 err = shh.Send(envelope) 581 if err != nil { 582 fmt.Printf("failed to send message: %v \n", err) 583 return common.Hash{} 584 } 585 586 return envelope.Hash() 587 } 588 589 func messageLoop() { 590 sf := shh.GetFilter(symFilterID) 591 if sf == nil { 592 utils.Fatalf("symmetric filter is not installed") 593 } 594 595 af := shh.GetFilter(asymFilterID) 596 if af == nil { 597 utils.Fatalf("asymmetric filter is not installed") 598 } 599 600 ticker := time.NewTicker(time.Millisecond * 50) 601 602 for { 603 select { 604 case <-ticker.C: 605 m1 := sf.Retrieve() 606 m2 := af.Retrieve() 607 messages := append(m1, m2...) 608 for _, msg := range messages { 609 reportedOnce := false 610 if !*fileExMode && len(msg.Payload) <= 2048 { 611 printMessageInfo(msg) 612 reportedOnce = true 613 } 614 615 // All messages are saved upon specifying argSaveDir. 616 // fileExMode only specifies how messages are displayed on the console after they are saved. 617 // if fileExMode == true, only the hashes are displayed, since messages might be too big. 618 if len(*argSaveDir) > 0 { 619 writeMessageToFile(*argSaveDir, msg, !reportedOnce) 620 } 621 } 622 case <-done: 623 return 624 } 625 } 626 } 627 628 func printMessageInfo(msg *whisper.ReceivedMessage) { 629 timestamp := fmt.Sprintf("%d", msg.Sent) // unix timestamp for diagnostics 630 text := string(msg.Payload) 631 632 var address common.Address 633 if msg.Src != nil { 634 address = crypto.PubkeyToAddress(*msg.Src) 635 } 636 637 if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) { 638 fmt.Printf("\n%s <%x>: %s\n", timestamp, address, text) // message from myself 639 } else { 640 fmt.Printf("\n%s [%x]: %s\n", timestamp, address, text) // message from a peer 641 } 642 } 643 644 func writeMessageToFile(dir string, msg *whisper.ReceivedMessage, show bool) { 645 if len(dir) == 0 { 646 return 647 } 648 649 timestamp := fmt.Sprintf("%d", msg.Sent) 650 name := fmt.Sprintf("%x", msg.EnvelopeHash) 651 652 var address common.Address 653 if msg.Src != nil { 654 address = crypto.PubkeyToAddress(*msg.Src) 655 } 656 657 env := shh.GetEnvelope(msg.EnvelopeHash) 658 if env == nil { 659 fmt.Printf("\nUnexpected error: envelope not found: %x\n", msg.EnvelopeHash) 660 return 661 } 662 663 // this is a sample code; uncomment if you don't want to save your own messages. 664 //if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) { 665 // fmt.Printf("\n%s <%x>: message from myself received, not saved: '%s'\n", timestamp, address, name) 666 // return 667 //} 668 669 fullpath := filepath.Join(dir, name) 670 err := ioutil.WriteFile(fullpath, env.Data, 0644) 671 if err != nil { 672 fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err) 673 } else if show { 674 fmt.Printf("\n%s {%x}: message received and saved as '%s' (%d bytes)\n", timestamp, address, name, len(env.Data)) 675 } 676 } 677 678 func requestExpiredMessagesLoop() { 679 var key, peerID, bloom []byte 680 var timeLow, timeUpp uint32 681 var t string 682 var xt whisper.TopicType 683 684 keyID, err := shh.AddSymKeyFromPassword(msPassword) 685 if err != nil { 686 utils.Fatalf("Failed to create symmetric key for mail request: %s", err) 687 } 688 key, err = shh.GetSymKey(keyID) 689 if err != nil { 690 utils.Fatalf("Failed to save symmetric key for mail request: %s", err) 691 } 692 peerID = extractIDFromEnode(*argEnode) 693 shh.AllowP2PMessagesFromPeer(peerID) 694 695 for { 696 timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ") 697 timeUpp = scanUint("Please enter the upper limit of the time range (unix timestamp): ") 698 t = scanLine("Enter the topic (hex). Press enter to request all messages, regardless of the topic: ") 699 if len(t) == whisper.TopicLength*2 { 700 x, err := hex.DecodeString(t) 701 if err != nil { 702 fmt.Printf("Failed to parse the topic: %s \n", err) 703 continue 704 } 705 xt = whisper.BytesToTopic(x) 706 bloom = whisper.TopicToBloom(xt) 707 obfuscateBloom(bloom) 708 } else if len(t) == 0 { 709 bloom = whisper.MakeFullNodeBloom() 710 } else { 711 fmt.Println("Error: topic is invalid, request aborted") 712 continue 713 } 714 715 if timeUpp == 0 { 716 timeUpp = 0xFFFFFFFF 717 } 718 719 data := make([]byte, 8, 8+whisper.BloomFilterSize) 720 binary.BigEndian.PutUint32(data, timeLow) 721 binary.BigEndian.PutUint32(data[4:], timeUpp) 722 data = append(data, bloom...) 723 724 var params whisper.MessageParams 725 params.PoW = *argServerPoW 726 params.Payload = data 727 params.KeySym = key 728 params.Src = asymKey 729 params.WorkTime = 5 730 731 msg, err := whisper.NewSentMessage(¶ms) 732 if err != nil { 733 utils.Fatalf("failed to create new message: %s", err) 734 } 735 env, err := msg.Wrap(¶ms) 736 if err != nil { 737 utils.Fatalf("Wrap failed: %s", err) 738 } 739 740 err = shh.RequestHistoricMessages(peerID, env) 741 if err != nil { 742 utils.Fatalf("Failed to send P2P message: %s", err) 743 } 744 745 time.Sleep(time.Second * 5) 746 } 747 } 748 749 func extractIDFromEnode(s string) []byte { 750 n, err := discover.ParseNode(s) 751 if err != nil { 752 utils.Fatalf("Failed to parse enode: %s", err) 753 } 754 return n.ID[:] 755 } 756 757 // obfuscateBloom adds 16 random bits to the the bloom 758 // filter, in order to obfuscate the containing topics. 759 // it does so deterministically within every session. 760 // despite additional bits, it will match on average 761 // 32000 times less messages than full node's bloom filter. 762 func obfuscateBloom(bloom []byte) { 763 const half = entropySize / 2 764 for i := 0; i < half; i++ { 765 x := int(entropy[i]) 766 if entropy[half+i] < 128 { 767 x += 256 768 } 769 770 bloom[x/8] = 1 << uint(x%8) // set the bit number X 771 } 772 }