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