Separate out default ports and utility funcs.

This change moves the chain and network parameter definitions, along
with the default client and server ports, to a package for reuse by
other utilities (most notably, tools in the cmd dir).  Along with it,
functions commonly used for config parsing and validation are moved to
an internal package since they will also be useful for distributed
tools.
This commit is contained in:
Josh Rickmar 2015-11-25 00:21:40 -05:00
parent 5482feecf4
commit b0566e09c8
7 changed files with 214 additions and 106 deletions

115
config.go
View file

@ -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.

31
internal/cfgutil/file.go Normal file
View file

@ -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
}

View file

@ -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
}

51
netparams/params.go Normal file
View file

@ -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",
}

View file

@ -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

View file

@ -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

View file

@ -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