2015-12-01 19:44:58 +01:00
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
2013-08-22 18:30:38 +02:00
2013-08-21 16:37:30 +02:00
package main
import (
"fmt"
2013-11-19 18:21:54 +01:00
"net"
2013-08-21 16:37:30 +02:00
"os"
2016-04-14 00:51:01 +02:00
"os/user"
2013-08-21 16:37:30 +02:00
"path/filepath"
2016-04-14 00:51:01 +02:00
"runtime"
2014-06-20 01:11:47 +02:00
"sort"
2013-08-21 16:37:30 +02:00
"strings"
2014-07-03 13:45:40 +02:00
2015-01-15 17:48:58 +01:00
"github.com/btcsuite/btcutil"
2015-11-25 06:21:40 +01:00
"github.com/btcsuite/btcwallet/internal/cfgutil"
2015-05-27 16:09:22 +02:00
"github.com/btcsuite/btcwallet/internal/legacy/keystore"
2015-11-25 06:21:40 +01:00
"github.com/btcsuite/btcwallet/netparams"
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
"github.com/btcsuite/btcwallet/wallet"
2015-01-16 08:00:14 +01:00
flags "github.com/btcsuite/go-flags"
2013-08-21 16:37:30 +02:00
)
const (
2015-04-02 20:13:38 +02:00
defaultCAFilename = "btcd.cert"
defaultConfigFilename = "btcwallet.conf"
defaultLogLevel = "info"
defaultLogDirname = "logs"
defaultLogFilename = "btcwallet.log"
defaultRPCMaxClients = 10
defaultRPCMaxWebsockets = 25
walletDbName = "wallet.db"
2013-08-21 16:37:30 +02:00
)
var (
2016-04-13 23:07:58 +02:00
btcdDefaultCAFile = filepath . Join ( btcutil . AppDataDir ( "btcd" , false ) , "rpc.cert" )
defaultAppDataDir = btcutil . AppDataDir ( "btcwallet" , false )
defaultConfigFile = filepath . Join ( defaultAppDataDir , defaultConfigFilename )
defaultRPCKeyFile = filepath . Join ( defaultAppDataDir , "rpc.key" )
defaultRPCCertFile = filepath . Join ( defaultAppDataDir , "rpc.cert" )
defaultLogDir = filepath . Join ( defaultAppDataDir , defaultLogDirname )
2013-08-21 16:37:30 +02:00
)
type config struct {
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
// General application behavior
ConfigFile string ` short:"C" long:"configfile" description:"Path to configuration file" `
ShowVersion bool ` short:"V" long:"version" description:"Display version information and exit" `
Create bool ` long:"create" description:"Create the wallet if it does not exist" `
CreateTemp bool ` long:"createtemp" description:"Create a temporary simulation wallet (pass=password) in the data directory indicated; must call with --datadir" `
2016-04-13 23:07:58 +02:00
AppDataDir string ` short:"A" long:"appdata" description:"Application data directory to save wallet database and logs" `
2016-03-11 05:21:06 +01:00
TestNet3 bool ` long:"testnet" description:"Use the test Bitcoin network (version 3) (default mainnet)" `
SimNet bool ` long:"simnet" description:"Use the simulation test network (default mainnet)" `
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
NoInitialLoad bool ` long:"noinitialload" description:"Defer wallet creation/opening on startup and enable loading wallets over RPC" `
DebugLevel string ` short:"d" long:"debuglevel" description:"Logging level { trace, debug, info, warn, error, critical}" `
LogDir string ` long:"logdir" description:"Directory to log output." `
Profile string ` long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536" `
// Wallet options
2016-02-28 05:30:56 +01:00
WalletPass string ` long:"walletpass" default-mask:"-" description:"The public wallet password -- Only required if the wallet was created with one" `
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
// RPC client options
2016-03-11 05:21:06 +01:00
RPCConnect string ` short:"c" long:"rpcconnect" description:"Hostname/IP and port of btcd RPC server to connect to (default localhost:8334, testnet: localhost:18334, simnet: localhost:18556)" `
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
CAFile string ` long:"cafile" description:"File containing root certificates to authenticate a TLS connections with btcd" `
DisableClientTLS bool ` long:"noclienttls" description:"Disable TLS for the RPC client -- NOTE: This is only allowed if the RPC client is connecting to localhost" `
BtcdUsername string ` long:"btcdusername" description:"Username for btcd authentication" `
BtcdPassword string ` long:"btcdpassword" default-mask:"-" description:"Password for btcd authentication" `
Proxy string ` long:"proxy" description:"Connect via SOCKS5 proxy (eg. 127.0.0.1:9050)" `
ProxyUser string ` long:"proxyuser" description:"Username for proxy server" `
ProxyPass string ` long:"proxypass" default-mask:"-" description:"Password for proxy server" `
// RPC server options
//
// The legacy server is still enabled by default (and eventually will be
// replaced with the experimental server) so prepare for that change by
// renaming the struct fields (but not the configuration options).
//
// Usernames can also be used for the consensus RPC client, so they
// aren't considered legacy.
RPCCert string ` long:"rpccert" description:"File containing the certificate file" `
RPCKey string ` long:"rpckey" description:"File containing the certificate key" `
2016-02-11 06:00:32 +01:00
OneTimeTLSKey bool ` long:"onetimetlskey" description:"Generate a new TLS certpair at startup, but only write the certificate to disk" `
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
DisableServerTLS bool ` long:"noservertls" description:"Disable TLS for the RPC server -- NOTE: This is only allowed if the RPC server is bound to localhost" `
2016-03-11 05:21:06 +01:00
LegacyRPCListeners [ ] string ` long:"rpclisten" description:"Listen for legacy RPC connections on this interface/port (default port: 8332, testnet: 18332, simnet: 18554)" `
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
LegacyRPCMaxClients int64 ` long:"rpcmaxclients" description:"Max number of legacy RPC clients for standard connections" `
LegacyRPCMaxWebsockets int64 ` long:"rpcmaxwebsockets" description:"Max number of legacy RPC websocket connections" `
Username string ` short:"u" long:"username" description:"Username for legacy RPC and btcd authentication (if btcdusername is unset)" `
Password string ` short:"P" long:"password" default-mask:"-" description:"Password for legacy RPC and btcd authentication (if btcdpassword is unset)" `
// EXPERIMENTAL RPC server options
//
// These options will change (and require changes to config files, etc.)
// when the new gRPC server is enabled.
ExperimentalRPCListeners [ ] string ` long:"experimentalrpclisten" description:"Listen for RPC connections on this interface/port" `
2016-04-13 23:07:58 +02:00
// Deprecated options
DataDir string ` short:"D" long:"datadir" default-mask:"-" description:"DEPRECATED -- use appdata instead" `
2013-08-21 16:37:30 +02:00
}
2013-11-21 16:02:27 +01:00
// cleanAndExpandPath expands environement variables and leading ~ in the
// passed path, cleans the result, and returns it.
func cleanAndExpandPath ( path string ) string {
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
// NOTE: The os.ExpandEnv doesn't work with Windows cmd.exe-style
// %VARIABLE%, but they variables can still be expanded via POSIX-style
// $VARIABLE.
2016-04-14 00:51:01 +02:00
path = os . ExpandEnv ( path )
if ! strings . HasPrefix ( path , "~" ) {
return filepath . Clean ( path )
}
// 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.
path = path [ 1 : ]
var pathSeparators string
if runtime . GOOS == "windows" {
pathSeparators = string ( os . PathSeparator ) + "/"
} else {
pathSeparators = string ( os . PathSeparator )
}
userName := ""
if i := strings . IndexAny ( path , pathSeparators ) ; i != - 1 {
userName = path [ : i ]
path = path [ i : ]
}
homeDir := ""
var u * user . User
var err error
if userName == "" {
u , err = user . Current ( )
} else {
u , err = user . Lookup ( userName )
}
if err == nil {
homeDir = u . HomeDir
}
// Fallback to CWD if user lookup fails or user has no home directory.
if homeDir == "" {
homeDir = "."
}
return filepath . Join ( homeDir , path )
2013-11-21 16:02:27 +01:00
}
2014-06-20 01:11:47 +02:00
// validLogLevel returns whether or not logLevel is a valid debug log level.
func validLogLevel ( logLevel string ) bool {
switch logLevel {
case "trace" :
fallthrough
case "debug" :
fallthrough
case "info" :
fallthrough
case "warn" :
fallthrough
case "error" :
fallthrough
case "critical" :
return true
}
return false
}
// supportedSubsystems returns a sorted slice of the supported subsystems for
// logging purposes.
func supportedSubsystems ( ) [ ] string {
// Convert the subsystemLoggers map keys to a slice.
subsystems := make ( [ ] string , 0 , len ( subsystemLoggers ) )
for subsysID := range subsystemLoggers {
subsystems = append ( subsystems , subsysID )
}
// Sort the subsytems for stable display.
sort . Strings ( subsystems )
return subsystems
}
// parseAndSetDebugLevels attempts to parse the specified debug level and set
// the levels accordingly. An appropriate error is returned if anything is
// invalid.
func parseAndSetDebugLevels ( debugLevel string ) error {
// When the specified string doesn't have any delimters, treat it as
// the log level for all subsystems.
if ! strings . Contains ( debugLevel , "," ) && ! strings . Contains ( debugLevel , "=" ) {
// Validate debug log level.
if ! validLogLevel ( debugLevel ) {
str := "The specified debug level [%v] is invalid"
return fmt . Errorf ( str , debugLevel )
}
// Change the logging level for all subsystems.
setLogLevels ( debugLevel )
return nil
}
// Split the specified string into subsystem/level pairs while detecting
// issues and update the log levels accordingly.
for _ , logLevelPair := range strings . Split ( debugLevel , "," ) {
if ! strings . Contains ( logLevelPair , "=" ) {
str := "The specified debug level contains an invalid " +
"subsystem/level pair [%v]"
return fmt . Errorf ( str , logLevelPair )
}
// Extract the specified subsystem and log level.
fields := strings . Split ( logLevelPair , "=" )
subsysID , logLevel := fields [ 0 ] , fields [ 1 ]
// Validate subsystem.
if _ , exists := subsystemLoggers [ subsysID ] ; ! exists {
str := "The specified subsystem [%v] is invalid -- " +
"supported subsytems %v"
return fmt . Errorf ( str , subsysID , supportedSubsystems ( ) )
}
// Validate log level.
if ! validLogLevel ( logLevel ) {
str := "The specified debug level [%v] is invalid"
return fmt . Errorf ( str , logLevel )
}
setLogLevel ( subsysID , logLevel )
}
return nil
}
2013-08-21 16:37:30 +02:00
// loadConfig initializes and parses the config using a config file and command
// line options.
//
// The configuration proceeds as follows:
// 1) Start with a default config with sane settings
// 2) Pre-parse the command line to check for an alternative config file
// 3) Load configuration file overwriting defaults with any specified options
// 4) Parse CLI options and overwrite/add any specified options
//
// The above results in btcwallet functioning properly without any config
// settings while still allowing the user to override settings with config files
// and command line options. Command line options always take precedence.
func loadConfig ( ) ( * config , [ ] string , error ) {
// Default config.
cfg := config {
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
DebugLevel : defaultLogLevel ,
ConfigFile : defaultConfigFile ,
2016-04-13 23:07:58 +02:00
AppDataDir : defaultAppDataDir ,
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
LogDir : defaultLogDir ,
WalletPass : wallet . InsecurePubPassphrase ,
RPCKey : defaultRPCKeyFile ,
RPCCert : defaultRPCCertFile ,
LegacyRPCMaxClients : defaultRPCMaxClients ,
LegacyRPCMaxWebsockets : defaultRPCMaxWebsockets ,
2016-04-13 23:07:58 +02:00
DataDir : defaultAppDataDir ,
2013-08-21 16:37:30 +02:00
}
// Pre-parse the command line options to see if an alternative config
// file or the version flag was specified.
preCfg := cfg
preParser := flags . NewParser ( & preCfg , flags . Default )
2016-04-18 17:10:16 +02:00
_ , err := preParser . Parse ( )
2013-08-21 16:37:30 +02:00
if err != nil {
if e , ok := err . ( * flags . Error ) ; ! ok || e . Type != flags . ErrHelp {
preParser . WriteHelp ( os . Stderr )
}
return nil , nil , err
}
// Show the version and exit if the version flag was specified.
2015-01-09 08:41:42 +01:00
funcName := "loadConfig"
appName := filepath . Base ( os . Args [ 0 ] )
appName = strings . TrimSuffix ( appName , filepath . Ext ( appName ) )
usageMessage := fmt . Sprintf ( "Use %s -h to show usage" , appName )
2013-08-21 16:37:30 +02:00
if preCfg . ShowVersion {
fmt . Println ( appName , "version" , version ( ) )
os . Exit ( 0 )
}
// Load additional config from file.
2013-11-25 18:20:37 +01:00
var configFileError error
2013-08-21 16:37:30 +02:00
parser := flags . NewParser ( & cfg , flags . Default )
2013-11-25 18:20:37 +01:00
err = flags . NewIniParser ( parser ) . ParseFile ( preCfg . ConfigFile )
2013-08-21 16:37:30 +02:00
if err != nil {
if _ , ok := err . ( * os . PathError ) ; ! ok {
fmt . Fprintln ( os . Stderr , err )
parser . WriteHelp ( os . Stderr )
return nil , nil , err
}
2013-11-25 18:20:37 +01:00
configFileError = err
2013-08-21 16:37:30 +02:00
}
// Parse command line options again to ensure they take precedence.
remainingArgs , err := parser . Parse ( )
if err != nil {
if e , ok := err . ( * flags . Error ) ; ! ok || e . Type != flags . ErrHelp {
parser . WriteHelp ( os . Stderr )
}
return nil , nil , err
}
2013-11-25 18:20:37 +01:00
// Warn about missing config file after the final command line parse
// succeeds. This prevents the warning on help messages and invalid
// options.
if configFileError != nil {
log . Warnf ( "%v" , configFileError )
}
2016-04-13 23:07:58 +02:00
// Check deprecated aliases. The new options receive priority when both
// are changed from the default.
if cfg . DataDir != defaultAppDataDir {
fmt . Fprintln ( os . Stderr , "datadir option has been replaced by " +
"appdata -- please update your config" )
if cfg . AppDataDir == defaultAppDataDir {
cfg . AppDataDir = cfg . DataDir
}
}
2014-05-28 19:55:37 +02:00
// If an alternate data directory was specified, and paths with defaults
// relative to the data dir are unchanged, modify each path to be
// relative to the new data dir.
2016-04-13 23:07:58 +02:00
if cfg . AppDataDir != defaultAppDataDir {
2014-05-28 19:55:37 +02:00
if cfg . RPCKey == defaultRPCKeyFile {
2016-04-13 23:07:58 +02:00
cfg . RPCKey = filepath . Join ( cfg . AppDataDir , "rpc.key" )
2014-05-28 19:55:37 +02:00
}
if cfg . RPCCert == defaultRPCCertFile {
2016-04-13 23:07:58 +02:00
cfg . RPCCert = filepath . Join ( cfg . AppDataDir , "rpc.cert" )
2014-05-28 19:55:37 +02:00
}
}
2014-07-30 19:17:19 +02:00
// Choose the active network params based on the selected network.
2014-05-29 23:15:32 +02:00
// Multiple networks can't be selected simultaneously.
numNets := 0
2016-03-11 05:21:06 +01:00
if cfg . TestNet3 {
activeNet = & netparams . TestNet3Params
2014-05-29 23:15:32 +02:00
numNets ++
}
if cfg . SimNet {
2015-11-25 06:21:40 +01:00
activeNet = & netparams . SimNetParams
2014-05-29 23:15:32 +02:00
numNets ++
}
if numNets > 1 {
2016-03-11 05:21:06 +01:00
str := "%s: The testnet and simnet params can't be used " +
2014-06-02 18:15:27 +02:00
"together -- choose one"
2014-05-29 23:15:32 +02:00
err := fmt . Errorf ( str , "loadConfig" )
fmt . Fprintln ( os . Stderr , err )
parser . WriteHelp ( os . Stderr )
return nil , nil , err
}
2014-07-30 19:17:19 +02:00
// Append the network type to the log directory so it is "namespaced"
// per network.
cfg . LogDir = cleanAndExpandPath ( cfg . LogDir )
cfg . LogDir = filepath . Join ( cfg . LogDir , activeNet . Params . Name )
2013-10-16 23:29:48 +02:00
2014-06-20 01:11:47 +02:00
// Special show command to list supported subsystems and exit.
if cfg . DebugLevel == "show" {
fmt . Println ( "Supported subsystems" , supportedSubsystems ( ) )
os . Exit ( 0 )
}
// Initialize logging at the default logging level.
initSeelogLogger ( filepath . Join ( cfg . LogDir , defaultLogFilename ) )
setLogLevels ( defaultLogLevel )
// Parse, validate, and set debug log level(s).
if err := parseAndSetDebugLevels ( cfg . DebugLevel ) ; err != nil {
err := fmt . Errorf ( "%s: %v" , "loadConfig" , err . Error ( ) )
2013-10-29 15:38:51 +01:00
fmt . Fprintln ( os . Stderr , err )
parser . WriteHelp ( os . Stderr )
return nil , nil , err
}
2014-10-29 07:43:29 +01:00
2015-03-04 18:48:34 +01:00
// Exit if you try to use a simulation wallet with a standard
// data directory.
2016-04-13 23:07:58 +02:00
if cfg . AppDataDir == defaultAppDataDir && cfg . CreateTemp {
2015-03-04 18:48:34 +01:00
fmt . Fprintln ( os . Stderr , "Tried to create a temporary simulation " +
"wallet, but failed to specify data directory!" )
os . Exit ( 0 )
}
// Exit if you try to use a simulation wallet on anything other than
// simnet or testnet3.
if ! cfg . SimNet && cfg . CreateTemp {
fmt . Fprintln ( os . Stderr , "Tried to create a temporary simulation " +
"wallet for network other than simnet!" )
os . Exit ( 0 )
}
2014-10-29 07:43:29 +01:00
// Ensure the wallet exists or create it when the create flag is set.
2016-04-13 23:07:58 +02:00
netDir := networkDir ( cfg . AppDataDir , activeNet . Params )
2014-10-29 07:43:29 +01:00
dbPath := filepath . Join ( netDir , walletDbName )
2015-03-04 18:48:34 +01:00
if cfg . CreateTemp && cfg . Create {
err := fmt . Errorf ( "The flags --create and --createtemp can not " +
"be specified together. Use --help for more information." )
fmt . Fprintln ( os . Stderr , err )
return nil , nil , err
}
2015-11-25 06:21:40 +01:00
dbFileExists , err := cfgutil . FileExists ( dbPath )
if err != nil {
fmt . Fprintln ( os . Stderr , err )
return nil , nil , err
}
2015-03-04 18:48:34 +01:00
if cfg . CreateTemp {
tempWalletExists := false
2015-11-25 06:21:40 +01:00
if dbFileExists {
2015-03-04 18:48:34 +01:00
str := fmt . Sprintf ( "The wallet already exists. Loading this " +
"wallet instead." )
fmt . Fprintln ( os . Stdout , str )
tempWalletExists = true
}
// Ensure the data directory for the network exists.
if err := checkCreateDir ( netDir ) ; err != nil {
fmt . Fprintln ( os . Stderr , err )
return nil , nil , err
}
if ! tempWalletExists {
// Perform the initial wallet creation wizard.
if err := createSimulationWallet ( & cfg ) ; err != nil {
fmt . Fprintln ( os . Stderr , "Unable to create wallet:" , err )
return nil , nil , err
}
}
} else if cfg . Create {
2014-10-29 07:43:29 +01:00
// Error if the create flag is set and the wallet already
// exists.
2015-11-25 06:21:40 +01:00
if dbFileExists {
2016-02-11 16:22:28 +01:00
err := fmt . Errorf ( "The wallet database file `%v` " +
"already exists." , dbPath )
2014-10-29 07:43:29 +01:00
fmt . Fprintln ( os . Stderr , err )
return nil , nil , err
}
// Ensure the data directory for the network exists.
if err := checkCreateDir ( netDir ) ; err != nil {
fmt . Fprintln ( os . Stderr , err )
return nil , nil , err
}
// Perform the initial wallet creation wizard.
if err := createWallet ( & cfg ) ; err != nil {
fmt . Fprintln ( os . Stderr , "Unable to create wallet:" , err )
return nil , nil , err
}
// Created successfully, so exit now with success.
os . Exit ( 0 )
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
} else if ! dbFileExists && ! cfg . NoInitialLoad {
2014-10-29 07:43:29 +01:00
keystorePath := filepath . Join ( netDir , keystore . Filename )
2015-11-25 06:21:40 +01:00
keystoreExists , err := cfgutil . FileExists ( keystorePath )
if err != nil {
fmt . Fprintln ( os . Stderr , err )
return nil , nil , err
}
if ! keystoreExists {
2014-10-29 07:43:29 +01:00
err = fmt . Errorf ( "The wallet does not exist. Run with the " +
"--create option to initialize and create it." )
} else {
err = fmt . Errorf ( "The wallet is in legacy format. Run with the " +
"--create option to import it." )
}
fmt . Fprintln ( os . Stderr , err )
return nil , nil , err
}
2013-10-29 15:38:51 +01:00
2014-05-06 20:25:56 +02:00
if cfg . RPCConnect == "" {
2015-11-25 06:21:40 +01:00
cfg . RPCConnect = net . JoinHostPort ( "localhost" , activeNet . RPCClientPort )
2013-12-09 22:46:38 +01:00
}
2013-11-19 18:21:54 +01:00
// Add default port to connect flag if missing.
2015-11-25 06:21:40 +01:00
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
}
2013-11-19 18:21:54 +01:00
2015-01-09 08:41:42 +01:00
localhostListeners := map [ string ] struct { } {
"localhost" : struct { } { } ,
"127.0.0.1" : struct { } { } ,
"::1" : struct { } { } ,
}
2015-01-09 10:20:59 +01:00
RPCHost , _ , err := net . SplitHostPort ( cfg . RPCConnect )
if err != nil {
return nil , nil , err
}
if cfg . DisableClientTLS {
if _ , ok := localhostListeners [ RPCHost ] ; ! ok {
str := "%s: the --noclienttls option may not be used " +
"when connecting RPC to non localhost " +
"addresses: %s"
err := fmt . Errorf ( str , funcName , cfg . RPCConnect )
fmt . Fprintln ( os . Stderr , err )
fmt . Fprintln ( os . Stderr , usageMessage )
return nil , nil , err
}
} else {
// If CAFile is unset, choose either the copy or local btcd cert.
if cfg . CAFile == "" {
2016-04-13 23:07:58 +02:00
cfg . CAFile = filepath . Join ( cfg . AppDataDir , defaultCAFilename )
2015-01-09 10:20:59 +01:00
// 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.
2015-11-25 06:21:40 +01:00
certExists , err := cfgutil . FileExists ( cfg . CAFile )
if err != nil {
fmt . Fprintln ( os . Stderr , err )
return nil , nil , err
}
if ! certExists {
2015-01-09 10:20:59 +01:00
if _ , ok := localhostListeners [ RPCHost ] ; ok {
2015-11-25 06:21:40 +01:00
btcdCertExists , err := cfgutil . FileExists (
2016-04-13 23:07:58 +02:00
btcdDefaultCAFile )
2015-11-25 06:21:40 +01:00
if err != nil {
fmt . Fprintln ( os . Stderr , err )
return nil , nil , err
}
if btcdCertExists {
2016-04-13 23:07:58 +02:00
cfg . CAFile = btcdDefaultCAFile
2015-01-09 10:20:59 +01:00
}
2014-01-10 17:34:06 +01:00
}
}
}
}
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
// Only set default RPC listeners when there are no listeners set for
// the experimental RPC server. This is required to prevent the old RPC
// server from sharing listen addresses, since it is impossible to
// remove defaults from go-flags slice options without assigning
// specific behavior to a particular string.
if len ( cfg . ExperimentalRPCListeners ) == 0 && len ( cfg . LegacyRPCListeners ) == 0 {
2013-12-05 23:20:52 +01:00
addrs , err := net . LookupHost ( "localhost" )
if err != nil {
return nil , nil , err
}
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
cfg . LegacyRPCListeners = make ( [ ] string , 0 , len ( addrs ) )
2013-12-05 23:20:52 +01:00
for _ , addr := range addrs {
2015-11-25 06:21:40 +01:00
addr = net . JoinHostPort ( addr , activeNet . RPCServerPort )
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
cfg . LegacyRPCListeners = append ( cfg . LegacyRPCListeners , addr )
2013-12-05 23:20:52 +01:00
}
}
// Add default port to all rpc listener addresses if needed and remove
// duplicate addresses.
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
cfg . LegacyRPCListeners , err = cfgutil . NormalizeAddresses (
cfg . LegacyRPCListeners , activeNet . RPCServerPort )
if err != nil {
fmt . Fprintf ( os . Stderr ,
"Invalid network address in legacy RPC listeners: %v\n" , err )
return nil , nil , err
}
cfg . ExperimentalRPCListeners , err = cfgutil . NormalizeAddresses (
cfg . ExperimentalRPCListeners , activeNet . RPCServerPort )
2015-11-25 06:21:40 +01:00
if err != nil {
fmt . Fprintf ( os . Stderr ,
"Invalid network address in RPC listeners: %v\n" , err )
return nil , nil , err
}
2013-12-05 23:20:52 +01:00
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
// Both RPC servers may not listen on the same interface/port.
if len ( cfg . LegacyRPCListeners ) > 0 && len ( cfg . ExperimentalRPCListeners ) > 0 {
seenAddresses := make ( map [ string ] struct { } , len ( cfg . LegacyRPCListeners ) )
for _ , addr := range cfg . LegacyRPCListeners {
seenAddresses [ addr ] = struct { } { }
}
for _ , addr := range cfg . ExperimentalRPCListeners {
_ , seen := seenAddresses [ addr ]
if seen {
err := fmt . Errorf ( "Address `%s` may not be " +
"used as a listener address for both " +
"RPC servers" , addr )
fmt . Fprintln ( os . Stderr , err )
return nil , nil , err
}
}
}
// Only allow server TLS to be disabled if the RPC server is bound to
// localhost addresses.
2015-01-09 08:41:42 +01:00
if cfg . DisableServerTLS {
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
allListeners := append ( cfg . LegacyRPCListeners ,
cfg . ExperimentalRPCListeners ... )
for _ , addr := range allListeners {
2015-01-09 08:41:42 +01:00
host , _ , err := net . SplitHostPort ( addr )
if err != nil {
str := "%s: RPC listen interface '%s' is " +
"invalid: %v"
err := fmt . Errorf ( str , funcName , addr , err )
fmt . Fprintln ( os . Stderr , err )
fmt . Fprintln ( os . Stderr , usageMessage )
return nil , nil , err
}
if _ , ok := localhostListeners [ host ] ; ! ok {
str := "%s: the --noservertls option may not be used " +
"when binding RPC to non localhost " +
"addresses: %s"
err := fmt . Errorf ( str , funcName , addr )
fmt . Fprintln ( os . Stderr , err )
fmt . Fprintln ( os . Stderr , usageMessage )
return nil , nil , err
}
}
}
2013-11-21 16:02:27 +01:00
// Expand environment variable and leading ~ for filepaths.
cfg . CAFile = cleanAndExpandPath ( cfg . CAFile )
2014-05-16 19:58:33 +02:00
// If the btcd username or password are unset, use the same auth as for
// the client. The two settings were previously shared for btcd and
// client auth, so this avoids breaking backwards compatibility while
// allowing users to use different auth settings for btcd and wallet.
if cfg . BtcdUsername == "" {
cfg . BtcdUsername = cfg . Username
}
if cfg . BtcdPassword == "" {
cfg . BtcdPassword = cfg . Password
}
2013-08-21 16:37:30 +02:00
return & cfg , remainingArgs , nil
}