github.com/devfans/go-ethereum@v1.5.10-0.20170326212234-7419d0c38291/cmd/wnode/main.go (about)

     1  // Copyright 2016 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  	"crypto/sha512"
    26  	"encoding/binary"
    27  	"encoding/hex"
    28  	"flag"
    29  	"fmt"
    30  	"os"
    31  	"strconv"
    32  	"strings"
    33  	"time"
    34  
    35  	"github.com/ethereum/go-ethereum/cmd/utils"
    36  	"github.com/ethereum/go-ethereum/common"
    37  	"github.com/ethereum/go-ethereum/console"
    38  	"github.com/ethereum/go-ethereum/crypto"
    39  	"github.com/ethereum/go-ethereum/log"
    40  	"github.com/ethereum/go-ethereum/p2p"
    41  	"github.com/ethereum/go-ethereum/p2p/discover"
    42  	"github.com/ethereum/go-ethereum/p2p/nat"
    43  	"github.com/ethereum/go-ethereum/whisper/mailserver"
    44  	whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
    45  	"golang.org/x/crypto/pbkdf2"
    46  )
    47  
    48  const quitCommand = "~Q"
    49  const symKeyName = "da919ea33001b04dfc630522e33078ec0df11"
    50  
    51  // singletons
    52  var (
    53  	server     *p2p.Server
    54  	shh        *whisper.Whisper
    55  	done       chan struct{}
    56  	mailServer mailserver.WMailServer
    57  
    58  	input = bufio.NewReader(os.Stdin)
    59  )
    60  
    61  // encryption
    62  var (
    63  	symKey     []byte
    64  	pub        *ecdsa.PublicKey
    65  	asymKey    *ecdsa.PrivateKey
    66  	nodeid     *ecdsa.PrivateKey
    67  	topic      whisper.TopicType
    68  	filterID   string
    69  	symPass    string
    70  	msPassword string
    71  )
    72  
    73  // cmd arguments
    74  var (
    75  	echoMode       = flag.Bool("e", false, "echo mode: prints some arguments for diagnostics")
    76  	bootstrapMode  = flag.Bool("b", false, "boostrap node: don't actively connect to peers, wait for incoming connections")
    77  	forwarderMode  = flag.Bool("f", false, "forwarder mode: only forward messages, neither send nor decrypt messages")
    78  	mailServerMode = flag.Bool("s", false, "mail server mode: delivers expired messages on demand")
    79  	requestMail    = flag.Bool("r", false, "request expired messages from the bootstrap server")
    80  	asymmetricMode = flag.Bool("a", false, "use asymmetric encryption")
    81  	testMode       = flag.Bool("t", false, "use of predefined parameters for diagnostics")
    82  	generateKey    = flag.Bool("k", false, "generate and show the private key")
    83  
    84  	argVerbosity = flag.Int("verbosity", int(log.LvlWarn), "log verbosity level")
    85  	argTTL       = flag.Uint("ttl", 30, "time-to-live for messages in seconds")
    86  	argWorkTime  = flag.Uint("work", 5, "work time in seconds")
    87  	argPoW       = flag.Float64("pow", whisper.MinimumPoW, "PoW for normal messages in float format (e.g. 2.7)")
    88  	argServerPoW = flag.Float64("mspow", whisper.MinimumPoW, "PoW requirement for Mail Server request")
    89  
    90  	argIP     = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)")
    91  	argPub    = flag.String("pub", "", "public key for asymmetric encryption")
    92  	argDBPath = flag.String("dbpath", "", "path to the server's DB directory")
    93  	argIDFile = flag.String("idfile", "", "file name with node id (private key)")
    94  	argEnode  = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)")
    95  	argTopic  = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)")
    96  )
    97  
    98  func main() {
    99  	processArgs()
   100  	initialize()
   101  	run()
   102  }
   103  
   104  func processArgs() {
   105  	flag.Parse()
   106  
   107  	if len(*argIDFile) > 0 {
   108  		var err error
   109  		nodeid, err = crypto.LoadECDSA(*argIDFile)
   110  		if err != nil {
   111  			utils.Fatalf("Failed to load file [%s]: %s.", *argIDFile, err)
   112  		}
   113  	}
   114  
   115  	const enodePrefix = "enode://"
   116  	if len(*argEnode) > 0 {
   117  		if (*argEnode)[:len(enodePrefix)] != enodePrefix {
   118  			*argEnode = enodePrefix + *argEnode
   119  		}
   120  	}
   121  
   122  	if len(*argTopic) > 0 {
   123  		x, err := hex.DecodeString(*argTopic)
   124  		if err != nil {
   125  			utils.Fatalf("Failed to parse the topic: %s", err)
   126  		}
   127  		topic = whisper.BytesToTopic(x)
   128  	}
   129  
   130  	if *asymmetricMode && len(*argPub) > 0 {
   131  		pub = crypto.ToECDSAPub(common.FromHex(*argPub))
   132  		if !isKeyValid(pub) {
   133  			utils.Fatalf("invalid public key")
   134  		}
   135  	}
   136  
   137  	if *echoMode {
   138  		echo()
   139  	}
   140  }
   141  
   142  func echo() {
   143  	fmt.Printf("ttl = %d \n", *argTTL)
   144  	fmt.Printf("workTime = %d \n", *argWorkTime)
   145  	fmt.Printf("pow = %f \n", *argPoW)
   146  	fmt.Printf("mspow = %f \n", *argServerPoW)
   147  	fmt.Printf("ip = %s \n", *argIP)
   148  	fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub)))
   149  	fmt.Printf("idfile = %s \n", *argIDFile)
   150  	fmt.Printf("dbpath = %s \n", *argDBPath)
   151  	fmt.Printf("boot = %s \n", *argEnode)
   152  }
   153  
   154  func initialize() {
   155  	log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*argVerbosity), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
   156  
   157  	done = make(chan struct{})
   158  	var peers []*discover.Node
   159  	var err error
   160  
   161  	if *generateKey {
   162  		key, err := crypto.GenerateKey()
   163  		if err != nil {
   164  			utils.Fatalf("Failed to generate private key: %s", err)
   165  		}
   166  		k := hex.EncodeToString(crypto.FromECDSA(key))
   167  		fmt.Printf("Random private key: %s \n", k)
   168  		os.Exit(0)
   169  	}
   170  
   171  	if *testMode {
   172  		symPass = "wwww" // ascii code: 0x77777777
   173  		msPassword = "mail server test password"
   174  	}
   175  
   176  	if *bootstrapMode {
   177  		if len(*argIP) == 0 {
   178  			argIP = scanLineA("Please enter your IP and port (e.g. 127.0.0.1:30348): ")
   179  		}
   180  	} else {
   181  		if len(*argEnode) == 0 {
   182  			argEnode = scanLineA("Please enter the peer's enode: ")
   183  		}
   184  		peer := discover.MustParseNode(*argEnode)
   185  		peers = append(peers, peer)
   186  	}
   187  
   188  	if *mailServerMode {
   189  		if len(msPassword) == 0 {
   190  			msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ")
   191  			if err != nil {
   192  				utils.Fatalf("Failed to read Mail Server password: %s", err)
   193  			}
   194  		}
   195  		shh = whisper.New()
   196  		shh.RegisterServer(&mailServer)
   197  		mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW)
   198  	} else {
   199  		shh = whisper.New()
   200  	}
   201  
   202  	asymKey = shh.NewIdentity()
   203  	if nodeid == nil {
   204  		nodeid = shh.NewIdentity()
   205  	}
   206  
   207  	maxPeers := 80
   208  	if *bootstrapMode {
   209  		maxPeers = 800
   210  	}
   211  
   212  	server = &p2p.Server{
   213  		Config: p2p.Config{
   214  			PrivateKey:     nodeid,
   215  			MaxPeers:       maxPeers,
   216  			Name:           common.MakeName("whisper-go", "5.0"),
   217  			Protocols:      shh.Protocols(),
   218  			ListenAddr:     *argIP,
   219  			NAT:            nat.Any(),
   220  			BootstrapNodes: peers,
   221  			StaticNodes:    peers,
   222  			TrustedNodes:   peers,
   223  		},
   224  	}
   225  }
   226  
   227  func startServer() {
   228  	err := server.Start()
   229  	if err != nil {
   230  		utils.Fatalf("Failed to start Whisper peer: %s.", err)
   231  	}
   232  
   233  	fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey)))
   234  	fmt.Println(server.NodeInfo().Enode)
   235  
   236  	if *bootstrapMode {
   237  		configureNode()
   238  		fmt.Println("Bootstrap Whisper node started")
   239  	} else {
   240  		fmt.Println("Whisper node started")
   241  		// first see if we can establish connection, then ask for user input
   242  		waitForConnection(true)
   243  		configureNode()
   244  	}
   245  
   246  	if !*forwarderMode {
   247  		fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand)
   248  	}
   249  }
   250  
   251  func isKeyValid(k *ecdsa.PublicKey) bool {
   252  	return k.X != nil && k.Y != nil
   253  }
   254  
   255  func configureNode() {
   256  	var err error
   257  	var p2pAccept bool
   258  
   259  	if *forwarderMode {
   260  		return
   261  	}
   262  
   263  	if *asymmetricMode {
   264  		if len(*argPub) == 0 {
   265  			s := scanLine("Please enter the peer's public key: ")
   266  			pub = crypto.ToECDSAPub(common.FromHex(s))
   267  			if !isKeyValid(pub) {
   268  				utils.Fatalf("Error: invalid public key")
   269  			}
   270  		}
   271  	}
   272  
   273  	if *requestMail {
   274  		p2pAccept = true
   275  		if len(msPassword) == 0 {
   276  			msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ")
   277  			if err != nil {
   278  				utils.Fatalf("Failed to read Mail Server password: %s", err)
   279  			}
   280  		}
   281  	}
   282  
   283  	if !*asymmetricMode && !*forwarderMode {
   284  		if len(symPass) == 0 {
   285  			symPass, err = console.Stdin.PromptPassword("Please enter the password: ")
   286  			if err != nil {
   287  				utils.Fatalf("Failed to read passphrase: %v", err)
   288  			}
   289  		}
   290  
   291  		shh.AddSymKey(symKeyName, []byte(symPass))
   292  		symKey = shh.GetSymKey(symKeyName)
   293  		if len(*argTopic) == 0 {
   294  			generateTopic([]byte(symPass))
   295  		}
   296  	}
   297  
   298  	if *mailServerMode {
   299  		if len(*argDBPath) == 0 {
   300  			argDBPath = scanLineA("Please enter the path to DB file: ")
   301  		}
   302  	}
   303  
   304  	filter := whisper.Filter{
   305  		KeySym:    symKey,
   306  		KeyAsym:   asymKey,
   307  		Topics:    []whisper.TopicType{topic},
   308  		AcceptP2P: p2pAccept,
   309  	}
   310  	filterID, err = shh.Watch(&filter)
   311  	if err != nil {
   312  		utils.Fatalf("Failed to install filter: %s", err)
   313  	}
   314  	fmt.Printf("Filter is configured for the topic: %x \n", topic)
   315  }
   316  
   317  func generateTopic(password []byte) {
   318  	x := pbkdf2.Key(password, password, 8196, 128, sha512.New)
   319  	for i := 0; i < len(x); i++ {
   320  		topic[i%whisper.TopicLength] ^= x[i]
   321  	}
   322  }
   323  
   324  func waitForConnection(timeout bool) {
   325  	var cnt int
   326  	var connected bool
   327  	for !connected {
   328  		time.Sleep(time.Millisecond * 50)
   329  		connected = server.PeerCount() > 0
   330  		if timeout {
   331  			cnt++
   332  			if cnt > 1000 {
   333  				utils.Fatalf("Timeout expired, failed to connect")
   334  			}
   335  		}
   336  	}
   337  
   338  	fmt.Println("Connected to peer.")
   339  }
   340  
   341  func run() {
   342  	defer mailServer.Close()
   343  	startServer()
   344  	defer server.Stop()
   345  	shh.Start(nil)
   346  	defer shh.Stop()
   347  
   348  	if !*forwarderMode {
   349  		go messageLoop()
   350  	}
   351  
   352  	if *requestMail {
   353  		requestExpiredMessagesLoop()
   354  	} else {
   355  		sendLoop()
   356  	}
   357  }
   358  
   359  func sendLoop() {
   360  	for {
   361  		s := scanLine("")
   362  		if s == quitCommand {
   363  			fmt.Println("Quit command received")
   364  			close(done)
   365  			break
   366  		}
   367  		sendMsg([]byte(s))
   368  
   369  		if *asymmetricMode {
   370  			// print your own message for convenience,
   371  			// because in asymmetric mode it is impossible to decrypt it
   372  			timestamp := time.Now().Unix()
   373  			from := crypto.PubkeyToAddress(asymKey.PublicKey)
   374  			fmt.Printf("\n%d <%x>: %s\n", timestamp, from, s)
   375  		}
   376  	}
   377  }
   378  
   379  func scanLine(prompt string) string {
   380  	if len(prompt) > 0 {
   381  		fmt.Print(prompt)
   382  	}
   383  	txt, err := input.ReadString('\n')
   384  	if err != nil {
   385  		utils.Fatalf("input error: %s", err)
   386  	}
   387  	txt = strings.TrimRight(txt, "\n\r")
   388  	return txt
   389  }
   390  
   391  func scanLineA(prompt string) *string {
   392  	s := scanLine(prompt)
   393  	return &s
   394  }
   395  
   396  func scanUint(prompt string) uint32 {
   397  	s := scanLine(prompt)
   398  	i, err := strconv.Atoi(s)
   399  	if err != nil {
   400  		utils.Fatalf("Fail to parse the lower time limit: %s", err)
   401  	}
   402  	return uint32(i)
   403  }
   404  
   405  func sendMsg(payload []byte) {
   406  	params := whisper.MessageParams{
   407  		Src:      asymKey,
   408  		Dst:      pub,
   409  		KeySym:   symKey,
   410  		Payload:  payload,
   411  		Topic:    topic,
   412  		TTL:      uint32(*argTTL),
   413  		PoW:      *argPoW,
   414  		WorkTime: uint32(*argWorkTime),
   415  	}
   416  
   417  	msg := whisper.NewSentMessage(&params)
   418  	envelope, err := msg.Wrap(&params)
   419  	if err != nil {
   420  		fmt.Printf("failed to seal message: %v \n", err)
   421  		return
   422  	}
   423  
   424  	err = shh.Send(envelope)
   425  	if err != nil {
   426  		fmt.Printf("failed to send message: %v \n", err)
   427  	}
   428  }
   429  
   430  func messageLoop() {
   431  	f := shh.GetFilter(filterID)
   432  	if f == nil {
   433  		utils.Fatalf("filter is not installed")
   434  	}
   435  
   436  	ticker := time.NewTicker(time.Millisecond * 50)
   437  
   438  	for {
   439  		select {
   440  		case <-ticker.C:
   441  			messages := f.Retrieve()
   442  			for _, msg := range messages {
   443  				printMessageInfo(msg)
   444  			}
   445  		case <-done:
   446  			return
   447  		}
   448  	}
   449  }
   450  
   451  func printMessageInfo(msg *whisper.ReceivedMessage) {
   452  	timestamp := fmt.Sprintf("%d", msg.Sent) // unix timestamp for diagnostics
   453  	text := string(msg.Payload)
   454  
   455  	var address common.Address
   456  	if msg.Src != nil {
   457  		address = crypto.PubkeyToAddress(*msg.Src)
   458  	}
   459  
   460  	if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) {
   461  		fmt.Printf("\n%s <%x>: %s\n", timestamp, address, text) // message from myself
   462  	} else {
   463  		fmt.Printf("\n%s [%x]: %s\n", timestamp, address, text) // message from a peer
   464  	}
   465  }
   466  
   467  func requestExpiredMessagesLoop() {
   468  	var key, peerID []byte
   469  	var timeLow, timeUpp uint32
   470  	var t string
   471  	var xt, empty whisper.TopicType
   472  
   473  	err := shh.AddSymKey(mailserver.MailServerKeyName, []byte(msPassword))
   474  	if err != nil {
   475  		utils.Fatalf("Failed to create symmetric key for mail request: %s", err)
   476  	}
   477  	key = shh.GetSymKey(mailserver.MailServerKeyName)
   478  	peerID = extractIdFromEnode(*argEnode)
   479  	shh.MarkPeerTrusted(peerID)
   480  
   481  	for {
   482  		timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ")
   483  		timeUpp = scanUint("Please enter the upper limit of the time range (unix timestamp): ")
   484  		t = scanLine("Please enter the topic (hexadecimal): ")
   485  		if len(t) >= whisper.TopicLength*2 {
   486  			x, err := hex.DecodeString(t)
   487  			if err != nil {
   488  				utils.Fatalf("Failed to parse the topic: %s", err)
   489  			}
   490  			xt = whisper.BytesToTopic(x)
   491  		}
   492  		if timeUpp == 0 {
   493  			timeUpp = 0xFFFFFFFF
   494  		}
   495  
   496  		data := make([]byte, 8+whisper.TopicLength)
   497  		binary.BigEndian.PutUint32(data, timeLow)
   498  		binary.BigEndian.PutUint32(data[4:], timeUpp)
   499  		copy(data[8:], xt[:])
   500  		if xt == empty {
   501  			data = data[:8]
   502  		}
   503  
   504  		var params whisper.MessageParams
   505  		params.PoW = *argServerPoW
   506  		params.Payload = data
   507  		params.KeySym = key
   508  		params.Src = nodeid
   509  		params.WorkTime = 5
   510  
   511  		msg := whisper.NewSentMessage(&params)
   512  		env, err := msg.Wrap(&params)
   513  		if err != nil {
   514  			utils.Fatalf("Wrap failed: %s", err)
   515  		}
   516  
   517  		err = shh.RequestHistoricMessages(peerID, env)
   518  		if err != nil {
   519  			utils.Fatalf("Failed to send P2P message: %s", err)
   520  		}
   521  
   522  		time.Sleep(time.Second * 5)
   523  	}
   524  }
   525  
   526  func extractIdFromEnode(s string) []byte {
   527  	n, err := discover.ParseNode(s)
   528  	if err != nil {
   529  		utils.Fatalf("Failed to parse enode: %s", err)
   530  		return nil
   531  	}
   532  	return n.ID[:]
   533  }