diff --git a/config.go b/config.go index e822cfe..753cdc9 100644 --- a/config.go +++ b/config.go @@ -25,7 +25,9 @@ import ( "strings" "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcwallet/internal/cfgutil" "github.com/btcsuite/btcwallet/internal/legacy/keystore" + "github.com/btcsuite/btcwallet/netparams" flags "github.com/btcsuite/go-flags" ) @@ -194,50 +196,6 @@ func parseAndSetDebugLevels(debugLevel string) error { return nil } -// removeDuplicateAddresses returns a new slice with all duplicate entries in -// addrs removed. -func removeDuplicateAddresses(addrs []string) []string { - result := []string{} - seen := map[string]bool{} - for _, val := range addrs { - if _, ok := seen[val]; !ok { - result = append(result, val) - seen[val] = true - } - } - return result -} - -// normalizeAddresses returns a new slice with all the passed peer addresses -// normalized with the given default port, and all duplicates removed. -func normalizeAddresses(addrs []string, defaultPort string) []string { - for i, addr := range addrs { - addrs[i] = normalizeAddress(addr, defaultPort) - } - - return removeDuplicateAddresses(addrs) -} - -// filesExists reports whether the named file or directory exists. -func fileExists(name string) bool { - if _, err := os.Stat(name); err != nil { - if os.IsNotExist(err) { - return false - } - } - return true -} - -// normalizeAddress returns addr with the passed default port appended if -// there is not already a port specified. -func normalizeAddress(addr, defaultPort string) string { - _, _, err := net.SplitHostPort(addr) - if err != nil { - return net.JoinHostPort(addr, defaultPort) - } - return addr -} - // loadConfig initializes and parses the config using a config file and command // line options. // @@ -266,7 +224,12 @@ func loadConfig() (*config, []string, error) { } // A config file in the current directory takes precedence. - if fileExists(defaultConfigFilename) { + exists, err := cfgutil.FileExists(defaultConfigFilename) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return nil, nil, err + } + if exists { cfg.ConfigFile = defaultConfigFile } @@ -274,7 +237,7 @@ func loadConfig() (*config, []string, error) { // file or the version flag was specified. preCfg := cfg preParser := flags.NewParser(&preCfg, flags.Default) - _, err := preParser.Parse() + _, err = preParser.Parse() if err != nil { if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { preParser.WriteHelp(os.Stderr) @@ -337,11 +300,11 @@ func loadConfig() (*config, []string, error) { // Multiple networks can't be selected simultaneously. numNets := 0 if cfg.MainNet { - activeNet = &mainNetParams + activeNet = &netparams.MainNetParams numNets++ } if cfg.SimNet { - activeNet = &simNetParams + activeNet = &netparams.SimNetParams numNets++ } if numNets > 1 { @@ -403,10 +366,16 @@ func loadConfig() (*config, []string, error) { return nil, nil, err } + dbFileExists, err := cfgutil.FileExists(dbPath) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return nil, nil, err + } + if cfg.CreateTemp { tempWalletExists := false - if fileExists(dbPath) { + if dbFileExists { str := fmt.Sprintf("The wallet already exists. Loading this " + "wallet instead.") fmt.Fprintln(os.Stdout, str) @@ -429,7 +398,7 @@ func loadConfig() (*config, []string, error) { } else if cfg.Create { // Error if the create flag is set and the wallet already // exists. - if fileExists(dbPath) { + if dbFileExists { err := fmt.Errorf("The wallet already exists.") fmt.Fprintln(os.Stderr, err) return nil, nil, err @@ -449,10 +418,14 @@ func loadConfig() (*config, []string, error) { // Created successfully, so exit now with success. os.Exit(0) - } else if !fileExists(dbPath) { - var err error + } else if !dbFileExists { keystorePath := filepath.Join(netDir, keystore.Filename) - if !fileExists(keystorePath) { + keystoreExists, err := cfgutil.FileExists(keystorePath) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return nil, nil, err + } + if !keystoreExists { err = fmt.Errorf("The wallet does not exist. Run with the " + "--create option to initialize and create it.") } else { @@ -464,11 +437,17 @@ func loadConfig() (*config, []string, error) { } if cfg.RPCConnect == "" { - cfg.RPCConnect = activeNet.connect + cfg.RPCConnect = net.JoinHostPort("localhost", activeNet.RPCClientPort) } // Add default port to connect flag if missing. - cfg.RPCConnect = normalizeAddress(cfg.RPCConnect, activeNet.btcdPort) + cfg.RPCConnect, err = cfgutil.NormalizeAddress(cfg.RPCConnect, + activeNet.RPCClientPort) + if err != nil { + fmt.Fprintf(os.Stderr, + "Invalid rpcconnect network address: %v\n", err) + return nil, nil, err + } localhostListeners := map[string]struct{}{ "localhost": struct{}{}, @@ -496,9 +475,20 @@ func loadConfig() (*config, []string, error) { // If the CA copy does not exist, check if we're connecting to // a local btcd and switch to its RPC cert if it exists. - if !fileExists(cfg.CAFile) { + certExists, err := cfgutil.FileExists(cfg.CAFile) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return nil, nil, err + } + if !certExists { if _, ok := localhostListeners[RPCHost]; ok { - if fileExists(btcdHomedirCAFile) { + btcdCertExists, err := cfgutil.FileExists( + btcdHomedirCAFile) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return nil, nil, err + } + if btcdCertExists { cfg.CAFile = btcdHomedirCAFile } } @@ -513,15 +503,20 @@ func loadConfig() (*config, []string, error) { } cfg.SvrListeners = make([]string, 0, len(addrs)) for _, addr := range addrs { - addr = net.JoinHostPort(addr, activeNet.svrPort) + addr = net.JoinHostPort(addr, activeNet.RPCServerPort) cfg.SvrListeners = append(cfg.SvrListeners, addr) } } // Add default port to all rpc listener addresses if needed and remove // duplicate addresses. - cfg.SvrListeners = normalizeAddresses(cfg.SvrListeners, - activeNet.svrPort) + cfg.SvrListeners, err = cfgutil.NormalizeAddresses( + cfg.SvrListeners, activeNet.RPCServerPort) + if err != nil { + fmt.Fprintf(os.Stderr, + "Invalid network address in RPC listeners: %v\n", err) + return nil, nil, err + } // Only allow server TLS to be disabled if the RPC is bound to localhost // addresses. diff --git a/internal/cfgutil/file.go b/internal/cfgutil/file.go new file mode 100644 index 0000000..860054f --- /dev/null +++ b/internal/cfgutil/file.go @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015 The btcsuite developers + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package cfgutil + +import "os" + +// FilesExists reports whether the named file or directory exists. +func FileExists(filePath string) (bool, error) { + _, err := os.Stat(filePath) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, err + } + return true, nil +} diff --git a/internal/cfgutil/normalization.go b/internal/cfgutil/normalization.go new file mode 100644 index 0000000..a411c71 --- /dev/null +++ b/internal/cfgutil/normalization.go @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015 The btcsuite developers + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package cfgutil + +import "net" + +// NormalizeAddress returns the normalized form of the address, adding a default +// port if necessary. An error is returned if the address, even without a port, +// is not valid. +func NormalizeAddress(addr string, defaultPort string) (hostport string, err error) { + // If the first SplitHostPort errors because of a missing port and not + // for an invalid host, add the port. If the second SplitHostPort + // fails, then a port is not missing and the original error should be + // returned. + host, port, origErr := net.SplitHostPort(addr) + if origErr == nil { + return net.JoinHostPort(host, port), nil + } + addr = net.JoinHostPort(addr, defaultPort) + _, _, err = net.SplitHostPort(addr) + if err != nil { + return "", origErr + } + return addr, nil +} + +// NormalizeAddresses returns a new slice with all the passed peer addresses +// normalized with the given default port, and all duplicates removed. +func NormalizeAddresses(addrs []string, defaultPort string) ([]string, error) { + var ( + normalized = make([]string, 0, len(addrs)) + seenSet = make(map[string]struct{}) + ) + + for _, addr := range addrs { + normalizedAddr, err := NormalizeAddress(addr, defaultPort) + if err != nil { + return nil, err + } + _, seen := seenSet[normalizedAddr] + if !seen { + normalized = append(normalized, normalizedAddr) + seenSet[normalizedAddr] = struct{}{} + } + } + + return normalized, nil +} diff --git a/netparams/params.go b/netparams/params.go new file mode 100644 index 0000000..11b46b7 --- /dev/null +++ b/netparams/params.go @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013-2015 The btcsuite developers + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package netparams + +import "github.com/btcsuite/btcd/chaincfg" + +// Params is used to group parameters for various networks such as the main +// network and test networks. +type Params struct { + *chaincfg.Params + RPCClientPort string + RPCServerPort string +} + +// MainNetParams contains parameters specific running btcwallet and +// btcd on the main network (wire.MainNet). +var MainNetParams = Params{ + Params: &chaincfg.MainNetParams, + RPCClientPort: "8334", + RPCServerPort: "8332", +} + +// TestNet3Params contains parameters specific running btcwallet and +// btcd on the test network (version 3) (wire.TestNet3). +var TestNet3Params = Params{ + Params: &chaincfg.TestNet3Params, + RPCClientPort: "18334", + RPCServerPort: "18332", +} + +// SimNetParams contains parameters specific to the simulation test network +// (wire.SimNet). +var SimNetParams = Params{ + Params: &chaincfg.SimNetParams, + RPCClientPort: "18556", + RPCServerPort: "18554", +} diff --git a/params.go b/params.go index 449f490..16b8cb6 100644 --- a/params.go +++ b/params.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014 The btcsuite developers + * Copyright (c) 2013-2015 The btcsuite developers * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,44 +16,6 @@ package main -import ( - "github.com/btcsuite/btcd/chaincfg" -) +import "github.com/btcsuite/btcwallet/netparams" -var activeNet = &testNet3Params - -// params is used to group parameters for various networks such as the main -// network and test networks. -type params struct { - *chaincfg.Params - connect string - btcdPort string - svrPort string -} - -// mainNetParams contains parameters specific running btcwallet and -// btcd on the main network (wire.MainNet). -var mainNetParams = params{ - Params: &chaincfg.MainNetParams, - connect: "localhost:8334", - btcdPort: "8334", - svrPort: "8332", -} - -// testNet3Params contains parameters specific running btcwallet and -// btcd on the test network (version 3) (wire.TestNet3). -var testNet3Params = params{ - Params: &chaincfg.TestNet3Params, - connect: "localhost:18334", - btcdPort: "18334", - svrPort: "18332", -} - -// simNetParams contains parameters specific to the simulation test network -// (wire.SimNet). -var simNetParams = params{ - Params: &chaincfg.SimNetParams, - connect: "localhost:18556", - btcdPort: "18556", - svrPort: "18554", -} +var activeNet = &netparams.TestNet3Params diff --git a/rpcserver.go b/rpcserver.go index c03abf6..3beae81 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -322,9 +322,11 @@ func newRPCServer(listenAddrs []string, maxPost, maxWebsockets int64) (*rpcServe // Setup TLS if not disabled. listenFunc := net.Listen if !cfg.DisableServerTLS { - // Check for existence of cert file and key file - if !fileExists(cfg.RPCKey) && !fileExists(cfg.RPCCert) { - // if both files do not exist, we generate them. + // Check for existence of cert file and key file. Generate a + // new keypair if both are missing. + _, e1 := os.Stat(cfg.RPCKey) + _, e2 := os.Stat(cfg.RPCCert) + if os.IsNotExist(e1) && os.IsNotExist(e2) { err := genCertPair(cfg.RPCCert, cfg.RPCKey) if err != nil { return nil, err diff --git a/walletsetup.go b/walletsetup.go index 6ad060c..5a74599 100644 --- a/walletsetup.go +++ b/walletsetup.go @@ -432,8 +432,13 @@ func createWallet(cfg *config) error { netDir := networkDir(cfg.DataDir, activeNet.Params) keystorePath := filepath.Join(netDir, keystore.Filename) var legacyKeyStore *keystore.Store - if fileExists(keystorePath) { - var err error + _, err := os.Stat(keystorePath) + if err != nil && !os.IsNotExist(err) { + // A stat error not due to a non-existant file should be + // returned to the caller. + return err + } else if err == nil { + // Keystore file exists. legacyKeyStore, err = keystore.OpenDir(netDir) if err != nil { return err