2015-05-01 08:28:01 +02:00
// Copyright (c) 2013-2014 The btcsuite developers
2013-08-06 23:55:22 +02:00
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
2013-12-17 16:10:59 +01:00
"errors"
2013-08-06 23:55:22 +02:00
"fmt"
"net"
"os"
"path/filepath"
2013-11-20 16:35:14 +01:00
"runtime"
2013-11-22 00:03:01 +01:00
"sort"
2013-09-17 23:40:27 +02:00
"strconv"
2013-08-08 01:53:01 +02:00
"strings"
2013-08-06 23:55:22 +02:00
"time"
2014-07-02 15:50:08 +02:00
2015-01-27 22:38:23 +01:00
"github.com/btcsuite/btcd/database"
_ "github.com/btcsuite/btcd/database/ldb"
_ "github.com/btcsuite/btcd/database/memdb"
2015-02-05 22:16:39 +01:00
"github.com/btcsuite/btcd/wire"
2015-01-15 17:30:38 +01:00
"github.com/btcsuite/btcutil"
2015-01-16 07:56:49 +01:00
flags "github.com/btcsuite/go-flags"
2015-01-16 08:21:57 +01:00
"github.com/btcsuite/go-socks/socks"
2013-08-06 23:55:22 +02:00
)
const (
2014-03-02 11:33:56 +01:00
defaultConfigFilename = "btcd.conf"
defaultDataDirname = "data"
defaultLogLevel = "info"
defaultLogDirname = "logs"
defaultLogFilename = "btcd.log"
defaultMaxPeers = 125
defaultBanDuration = time . Hour * 24
defaultMaxRPCClients = 10
defaultMaxRPCWebsockets = 25
defaultVerifyEnabled = false
defaultDbType = "leveldb"
defaultFreeTxRelayLimit = 15.0
defaultBlockMinSize = 0
defaultBlockMaxSize = 750000
blockMaxSizeMin = 1000
2015-02-05 22:16:39 +01:00
blockMaxSizeMax = wire . MaxBlockPayload - 1000
2014-03-02 11:33:56 +01:00
defaultBlockPrioritySize = 50000
2014-06-12 03:09:38 +02:00
defaultGenerate = false
2015-01-04 02:42:01 +01:00
defaultAddrIndex = false
2013-08-06 23:55:22 +02:00
)
var (
2013-11-19 18:37:22 +01:00
btcdHomeDir = btcutil . AppDataDir ( "btcd" , false )
defaultConfigFile = filepath . Join ( btcdHomeDir , defaultConfigFilename )
defaultDataDir = filepath . Join ( btcdHomeDir , defaultDataDirname )
2015-01-27 22:38:23 +01:00
knownDbTypes = database . SupportedDBs ( )
2013-11-19 18:37:22 +01:00
defaultRPCKeyFile = filepath . Join ( btcdHomeDir , "rpc.key" )
defaultRPCCertFile = filepath . Join ( btcdHomeDir , "rpc.cert" )
2014-02-12 22:43:27 +01:00
defaultLogDir = filepath . Join ( btcdHomeDir , defaultLogDirname )
2013-08-06 23:55:22 +02:00
)
2013-11-26 03:58:18 +01:00
// runServiceCommand is only set to a real function on Windows. It is used
// to parse and execute service commands specified via the -s flag.
var runServiceCommand func ( string ) error
2013-08-06 23:55:22 +02:00
// config defines the configuration options for btcd.
//
// See loadConfig for details on the configuration load process.
type config struct {
2013-12-25 00:01:32 +01:00
ShowVersion bool ` short:"V" long:"version" description:"Display version information and exit" `
ConfigFile string ` short:"C" long:"configfile" description:"Path to configuration file" `
DataDir string ` short:"b" long:"datadir" description:"Directory to store data" `
2014-02-12 22:43:27 +01:00
LogDir string ` long:"logdir" description:"Directory to log output." `
2013-12-25 00:01:32 +01:00
AddPeers [ ] string ` short:"a" long:"addpeer" description:"Add a peer to connect with at startup" `
ConnectPeers [ ] string ` long:"connect" description:"Connect only to the specified peers at startup" `
DisableListen bool ` long:"nolisten" description:"Disable listening for incoming connections -- NOTE: Listening is automatically disabled if the --connect or --proxy options are used without also specifying listen interfaces via --listen" `
Listeners [ ] string ` long:"listen" description:"Add an interface/port to listen for connections (default all interfaces port: 8333, testnet: 18333)" `
MaxPeers int ` long:"maxpeers" description:"Max number of inbound and outbound peers" `
BanDuration time . Duration ` long:"banduration" description:"How long to ban misbehaving peers. Valid time units are { s, m, h}. Minimum 1 second" `
RPCUser string ` short:"u" long:"rpcuser" description:"Username for RPC connections" `
RPCPass string ` short:"P" long:"rpcpass" default-mask:"-" description:"Password for RPC connections" `
2015-03-30 19:45:31 +02:00
RPCLimitUser string ` long:"rpclimituser" description:"Username for limited RPC connections" `
RPCLimitPass string ` long:"rpclimitpass" default-mask:"-" description:"Password for limited RPC connections" `
2013-12-25 00:01:32 +01:00
RPCListeners [ ] string ` long:"rpclisten" description:"Add an interface/port to listen for RPC connections (default port: 8334, testnet: 18334)" `
RPCCert string ` long:"rpccert" description:"File containing the certificate file" `
RPCKey string ` long:"rpckey" description:"File containing the certificate key" `
2014-02-19 03:44:37 +01:00
RPCMaxClients int ` long:"rpcmaxclients" description:"Max number of RPC clients for standard connections" `
2014-02-19 04:05:42 +01:00
RPCMaxWebsockets int ` long:"rpcmaxwebsockets" description:"Max number of RPC websocket connections" `
2015-03-30 19:45:31 +02:00
DisableRPC bool ` long:"norpc" description:"Disable built-in RPC server -- NOTE: The RPC server is disabled by default if no rpcuser/rpcpass or rpclimituser/rpclimitpass is specified" `
2014-12-21 01:04:07 +01:00
DisableTLS bool ` long:"notls" description:"Disable TLS for the RPC server -- NOTE: This is only allowed if the RPC server is bound to localhost" `
2013-12-25 00:01:32 +01:00
DisableDNSSeed bool ` long:"nodnsseed" description:"Disable DNS seeding for peers" `
ExternalIPs [ ] string ` long:"externalip" description:"Add an ip to the list of local addresses we claim to listen on to peers" `
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" `
2013-12-17 16:10:59 +01:00
OnionProxy string ` long:"onion" description:"Connect to tor hidden services via SOCKS5 proxy (eg. 127.0.0.1:9050)" `
OnionProxyUser string ` long:"onionuser" description:"Username for onion proxy server" `
OnionProxyPass string ` long:"onionpass" default-mask:"-" description:"Password for onion proxy server" `
NoOnion bool ` long:"noonion" description:"Disable connecting to tor hidden services" `
2015-05-13 22:34:33 +02:00
TorIsolation bool ` long:"torisolation" description:"Enable Tor stream isolation by randomizing user credentials for each connection." `
2013-12-25 00:01:32 +01:00
TestNet3 bool ` long:"testnet" description:"Use the test network" `
RegressionTest bool ` long:"regtest" description:"Use the regression test network" `
2014-05-28 22:19:38 +02:00
SimNet bool ` long:"simnet" description:"Use the simulation test network" `
2013-12-25 00:01:32 +01:00
DisableCheckpoints bool ` long:"nocheckpoints" description:"Disable built-in checkpoints. Don't do this unless you know what you're doing." `
DbType string ` long:"dbtype" description:"Database backend to use for the Block Chain" `
Profile string ` long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536" `
2014-07-02 16:25:42 +02:00
CPUProfile string ` long:"cpuprofile" description:"Write CPU profile to the specified file" `
2013-12-25 00:01:32 +01:00
DebugLevel string ` short:"d" long:"debuglevel" description:"Logging level for all subsystems { trace, debug, info, warn, error, critical} -- You may also specify <subsystem>=<level>,<subsystem2>=<level>,... to set the log level for individual subsystems -- Use show to list available subsystems" `
Upnp bool ` long:"upnp" description:"Use UPnP to map our listening port outside of NAT" `
2014-02-19 19:15:47 +01:00
FreeTxRelayLimit float64 ` long:"limitfreerelay" description:"Limit relay of transactions with no transaction fee to the given amount in thousands of bytes per minute" `
2015-02-23 16:28:31 +01:00
NoRelayPriority bool ` long:"norelaypriority" description:"Do not require free or low-fee transactions to have high priority for relaying" `
2015-05-05 16:53:15 +02:00
MaxOrphanTxs int ` long:"maxorphantx" description:"Max number of orphan transactions to keep in memory" `
2014-06-12 03:09:38 +02:00
Generate bool ` long:"generate" description:"Generate (mine) bitcoins using the CPU" `
MiningAddrs [ ] string ` long:"miningaddr" description:"Add the specified payment address to the list of addresses to use for generated blocks -- At least one address is required if the generate option is set" `
2014-03-02 11:33:56 +01:00
BlockMinSize uint32 ` long:"blockminsize" description:"Mininum block size in bytes to be used when creating a block" `
BlockMaxSize uint32 ` long:"blockmaxsize" description:"Maximum block size in bytes to be used when creating a block" `
BlockPrioritySize uint32 ` long:"blockprioritysize" description:"Size in bytes for high-priority/low-fee transactions when creating a block" `
2014-06-12 03:09:38 +02:00
GetWorkKeys [ ] string ` long:"getworkkey" description:"DEPRECATED -- Use the --miningaddr option instead" `
2015-01-04 02:42:01 +01:00
AddrIndex bool ` long:"addrindex" description:"Build and maintain a full address index. Currently only supported by leveldb." `
DropAddrIndex bool ` long:"dropaddrindex" description:"Deletes the address-based transaction index from the database on start up, and the exits." `
2015-08-24 17:48:59 +02:00
NoPeerBloomFilters bool ` long:"nopeerbloomfilters" description:"Disable bloom filtering support." `
2013-12-17 16:10:59 +01:00
onionlookup func ( string ) ( [ ] net . IP , error )
lookup func ( string ) ( [ ] net . IP , error )
oniondial func ( string , string ) ( net . Conn , error )
dial func ( string , string ) ( net . Conn , error )
2014-06-12 03:09:38 +02:00
miningAddrs [ ] btcutil . Address
2013-08-06 23:55:22 +02:00
}
2013-11-20 16:35:14 +01:00
// serviceOptions defines the configuration options for btcd as a service on
// Windows.
type serviceOptions struct {
ServiceCommand string ` short:"s" long:"service" description:"Service command { install, remove, start, stop}" `
}
2014-09-08 21:19:47 +02:00
// cleanAndExpandPath expands environment variables and leading ~ in the
2013-09-18 07:13:36 +02:00
// passed path, cleans the result, and returns it.
func cleanAndExpandPath ( path string ) string {
// Expand initial ~ to OS specific home directory.
if strings . HasPrefix ( path , "~" ) {
2013-11-11 00:12:45 +01:00
homeDir := filepath . Dir ( btcdHomeDir )
2013-09-18 07:13:36 +02:00
path = strings . Replace ( path , "~" , homeDir , 1 )
}
// NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
// but they variables can still be expanded via POSIX-style $VARIABLE.
return filepath . Clean ( os . ExpandEnv ( path ) )
}
2013-08-06 23:55:22 +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
}
2013-11-22 00:03:01 +01:00
// 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
}
2013-11-22 17:46:56 +01:00
// 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 {
2013-11-22 00:03:01 +01:00
// 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 )
}
2013-11-22 19:44:49 +01:00
// Change the logging level for all subsystems.
setLogLevels ( debugLevel )
2013-11-22 00:03:01 +01:00
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 {
2013-11-22 17:46:56 +01:00
str := "The specified subsystem [%v] is invalid -- " +
"supported subsytems %v"
return fmt . Errorf ( str , subsysID , supportedSubsystems ( ) )
2013-11-22 00:03:01 +01:00
}
// 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-09-15 19:08:42 +02:00
// validDbType returns whether or not dbType is a supported database type.
func validDbType ( dbType string ) bool {
for _ , knownType := range knownDbTypes {
if dbType == knownType {
return true
}
}
return false
}
2013-08-06 23:55:22 +02:00
// removeDuplicateAddresses returns a new slice with all duplicate entries in
// addrs removed.
func removeDuplicateAddresses ( addrs [ ] string ) [ ] string {
2014-07-02 17:14:36 +02:00
result := make ( [ ] string , 0 , len ( addrs ) )
2014-07-02 16:45:17 +02:00
seen := map [ string ] struct { } { }
2013-08-06 23:55:22 +02:00
for _ , val := range addrs {
if _ , ok := seen [ val ] ; ! ok {
result = append ( result , val )
2014-07-02 16:45:17 +02:00
seen [ val ] = struct { } { }
2013-08-06 23:55:22 +02:00
}
}
return result
}
2013-09-19 17:46:33 +02:00
// 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
}
// 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 {
2013-08-06 23:55:22 +02:00
for i , addr := range addrs {
2013-09-19 17:46:33 +02:00
addrs [ i ] = normalizeAddress ( addr , defaultPort )
2013-08-06 23:55:22 +02:00
}
2013-09-19 17:46:33 +02:00
return removeDuplicateAddresses ( addrs )
}
2013-08-06 23:55:22 +02:00
// 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
}
2013-11-20 16:35:14 +01:00
// newConfigParser returns a new command line flags parser.
func newConfigParser ( cfg * config , so * serviceOptions , options flags . Options ) * flags . Parser {
parser := flags . NewParser ( cfg , options )
if runtime . GOOS == "windows" {
parser . AddGroup ( "Service Options" , "Service Options" , so )
}
return parser
}
2013-08-06 23:55:22 +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 btcd 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 {
2014-03-02 11:33:56 +01:00
ConfigFile : defaultConfigFile ,
DebugLevel : defaultLogLevel ,
MaxPeers : defaultMaxPeers ,
BanDuration : defaultBanDuration ,
RPCMaxClients : defaultMaxRPCClients ,
RPCMaxWebsockets : defaultMaxRPCWebsockets ,
DataDir : defaultDataDir ,
LogDir : defaultLogDir ,
DbType : defaultDbType ,
RPCKey : defaultRPCKeyFile ,
RPCCert : defaultRPCCertFile ,
FreeTxRelayLimit : defaultFreeTxRelayLimit ,
BlockMinSize : defaultBlockMinSize ,
BlockMaxSize : defaultBlockMaxSize ,
BlockPrioritySize : defaultBlockPrioritySize ,
2015-05-05 16:53:15 +02:00
MaxOrphanTxs : maxOrphanTransactions ,
2014-06-12 03:09:38 +02:00
Generate : defaultGenerate ,
2015-01-04 02:42:01 +01:00
AddrIndex : defaultAddrIndex ,
2013-08-06 23:55:22 +02:00
}
2013-11-20 16:35:14 +01:00
// Service options which are only added on Windows.
serviceOpts := serviceOptions { }
2013-08-06 23:55:22 +02:00
// Pre-parse the command line options to see if an alternative config
2014-08-20 03:06:03 +02:00
// file or the version flag was specified. Any errors aside from the
// help message error can be ignored here since they will be caught by
// the final parse below.
2013-08-06 23:55:22 +02:00
preCfg := cfg
2014-08-20 03:06:03 +02:00
preParser := newConfigParser ( & preCfg , & serviceOpts , flags . HelpFlag )
2014-12-21 22:53:33 +01:00
_ , err := preParser . Parse ( )
2014-08-20 03:06:03 +02:00
if err != nil {
if e , ok := err . ( * flags . Error ) ; ok && e . Type == flags . ErrHelp {
fmt . Fprintln ( os . Stderr , err )
return nil , nil , err
}
}
2013-08-06 23:55:22 +02:00
2013-08-08 01:53:01 +02:00
// Show the version and exit if the version flag was specified.
2014-07-15 03:27:22 +02:00
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-08 01:53:01 +02:00
if preCfg . ShowVersion {
fmt . Println ( appName , "version" , version ( ) )
os . Exit ( 0 )
}
2013-11-20 16:35:14 +01:00
// Perform service command and exit if specified. Invalid service
// commands show an appropriate error. Only runs on Windows since
// the runServiceCommand function will be nil when not on Windows.
if serviceOpts . ServiceCommand != "" && runServiceCommand != nil {
err := runServiceCommand ( serviceOpts . ServiceCommand )
if err != nil {
fmt . Fprintln ( os . Stderr , err )
}
os . Exit ( 0 )
}
2013-08-06 23:55:22 +02:00
// Load additional config from file.
2013-11-24 19:33:36 +01:00
var configFileError error
2013-11-20 16:35:14 +01:00
parser := newConfigParser ( & cfg , & serviceOpts , flags . Default )
2014-05-28 22:19:38 +02:00
if ! ( preCfg . RegressionTest || preCfg . SimNet ) || preCfg . ConfigFile !=
defaultConfigFile {
2013-11-24 19:33:36 +01:00
err := flags . NewIniParser ( parser ) . ParseFile ( preCfg . ConfigFile )
2013-11-15 21:42:53 +01:00
if err != nil {
if _ , ok := err . ( * os . PathError ) ; ! ok {
2014-07-15 03:27:22 +02:00
fmt . Fprintf ( os . Stderr , "Error parsing config " +
"file: %v\n" , err )
fmt . Fprintln ( os . Stderr , usageMessage )
2013-11-15 21:42:53 +01:00
return nil , nil , err
}
2013-11-24 19:33:36 +01:00
configFileError = err
2013-08-06 23:55:22 +02:00
}
}
2013-08-17 21:44:43 +02:00
// Don't add peers from the config file when in regression test mode.
if preCfg . RegressionTest && len ( cfg . AddPeers ) > 0 {
cfg . AddPeers = nil
}
2013-08-06 23:55:22 +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 {
2014-07-15 03:27:22 +02:00
fmt . Fprintln ( os . Stderr , usageMessage )
2013-08-06 23:55:22 +02:00
}
return nil , nil , err
}
2014-12-21 22:53:33 +01:00
// Create the home directory if it doesn't already exist.
2014-06-07 21:42:54 +02:00
funcName := "loadConfig"
2014-12-21 22:53:33 +01:00
err = os . MkdirAll ( btcdHomeDir , 0700 )
if err != nil {
// Show a nicer error message if it's because a symlink is
// linked to a directory that does not exist (probably because
// it's not mounted).
if e , ok := err . ( * os . PathError ) ; ok && os . IsExist ( err ) {
if link , lerr := os . Readlink ( e . Path ) ; lerr == nil {
str := "is symlink %s -> %s mounted?"
err = fmt . Errorf ( str , e . Path , link )
}
}
str := "%s: Failed to create home directory: %v"
err := fmt . Errorf ( str , funcName , err )
fmt . Fprintln ( os . Stderr , err )
return nil , nil , err
}
// Multiple networks can't be selected simultaneously.
2014-05-28 22:19:38 +02:00
numNets := 0
2014-07-21 07:42:41 +02:00
// Count number of network flags passed; assign active network params
// while we're at it
2014-05-28 22:19:38 +02:00
if cfg . TestNet3 {
numNets ++
2014-07-21 07:42:41 +02:00
activeNetParams = & testNet3Params
2014-05-28 22:19:38 +02:00
}
if cfg . RegressionTest {
numNets ++
2014-07-21 07:42:41 +02:00
activeNetParams = & regressionNetParams
2014-05-28 22:19:38 +02:00
}
if cfg . SimNet {
numNets ++
2014-07-21 07:42:41 +02:00
// Also disable dns seeding on the simulation test network.
activeNetParams = & simNetParams
cfg . DisableDNSSeed = true
2014-05-28 22:19:38 +02:00
}
if numNets > 1 {
str := "%s: The testnet, regtest, and simnet params can't be " +
"used together -- choose one of the three"
2014-06-07 21:42:54 +02:00
err := fmt . Errorf ( str , funcName )
2013-08-06 23:55:22 +02:00
fmt . Fprintln ( os . Stderr , err )
2014-07-15 03:27:22 +02:00
fmt . Fprintln ( os . Stderr , usageMessage )
2013-08-06 23:55:22 +02:00
return nil , nil , err
}
2014-02-12 22:43:27 +01:00
// Append the network type to the data directory so it is "namespaced"
// per network. In addition to the block database, there are other
// pieces of data that are saved to disk such as address manager state.
// All data is specific to a network, so namespacing the data directory
// means each individual piece of serialized data does not have to
// worry about changing names per network and such.
cfg . DataDir = cleanAndExpandPath ( cfg . DataDir )
2014-05-23 06:14:36 +02:00
cfg . DataDir = filepath . Join ( cfg . DataDir , netName ( activeNetParams ) )
2014-02-12 22:43:27 +01:00
// Append the network type to the log directory so it is "namespaced"
// per network in the same fashion as the data directory.
cfg . LogDir = cleanAndExpandPath ( cfg . LogDir )
2014-05-23 06:14:36 +02:00
cfg . LogDir = filepath . Join ( cfg . LogDir , netName ( activeNetParams ) )
2014-02-12 22:43:27 +01:00
2013-11-22 17:46:56 +01:00
// Special show command to list supported subsystems and exit.
if cfg . DebugLevel == "show" {
fmt . Println ( "Supported subsystems" , supportedSubsystems ( ) )
os . Exit ( 0 )
}
2014-02-12 22:43:27 +01:00
// Initialize logging at the default logging level.
initSeelogLogger ( filepath . Join ( cfg . LogDir , defaultLogFilename ) )
setLogLevels ( defaultLogLevel )
2013-11-22 00:03:01 +01:00
// Parse, validate, and set debug log level(s).
2013-11-22 17:46:56 +01:00
if err := parseAndSetDebugLevels ( cfg . DebugLevel ) ; err != nil {
2014-06-07 21:42:54 +02:00
err := fmt . Errorf ( "%s: %v" , funcName , err . Error ( ) )
2013-08-06 23:55:22 +02:00
fmt . Fprintln ( os . Stderr , err )
2014-07-15 03:27:22 +02:00
fmt . Fprintln ( os . Stderr , usageMessage )
2013-08-06 23:55:22 +02:00
return nil , nil , err
}
2013-09-15 19:08:42 +02:00
// Validate database type.
if ! validDbType ( cfg . DbType ) {
str := "%s: The specified database type [%v] is invalid -- " +
"supported types %v"
2014-06-07 21:42:54 +02:00
err := fmt . Errorf ( str , funcName , cfg . DbType , knownDbTypes )
2013-09-15 19:08:42 +02:00
fmt . Fprintln ( os . Stderr , err )
2014-07-15 03:27:22 +02:00
fmt . Fprintln ( os . Stderr , usageMessage )
2013-09-15 19:08:42 +02:00
return nil , nil , err
}
2015-01-04 02:42:01 +01:00
if cfg . AddrIndex && cfg . DropAddrIndex {
err := fmt . Errorf ( "addrindex and dropaddrindex cannot be " +
"activated at the same" )
fmt . Fprintln ( os . Stderr , err )
fmt . Fprintln ( os . Stderr , usageMessage )
return nil , nil , err
}
// Memdb does not currently support the addrindex.
if cfg . DbType == "memdb" && cfg . AddrIndex {
err := fmt . Errorf ( "memdb does not currently support the addrindex" )
fmt . Fprintln ( os . Stderr , err )
fmt . Fprintln ( os . Stderr , usageMessage )
return nil , nil , err
}
2013-09-17 23:40:27 +02:00
// Validate profile port number
2013-09-17 23:46:03 +02:00
if cfg . Profile != "" {
2013-09-18 00:04:40 +02:00
profilePort , err := strconv . Atoi ( cfg . Profile )
if err != nil || profilePort < 1024 || profilePort > 65535 {
str := "%s: The profile port must be between 1024 and 65535"
2014-06-07 21:42:54 +02:00
err := fmt . Errorf ( str , funcName )
2013-09-18 00:04:40 +02:00
fmt . Fprintln ( os . Stderr , err )
2014-07-15 03:27:22 +02:00
fmt . Fprintln ( os . Stderr , usageMessage )
2013-09-18 00:04:40 +02:00
return nil , nil , err
}
2013-09-17 23:40:27 +02:00
}
2013-08-06 23:55:22 +02:00
// Don't allow ban durations that are too short.
if cfg . BanDuration < time . Duration ( time . Second ) {
str := "%s: The banduration option may not be less than 1s -- parsed [%v]"
2014-06-07 21:42:54 +02:00
err := fmt . Errorf ( str , funcName , cfg . BanDuration )
2013-08-06 23:55:22 +02:00
fmt . Fprintln ( os . Stderr , err )
2014-07-15 03:27:22 +02:00
fmt . Fprintln ( os . Stderr , usageMessage )
2013-08-06 23:55:22 +02:00
return nil , nil , err
}
// --addPeer and --connect do not mix.
if len ( cfg . AddPeers ) > 0 && len ( cfg . ConnectPeers ) > 0 {
str := "%s: the --addpeer and --connect options can not be " +
"mixed"
2014-06-07 21:42:54 +02:00
err := fmt . Errorf ( str , funcName )
2013-08-06 23:55:22 +02:00
fmt . Fprintln ( os . Stderr , err )
2014-07-15 03:27:22 +02:00
fmt . Fprintln ( os . Stderr , usageMessage )
2013-08-06 23:55:22 +02:00
return nil , nil , err
}
2013-09-19 17:46:33 +02:00
// --proxy or --connect without --listen disables listening.
if ( cfg . Proxy != "" || len ( cfg . ConnectPeers ) > 0 ) &&
len ( cfg . Listeners ) == 0 {
2013-08-08 18:11:39 +02:00
cfg . DisableListen = true
}
2013-09-19 17:46:33 +02:00
// Connect means no DNS seeding.
2013-08-06 23:55:22 +02:00
if len ( cfg . ConnectPeers ) > 0 {
cfg . DisableDNSSeed = true
2013-09-19 17:46:33 +02:00
}
// Add the default listener if none were specified. The default
// listener is all addresses on the listen port for the network
// we are to connect to.
if len ( cfg . Listeners ) == 0 {
cfg . Listeners = [ ] string {
2014-05-29 19:35:09 +02:00
net . JoinHostPort ( "" , activeNetParams . DefaultPort ) ,
2013-09-19 17:46:33 +02:00
}
2013-08-06 23:55:22 +02:00
}
2015-03-30 19:45:31 +02:00
// Check to make sure limited and admin users don't have the same username
if cfg . RPCUser == cfg . RPCLimitUser && cfg . RPCUser != "" {
str := "%s: --rpcuser and --rpclimituser must not specify the " +
"same username"
err := fmt . Errorf ( str , funcName )
fmt . Fprintln ( os . Stderr , err )
fmt . Fprintln ( os . Stderr , usageMessage )
return nil , nil , err
}
// Check to make sure limited and admin users don't have the same password
if cfg . RPCPass == cfg . RPCLimitPass && cfg . RPCPass != "" {
str := "%s: --rpcpass and --rpclimitpass must not specify the " +
"same password"
err := fmt . Errorf ( str , funcName )
fmt . Fprintln ( os . Stderr , err )
fmt . Fprintln ( os . Stderr , usageMessage )
return nil , nil , err
}
2013-08-07 18:02:31 +02:00
// The RPC server is disabled if no username or password is provided.
2015-03-30 19:45:31 +02:00
if ( cfg . RPCUser == "" || cfg . RPCPass == "" ) &&
( cfg . RPCLimitUser == "" || cfg . RPCLimitPass == "" ) {
2013-09-18 07:36:40 +02:00
cfg . DisableRPC = true
2013-08-07 18:02:31 +02:00
}
2013-11-20 00:33:07 +01:00
// Default RPC to listen on localhost only.
if ! cfg . DisableRPC && len ( cfg . RPCListeners ) == 0 {
addrs , err := net . LookupHost ( "localhost" )
if err != nil {
return nil , nil , err
2013-11-14 02:51:37 +01:00
}
2013-11-20 00:33:07 +01:00
cfg . RPCListeners = make ( [ ] string , 0 , len ( addrs ) )
for _ , addr := range addrs {
addr = net . JoinHostPort ( addr , activeNetParams . rpcPort )
cfg . RPCListeners = append ( cfg . RPCListeners , addr )
}
2013-11-14 02:51:37 +01:00
}
2014-03-02 11:33:56 +01:00
// Limit the max block size to a sane value.
if cfg . BlockMaxSize < blockMaxSizeMin || cfg . BlockMaxSize >
blockMaxSizeMax {
str := "%s: The blockmaxsize option must be in between %d " +
"and %d -- parsed [%d]"
2014-06-07 21:42:54 +02:00
err := fmt . Errorf ( str , funcName , blockMaxSizeMin ,
2014-03-02 11:33:56 +01:00
blockMaxSizeMax , cfg . BlockMaxSize )
fmt . Fprintln ( os . Stderr , err )
2014-07-15 03:27:22 +02:00
fmt . Fprintln ( os . Stderr , usageMessage )
2015-05-05 16:53:15 +02:00
return nil , nil , err
}
// Limit the max orphan count to a sane vlue.
if cfg . MaxOrphanTxs < 0 {
str := "%s: The maxorphantx option may not be less than 0 " +
"-- parsed [%d]"
err := fmt . Errorf ( str , funcName , cfg . MaxOrphanTxs )
fmt . Fprintln ( os . Stderr , err )
fmt . Fprintln ( os . Stderr , usageMessage )
2014-03-02 11:33:56 +01:00
return nil , nil , err
}
// Limit the block priority and minimum block sizes to max block size.
cfg . BlockPrioritySize = minUint32 ( cfg . BlockPrioritySize , cfg . BlockMaxSize )
cfg . BlockMinSize = minUint32 ( cfg . BlockMinSize , cfg . BlockMaxSize )
2014-06-12 03:09:38 +02:00
// Check getwork keys are valid and saved parsed versions.
cfg . miningAddrs = make ( [ ] btcutil . Address , 0 , len ( cfg . GetWorkKeys ) +
len ( cfg . MiningAddrs ) )
2014-03-20 08:06:10 +01:00
for _ , strAddr := range cfg . GetWorkKeys {
2014-06-12 03:09:38 +02:00
addr , err := btcutil . DecodeAddress ( strAddr ,
activeNetParams . Params )
if err != nil {
str := "%s: getworkkey '%s' failed to decode: %v"
err := fmt . Errorf ( str , funcName , strAddr , err )
fmt . Fprintln ( os . Stderr , err )
2014-07-15 03:27:22 +02:00
fmt . Fprintln ( os . Stderr , usageMessage )
2014-06-12 03:09:38 +02:00
return nil , nil , err
}
if ! addr . IsForNet ( activeNetParams . Params ) {
str := "%s: getworkkey '%s' is on the wrong network"
err := fmt . Errorf ( str , funcName , strAddr )
fmt . Fprintln ( os . Stderr , err )
2014-07-15 03:27:22 +02:00
fmt . Fprintln ( os . Stderr , usageMessage )
2014-06-12 03:09:38 +02:00
return nil , nil , err
}
cfg . miningAddrs = append ( cfg . miningAddrs , addr )
}
// Check mining addresses are valid and saved parsed versions.
for _ , strAddr := range cfg . MiningAddrs {
2014-05-28 00:44:55 +02:00
addr , err := btcutil . DecodeAddress ( strAddr , activeNetParams . Params )
2014-03-20 08:06:10 +01:00
if err != nil {
2014-06-12 03:09:38 +02:00
str := "%s: mining address '%s' failed to decode: %v"
2014-06-07 21:42:54 +02:00
err := fmt . Errorf ( str , funcName , strAddr , err )
2014-03-20 08:06:10 +01:00
fmt . Fprintln ( os . Stderr , err )
2014-07-15 03:27:22 +02:00
fmt . Fprintln ( os . Stderr , usageMessage )
2014-03-20 08:06:10 +01:00
return nil , nil , err
}
2014-05-28 00:44:55 +02:00
if ! addr . IsForNet ( activeNetParams . Params ) {
2014-06-12 03:09:38 +02:00
str := "%s: mining address '%s' is on the wrong network"
2014-06-07 21:42:54 +02:00
err := fmt . Errorf ( str , funcName , strAddr )
2014-03-20 08:06:10 +01:00
fmt . Fprintln ( os . Stderr , err )
2014-07-15 03:27:22 +02:00
fmt . Fprintln ( os . Stderr , usageMessage )
2014-03-20 08:06:10 +01:00
return nil , nil , err
}
2014-06-12 03:09:38 +02:00
cfg . miningAddrs = append ( cfg . miningAddrs , addr )
}
// Ensure there is at least one mining address when the generate flag is
// set.
if cfg . Generate && len ( cfg . MiningAddrs ) == 0 {
str := "%s: the generate flag is set, but there are no mining " +
"addresses specified "
err := fmt . Errorf ( str , funcName )
fmt . Fprintln ( os . Stderr , err )
2014-07-15 03:27:22 +02:00
fmt . Fprintln ( os . Stderr , usageMessage )
2014-06-12 03:09:38 +02:00
return nil , nil , err
2014-03-20 08:06:10 +01:00
}
2013-11-14 02:51:37 +01:00
// Add default port to all listener addresses if needed and remove
// duplicate addresses.
cfg . Listeners = normalizeAddresses ( cfg . Listeners ,
2014-05-29 19:35:09 +02:00
activeNetParams . DefaultPort )
2013-11-14 02:51:37 +01:00
// Add default port to all rpc listener addresses if needed and remove
2013-09-19 17:46:33 +02:00
// duplicate addresses.
2013-11-14 02:51:37 +01:00
cfg . RPCListeners = normalizeAddresses ( cfg . RPCListeners ,
activeNetParams . rpcPort )
2013-09-19 17:46:33 +02:00
2014-12-21 01:04:07 +01:00
// Only allow TLS to be disabled if the RPC is bound to localhost
// addresses.
if ! cfg . DisableRPC && cfg . DisableTLS {
allowedTLSListeners := map [ string ] struct { } {
"localhost" : struct { } { } ,
"127.0.0.1" : struct { } { } ,
"::1" : struct { } { } ,
}
for _ , addr := range cfg . RPCListeners {
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 := allowedTLSListeners [ host ] ; ! ok {
str := "%s: the --notls 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-08-06 23:55:22 +02:00
// Add default port to all added peer addresses if needed and remove
// duplicate addresses.
2013-11-14 02:51:37 +01:00
cfg . AddPeers = normalizeAddresses ( cfg . AddPeers ,
2014-05-29 19:35:09 +02:00
activeNetParams . DefaultPort )
2013-11-14 02:51:37 +01:00
cfg . ConnectPeers = normalizeAddresses ( cfg . ConnectPeers ,
2014-05-29 19:35:09 +02:00
activeNetParams . DefaultPort )
2013-08-06 23:55:22 +02:00
2015-05-13 22:34:33 +02:00
// Tor stream isolation requires either proxy or onion proxy to be set.
if cfg . TorIsolation && cfg . Proxy == "" && cfg . OnionProxy == "" {
str := "%s: Tor stream isolation requires either proxy or " +
"onionproxy to be set"
err := fmt . Errorf ( str , funcName )
fmt . Fprintln ( os . Stderr , err )
fmt . Fprintln ( os . Stderr , usageMessage )
return nil , nil , err
}
2014-01-10 08:52:15 +01:00
// Setup dial and DNS resolution (lookup) functions depending on the
// specified options. The default is to use the standard net.Dial
// function as well as the system DNS resolver. When a proxy is
// specified, the dial function is set to the proxy specific dial
// function and the lookup is set to use tor (unless --noonion is
// specified in which case the system DNS resolver is used).
2013-12-17 16:10:59 +01:00
cfg . dial = net . Dial
cfg . lookup = net . LookupIP
if cfg . Proxy != "" {
2015-05-13 22:34:33 +02:00
_ , _ , err := net . SplitHostPort ( cfg . Proxy )
if err != nil {
str := "%s: Proxy address '%s' is invalid: %v"
err := fmt . Errorf ( str , funcName , cfg . Proxy , err )
fmt . Fprintln ( os . Stderr , err )
fmt . Fprintln ( os . Stderr , usageMessage )
return nil , nil , err
}
if cfg . TorIsolation &&
( cfg . ProxyUser != "" || cfg . ProxyPass != "" ) {
btcdLog . Warn ( "Tor isolation set -- overriding " +
"specified proxy user credentials" )
}
2013-12-17 16:10:59 +01:00
proxy := & socks . Proxy {
2015-05-13 22:34:33 +02:00
Addr : cfg . Proxy ,
Username : cfg . ProxyUser ,
Password : cfg . ProxyPass ,
TorIsolation : cfg . TorIsolation ,
2013-12-17 16:10:59 +01:00
}
cfg . dial = proxy . Dial
if ! cfg . NoOnion {
cfg . lookup = func ( host string ) ( [ ] net . IP , error ) {
return torLookupIP ( host , cfg . Proxy )
}
}
}
2014-01-10 08:52:15 +01:00
// Setup onion address dial and DNS resolution (lookup) functions
// depending on the specified options. The default is to use the
// same dial and lookup functions selected above. However, when an
// onion-specific proxy is specified, the onion address dial and
// lookup functions are set to use the onion-specific proxy while
// leaving the normal dial and lookup functions as selected above.
// This allows .onion address traffic to be routed through a different
// proxy than normal traffic.
2013-12-17 16:10:59 +01:00
if cfg . OnionProxy != "" {
2015-05-13 22:34:33 +02:00
_ , _ , err := net . SplitHostPort ( cfg . OnionProxy )
if err != nil {
str := "%s: Onion proxy address '%s' is invalid: %v"
err := fmt . Errorf ( str , funcName , cfg . OnionProxy , err )
fmt . Fprintln ( os . Stderr , err )
fmt . Fprintln ( os . Stderr , usageMessage )
return nil , nil , err
}
if cfg . TorIsolation &&
( cfg . OnionProxyUser != "" || cfg . OnionProxyPass != "" ) {
btcdLog . Warn ( "Tor isolation set -- overriding " +
"specified onionproxy user credentials " )
}
2013-12-17 16:10:59 +01:00
cfg . oniondial = func ( a , b string ) ( net . Conn , error ) {
proxy := & socks . Proxy {
2015-05-13 22:34:33 +02:00
Addr : cfg . OnionProxy ,
Username : cfg . OnionProxyUser ,
Password : cfg . OnionProxyPass ,
TorIsolation : cfg . TorIsolation ,
2013-12-17 16:10:59 +01:00
}
return proxy . Dial ( a , b )
}
cfg . onionlookup = func ( host string ) ( [ ] net . IP , error ) {
return torLookupIP ( host , cfg . OnionProxy )
}
} else {
cfg . oniondial = cfg . dial
cfg . onionlookup = cfg . lookup
}
2014-01-10 08:52:15 +01:00
// Specifying --noonion means the onion address dial and DNS resolution
// (lookup) functions result in an error.
2013-12-17 16:10:59 +01:00
if cfg . NoOnion {
cfg . oniondial = func ( a , b string ) ( net . Conn , error ) {
return nil , errors . New ( "tor has been disabled" )
}
cfg . onionlookup = func ( a string ) ( [ ] net . IP , error ) {
return nil , errors . New ( "tor has been disabled" )
}
}
2014-01-10 08:34:50 +01:00
// Warn about missing config file only after all other configuration is
// done. This prevents the warning on help messages and invalid
// options. Note this should go directly before the return.
if configFileError != nil {
btcdLog . Warnf ( "%v" , configFileError )
}
2013-08-06 23:55:22 +02:00
return & cfg , remainingArgs , nil
}
2013-12-17 16:10:59 +01:00
2014-01-10 08:31:20 +01:00
// btcdDial connects to the address on the named network using the appropriate
// dial function depending on the address and configuration options. For
// example, .onion addresses will be dialed using the onion specific proxy if
// one was specified, but will otherwise use the normal dial function (which
// could itself use a proxy or not).
func btcdDial ( network , address string ) ( net . Conn , error ) {
2015-06-10 15:56:25 +02:00
if strings . Contains ( address , ".onion:" ) {
2013-12-17 16:10:59 +01:00
return cfg . oniondial ( network , address )
}
return cfg . dial ( network , address )
}
2014-01-10 08:31:20 +01:00
// btcdLookup returns the correct DNS lookup function to use depending on the
// passed host and configuration options. For example, .onion addresses will be
// resolved using the onion specific proxy if one was specified, but will
// otherwise treat the normal proxy as tor unless --noonion was specified in
// which case the lookup will fail. Meanwhile, normal IP addresses will be
// resolved using tor if a proxy was specified unless --noonion was also
// specified in which case the normal system DNS resolver will be used.
func btcdLookup ( host string ) ( [ ] net . IP , error ) {
2013-12-17 16:10:59 +01:00
if strings . HasSuffix ( host , ".onion" ) {
return cfg . onionlookup ( host )
}
return cfg . lookup ( host )
}