_config.go_ raw

   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