1 package wallet
2 3 import (
4 "time"
5 6 "github.com/urfave/cli"
7 )
8 9 // Config is the main configuration for wallet
10 type Config struct {
11 // General application behavior
12 ConfigFile *string `short:"C" long:"configfile" description:"Path to configuration file"`
13 ShowVersion *bool `short:"True" long:"version" description:"Display version information and exit"`
14 LogLevel *string
15 Create *bool `long:"create" description:"Create the wallet if it does not exist"`
16 CreateTemp *bool `long:"createtemp" description:"Create a temporary simulation wallet (pass=password) in the data directory indicated; must call with --datadir"`
17 AppDataDir *string `short:"A" long:"appdata" description:"Application data directory for wallet config, databases and logs"`
18 TestNet3 *bool `long:"testnet" description:"Use the test Bitcoin network (version 3) (default mainnet)"`
19 SimNet *bool `long:"simnet" description:"Use the simulation test network (default mainnet)"`
20 NoInitialLoad *bool `long:"noinitialload" description:"Defer wallet creation/opening on startup and enable loading wallets over RPC"`
21 LogDir *string `long:"logdir" description:"Directory to log output."`
22 Profile *string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"`
23 // GUI *bool `long:"__OLDgui" description:"Launch GUI"`
24 // Wallet options
25 WalletPass *string `long:"walletpass" default-mask:"-" description:"The public wallet password -- Only required if the wallet was created with one"`
26 // RPC client options
27 RPCConnect *string `short:"c" long:"rpcconnect" description:"Hostname/IP and port of pod RPC server to connect to (default localhost:11048, testnet: localhost:21048, simnet: localhost:41048)"`
28 CAFile *string `long:"cafile" description:"File containing root certificates to authenticate a TLS connections with pod"`
29 EnableClientTLS *bool `long:"clienttls" description:"Enable TLS for the RPC client"`
30 PodUsername *string `long:"podusername" description:"Username for pod authentication"`
31 PodPassword *string `long:"podpassword" default-mask:"-" description:"Password for pod authentication"`
32 Proxy *string `long:"proxy" description:"Connect via SOCKS5 proxy (eg. 127.0.0.1:9050)"`
33 ProxyUser *string `long:"proxyuser" description:"Username for proxy server"`
34 ProxyPass *string `long:"proxypass" default-mask:"-" description:"Password for proxy server"`
35 // SPV client options
36 UseSPV *bool `long:"usespv" description:"Enables the experimental use of SPV rather than RPC for chain synchronization"`
37 AddPeers *cli.StringSlice `short:"a" long:"addpeer" description:"Add a peer to connect with at startup"`
38 ConnectPeers *cli.StringSlice `long:"connect" description:"Connect only to the specified peers at startup"`
39 MaxPeers *int `long:"maxpeers" description:"Max number of inbound and outbound peers"`
40 BanDuration *time.Duration `long:"banduration" description:"How long to ban misbehaving peers. Valid time units are {s, m, h}. Minimum 1 second"`
41 BanThreshold *int `long:"banthreshold" description:"Maximum allowed ban score before disconnecting and banning misbehaving peers."`
42 // RPC server options
43 //
44 // The legacy server is still enabled by default (and eventually will be replaced with the experimental server) so
45 // prepare for that change by renaming the struct fields (but not the configuration options).
46 //
47 // Usernames can also be used for the consensus RPC client, so they aren't considered legacy.
48 RPCCert *string `long:"rpccert" description:"File containing the certificate file"`
49 RPCKey *string `long:"rpckey" description:"File containing the certificate key"`
50 OneTimeTLSKey *bool `long:"onetimetlskey" description:"Generate a new TLS certpair at startup, but only write the certificate to disk"`
51 EnableServerTLS *bool `long:"servertls" description:"Enable TLS for the RPC server"`
52 LegacyRPCListeners *cli.StringSlice `long:"rpclisten" description:"Listen for legacy RPC connections on this interface/port (default port: 11046, testnet: 21046, simnet: 41046)"`
53 LegacyRPCMaxClients *int `long:"rpcmaxclients" description:"Max number of legacy RPC clients for standard connections"`
54 LegacyRPCMaxWebsockets *int `long:"rpcmaxwebsockets" description:"Max number of legacy RPC websocket connections"`
55 Username *string `short:"u" long:"username" description:"Username for legacy RPC and pod authentication (if podusername is unset)"`
56 Password *string `short:"P" long:"password" default-mask:"-" description:"Password for legacy RPC and pod authentication (if podpassword is unset)"`
57 // EXPERIMENTAL RPC server options
58 //
59 // These options will change (and require changes to config files, etc.) when the new gRPC server is enabled.
60 ExperimentalRPCListeners *cli.StringSlice `long:"experimentalrpclisten" description:"Listen for RPC connections on this interface/port"`
61 // Deprecated options
62 DataDir *string `short:"b" long:"datadir" default-mask:"-" description:"DEPRECATED -- use appdata instead"`
63 }
64 65 // A bunch of constants
66 const ()
67 68 /*
69 // cleanAndExpandPath expands environement variables and leading ~ in the
70 // passed path, cleans the result, and returns it.
71 func cleanAndExpandPath(path string) string {
72 // NOTE: The os.ExpandEnv doesn't work with Windows cmd.exe-style
73 // %VARIABLE%, but they variables can still be expanded via POSIX-style
74 // $VARIABLE.
75 path = os.ExpandEnv(path)
76 if !strings.HasPrefix(path, "~") {
77 return filepath.Clean(path)
78 }
79 // Expand initial ~ to the current user's home directory, or ~otheruser to otheruser's home directory. On Windows, both forward and backward slashes can be used.
80 path = path[1:]
81 var pathSeparators string
82 if runtime.GOOS == "windows" {
83 pathSeparators = string(os.PathSeparator) + "/"
84 } else {
85 pathSeparators = string(os.PathSeparator)
86 }
87 userName := ""
88 if i := strings.IndexAny(path, pathSeparators); i != -1 {
89 userName = path[:i]
90 path = path[i:]
91 }
92 homeDir := ""
93 var u *user.User
94 var e error
95 if userName == "" {
96 u, e = user.Current()
97 } else {
98 u, e = user.Lookup(userName)
99 }
100 if e == nil {
101 homeDir = u.HomeDir
102 }
103 // Fallback to CWD if user lookup fails or user has no home directory.
104 if homeDir == "" {
105 homeDir = "."
106 }
107 return filepath.Join(homeDir, path)
108 }
109 // createDefaultConfig creates a basic config file at the given destination path.
110 // For this it tries to read the config file for the RPC server (either pod or
111 // sac), and extract the RPC user and password from it.
112 func createDefaultConfigFile(destinationPath, serverConfigPath,
113 serverDataDir, walletDataDir string) (e error) {
114 // fmt.Println("server config path", serverConfigPath)
115 // Read the RPC server config
116 serverConfigFile, e := os.Open(serverConfigPath)
117 if e != nil {
118 return e
119 }
120 defer serverConfigFile.Close()
121 content, e := ioutil.ReadAll(serverConfigFile)
122 if e != nil {
123 return e
124 }
125 // content := []byte(samplePodCtlConf)
126 // Extract the rpcuser
127 rpcUserRegexp, e := regexp.Compile(`(?m)^\s*rpcuser=([^\s]+)`)
128 if e != nil {
129 return e
130 }
131 userSubmatches := rpcUserRegexp.FindSubmatch(content)
132 if userSubmatches == nil {
133 // No user found, nothing to do
134 return nil
135 }
136 // Extract the rpcpass
137 rpcPassRegexp, e := regexp.Compile(`(?m)^\s*rpcpass=([^\s]+)`)
138 if e != nil {
139 return e
140 }
141 passSubmatches := rpcPassRegexp.FindSubmatch(content)
142 if passSubmatches == nil {
143 // No password found, nothing to do
144 return nil
145 }
146 // Extract the TLS
147 TLSRegexp, e := regexp.Compile(`(?m)^\s*tls=(0|1)(?:\s|$)`)
148 if e != nil {
149 return e
150 }
151 TLSSubmatches := TLSRegexp.FindSubmatch(content)
152 // Create the destination directory if it does not exists
153 e = os.MkdirAll(filepath.Dir(destinationPath), 0700)
154 if e != nil {
155 return e
156 }
157 // fmt.Println("config path", destinationPath)
158 // Create the destination file and write the rpcuser and rpcpass to it
159 dest, e := os.OpenFile(destinationPath,
160 os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
161 if e != nil {
162 return e
163 }
164 defer dest.Close()
165 destString := fmt.Sprintf("username=%s\npassword=%s\n",
166 string(userSubmatches[1]), string(passSubmatches[1]))
167 if TLSSubmatches != nil {
168 fmt.Println("TLS is enabled but more than likely the certificates will
169 fail verification because of the CA.
170 Currently there is no adequate tool for this, but will be soon.")
171 destString += fmt.Sprintf("clienttls=%s\n", TLSSubmatches[1])
172 }
173 output := ";;; Defaults created from local pod/sac configuration:\n" + destString + "\n" + string(sampleModConf)
174 dest.WriteString(output)
175 return nil
176 }
177 func copy(src, dst string) (int64, error) {
178 // fmt.Println(src, dst)
179 sourceFileStat, e := os.Stat(src)
180 if e != nil {
181 return 0, e
182 }
183 if !sourceFileStat.Mode().IsRegular() {
184 return 0, fmt.Errorf("%s is not a regular file", src)
185 }
186 source, e := os.Open(src)
187 if e != nil {
188 return 0, e
189 }
190 defer source.Close()
191 destination, e := os.Create(dst)
192 if e != nil {
193 return 0, e
194 }
195 defer destination.Close()
196 nBytes, e := io.Copy(destination, source)
197 return nBytes, e
198 }
199 // supportedSubsystems returns a sorted slice of the supported subsystems for
200 // logging purposes.
201 func supportedSubsystems() []string {
202 // Convert the subsystemLoggers map keys to a slice.
203 subsystems := make([]string, 0, len(subsystemLoggers))
204 for subsysID := range subsystemLoggers {
205 subsystems = append(subsystems, subsysID)
206 }
207 // Sort the subsytems for stable display.
208 txsort.Strings(subsystems)
209 return subsystems
210 }
211 // parseAndSetDebugLevels attempts to parse the specified debug level and set
212 // the levels accordingly. An appropriate error is returned if anything is
213 // invalid.
214 func parseAndSetDebugLevels(debugLevel string) (e error) {
215 // When the specified string doesn't have any delimters, treat it as
216 // the log level for all subsystems.
217 if !strings.Contains(debugLevel, ",") && !strings.Contains(debugLevel, "=") {
218 // Validate debug log level.
219 if !validLogLevel(debugLevel) {
220 str := "The specified debug level [%v] is invalid"
221 return fmt.Errorf(str, debugLevel)
222 }
223 // Change the logging level for all subsystems.
224 setLogLevels(debugLevel)
225 return nil
226 }
227 // Split the specified string into subsystem/level pairs while detecting
228 // issues and update the log levels accordingly.
229 for _, logLevelPair := range strings.Split(debugLevel, ",") {
230 if !strings.Contains(logLevelPair, "=") {
231 str := "The specified debug level contains an invalid " +
232 "subsystem/level pair [%v]"
233 return fmt.Errorf(str, logLevelPair)
234 }
235 // Extract the specified subsystem and log level.
236 fields := strings.Split(logLevelPair, "=")
237 subsysID, logLevel := fields[0], fields[1]
238 // Validate subsystem.
239 if _, exists := subsystemLoggers[subsysID]; !exists {
240 str := "The specified subsystem [%v] is invalid -- " +
241 "supported subsytems %v"
242 return fmt.Errorf(str, subsysID, supportedSubsystems())
243 }
244 // Validate log level.
245 if !validLogLevel(logLevel) {
246 str := "The specified debug level [%v] is invalid"
247 return fmt.Errorf(str, logLevel)
248 }
249 setLogLevel(subsysID, logLevel)
250 }
251 return nil
252 }
253 // loadConfig initializes and parses the config using a config file and command
254 // line options.
255 //
256 // The configuration proceeds as follows:
257 // 1) Start with a default config with sane settings
258 // 2) Pre-parse the command line to check for an alternative config file
259 // 3) Load configuration file overwriting defaults with any specified options
260 // 4) Parse CLI options and overwrite/add any specified options
261 //
262 // The above results in btcwallet functioning properly without any config
263 // settings while still allowing the user to override settings with config files
264 // and command line options. Command line options always take precedence.
265 func loadConfig( cfg *Config) (*Config, []string, error) {
266 cfg = Config{
267 ConfigFile: DefaultConfigFile,
268 AppDataDir: DefaultAppDataDir,
269 LogDir: DefaultLogDir,
270 WalletPass: wallet.InsecurePubPassphrase,
271 CAFile: "",
272 RPCKey: DefaultRPCKeyFile,
273 RPCCert: DefaultRPCCertFile,
274 WalletRPCMaxClients: DefaultRPCMaxClients,
275 WalletRPCMaxWebsockets: DefaultRPCMaxWebsockets,
276 DataDir: DefaultAppDataDir,
277 // AddPeers: []string{},
278 // ConnectPeers: []string{},
279 }
280 // Pre-parse the command line options to see if an alternative config
281 // file or the version flag was specified.
282 preCfg := cfg
283 preParser := flags.NewParser(&preCfg, flags.Default)
284 _, e := preParser.Parse()
285 if e != nil {
286 if e, ok := e.(*flags.Error); !ok || e.Type != flags.ErrHelp {
287 preParser.WriteHelp(os.Stderr)
288 }
289 return nil, nil, e
290 }
291 // Show the version and exit if the version flag was specified.
292 funcName := "loadConfig"
293 appName := filepath.Base(os.Args[0])
294 appName = strings.TrimSuffix(appName, filepath.Ext(appName))
295 usageMessage := fmt.Sprintf("Use %s -h to show usage", appName)
296 if preCfg.ShowVersion {
297 fmt.Println(appName, "version", version())
298 os.Exit(0)
299 }
300 // Load additional config from file.
301 var configFileError error
302 parser := flags.NewParser(&cfg, flags.Default)
303 configFilePath := preCfg.ConfigFile.value
304 if preCfg.ConfigFile.ExplicitlySet() {
305 configFilePath = cleanAndExpandPath(configFilePath)
306 } else {
307 appDataDir := preCfg.AppDataDir.value
308 if !preCfg.AppDataDir.ExplicitlySet() && preCfg.DataDir.ExplicitlySet() {
309 appDataDir = cleanAndExpandPath(preCfg.DataDir.value)
310 }
311 if appDataDir != DefaultAppDataDir {
312 configFilePath = filepath.Join(appDataDir, DefaultConfigFilename)
313 }
314 }
315 e = flags.NewIniParser(parser).ParseFile(configFilePath)
316 if e != nil {
317 if _, ok := e.(*os.PathError); !ok {
318 fmt.Fprintln(os.Stderr, e)
319 parser.WriteHelp(os.Stderr)
320 return nil, nil, e
321 }
322 configFileError = e
323 }
324 // Parse command line options again to ensure they take precedence.
325 remainingArgs, e := parser.Parse()
326 if e != nil {
327 if e, ok := e.(*flags.Error); !ok || e.Type != flags.ErrHelp {
328 parser.WriteHelp(os.Stderr)
329 }
330 return nil, nil, e
331 }
332 // Chk deprecated aliases. The new options receive priority when both
333 // are changed from the default.
334 if cfg.DataDir.ExplicitlySet() {
335 fmt.Fprintln(os.Stderr, "datadir opt has been replaced by "+
336 "appdata -- please update your config")
337 if !cfg.AppDataDir.ExplicitlySet() {
338 cfg.AppDataDir.value = cfg.DataDir.value
339 }
340 }
341 // If an alternate data directory was specified, and paths with defaults
342 // relative to the data dir are unchanged, modify each path to be
343 // relative to the new data dir.
344 if cfg.AppDataDir.ExplicitlySet() {
345 cfg.AppDataDir.value = cleanAndExpandPath(cfg.AppDataDir.value)
346 if !cfg.RPCKey.ExplicitlySet() {
347 cfg.RPCKey.value = filepath.Join(cfg.AppDataDir.value, "rpc.key")
348 }
349 if !cfg.RPCCert.ExplicitlySet() {
350 cfg.RPCCert.value = filepath.Join(cfg.AppDataDir.value, "rpc.cert")
351 }
352 }
353 if _, e = os.Stat(cfg.DataDir.value); os.IsNotExist(e) {
354 // Create the destination directory if it does not exists
355 e = os.MkdirAll(cfg.DataDir.value, 0700)
356 if e != nil {
357 fmt.Println("ERROR", e)
358 return nil, nil, e
359 }
360 }
361 var generatedRPCPass, generatedRPCUser string
362 if _, e = os.Stat(cfg.ConfigFile.value); os.IsNotExist(e) {
363 // If we can find a pod.conf in the standard location, copy
364 // copy the rpcuser and rpcpassword and TLS setting
365 c := cleanAndExpandPath("~/.pod/pod.conf")
366 // fmt.Println("server config path:", c)
367 // _, e = os.Stat(c)
368 // fmt.Println(e)
369 // fmt.Println(os.IsNotExist(err))
370 if _, e = os.Stat(c); e == nil {
371 fmt.Println("Creating config from pod config")
372 createDefaultConfigFile(cfg.ConfigFile.value, c, cleanAndExpandPath("~/.pod"),
373 cfg.AppDataDir.value)
374 } else {
375 var bb bytes.Buffer
376 bb.Write(sampleModConf)
377 fmt.Println("Writing config file:", cfg.ConfigFile.value)
378 dest, e := os.OpenFile(cfg.ConfigFile.value,
379 os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
380 if e != nil {
381 fmt.Println("ERROR", e)
382 return nil, nil, e
383 }
384 defer dest.Close()
385 // We generate a random user and password
386 randomBytes := make([]byte, 20)
387 _, e = rand.Read(randomBytes)
388 if e != nil {
389 return nil, nil, e
390 }
391 generatedRPCUser = base64.StdEncoding.EncodeToString(randomBytes)
392 _, e = rand.Read(randomBytes)
393 if e != nil {
394 return nil, nil, e
395 }
396 generatedRPCPass = base64.StdEncoding.EncodeToString(randomBytes)
397 // We copy every line from the sample config file to the destination,
398 // only replacing the two lines for rpcuser and rpcpass
399 //
400 var line string
401 reader := bufio.NewReader(&bb)
402 for e != io.EOF {
403 line, e = reader.ReadString('\n')
404 if e != nil && e != io.EOF {
405 return nil, nil, e
406 }
407 if !strings.Contains(line, "podusername=") && !strings.Contains(line, "podpassword=") {
408 if strings.Contains(line, "username=") {
409 line = "username=" + generatedRPCUser + "\n"
410 } else if strings.Contains(line, "password=") {
411 line = "password=" + generatedRPCPass + "\n"
412 }
413 }
414 _, _ = generatedRPCPass, generatedRPCUser
415 if _, e = dest.WriteString(line); E.Chk(e) {
416 return nil, nil, e
417 }
418 }
419 }
420 }
421 // Choose the active network netparams based on the selected network.
422 // Multiple networks can't be selected simultaneously.
423 numNets := 0
424 if cfg.TestNet3 {
425 activeNet = &chaincfg.TestNet3Params
426 numNets++
427 }
428 if cfg.SimNet {
429 activeNet = &chaincfg.SimNetParams
430 numNets++
431 }
432 if numNets > 1 {
433 str := "%s: The testnet and simnet netparams can't be used " +
434 "together -- choose one"
435 e := fmt.Errorf(str, "loadConfig")
436 fmt.Fprintln(os.Stderr, e)
437 parser.WriteHelp(os.Stderr)
438 return nil, nil, e
439 }
440 // Append the network type to the log directory so it is "namespaced"
441 // per network.
442 cfg.LogDir = cleanAndExpandPath(cfg.LogDir)
443 cfg.LogDir = filepath.Join(cfg.LogDir, activeNet.Params.Name)
444 // Special show command to list supported subsystems and exit.
445 if cfg.DebugLevel == "show" {
446 fmt.Println("Supported subsystems", supportedSubsystems())
447 os.Exit(0)
448 }
449 // Initialize log rotation. After log rotation has been initialized, the
450 // logger variables may be used.
451 initLogRotator(filepath.Join(cfg.LogDir, DefaultLogFilename))
452 // Parse, validate, and set debug log level(s).
453 if e := parseAndSetDebugLevels(cfg.DebugLevel); E.Chk(e) {
454 e := fmt.Errorf("%s: %v", "loadConfig", e.Error())
455 fmt.Fprintln(os.Stderr, e)
456 parser.WriteHelp(os.Stderr)
457 return nil, nil, e
458 }
459 // Exit if you try to use a simulation wallet with a standard
460 // data directory.
461 if !(cfg.AppDataDir.ExplicitlySet() || cfg.DataDir.ExplicitlySet()) && cfg.CreateTemp {
462 fmt.Fprintln(os.Stderr, "Tried to create a temporary simulation "+
463 "wallet, but failed to specify data directory!")
464 os.Exit(0)
465 }
466 // Exit if you try to use a simulation wallet on anything other than
467 // simnet or testnet3.
468 if !cfg.SimNet && cfg.CreateTemp {
469 fmt.Fprintln(os.Stderr, "Tried to create a temporary simulation "+
470 "wallet for network other than simnet!")
471 os.Exit(0)
472 }
473 // Ensure the wallet exists or create it when the create flag is set.
474 netDir := NetworkDir(cfg.AppDataDir, ActiveNet.Params)
475 dbPath := filepath.Join(netDir, WalletDbName)
476 if cfg.CreateTemp && cfg.Create {
477 e := fmt.Errorf("The flags --create and --createtemp can not " +
478 "be specified together. Use --help for more information.")
479 fmt.Fprintln(os.Stderr, e)
480 return nil, nil, e
481 }
482 dbFileExists, e := cfgutil.FileExists(dbPath)
483 if e != nil {
484 log <- cl.Error{err}
485 return nil, nil, e
486 }
487 if cfg.CreateTemp {
488 tempWalletExists := false
489 if dbFileExists {
490 str := fmt.Sprintf("The wallet already exists. Loading this " +
491 "wallet instead.")
492 fmt.Fprintln(os.Out, str)
493 tempWalletExists = true
494 }
495 // Ensure the data directory for the network exists.
496 if e := checkCreateDir(netDir); E.Chk(e) {
497 fmt.Fprintln(os.Stderr, e)
498 return nil, nil, e
499 }
500 if !tempWalletExists {
501 // Perform the initial wallet creation wizard.
502 if e := createSimulationWallet(&cfg); E.Chk(e) {
503 fmt.Fprintln(os.Stderr, "Unable to create wallet:", e)
504 return nil, nil, e
505 }
506 }
507 } else if cfg.Create {
508 // Error if the create flag is set and the wallet already
509 // exists.
510 if dbFileExists {
511 e := fmt.Errorf("The wallet database file `%v` "+
512 "already exists.", dbPath)
513 fmt.Fprintln(os.Stderr, e)
514 return nil, nil, e
515 }
516 // Ensure the data directory for the network exists.
517 if e := checkCreateDir(netDir); E.Chk(e) {
518 fmt.Fprintln(os.Stderr, e)
519 return nil, nil, e
520 }
521 // Perform the initial wallet creation wizard.
522 if e := createWallet(&cfg); E.Chk(e) {
523 fmt.Fprintln(os.Stderr, "Unable to create wallet:", e)
524 return nil, nil, e
525 }
526 // Created successfully, so exit now with success.
527 os.Exit(0)
528 } else if !dbFileExists && !cfg.NoInitialLoad {
529 keystorePath := filepath.Join(netDir, keystore.Filename)
530 keystoreExists, e := cfgutil.FileExists(keystorePath)
531 if e != nil {
532 fmt.Fprintln(os.Stderr, e)
533 return nil, nil, e
534 }
535 if !keystoreExists {
536 // e = fmt.Errorf("The wallet does not exist. Run with the " +
537 // "--create opt to initialize and create it...")
538 // Ensure the data directory for the network exists.
539 fmt.Println("Existing wallet not found in", cfg.ConfigFile.value)
540 if e := checkCreateDir(netDir); E.Chk(e) {
541 fmt.Fprintln(os.Stderr, e)
542 return nil, nil, e
543 }
544 // Perform the initial wallet creation wizard.
545 if e := createWallet(&cfg); E.Chk(e) {
546 fmt.Fprintln(os.Stderr, "Unable to create wallet:", e)
547 return nil, nil, e
548 }
549 // Created successfully, so exit now with success.
550 os.Exit(0)
551 } else {
552 e = fmt.Errorf("The wallet is in legacy format. Run with the " +
553 "--create opt to import it.")
554 }
555 fmt.Fprintln(os.Stderr, e)
556 return nil, nil, e
557 }
558 // localhostListeners := map[string]struct{}{
559 // "localhost": {},
560 // "127.0.0.1": {},
561 // "::1": {},
562 // }
563 // if cfg.UseSPV {
564 // sac.MaxPeers = cfg.MaxPeers
565 // sac.BanDuration = cfg.BanDuration
566 // sac.BanThreshold = cfg.BanThreshold
567 // } else {
568 if cfg.RPCConnect == "" {
569 cfg.RPCConnect = net.JoinHostPort("localhost", activeNet.RPCClientPort)
570 }
571 // Add default port to connect flag if missing.
572 cfg.RPCConnect, e = cfgutil.NormalizeAddress(cfg.RPCConnect,
573 activeNet.RPCClientPort)
574 if e != nil {
575 fmt.Fprintf(os.Stderr,
576 "Invalid rpcconnect network address: %v\n", e)
577 return nil, nil, e
578 }
579 // RPCHost, _, e = net.SplitHostPort(cfg.RPCConnect)
580 // if e != nil {
581 // return nil, nil, e
582 // }
583 if cfg.EnableClientTLS {
584 // if _, ok := localhostListeners[RPCHost]; !ok {
585 // str := "%s: the --noclienttls opt may not be used " +
586 // "when connecting RPC to non localhost " +
587 // "addresses: %s"
588 // e := fmt.Errorf(str, funcName, cfg.RPCConnect)
589 // fmt.Fprintln(os.Stderr, e)
590 // fmt.Fprintln(os.Stderr, usageMessage)
591 // return nil, nil, e
592 // }
593 // } else {
594 // If CAFile is unset, choose either the copy or local pod cert.
595 if !cfg.CAFile.ExplicitlySet() {
596 cfg.CAFile.value = filepath.Join(cfg.AppDataDir.value, DefaultCAFilename)
597 // If the CA copy does not exist, check if we're connecting to
598 // a local pod and switch to its RPC cert if it exists.
599 certExists, e := cfgutil.FileExists(cfg.CAFile.value)
600 if e != nil {
601 fmt.Fprintln(os.Stderr, e)
602 return nil, nil, e
603 }
604 if !certExists {
605 // if _, ok := localhostListeners[RPCHost]; ok {
606 podCertExists, e := cfgutil.FileExists(
607 DefaultCAFile)
608 if e != nil {
609 fmt.Fprintln(os.Stderr, e)
610 return nil, nil, e
611 }
612 if podCertExists {
613 cfg.CAFile.value = DefaultCAFile
614 }
615 // }
616 }
617 }
618 }
619 // }
620 // Only set default RPC listeners when there are no listeners set for
621 // the experimental RPC server. This is required to prevent the old RPC
622 // server from sharing listen addresses, since it is impossible to
623 // remove defaults from go-flags slice options without assigning
624 // specific behavior to a particular string.
625 if len(cfg.ExperimentalRPCListeners) == 0 && len(cfg.WalletRPCListeners) == 0 {
626 addrs, e := net.LookupHost("localhost")
627 if e != nil {
628 return nil, nil, e
629 }
630 cfg.WalletRPCListeners = make([]string, 0, len(addrs))
631 for _, addr := range addrs {
632 addr = net.JoinHostPort(addr, activeNet.WalletRPCServerPort)
633 cfg.WalletRPCListeners = append(cfg.WalletRPCListeners, addr)
634 }
635 }
636 // Add default port to all rpc listener addresses if needed and remove
637 // duplicate addresses.
638 cfg.WalletRPCListeners, e = cfgutil.NormalizeAddresses(
639 cfg.WalletRPCListeners, activeNet.WalletRPCServerPort)
640 if e != nil {
641 fmt.Fprintf(os.Stderr,
642 "Invalid network address in legacy RPC listeners: %v\n", e)
643 return nil, nil, e
644 }
645 cfg.ExperimentalRPCListeners, e = cfgutil.NormalizeAddresses(
646 cfg.ExperimentalRPCListeners, activeNet.WalletRPCServerPort)
647 if e != nil {
648 fmt.Fprintf(os.Stderr,
649 "Invalid network address in RPC listeners: %v\n", e)
650 return nil, nil, e
651 }
652 // Both RPC servers may not listen on the same interface/port.
653 if len(cfg.WalletRPCListeners) > 0 && len(cfg.ExperimentalRPCListeners) > 0 {
654 seenAddresses := make(map[string]struct{}, len(cfg.WalletRPCListeners))
655 for _, addr := range cfg.WalletRPCListeners {
656 seenAddresses[addr] = struct{}{}
657 }
658 for _, addr := range cfg.ExperimentalRPCListeners {
659 _, seen := seenAddresses[addr]
660 if seen {
661 e := fmt.Errorf("Address `%s` may not be "+
662 "used as a listener address for both "+
663 "RPC servers", addr)
664 fmt.Fprintln(os.Stderr, e)
665 return nil, nil, e
666 }
667 }
668 }
669 // Only allow server TLS to be disabled if the RPC server is bound to
670 // localhost addresses.
671 if !cfg.EnableServerTLS {
672 allListeners := append(cfg.WalletRPCListeners,
673 cfg.ExperimentalRPCListeners...)
674 for _, addr := range allListeners {
675 if e != nil {
676 str := "%s: RPC listen interface '%s' is " +
677 "invalid: %v"
678 e := fmt.Errorf(str, funcName, addr, e)
679 fmt.Fprintln(os.Stderr, e)
680 fmt.Fprintln(os.Stderr, usageMessage)
681 return nil, nil, e
682 }
683 // host, _, e = net.SplitHostPort(addr)
684 // if _, ok := localhostListeners[host]; !ok {
685 // str := "%s: the --noservertls opt may not be used " +
686 // "when binding RPC to non localhost " +
687 // "addresses: %s"
688 // e := fmt.Errorf(str, funcName, addr)
689 // fmt.Fprintln(os.Stderr, e)
690 // fmt.Fprintln(os.Stderr, usageMessage)
691 // return nil, nil, e
692 // }
693 }
694 }
695 // Expand environment variable and leading ~ for filepaths.
696 cfg.CAFile.value = cleanAndExpandPath(cfg.CAFile.value)
697 cfg.RPCCert.value = cleanAndExpandPath(cfg.RPCCert.value)
698 cfg.RPCKey.value = cleanAndExpandPath(cfg.RPCKey.value)
699 // If the pod username or password are unset, use the same auth as for
700 // the client. The two settings were previously shared for pod and
701 // client auth, so this avoids breaking backwards compatibility while
702 // allowing users to use different auth settings for pod and wallet.
703 if cfg.PodUsername == "" {
704 cfg.PodUsername = cfg.Username
705 }
706 if cfg.PodPassword == "" {
707 cfg.PodPassword = cfg.Password
708 }
709 // Warn about missing config file after the final command line parse
710 // succeeds. This prevents the warning on help messages and invalid
711 // options.
712 if configFileError != nil {
713 Log.Warnf.Print("%v", configFileError)
714 }
715 return cfg, nil, nil
716 }
717 // validLogLevel returns whether or not logLevel is a valid debug log level.
718 func validLogLevel( logLevel string) bool {
719 switch logLevel {
720 case "trace":
721 fallthrough
722 case "debug":
723 fallthrough
724 case "info":
725 fallthrough
726 case "warn":
727 fallthrough
728 case "error":
729 fallthrough
730 case "critical":
731 return true
732 }
733 return false
734 }
735 */
736