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