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(&params)
   571  	if err != nil {
   572  		utils.Fatalf("failed to create new message: %s", err)
   573  	}
   574  
   575  	envelope, err := msg.Wrap(&params)
   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(&params)
   733  		if err != nil {
   734  			utils.Fatalf("failed to create new message: %s", err)
   735  		}
   736  		env, err := msg.Wrap(&params)
   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  }