mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
598 lines
24 KiB
Go
598 lines
24 KiB
Go
// Copyright (c) 2013-2017 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package config
|
|
|
|
import (
|
|
// _ "embed" is necessary for the go:embed feature.
|
|
_ "embed"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/btcsuite/go-socks/socks"
|
|
"github.com/jessevdk/go-flags"
|
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
|
"github.com/kaspanet/kaspad/infrastructure/logger"
|
|
"github.com/kaspanet/kaspad/util"
|
|
"github.com/kaspanet/kaspad/util/network"
|
|
"github.com/kaspanet/kaspad/version"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
const (
|
|
defaultConfigFilename = "kaspad.conf"
|
|
defaultLogLevel = "info"
|
|
defaultLogDirname = "logs"
|
|
defaultLogFilename = "kaspad.log"
|
|
defaultErrLogFilename = "kaspad_err.log"
|
|
defaultTargetOutboundPeers = 8
|
|
defaultMaxInboundPeers = 117
|
|
defaultBanDuration = time.Hour * 24
|
|
defaultBanThreshold = 100
|
|
//DefaultConnectTimeout is the default connection timeout when dialing
|
|
DefaultConnectTimeout = time.Second * 30
|
|
//DefaultMaxRPCClients is the default max number of RPC clients
|
|
DefaultMaxRPCClients = 128
|
|
defaultMaxRPCWebsockets = 25
|
|
defaultMaxRPCConcurrentReqs = 20
|
|
defaultBlockMaxMass = 10_000_000
|
|
blockMaxMassMin = 1000
|
|
blockMaxMassMax = 10_000_000
|
|
defaultMinRelayTxFee = 1e-5 // 1 sompi per byte
|
|
defaultMaxOrphanTransactions = 100
|
|
//DefaultMaxOrphanTxSize is the default maximum size for an orphan transaction
|
|
DefaultMaxOrphanTxSize = 100_000
|
|
defaultSigCacheMaxSize = 100_000
|
|
sampleConfigFilename = "sample-kaspad.conf"
|
|
defaultMaxUTXOCacheSize = 5_000_000_000
|
|
defaultProtocolVersion = 5
|
|
)
|
|
|
|
var (
|
|
// DefaultAppDir is the default home directory for kaspad.
|
|
DefaultAppDir = util.AppDir("kaspad", false)
|
|
|
|
defaultConfigFile = filepath.Join(DefaultAppDir, defaultConfigFilename)
|
|
defaultDataDir = filepath.Join(DefaultAppDir)
|
|
defaultRPCKeyFile = filepath.Join(DefaultAppDir, "rpc.key")
|
|
defaultRPCCertFile = filepath.Join(DefaultAppDir, "rpc.cert")
|
|
)
|
|
|
|
//go:embed sample-kaspad.conf
|
|
var sampleConfig string
|
|
|
|
// 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
|
|
|
|
// Flags defines the configuration options for kaspad.
|
|
//
|
|
// See loadConfig for details on the configuration load process.
|
|
type Flags struct {
|
|
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
|
|
ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"`
|
|
AppDir string `short:"b" long:"appdir" description:"Directory to store data"`
|
|
LogDir string `long:"logdir" description:"Directory to log output."`
|
|
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: 16111, testnet: 16211)"`
|
|
TargetOutboundPeers int `long:"outpeers" description:"Target number of outbound peers"`
|
|
MaxInboundPeers int `long:"maxinpeers" description:"Max number of inbound peers"`
|
|
EnableBanning bool `long:"enablebanning" description:"Enable banning of misbehaving peers"`
|
|
BanDuration time.Duration `long:"banduration" description:"How long to ban misbehaving peers. Valid time units are {s, m, h}. Minimum 1 second"`
|
|
BanThreshold uint32 `long:"banthreshold" description:"Maximum allowed ban score before disconnecting and banning misbehaving peers."`
|
|
Whitelists []string `long:"whitelist" description:"Add an IP network or IP that will not be banned. (eg. 192.168.1.0/24 or ::1)"`
|
|
RPCListeners []string `long:"rpclisten" description:"Add an interface/port to listen for RPC connections (default port: 16110, testnet: 16210)"`
|
|
RPCCert string `long:"rpccert" description:"File containing the certificate file"`
|
|
RPCKey string `long:"rpckey" description:"File containing the certificate key"`
|
|
RPCMaxClients int `long:"rpcmaxclients" description:"Max number of RPC clients for standard connections"`
|
|
RPCMaxWebsockets int `long:"rpcmaxwebsockets" description:"Max number of RPC websocket connections"`
|
|
RPCMaxConcurrentReqs int `long:"rpcmaxconcurrentreqs" description:"Max number of concurrent RPC requests that may be processed concurrently"`
|
|
DisableRPC bool `long:"norpc" description:"Disable built-in RPC server"`
|
|
SafeRPC bool `long:"saferpc" description:"Disable RPC commands which affect the state of the node"`
|
|
DisableDNSSeed bool `long:"nodnsseed" description:"Disable DNS seeding for peers"`
|
|
DNSSeed string `long:"dnsseed" description:"Override DNS seeds with specified hostname (Only 1 hostname allowed)"`
|
|
GRPCSeed string `long:"grpcseed" description:"Hostname of gRPC server for seeding 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"`
|
|
DbType string `long:"dbtype" description:"Database backend to use for the Block DAG"`
|
|
Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"`
|
|
LogLevel string `short:"d" long:"loglevel" 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"`
|
|
MinRelayTxFee float64 `long:"minrelaytxfee" description:"The minimum transaction fee in KAS/kB to be considered a non-zero fee."`
|
|
MaxOrphanTxs uint64 `long:"maxorphantx" description:"Max number of orphan transactions to keep in memory"`
|
|
BlockMaxMass uint64 `long:"blockmaxmass" description:"Maximum transaction mass to be used when creating a block"`
|
|
UserAgentComments []string `long:"uacomment" description:"Comment to add to the user agent -- See BIP 14 for more information."`
|
|
NoPeerBloomFilters bool `long:"nopeerbloomfilters" description:"Disable bloom filtering support"`
|
|
SigCacheMaxSize uint `long:"sigcachemaxsize" description:"The maximum number of entries in the signature verification cache"`
|
|
BlocksOnly bool `long:"blocksonly" description:"Do not accept transactions from remote peers."`
|
|
RelayNonStd bool `long:"relaynonstd" description:"Relay non-standard transactions regardless of the default settings for the active network."`
|
|
RejectNonStd bool `long:"rejectnonstd" description:"Reject non-standard transactions regardless of the default settings for the active network."`
|
|
ResetDatabase bool `long:"reset-db" description:"Reset database before starting node. It's needed when switching between subnetworks."`
|
|
MaxUTXOCacheSize uint64 `long:"maxutxocachesize" description:"Max size of loaded UTXO into ram from the disk in bytes"`
|
|
UTXOIndex bool `long:"utxoindex" description:"Enable the UTXO index"`
|
|
IsArchivalNode bool `long:"archival" description:"Run as an archival node: don't delete old block data when moving the pruning point (Warning: heavy disk usage)'"`
|
|
AllowSubmitBlockWhenNotSynced bool `long:"allow-submit-block-when-not-synced" hidden:"true" description:"Allow the node to accept blocks from RPC while not synced (this flag is mainly used for testing)"`
|
|
EnableSanityCheckPruningUTXOSet bool `long:"enable-sanity-check-pruning-utxo" hidden:"true" description:"When moving the pruning point - check that the utxo set matches the utxo commitment"`
|
|
ProtocolVersion uint32 `long:"protocol-version" description:"Use non default p2p protocol version"`
|
|
NetworkFlags
|
|
ServiceOptions *ServiceOptions
|
|
}
|
|
|
|
// Config defines the configuration options for kaspad.
|
|
//
|
|
// See loadConfig for details on the configuration load process.
|
|
type Config struct {
|
|
*Flags
|
|
Lookup func(string) ([]net.IP, error)
|
|
Dial func(string, string, time.Duration) (net.Conn, error)
|
|
MiningAddrs []util.Address
|
|
MinRelayTxFee util.Amount
|
|
Whitelists []*net.IPNet
|
|
SubnetworkID *externalapi.DomainSubnetworkID // nil in full nodes
|
|
}
|
|
|
|
// ServiceOptions defines the configuration options for the daemon as a service on
|
|
// Windows.
|
|
type ServiceOptions struct {
|
|
ServiceCommand string `short:"s" long:"service" description:"Service command {install, remove, start, stop}"`
|
|
}
|
|
|
|
// cleanAndExpandPath expands environment variables and leading ~ in the
|
|
// passed path, cleans the result, and returns it.
|
|
func cleanAndExpandPath(path string) string {
|
|
// Expand initial ~ to OS specific home directory.
|
|
if strings.HasPrefix(path, "~") {
|
|
homeDir := filepath.Dir(DefaultAppDir)
|
|
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))
|
|
}
|
|
|
|
// newConfigParser returns a new command line flags parser.
|
|
func newConfigParser(cfgFlags *Flags, options flags.Options) *flags.Parser {
|
|
parser := flags.NewParser(cfgFlags, options)
|
|
if runtime.GOOS == "windows" {
|
|
parser.AddGroup("Service Options", "Service Options", cfgFlags.ServiceOptions)
|
|
}
|
|
return parser
|
|
}
|
|
|
|
func defaultFlags() *Flags {
|
|
return &Flags{
|
|
ConfigFile: defaultConfigFile,
|
|
LogLevel: defaultLogLevel,
|
|
TargetOutboundPeers: defaultTargetOutboundPeers,
|
|
MaxInboundPeers: defaultMaxInboundPeers,
|
|
BanDuration: defaultBanDuration,
|
|
BanThreshold: defaultBanThreshold,
|
|
RPCMaxClients: DefaultMaxRPCClients,
|
|
RPCMaxWebsockets: defaultMaxRPCWebsockets,
|
|
RPCMaxConcurrentReqs: defaultMaxRPCConcurrentReqs,
|
|
AppDir: defaultDataDir,
|
|
RPCKey: defaultRPCKeyFile,
|
|
RPCCert: defaultRPCCertFile,
|
|
BlockMaxMass: defaultBlockMaxMass,
|
|
MaxOrphanTxs: defaultMaxOrphanTransactions,
|
|
SigCacheMaxSize: defaultSigCacheMaxSize,
|
|
MinRelayTxFee: defaultMinRelayTxFee,
|
|
MaxUTXOCacheSize: defaultMaxUTXOCacheSize,
|
|
ServiceOptions: &ServiceOptions{},
|
|
ProtocolVersion: defaultProtocolVersion,
|
|
}
|
|
}
|
|
|
|
// DefaultConfig returns the default kaspad configuration
|
|
func DefaultConfig() *Config {
|
|
config := &Config{Flags: defaultFlags()}
|
|
config.NetworkFlags.ActiveNetParams = &dagconfig.MainnetParams
|
|
return config
|
|
}
|
|
|
|
// 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 kaspad 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, error) {
|
|
cfgFlags := defaultFlags()
|
|
|
|
// Pre-parse the command line options to see if an alternative config
|
|
// 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.
|
|
preCfg := cfgFlags
|
|
preParser := newConfigParser(preCfg, flags.HelpFlag)
|
|
_, err := preParser.Parse()
|
|
if err != nil {
|
|
var flagsErr *flags.Error
|
|
if ok := errors.As(err, &flagsErr); ok && flagsErr.Type == flags.ErrHelp {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
appName := filepath.Base(os.Args[0])
|
|
appName = strings.TrimSuffix(appName, filepath.Ext(appName))
|
|
usageMessage := fmt.Sprintf("Use %s -h to show usage", appName)
|
|
|
|
// Show the version and exit if the version flag was specified.
|
|
if preCfg.ShowVersion {
|
|
fmt.Println(appName, "version", version.Version())
|
|
os.Exit(0)
|
|
}
|
|
|
|
// Load additional config from file.
|
|
var configFileError error
|
|
parser := newConfigParser(cfgFlags, flags.Default)
|
|
cfg := &Config{
|
|
Flags: cfgFlags,
|
|
}
|
|
if !preCfg.Simnet || preCfg.ConfigFile != defaultConfigFile {
|
|
if _, err := os.Stat(preCfg.ConfigFile); os.IsNotExist(err) {
|
|
err := createDefaultConfigFile(preCfg.ConfigFile)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "Error creating a default config file")
|
|
}
|
|
}
|
|
|
|
err := flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile)
|
|
if err != nil {
|
|
if pErr := &(os.PathError{}); !errors.As(err, &pErr) {
|
|
return nil, errors.Wrapf(err, "Error parsing config file: %s\n\n%s", err, usageMessage)
|
|
}
|
|
configFileError = err
|
|
}
|
|
}
|
|
|
|
// Parse command line options again to ensure they take precedence.
|
|
_, err = parser.Parse()
|
|
if err != nil {
|
|
var flagsErr *flags.Error
|
|
if ok := errors.As(err, &flagsErr); !ok || flagsErr.Type != flags.ErrHelp {
|
|
return nil, errors.Wrapf(err, "Error parsing command line arguments: %s\n\n%s", err, usageMessage)
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
// Create the home directory if it doesn't already exist.
|
|
funcName := "loadConfig"
|
|
err = os.MkdirAll(DefaultAppDir, 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).
|
|
var e *os.PathError
|
|
if ok := errors.As(err, &e); ok && os.IsExist(err) {
|
|
if link, lerr := os.Readlink(e.Path); lerr == nil {
|
|
str := "is symlink %s -> %s mounted?"
|
|
err = errors.Errorf(str, e.Path, link)
|
|
}
|
|
}
|
|
|
|
str := "%s: Failed to create home directory: %s"
|
|
err := errors.Errorf(str, funcName, err)
|
|
return nil, err
|
|
}
|
|
|
|
err = cfg.ResolveNetwork(parser)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Set the default policy for relaying non-standard transactions
|
|
// according to the default of the active network. The set
|
|
// configuration value takes precedence over the default value for the
|
|
// selected network.
|
|
relayNonStd := cfg.NetParams().RelayNonStdTxs
|
|
switch {
|
|
case cfg.RelayNonStd && cfg.RejectNonStd:
|
|
str := "%s: rejectnonstd and relaynonstd cannot be used " +
|
|
"together -- choose only one"
|
|
err := errors.Errorf(str, funcName)
|
|
fmt.Fprintln(os.Stderr, err)
|
|
fmt.Fprintln(os.Stderr, usageMessage)
|
|
return nil, err
|
|
case cfg.RejectNonStd:
|
|
relayNonStd = false
|
|
case cfg.RelayNonStd:
|
|
relayNonStd = true
|
|
}
|
|
cfg.RelayNonStd = relayNonStd
|
|
|
|
cfg.AppDir = cleanAndExpandPath(cfg.AppDir)
|
|
// Append the network type to the app directory so it is "namespaced"
|
|
// per network.
|
|
// 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.AppDir = filepath.Join(cfg.AppDir, cfg.NetParams().Name)
|
|
|
|
// Logs directory is usually under the home directory, unless otherwise specified
|
|
if cfg.LogDir == "" {
|
|
cfg.LogDir = filepath.Join(cfg.AppDir, defaultLogDirname)
|
|
}
|
|
cfg.LogDir = cleanAndExpandPath(cfg.LogDir)
|
|
|
|
// Special show command to list supported subsystems and exit.
|
|
if cfg.LogLevel == "show" {
|
|
fmt.Println("Supported subsystems", logger.SupportedSubsystems())
|
|
os.Exit(0)
|
|
}
|
|
|
|
// Initialize log rotation. After log rotation has been initialized, the
|
|
// logger variables may be used.
|
|
logger.InitLog(filepath.Join(cfg.LogDir, defaultLogFilename), filepath.Join(cfg.LogDir, defaultErrLogFilename))
|
|
|
|
// Parse, validate, and set debug log level(s).
|
|
if err := logger.ParseAndSetLogLevels(cfg.LogLevel); err != nil {
|
|
err := errors.Errorf("%s: %s", funcName, err.Error())
|
|
fmt.Fprintln(os.Stderr, err)
|
|
fmt.Fprintln(os.Stderr, usageMessage)
|
|
return nil, err
|
|
}
|
|
|
|
// Validate profile port number
|
|
if cfg.Profile != "" {
|
|
profilePort, err := strconv.Atoi(cfg.Profile)
|
|
if err != nil || profilePort < 1024 || profilePort > 65535 {
|
|
str := "%s: The profile port must be between 1024 and 65535"
|
|
err := errors.Errorf(str, funcName)
|
|
fmt.Fprintln(os.Stderr, err)
|
|
fmt.Fprintln(os.Stderr, usageMessage)
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Don't allow ban durations that are too short.
|
|
if cfg.BanDuration < time.Second {
|
|
str := "%s: The banduration option may not be less than 1s -- parsed [%s]"
|
|
err := errors.Errorf(str, funcName, cfg.BanDuration)
|
|
fmt.Fprintln(os.Stderr, err)
|
|
fmt.Fprintln(os.Stderr, usageMessage)
|
|
return nil, err
|
|
}
|
|
|
|
// Validate any given whitelisted IP addresses and networks.
|
|
if len(cfg.Whitelists) > 0 {
|
|
var ip net.IP
|
|
cfg.Whitelists = make([]*net.IPNet, 0, len(cfg.Flags.Whitelists))
|
|
|
|
for _, addr := range cfg.Flags.Whitelists {
|
|
_, ipnet, err := net.ParseCIDR(addr)
|
|
if err != nil {
|
|
ip = net.ParseIP(addr)
|
|
if ip == nil {
|
|
str := "%s: The whitelist value of '%s' is invalid"
|
|
err = errors.Errorf(str, funcName, addr)
|
|
fmt.Fprintln(os.Stderr, err)
|
|
fmt.Fprintln(os.Stderr, usageMessage)
|
|
return nil, err
|
|
}
|
|
var bits int
|
|
if ip.To4() == nil {
|
|
// IPv6
|
|
bits = 128
|
|
} else {
|
|
bits = 32
|
|
}
|
|
ipnet = &net.IPNet{
|
|
IP: ip,
|
|
Mask: net.CIDRMask(bits, bits),
|
|
}
|
|
}
|
|
cfg.Whitelists = append(cfg.Whitelists, ipnet)
|
|
}
|
|
}
|
|
|
|
// --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"
|
|
err := errors.Errorf(str, funcName)
|
|
fmt.Fprintln(os.Stderr, err)
|
|
fmt.Fprintln(os.Stderr, usageMessage)
|
|
return nil, err
|
|
}
|
|
|
|
// --proxy or --connect without --listen disables listening.
|
|
if (cfg.Proxy != "" || len(cfg.ConnectPeers) > 0) &&
|
|
len(cfg.Listeners) == 0 {
|
|
cfg.DisableListen = true
|
|
}
|
|
|
|
// ConnectPeers means no DNS seeding and no outbound peers
|
|
if len(cfg.ConnectPeers) > 0 {
|
|
cfg.DisableDNSSeed = true
|
|
cfg.TargetOutboundPeers = 0
|
|
}
|
|
|
|
// 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{
|
|
net.JoinHostPort("", cfg.NetParams().DefaultPort),
|
|
}
|
|
}
|
|
|
|
if cfg.DisableRPC {
|
|
log.Infof("RPC service is disabled")
|
|
}
|
|
|
|
// Add the default RPC listener if none were specified. The default
|
|
// RPC listener is all addresses on the RPC listen port for the
|
|
// network we are to connect to.
|
|
if !cfg.DisableRPC && len(cfg.RPCListeners) == 0 {
|
|
cfg.RPCListeners = []string{
|
|
net.JoinHostPort("", cfg.NetParams().RPCPort),
|
|
}
|
|
}
|
|
|
|
if cfg.RPCMaxConcurrentReqs < 0 {
|
|
str := "%s: The rpcmaxwebsocketconcurrentrequests option may " +
|
|
"not be less than 0 -- parsed [%d]"
|
|
err := errors.Errorf(str, funcName, cfg.RPCMaxConcurrentReqs)
|
|
fmt.Fprintln(os.Stderr, err)
|
|
fmt.Fprintln(os.Stderr, usageMessage)
|
|
return nil, err
|
|
}
|
|
|
|
// Validate the the minrelaytxfee.
|
|
cfg.MinRelayTxFee, err = util.NewAmount(cfg.Flags.MinRelayTxFee)
|
|
if err != nil {
|
|
str := "%s: invalid minrelaytxfee: %s"
|
|
err := errors.Errorf(str, funcName, err)
|
|
fmt.Fprintln(os.Stderr, err)
|
|
fmt.Fprintln(os.Stderr, usageMessage)
|
|
return nil, err
|
|
}
|
|
|
|
// Disallow 0 and negative min tx fees.
|
|
if cfg.MinRelayTxFee == 0 {
|
|
str := "%s: The minrelaytxfee option must be greater than 0 -- parsed [%d]"
|
|
err := errors.Errorf(str, funcName, cfg.MinRelayTxFee)
|
|
fmt.Fprintln(os.Stderr, err)
|
|
fmt.Fprintln(os.Stderr, usageMessage)
|
|
return nil, err
|
|
}
|
|
|
|
// Limit the max block mass to a sane value.
|
|
if cfg.BlockMaxMass < blockMaxMassMin || cfg.BlockMaxMass >
|
|
blockMaxMassMax {
|
|
|
|
str := "%s: The blockmaxmass option must be in between %d " +
|
|
"and %d -- parsed [%d]"
|
|
err := errors.Errorf(str, funcName, blockMaxMassMin,
|
|
blockMaxMassMax, cfg.BlockMaxMass)
|
|
fmt.Fprintln(os.Stderr, err)
|
|
fmt.Fprintln(os.Stderr, usageMessage)
|
|
return nil, err
|
|
}
|
|
|
|
// Look for illegal characters in the user agent comments.
|
|
for _, uaComment := range cfg.UserAgentComments {
|
|
if strings.ContainsAny(uaComment, "/:()") {
|
|
err := errors.Errorf("%s: The following characters must not "+
|
|
"appear in user agent comments: '/', ':', '(', ')'",
|
|
funcName)
|
|
fmt.Fprintln(os.Stderr, err)
|
|
fmt.Fprintln(os.Stderr, usageMessage)
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Add default port to all listener addresses if needed and remove
|
|
// duplicate addresses.
|
|
cfg.Listeners, err = network.NormalizeAddresses(cfg.Listeners,
|
|
cfg.NetParams().DefaultPort)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Add default port to all rpc listener addresses if needed and remove
|
|
// duplicate addresses.
|
|
cfg.RPCListeners, err = network.NormalizeAddresses(cfg.RPCListeners,
|
|
cfg.NetParams().RPCPort)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Disallow --addpeer and --connect used together
|
|
if len(cfg.AddPeers) > 0 && len(cfg.ConnectPeers) > 0 {
|
|
str := "%s: --addpeer and --connect can not be used together"
|
|
err := errors.Errorf(str, funcName)
|
|
fmt.Fprintln(os.Stderr, err)
|
|
fmt.Fprintln(os.Stderr, usageMessage)
|
|
return nil, err
|
|
}
|
|
|
|
// Add default port to all added peer addresses if needed and remove
|
|
// duplicate addresses.
|
|
cfg.AddPeers, err = network.NormalizeAddresses(cfg.AddPeers,
|
|
cfg.NetParams().DefaultPort)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cfg.ConnectPeers, err = network.NormalizeAddresses(cfg.ConnectPeers,
|
|
cfg.NetParams().DefaultPort)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Setup dial and DNS resolution (lookup) functions depending on the
|
|
// specified options. The default is to use the standard
|
|
// net.DialTimeout function as well as the system DNS resolver. When a
|
|
// proxy is specified, the dial function is set to the proxy specific
|
|
// dial function.
|
|
cfg.Dial = net.DialTimeout
|
|
cfg.Lookup = net.LookupIP
|
|
if cfg.Proxy != "" {
|
|
_, _, err := net.SplitHostPort(cfg.Proxy)
|
|
if err != nil {
|
|
str := "%s: Proxy address '%s' is invalid: %s"
|
|
err := errors.Errorf(str, funcName, cfg.Proxy, err)
|
|
fmt.Fprintln(os.Stderr, err)
|
|
fmt.Fprintln(os.Stderr, usageMessage)
|
|
return nil, err
|
|
}
|
|
|
|
proxy := &socks.Proxy{
|
|
Addr: cfg.Proxy,
|
|
Username: cfg.ProxyUser,
|
|
Password: cfg.ProxyPass,
|
|
}
|
|
cfg.Dial = proxy.DialTimeout
|
|
}
|
|
|
|
// 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 {
|
|
log.Warnf("%s", configFileError)
|
|
}
|
|
return cfg, nil
|
|
}
|
|
|
|
// createDefaultConfig copies the file sample-kaspad.conf to the given destination path,
|
|
// and populates it with some randomly generated RPC username and password.
|
|
func createDefaultConfigFile(destinationPath string) error {
|
|
// Create the destination directory if it does not exists
|
|
err := os.MkdirAll(filepath.Dir(destinationPath), 0700)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dest, err := os.OpenFile(destinationPath,
|
|
os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer dest.Close()
|
|
|
|
_, err = dest.WriteString(sampleConfig)
|
|
|
|
return err
|
|
}
|