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