diff --git a/addrmgr/log.go b/addrmgr/log.go index b3ebbd15d..af1db624d 100644 --- a/addrmgr/log.go +++ b/addrmgr/log.go @@ -6,6 +6,7 @@ package addrmgr import ( "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/logger" ) // log is a logger that is initialized with no output filters. This @@ -13,20 +14,6 @@ import ( // requests it. var log btclog.Logger -// The default amount of logging is none. func init() { - DisableLog() -} - -// DisableLog disables all library log output. Logging output is disabled -// by default until either UseLogger or SetLogWriter are called. -func DisableLog() { - log = btclog.Disabled -} - -// UseLogger uses a specified Logger to output package logging info. -// This should be used in preference to SetLogWriter if the caller is also -// using btclog. -func UseLogger(logger btclog.Logger) { - log = logger + log, _ = logger.Get(logger.SubsystemTags.ADXR) } diff --git a/blockdag/fullblocktests/generate.go b/blockdag/fullblocktests/generate.go index 48e4549cc..010d0d256 100644 --- a/blockdag/fullblocktests/generate.go +++ b/blockdag/fullblocktests/generate.go @@ -811,7 +811,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { // Create a test generator instance initialized with the genesis block // as the tip. - g, err := makeTestGenerator(regressionNetParams) + g, err := makeTestGenerator(&dagconfig.RegressionNetParams) if err != nil { return nil, err } diff --git a/blockdag/indexers/log.go b/blockdag/indexers/log.go index 0172da078..2a09287be 100644 --- a/blockdag/indexers/log.go +++ b/blockdag/indexers/log.go @@ -4,7 +4,10 @@ package indexers -import "github.com/btcsuite/btclog" +import ( + "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/logger" +) // log is a logger that is initialized with no output filters. This // means the package will not perform any logging by default until the caller @@ -13,18 +16,5 @@ var log btclog.Logger // The default amount of logging is none. func init() { - DisableLog() -} - -// DisableLog disables all library log output. Logging output is disabled -// by default until either UseLogger or SetLogWriter are called. -func DisableLog() { - log = btclog.Disabled -} - -// UseLogger uses a specified Logger to output package logging info. -// This should be used in preference to SetLogWriter if the caller is also -// using btclog. -func UseLogger(logger btclog.Logger) { - log = logger + log, _ = logger.Get(logger.SubsystemTags.INDX) } diff --git a/blockdag/log.go b/blockdag/log.go index 56725b296..a550d95c5 100644 --- a/blockdag/log.go +++ b/blockdag/log.go @@ -6,6 +6,7 @@ package blockdag import ( "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/logger" ) // log is a logger that is initialized with no output filters. This @@ -15,16 +16,5 @@ var log btclog.Logger // The default amount of logging is none. func init() { - DisableLog() -} - -// DisableLog disables all library log output. Logging output is disabled -// by default until UseLogger is called. -func DisableLog() { - log = btclog.Disabled -} - -// UseLogger uses a specified Logger to output package logging info. -func UseLogger(logger btclog.Logger) { - log = logger + log, _ = logger.Get(logger.SubsystemTags.CHAN) } diff --git a/btcd.go b/btcd.go index 718bd5ec8..8a83822a0 100644 --- a/btcd.go +++ b/btcd.go @@ -16,8 +16,15 @@ import ( "runtime/pprof" "github.com/daglabs/btcd/blockdag/indexers" + "github.com/daglabs/btcd/config" "github.com/daglabs/btcd/database" + _ "github.com/daglabs/btcd/database/ffldb" "github.com/daglabs/btcd/limits" + "github.com/daglabs/btcd/logger" + "github.com/daglabs/btcd/server" + "github.com/daglabs/btcd/signal" + "github.com/daglabs/btcd/version" + "github.com/daglabs/btcutil/fs" ) const ( @@ -28,7 +35,7 @@ const ( ) var ( - cfg *config + cfg *config.Config ) // winServiceMain is only invoked on Windows. It detects when btcd is running @@ -40,28 +47,28 @@ var winServiceMain func() (bool, error) // optional serverChan parameter is mainly used by the service code to be // notified with the server once it is setup so it can gracefully stop it when // requested from the service control manager. -func btcdMain(serverChan chan<- *server) error { +func btcdMain(serverChan chan<- *server.Server) error { // Load configuration and parse command line. This function also // initializes logging and configures it accordingly. - tcfg, _, err := loadConfig() + err := config.LoadAndSetMainConfig() if err != nil { return err } - cfg = tcfg + cfg = config.MainConfig() defer func() { - if logRotator != nil { - logRotator.Close() + if logger.LogRotator != nil { + logger.LogRotator.Close() } }() // Get a channel that will be closed when a shutdown signal has been // triggered either from an OS signal such as SIGINT (Ctrl+C) or from // another subsystem such as the RPC server. - interrupt := interruptListener() + interrupt := signal.InterruptListener() defer btcdLog.Info("Shutdown complete") // Show version at startup. - btcdLog.Infof("Version %s", version()) + btcdLog.Infof("Version %s", version.Version()) // Enable http profiling server if requested. if cfg.Profile != "" { @@ -94,7 +101,7 @@ func btcdMain(serverChan chan<- *server) error { } // Return now if an interrupt signal was triggered. - if interruptRequested(interrupt) { + if signal.InterruptRequested(interrupt) { return nil } @@ -111,7 +118,7 @@ func btcdMain(serverChan chan<- *server) error { }() // Return now if an interrupt signal was triggered. - if interruptRequested(interrupt) { + if signal.InterruptRequested(interrupt) { return nil } @@ -145,7 +152,7 @@ func btcdMain(serverChan chan<- *server) error { } // Create server and start it. - server, err := newServer(cfg.Listeners, db, activeNetParams.Params, + server, err := server.NewServer(cfg.Listeners, db, config.ActiveNetParams(), interrupt) if err != nil { // TODO: this logging could do with some beautifying. @@ -226,7 +233,7 @@ func warnMultipleDBs() { // Store db path as a duplicate db if it exists. dbPath := blockDbPath(dbType) - if fileExists(dbPath) { + if fs.FileExists(dbPath) { duplicateDbPaths = append(duplicateDbPaths, dbPath) } } @@ -271,7 +278,7 @@ func loadBlockDB() (database.DB, error) { removeRegressionDB(dbPath) btcdLog.Infof("Loading block database from '%s'", dbPath) - db, err := database.Open(cfg.DbType, dbPath, activeNetParams.Net) + db, err := database.Open(cfg.DbType, dbPath, config.ActiveNetParams().Net) if err != nil { // Return the error if it's not because the database doesn't // exist. @@ -286,7 +293,7 @@ func loadBlockDB() (database.DB, error) { if err != nil { return nil, err } - db, err = database.Create(cfg.DbType, dbPath, activeNetParams.Net) + db, err = database.Create(cfg.DbType, dbPath, config.ActiveNetParams().Net) if err != nil { return nil, err } diff --git a/cmd/addblock/addblock.go b/cmd/addblock/addblock.go index cba2e3288..08f599295 100644 --- a/cmd/addblock/addblock.go +++ b/cmd/addblock/addblock.go @@ -9,11 +9,9 @@ import ( "path/filepath" "runtime" - "github.com/daglabs/btcd/blockdag" - "github.com/daglabs/btcd/blockdag/indexers" + "github.com/btcsuite/btclog" "github.com/daglabs/btcd/database" "github.com/daglabs/btcd/limits" - "github.com/btcsuite/btclog" ) const ( @@ -72,9 +70,6 @@ func realMain() error { backendLogger := btclog.NewBackend(os.Stdout) defer os.Stdout.Sync() log = backendLogger.Logger("MAIN") - database.UseLogger(backendLogger.Logger("BCDB")) - blockdag.UseLogger(backendLogger.Logger("CHAN")) - indexers.UseLogger(backendLogger.Logger("INDX")) // Load the block database. db, err := loadBlockDB() diff --git a/config.go b/config/config.go similarity index 80% rename from config.go rename to config/config.go index 38f772d72..460e1e05c 100644 --- a/config.go +++ b/config/config.go @@ -2,7 +2,7 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package main +package config import ( "bufio" @@ -15,33 +15,35 @@ import ( "os" "path/filepath" "runtime" - "sort" "strconv" "strings" "time" "github.com/btcsuite/go-socks/socks" + "github.com/daglabs/btcd/connmgr" "github.com/daglabs/btcd/dagconfig" "github.com/daglabs/btcd/dagconfig/daghash" - "github.com/daglabs/btcd/connmgr" "github.com/daglabs/btcd/database" - _ "github.com/daglabs/btcd/database/ffldb" + "github.com/daglabs/btcd/logger" "github.com/daglabs/btcd/mempool" + "github.com/daglabs/btcd/version" "github.com/daglabs/btcd/wire" "github.com/daglabs/btcutil" + "github.com/daglabs/btcutil/network" flags "github.com/jessevdk/go-flags" ) const ( - defaultConfigFilename = "btcd.conf" - defaultDataDirname = "data" - defaultLogLevel = "info" - defaultLogDirname = "logs" - defaultLogFilename = "btcd.log" - defaultMaxPeers = 125 - defaultBanDuration = time.Hour * 24 - defaultBanThreshold = 100 - defaultConnectTimeout = time.Second * 30 + defaultConfigFilename = "btcd.conf" + defaultDataDirname = "data" + defaultLogLevel = "info" + defaultLogDirname = "logs" + defaultLogFilename = "btcd.log" + defaultMaxPeers = 125 + defaultBanDuration = time.Hour * 24 + defaultBanThreshold = 100 + //DefaultConnectTimeout is the default connection timeout when dialing + DefaultConnectTimeout = time.Second * 30 defaultMaxRPCClients = 10 defaultMaxRPCWebsockets = 25 defaultMaxRPCConcurrentReqs = 20 @@ -53,26 +55,33 @@ const ( blockMaxSizeMax = wire.MaxBlockPayload - 1000 defaultGenerate = false defaultMaxOrphanTransactions = 100 - defaultMaxOrphanTxSize = 100000 - defaultSigCacheMaxSize = 100000 - sampleConfigFilename = "sample-btcd.conf" - defaultTxIndex = false - defaultAddrIndex = false + //DefaultMaxOrphanTxSize is the default maximum size for an orphan transaction + DefaultMaxOrphanTxSize = 100000 + defaultSigCacheMaxSize = 100000 + sampleConfigFilename = "sample-btcd.conf" + defaultTxIndex = false + defaultAddrIndex = false ) var ( - defaultHomeDir = btcutil.AppDataDir("btcd", false) - defaultConfigFile = filepath.Join(defaultHomeDir, defaultConfigFilename) - defaultDataDir = filepath.Join(defaultHomeDir, defaultDataDirname) + DefaultHomeDir = btcutil.AppDataDir("btcd", false) + defaultConfigFile = filepath.Join(DefaultHomeDir, defaultConfigFilename) + defaultDataDir = filepath.Join(DefaultHomeDir, defaultDataDirname) knownDbTypes = database.SupportedDrivers() - defaultRPCKeyFile = filepath.Join(defaultHomeDir, "rpc.key") - defaultRPCCertFile = filepath.Join(defaultHomeDir, "rpc.cert") - defaultLogDir = filepath.Join(defaultHomeDir, defaultLogDirname) + defaultRPCKeyFile = filepath.Join(DefaultHomeDir, "rpc.key") + defaultRPCCertFile = filepath.Join(DefaultHomeDir, "rpc.cert") + defaultLogDir = filepath.Join(DefaultHomeDir, defaultLogDirname) ) -// runServiceCommand is only set to a real function on Windows. It is used +// activeNetParams is a pointer to the parameters specific to the +// currently active bitcoin network. +var activeNetParams = &dagconfig.MainNetParams + +var mainCfg *Config + +// 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 +var RunServiceCommand func(string) error // minUint32 is a helper function to return the minimum of two uint32s. // This avoids a math import and the need to cast to floats. @@ -83,10 +92,10 @@ func minUint32(a, b uint32) uint32 { return b } -// config defines the configuration options for btcd. +// Config defines the configuration options for btcd. // // See loadConfig for details on the configuration load process. -type config struct { +type configFlags struct { 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"` @@ -154,13 +163,20 @@ type config struct { DropAddrIndex bool `long:"dropaddrindex" description:"Deletes the address-based transaction index from the database on start up and then exits."` 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."` - lookup func(string) ([]net.IP, error) - oniondial func(string, string, time.Duration) (net.Conn, error) - dial func(string, string, time.Duration) (net.Conn, error) - addCheckpoints []dagconfig.Checkpoint - miningAddrs []btcutil.Address - minRelayTxFee btcutil.Amount - whitelists []*net.IPNet +} + +// Config defines the configuration options for btcd. +// +// See loadConfig for details on the configuration load process. +type Config struct { + *configFlags + Lookup func(string) ([]net.IP, error) + OnionDial func(string, string, time.Duration) (net.Conn, error) + Dial func(string, string, time.Duration) (net.Conn, error) + AddCheckpoints []dagconfig.Checkpoint + MiningAddrs []btcutil.Address + MinRelayTxFee btcutil.Amount + Whitelists []*net.IPNet } // serviceOptions defines the configuration options for the daemon as a service on @@ -174,7 +190,7 @@ type serviceOptions struct { func cleanAndExpandPath(path string) string { // Expand initial ~ to OS specific home directory. if strings.HasPrefix(path, "~") { - homeDir := filepath.Dir(defaultHomeDir) + homeDir := filepath.Dir(DefaultHomeDir) path = strings.Replace(path, "~", homeDir, 1) } @@ -183,90 +199,6 @@ func cleanAndExpandPath(path string) string { return filepath.Clean(os.ExpandEnv(path)) } -// 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 subsystems 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 -} - // validDbType returns whether or not dbType is a supported database type. func validDbType(dbType string) bool { for _, knownType := range knownDbTypes { @@ -278,40 +210,6 @@ func validDbType(dbType string) bool { return false } -// removeDuplicateAddresses returns a new slice with all duplicate entries in -// addrs removed. -func removeDuplicateAddresses(addrs []string) []string { - result := make([]string, 0, len(addrs)) - seen := map[string]struct{}{} - for _, val := range addrs { - if _, ok := seen[val]; !ok { - result = append(result, val) - seen[val] = struct{}{} - } - } - return result -} - -// 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 { - for i, addr := range addrs { - addrs[i] = normalizeAddress(addr, defaultPort) - } - - return removeDuplicateAddresses(addrs) -} - // newCheckpointFromStr parses checkpoints in the ':' format. func newCheckpointFromStr(checkpoint string) (dagconfig.Checkpoint, error) { parts := strings.Split(checkpoint, ":") @@ -360,25 +258,30 @@ func parseCheckpoints(checkpointStrings []string) ([]dagconfig.Checkpoint, error return checkpoints, nil } -// 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 -} - // newConfigParser returns a new command line flags parser. -func newConfigParser(cfg *config, so *serviceOptions, options flags.Options) *flags.Parser { - parser := flags.NewParser(cfg, options) +func newConfigParser(cfgFlags *configFlags, so *serviceOptions, options flags.Options) *flags.Parser { + parser := flags.NewParser(cfgFlags, options) if runtime.GOOS == "windows" { parser.AddGroup("Service Options", "Service Options", so) } return parser } +//LoadAndSetMainConfig loads the config that can be afterward be accesible through MainConfig() +func LoadAndSetMainConfig() error { + tcfg, _, err := loadConfig() + if err != nil { + return err + } + mainCfg = tcfg + return nil +} + +//MainConfig is a getter to the main config +func MainConfig() *Config { + return mainCfg +} + // loadConfig initializes and parses the config using a config file and command // line options. // @@ -391,9 +294,9 @@ func newConfigParser(cfg *config, so *serviceOptions, options flags.Options) *fl // 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) { +func loadConfig() (*Config, []string, error) { // Default config. - cfg := config{ + cfgFlags := configFlags{ ConfigFile: defaultConfigFile, DebugLevel: defaultLogLevel, MaxPeers: defaultMaxPeers, @@ -426,7 +329,7 @@ func loadConfig() (*config, []string, error) { // 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 := cfg + preCfg := cfgFlags preParser := newConfigParser(&preCfg, &serviceOpts, flags.HelpFlag) _, err := preParser.Parse() if err != nil { @@ -441,15 +344,15 @@ func loadConfig() (*config, []string, error) { appName = strings.TrimSuffix(appName, filepath.Ext(appName)) usageMessage := fmt.Sprintf("Use %s -h to show usage", appName) if preCfg.ShowVersion { - fmt.Println(appName, "version", version()) + fmt.Println(appName, "version", version.Version()) os.Exit(0) } // 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) + // 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) } @@ -458,7 +361,10 @@ func loadConfig() (*config, []string, error) { // Load additional config from file. var configFileError error - parser := newConfigParser(&cfg, &serviceOpts, flags.Default) + parser := newConfigParser(&cfgFlags, &serviceOpts, flags.Default) + cfg := Config{ + configFlags: &cfgFlags, + } if !(preCfg.RegressionTest || preCfg.SimNet) || preCfg.ConfigFile != defaultConfigFile { @@ -498,7 +404,7 @@ func loadConfig() (*config, []string, error) { // Create the home directory if it doesn't already exist. funcName := "loadConfig" - err = os.MkdirAll(defaultHomeDir, 0700) + err = os.MkdirAll(DefaultHomeDir, 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 @@ -522,16 +428,16 @@ func loadConfig() (*config, []string, error) { // while we're at it if cfg.TestNet3 { numNets++ - activeNetParams = &testNet3Params + activeNetParams = &dagconfig.TestNet3Params } if cfg.RegressionTest { numNets++ - activeNetParams = ®ressionNetParams + activeNetParams = &dagconfig.RegressionNetParams } if cfg.SimNet { numNets++ // Also disable dns seeding on the simulation test network. - activeNetParams = &simNetParams + activeNetParams = &dagconfig.SimNetParams cfg.DisableDNSSeed = true } if numNets > 1 { @@ -570,25 +476,25 @@ func loadConfig() (*config, []string, error) { // means each individual piece of serialized data does not have to // worry about changing names per network and such. cfg.DataDir = cleanAndExpandPath(cfg.DataDir) - cfg.DataDir = filepath.Join(cfg.DataDir, netName(activeNetParams)) + cfg.DataDir = filepath.Join(cfg.DataDir, activeNetParams.Name) // 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) - cfg.LogDir = filepath.Join(cfg.LogDir, netName(activeNetParams)) + cfg.LogDir = filepath.Join(cfg.LogDir, activeNetParams.Name) // Special show command to list supported subsystems and exit. if cfg.DebugLevel == "show" { - fmt.Println("Supported subsystems", supportedSubsystems()) + fmt.Println("Supported subsystems", logger.SupportedSubsystems()) os.Exit(0) } // Initialize log rotation. After log rotation has been initialized, the // logger variables may be used. - initLogRotator(filepath.Join(cfg.LogDir, defaultLogFilename)) + logger.InitLogRotator(filepath.Join(mainCfg.LogDir, defaultLogFilename)) // Parse, validate, and set debug log level(s). - if err := parseAndSetDebugLevels(cfg.DebugLevel); err != nil { + if err := logger.ParseAndSetDebugLevels(cfg.DebugLevel); err != nil { err := fmt.Errorf("%s: %v", funcName, err.Error()) fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, usageMessage) @@ -629,9 +535,9 @@ func loadConfig() (*config, []string, error) { // 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.Whitelists)) + cfg.Whitelists = make([]*net.IPNet, 0, len(cfg.configFlags.Whitelists)) - for _, addr := range cfg.Whitelists { + for _, addr := range cfg.configFlags.Whitelists { _, ipnet, err := net.ParseCIDR(addr) if err != nil { ip = net.ParseIP(addr) @@ -654,7 +560,7 @@ func loadConfig() (*config, []string, error) { Mask: net.CIDRMask(bits, bits), } } - cfg.whitelists = append(cfg.whitelists, ipnet) + cfg.Whitelists = append(cfg.Whitelists, ipnet) } } @@ -715,7 +621,7 @@ func loadConfig() (*config, []string, error) { } if cfg.DisableRPC { - btcdLog.Infof("RPC service is disabled") + log.Infof("RPC service is disabled") } // Default RPC to listen on localhost only. @@ -726,7 +632,7 @@ func loadConfig() (*config, []string, error) { } cfg.RPCListeners = make([]string, 0, len(addrs)) for _, addr := range addrs { - addr = net.JoinHostPort(addr, activeNetParams.rpcPort) + addr = net.JoinHostPort(addr, activeNetParams.RPCPort) cfg.RPCListeners = append(cfg.RPCListeners, addr) } } @@ -741,7 +647,7 @@ func loadConfig() (*config, []string, error) { } // Validate the the minrelaytxfee. - cfg.minRelayTxFee, err = btcutil.NewAmount(cfg.MinRelayTxFee) + cfg.MinRelayTxFee, err = btcutil.NewAmount(cfg.configFlags.MinRelayTxFee) if err != nil { str := "%s: invalid minrelaytxfee: %v" err := fmt.Errorf(str, funcName, err) @@ -822,9 +728,9 @@ func loadConfig() (*config, []string, error) { } // Check mining addresses are valid and saved parsed versions. - cfg.miningAddrs = make([]btcutil.Address, 0, len(cfg.MiningAddrs)) - for _, strAddr := range cfg.MiningAddrs { - addr, err := btcutil.DecodeAddress(strAddr, activeNetParams.Params) + cfg.MiningAddrs = make([]btcutil.Address, 0, len(cfg.configFlags.MiningAddrs)) + for _, strAddr := range cfg.configFlags.MiningAddrs { + addr, err := btcutil.DecodeAddress(strAddr, activeNetParams) if err != nil { str := "%s: mining address '%s' failed to decode: %v" err := fmt.Errorf(str, funcName, strAddr, err) @@ -832,14 +738,14 @@ func loadConfig() (*config, []string, error) { fmt.Fprintln(os.Stderr, usageMessage) return nil, nil, err } - if !addr.IsForNet(activeNetParams.Params) { + if !addr.IsForNet(activeNetParams) { str := "%s: mining address '%s' is on the wrong network" err := fmt.Errorf(str, funcName, strAddr) fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, usageMessage) return nil, nil, err } - cfg.miningAddrs = append(cfg.miningAddrs, addr) + cfg.MiningAddrs = append(cfg.MiningAddrs, addr) } // Ensure there is at least one mining address when the generate flag is @@ -855,13 +761,13 @@ func loadConfig() (*config, []string, error) { // Add default port to all listener addresses if needed and remove // duplicate addresses. - cfg.Listeners = normalizeAddresses(cfg.Listeners, + cfg.Listeners = network.NormalizeAddresses(cfg.Listeners, activeNetParams.DefaultPort) // Add default port to all rpc listener addresses if needed and remove // duplicate addresses. - cfg.RPCListeners = normalizeAddresses(cfg.RPCListeners, - activeNetParams.rpcPort) + cfg.RPCListeners = network.NormalizeAddresses(cfg.RPCListeners, + activeNetParams.RPCPort) // Only allow TLS to be disabled if the RPC is bound to localhost // addresses. @@ -895,9 +801,9 @@ func loadConfig() (*config, []string, error) { // Add default port to all added peer addresses if needed and remove // duplicate addresses. - cfg.AddPeers = normalizeAddresses(cfg.AddPeers, + cfg.AddPeers = network.NormalizeAddresses(cfg.AddPeers, activeNetParams.DefaultPort) - cfg.ConnectPeers = normalizeAddresses(cfg.ConnectPeers, + cfg.ConnectPeers = network.NormalizeAddresses(cfg.ConnectPeers, activeNetParams.DefaultPort) // --noonion and --onion do not mix. @@ -910,7 +816,7 @@ func loadConfig() (*config, []string, error) { } // Check the checkpoints for syntax errors. - cfg.addCheckpoints, err = parseCheckpoints(cfg.AddCheckpoints) + cfg.AddCheckpoints, err = parseCheckpoints(cfg.configFlags.AddCheckpoints) if err != nil { str := "%s: Error parsing checkpoints: %v" err := fmt.Errorf(str, funcName, err) @@ -935,8 +841,8 @@ func loadConfig() (*config, []string, error) { // 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). - cfg.dial = net.DialTimeout - cfg.lookup = net.LookupIP + cfg.Dial = net.DialTimeout + cfg.Lookup = net.LookupIP if cfg.Proxy != "" { _, _, err := net.SplitHostPort(cfg.Proxy) if err != nil { @@ -965,13 +871,13 @@ func loadConfig() (*config, []string, error) { Password: cfg.ProxyPass, TorIsolation: torIsolation, } - cfg.dial = proxy.DialTimeout + cfg.Dial = proxy.DialTimeout // Treat the proxy as tor and perform DNS resolution through it // unless the --noonion flag is set or there is an // onion-specific proxy configured. if !cfg.NoOnion && cfg.OnionProxy == "" { - cfg.lookup = func(host string) ([]net.IP, error) { + cfg.Lookup = func(host string) ([]net.IP, error) { return connmgr.TorLookupIP(host, cfg.Proxy) } } @@ -1002,7 +908,7 @@ func loadConfig() (*config, []string, error) { "credentials ") } - cfg.oniondial = func(network, addr string, timeout time.Duration) (net.Conn, error) { + cfg.OnionDial = func(network, addr string, timeout time.Duration) (net.Conn, error) { proxy := &socks.Proxy{ Addr: cfg.OnionProxy, Username: cfg.OnionProxyUser, @@ -1017,18 +923,18 @@ func loadConfig() (*config, []string, error) { // not a tor proxy, so override the DNS resolution to use the // onion-specific proxy. if cfg.Proxy != "" { - cfg.lookup = func(host string) ([]net.IP, error) { + cfg.Lookup = func(host string) ([]net.IP, error) { return connmgr.TorLookupIP(host, cfg.OnionProxy) } } } else { - cfg.oniondial = cfg.dial + cfg.OnionDial = cfg.Dial } // Specifying --noonion means the onion address dial function results in // an error. if cfg.NoOnion { - cfg.oniondial = func(a, b string, t time.Duration) (net.Conn, error) { + cfg.OnionDial = func(a, b string, t time.Duration) (net.Conn, error) { return nil, errors.New("tor has been disabled") } } @@ -1037,7 +943,7 @@ func loadConfig() (*config, []string, error) { // 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) + log.Warnf("%v", configFileError) } return &cfg, remainingArgs, nil @@ -1110,30 +1016,7 @@ func createDefaultConfigFile(destinationPath string) error { return nil } -// 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(addr net.Addr) (net.Conn, error) { - if strings.Contains(addr.String(), ".onion:") { - return cfg.oniondial(addr.Network(), addr.String(), - defaultConnectTimeout) - } - return cfg.dial(addr.Network(), addr.String(), defaultConnectTimeout) -} - -// btcdLookup resolves the IP of the given host using the correct DNS lookup -// function depending on the configuration options. For example, addresses will -// be resolved using tor when the --proxy flag was specified unless --noonion -// was also specified in which case the normal system DNS resolver will be used. -// -// Any attempt to resolve a tor address (.onion) will return an error since they -// are not intended to be resolved outside of the tor proxy. -func btcdLookup(host string) ([]net.IP, error) { - if strings.HasSuffix(host, ".onion") { - return nil, fmt.Errorf("attempt to resolve tor address %s", host) - } - - return cfg.lookup(host) +//ActiveNetParams returns a pointer to the current active net params +func ActiveNetParams() *dagconfig.Params { + return activeNetParams } diff --git a/config_test.go b/config/config_test.go similarity index 94% rename from config_test.go rename to config/config_test.go index e54a9f5f2..918924ef2 100644 --- a/config_test.go +++ b/config/config_test.go @@ -1,4 +1,4 @@ -package main +package config import ( "io/ioutil" @@ -20,7 +20,7 @@ func TestCreateDefaultConfigFile(t *testing.T) { if !ok { t.Fatalf("Failed finding config file path") } - sampleConfigFile := filepath.Join(filepath.Dir(path), "sample-btcd.conf") + sampleConfigFile := filepath.Join(filepath.Dir(path), "..", "sample-btcd.conf") // Setup a temporary directory tmpDir, err := ioutil.TempDir("", "btcd") diff --git a/config/log.go b/config/log.go new file mode 100644 index 000000000..60180bb7a --- /dev/null +++ b/config/log.go @@ -0,0 +1,19 @@ +// 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. + +package config + +import ( + "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/logger" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log btclog.Logger + +func init() { + log, _ = logger.Get(logger.SubsystemTags.CNFG) +} diff --git a/connmgr/log.go b/connmgr/log.go index 1afa7ee64..c0125ef8e 100644 --- a/connmgr/log.go +++ b/connmgr/log.go @@ -4,7 +4,10 @@ package connmgr -import "github.com/btcsuite/btclog" +import ( + "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/logger" +) // log is a logger that is initialized with no output filters. This // means the package will not perform any logging by default until the caller @@ -13,18 +16,6 @@ var log btclog.Logger // The default amount of logging is none. func init() { - DisableLog() -} + log, _ = logger.Get(logger.SubsystemTags.CMGR) -// DisableLog disables all library log output. Logging output is disabled -// by default until either UseLogger or SetLogWriter are called. -func DisableLog() { - log = btclog.Disabled -} - -// UseLogger uses a specified Logger to output package logging info. -// This should be used in preference to SetLogWriter if the caller is also -// using btclog. -func UseLogger(logger btclog.Logger) { - log = logger } diff --git a/dagconfig/params.go b/dagconfig/params.go index b9f0d2f4b..48f326f50 100644 --- a/dagconfig/params.go +++ b/dagconfig/params.go @@ -159,6 +159,9 @@ type Params struct { // Net defines the magic bytes used to identify the network. Net wire.BitcoinNet + // RPCPort defines the rpc server port + RPCPort string + // DefaultPort defines the default peer-to-peer port for the network. DefaultPort string @@ -266,6 +269,7 @@ var MainNetParams = Params{ K: phantomK, Name: "mainnet", Net: wire.MainNet, + RPCPort: "8334", DefaultPort: "8333", DNSSeeds: []DNSSeed{ {"seed.bitcoin.sipa.be", true}, @@ -353,6 +357,7 @@ var RegressionNetParams = Params{ K: phantomK, Name: "regtest", Net: wire.TestNet, + RPCPort: "18334", DefaultPort: "18444", DNSSeeds: []DNSSeed{}, @@ -414,6 +419,7 @@ var TestNet3Params = Params{ K: phantomK, Name: "testnet3", Net: wire.TestNet3, + RPCPort: "18334", DefaultPort: "18333", DNSSeeds: []DNSSeed{ {"testnet-seed.bitcoin.jonasschnelli.ch", true}, @@ -496,6 +502,7 @@ var SimNetParams = Params{ K: phantomK, Name: "simnet", Net: wire.SimNet, + RPCPort: "18556", DefaultPort: "18555", DNSSeeds: []DNSSeed{}, // NOTE: There must NOT be any seeds. diff --git a/database/cmd/dbtool/main.go b/database/cmd/dbtool/main.go index 6576605b0..20424b3f5 100644 --- a/database/cmd/dbtool/main.go +++ b/database/cmd/dbtool/main.go @@ -10,8 +10,9 @@ import ( "runtime" "strings" - "github.com/daglabs/btcd/database" "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/database" + "github.com/daglabs/btcd/logger" flags "github.com/jessevdk/go-flags" ) @@ -64,9 +65,8 @@ func realMain() error { backendLogger := btclog.NewBackend(os.Stdout) defer os.Stdout.Sync() log = backendLogger.Logger("MAIN") - dbLog := backendLogger.Logger("BCDB") + dbLog, _ := logger.Get(logger.SubsystemTags.BCDB) dbLog.SetLevel(btclog.LevelDebug) - database.UseLogger(dbLog) // Setup the parser options and commands. appName := filepath.Base(os.Args[0]) diff --git a/database/log.go b/database/log.go index d2c5c44e3..461288e7a 100644 --- a/database/log.go +++ b/database/log.go @@ -6,6 +6,7 @@ package database import ( "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/logger" ) // log is a logger that is initialized with no output filters. This @@ -15,23 +16,11 @@ var log btclog.Logger // The default amount of logging is none. func init() { - DisableLog() -} - -// DisableLog disables all library log output. Logging output is disabled -// by default until UseLogger is called. -func DisableLog() { - log = btclog.Disabled -} - -// UseLogger uses a specified Logger to output package logging info. -func UseLogger(logger btclog.Logger) { - log = logger - + log, _ = logger.Get(logger.SubsystemTags.BCDB) // Update the logger for the registered drivers. for _, drv := range drivers { if drv.UseLogger != nil { - drv.UseLogger(logger) + drv.UseLogger(log) } } } diff --git a/log.go b/log.go index 4199fd565..fcd4fdc9d 100644 --- a/log.go +++ b/log.go @@ -6,164 +6,8 @@ package main import ( - "fmt" - "os" - "path/filepath" - - "github.com/daglabs/btcd/addrmgr" - "github.com/daglabs/btcd/blockdag" - "github.com/daglabs/btcd/blockdag/indexers" - "github.com/daglabs/btcd/connmgr" - "github.com/daglabs/btcd/database" - "github.com/daglabs/btcd/mempool" - "github.com/daglabs/btcd/mining" - "github.com/daglabs/btcd/mining/cpuminer" - "github.com/daglabs/btcd/netsync" - "github.com/daglabs/btcd/peer" - "github.com/daglabs/btcd/txscript" - - "github.com/btcsuite/btclog" - "github.com/jrick/logrotate/rotator" + "github.com/daglabs/btcd/logger" ) -// logWriter implements an io.Writer that outputs to both standard output and -// the write-end pipe of an initialized log rotator. -type logWriter struct{} - -func (logWriter) Write(p []byte) (n int, err error) { - os.Stdout.Write(p) - logRotator.Write(p) - return len(p), nil -} - -// Loggers per subsystem. A single backend logger is created and all subsytem -// loggers created from it will write to the backend. When adding new -// subsystems, add the subsystem logger variable here and to the -// subsystemLoggers map. -// -// Loggers can not be used before the log rotator has been initialized with a -// log file. This must be performed early during application startup by calling -// initLogRotator. -var ( - // backendLog is the logging backend used to create all subsystem loggers. - // The backend must not be used before the log rotator has been initialized, - // or data races and/or nil pointer dereferences will occur. - backendLog = btclog.NewBackend(logWriter{}) - - // logRotator is one of the logging outputs. It should be closed on - // application shutdown. - logRotator *rotator.Rotator - - adxrLog = backendLog.Logger("ADXR") - amgrLog = backendLog.Logger("AMGR") - cmgrLog = backendLog.Logger("CMGR") - bcdbLog = backendLog.Logger("BCDB") - btcdLog = backendLog.Logger("BTCD") - chanLog = backendLog.Logger("CHAN") - discLog = backendLog.Logger("DISC") - indxLog = backendLog.Logger("INDX") - minrLog = backendLog.Logger("MINR") - peerLog = backendLog.Logger("PEER") - rpcsLog = backendLog.Logger("RPCS") - scrpLog = backendLog.Logger("SCRP") - srvrLog = backendLog.Logger("SRVR") - syncLog = backendLog.Logger("SYNC") - txmpLog = backendLog.Logger("TXMP") -) - -// Initialize package-global logger variables. -func init() { - addrmgr.UseLogger(amgrLog) - connmgr.UseLogger(cmgrLog) - database.UseLogger(bcdbLog) - blockdag.UseLogger(chanLog) - indexers.UseLogger(indxLog) - mining.UseLogger(minrLog) - cpuminer.UseLogger(minrLog) - peer.UseLogger(peerLog) - txscript.UseLogger(scrpLog) - netsync.UseLogger(syncLog) - mempool.UseLogger(txmpLog) -} - -// subsystemLoggers maps each subsystem identifier to its associated logger. -var subsystemLoggers = map[string]btclog.Logger{ - "ADXR": adxrLog, - "AMGR": amgrLog, - "CMGR": cmgrLog, - "BCDB": bcdbLog, - "BTCD": btcdLog, - "CHAN": chanLog, - "DISC": discLog, - "INDX": indxLog, - "MINR": minrLog, - "PEER": peerLog, - "RPCS": rpcsLog, - "SCRP": scrpLog, - "SRVR": srvrLog, - "SYNC": syncLog, - "TXMP": txmpLog, -} - -// initLogRotator initializes the logging rotater to write logs to logFile and -// create roll files in the same directory. It must be called before the -// package-global log rotater variables are used. -func initLogRotator(logFile string) { - logDir, _ := filepath.Split(logFile) - err := os.MkdirAll(logDir, 0700) - if err != nil { - fmt.Fprintf(os.Stderr, "failed to create log directory: %v\n", err) - os.Exit(1) - } - r, err := rotator.New(logFile, 10*1024, false, 3) - if err != nil { - fmt.Fprintf(os.Stderr, "failed to create file rotator: %v\n", err) - os.Exit(1) - } - - logRotator = r -} - -// setLogLevel sets the logging level for provided subsystem. Invalid -// subsystems are ignored. Uninitialized subsystems are dynamically created as -// needed. -func setLogLevel(subsystemID string, logLevel string) { - // Ignore invalid subsystems. - logger, ok := subsystemLoggers[subsystemID] - if !ok { - return - } - - // Defaults to info if the log level is invalid. - level, _ := btclog.LevelFromString(logLevel) - logger.SetLevel(level) -} - -// setLogLevels sets the log level for all subsystem loggers to the passed -// level. It also dynamically creates the subsystem loggers as needed, so it -// can be used to initialize the logging system. -func setLogLevels(logLevel string) { - // Configure all sub-systems with the new logging level. Dynamically - // create loggers as needed. - for subsystemID := range subsystemLoggers { - setLogLevel(subsystemID, logLevel) - } -} - -// directionString is a helper function that returns a string that represents -// the direction of a connection (inbound or outbound). -func directionString(inbound bool) string { - if inbound { - return "inbound" - } - return "outbound" -} - -// pickNoun returns the singular or plural form of a noun depending -// on the count n. -func pickNoun(n uint64, singular, plural string) string { - if n == 1 { - return singular - } - return plural -} +var btcdLog, _ = logger.Get(logger.SubsystemTags.BTCD) +var srvrLog, _ = logger.Get(logger.SubsystemTags.SRVR) diff --git a/logger/logger.go b/logger/logger.go new file mode 100644 index 000000000..25f49f33b --- /dev/null +++ b/logger/logger.go @@ -0,0 +1,277 @@ +// Copyright (c) 2013-2017 The btcsuite developers +// Copyright (c) 2017 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package logger + +import ( + "fmt" + "os" + "path/filepath" + "sort" + "strings" + + "github.com/btcsuite/btclog" + "github.com/jrick/logrotate/rotator" +) + +// logWriter implements an io.Writer that outputs to both standard output and +// the write-end pipe of an initialized log rotator. +type logWriter struct{} + +func (logWriter) Write(p []byte) (n int, err error) { + if initiated { + os.Stdout.Write(p) + LogRotator.Write(p) + } + return len(p), nil +} + +// Loggers per subsystem. A single backend logger is created and all subsytem +// loggers created from it will write to the backend. When adding new +// subsystems, add the subsystem logger variable here and to the +// subsystemLoggers map. +// +// Loggers can not be used before the log rotator has been initialized with a +// log file. This must be performed early during application startup by calling +// InitLogRotator. +var ( + // backendLog is the logging backend used to create all subsystem loggers. + // The backend must not be used before the log rotator has been initialized, + // or data races and/or nil pointer dereferences will occur. + backendLog = btclog.NewBackend(logWriter{}) + + // LogRotator is one of the logging outputs. It should be closed on + // application shutdown. + LogRotator *rotator.Rotator + + adxrLog = backendLog.Logger("ADXR") + amgrLog = backendLog.Logger("AMGR") + cmgrLog = backendLog.Logger("CMGR") + bcdbLog = backendLog.Logger("BCDB") + btcdLog = backendLog.Logger("BTCD") + chanLog = backendLog.Logger("CHAN") + discLog = backendLog.Logger("DISC") + indxLog = backendLog.Logger("INDX") + minrLog = backendLog.Logger("MINR") + peerLog = backendLog.Logger("PEER") + rpcsLog = backendLog.Logger("RPCS") + scrpLog = backendLog.Logger("SCRP") + srvrLog = backendLog.Logger("SRVR") + syncLog = backendLog.Logger("SYNC") + txmpLog = backendLog.Logger("TXMP") + cnfgLog = backendLog.Logger("CNFG") + + initiated = false +) + +// SubsystemTags is an enum of all sub system tags +var SubsystemTags = struct { + ADXR, + AMGR, + CMGR, + BCDB, + BTCD, + CHAN, + DISC, + INDX, + MINR, + PEER, + RPCS, + SCRP, + SRVR, + SYNC, + TXMP, + CNFG string +}{ + ADXR: "ADXR", + AMGR: "AMGR", + CMGR: "CMGR", + BCDB: "BCDB", + BTCD: "BTCD", + CHAN: "CHAN", + DISC: "DISC", + INDX: "INDX", + MINR: "MINR", + PEER: "PEER", + RPCS: "RPCS", + SCRP: "SCRP", + SRVR: "SRVR", + SYNC: "SYNC", + TXMP: "TXMP", + CNFG: "CNFG", +} + +// subsystemLoggers maps each subsystem identifier to its associated logger. +var subsystemLoggers = map[string]btclog.Logger{ + SubsystemTags.ADXR: adxrLog, + SubsystemTags.AMGR: amgrLog, + SubsystemTags.CMGR: cmgrLog, + SubsystemTags.BCDB: bcdbLog, + SubsystemTags.BTCD: btcdLog, + SubsystemTags.CHAN: chanLog, + SubsystemTags.DISC: discLog, + SubsystemTags.INDX: indxLog, + SubsystemTags.MINR: minrLog, + SubsystemTags.PEER: peerLog, + SubsystemTags.RPCS: rpcsLog, + SubsystemTags.SCRP: scrpLog, + SubsystemTags.SRVR: srvrLog, + SubsystemTags.SYNC: syncLog, + SubsystemTags.TXMP: txmpLog, +} + +// InitLogRotator initializes the logging rotater to write logs to logFile and +// create roll files in the same directory. It must be called before the +// package-global log rotater variables are used. +func InitLogRotator(logFile string) { + initiated = true + logDir, _ := filepath.Split(logFile) + err := os.MkdirAll(logDir, 0700) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to create log directory: %v\n", err) + os.Exit(1) + } + r, err := rotator.New(logFile, 10*1024, false, 3) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to create file rotator: %v\n", err) + os.Exit(1) + } + + LogRotator = r +} + +// SetLogLevel sets the logging level for provided subsystem. Invalid +// subsystems are ignored. Uninitialized subsystems are dynamically created as +// needed. +func SetLogLevel(subsystemID string, logLevel string) { + // Ignore invalid subsystems. + logger, ok := subsystemLoggers[subsystemID] + if !ok { + return + } + + // Defaults to info if the log level is invalid. + level, _ := btclog.LevelFromString(logLevel) + logger.SetLevel(level) +} + +// SetLogLevels sets the log level for all subsystem loggers to the passed +// level. It also dynamically creates the subsystem loggers as needed, so it +// can be used to initialize the logging system. +func SetLogLevels(logLevel string) { + // Configure all sub-systems with the new logging level. Dynamically + // create loggers as needed. + for subsystemID := range subsystemLoggers { + SetLogLevel(subsystemID, logLevel) + } +} + +// DirectionString is a helper function that returns a string that represents +// the direction of a connection (inbound or outbound). +func DirectionString(inbound bool) string { + if inbound { + return "inbound" + } + return "outbound" +} + +// PickNoun returns the singular or plural form of a noun depending +// on the count n. +func PickNoun(n uint64, singular, plural string) string { + if n == 1 { + return singular + } + return plural +} + +// 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 subsystems for stable display. + sort.Strings(subsystems) + return subsystems +} + +// Get returns a logger of a specific sub system +func Get(tag string) (logger btclog.Logger, ok bool) { + logger, ok = subsystemLoggers[tag] + return +} + +// 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 := Get(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 +} + +// 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 +} diff --git a/mempool/log.go b/mempool/log.go index 1381af33f..caf0f2a55 100644 --- a/mempool/log.go +++ b/mempool/log.go @@ -6,6 +6,7 @@ package mempool import ( "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/logger" ) // log is a logger that is initialized with no output filters. This @@ -13,22 +14,8 @@ import ( // requests it. var log btclog.Logger -// The default amount of logging is none. func init() { - DisableLog() -} - -// DisableLog disables all library log output. Logging output is disabled -// by default until either UseLogger or SetLogWriter are called. -func DisableLog() { - log = btclog.Disabled -} - -// UseLogger uses a specified Logger to output package logging info. -// This should be used in preference to SetLogWriter if the caller is also -// using btclog. -func UseLogger(logger btclog.Logger) { - log = logger + log, _ = logger.Get(logger.SubsystemTags.TXMP) } // pickNoun returns the singular or plural form of a noun depending diff --git a/mining/cpuminer/log.go b/mining/cpuminer/log.go index 22c4dc729..76b0158e6 100644 --- a/mining/cpuminer/log.go +++ b/mining/cpuminer/log.go @@ -6,6 +6,7 @@ package cpuminer import ( "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/logger" ) // log is a logger that is initialized with no output filters. This @@ -13,18 +14,6 @@ import ( // requests it. var log btclog.Logger -// The default amount of logging is none. func init() { - DisableLog() -} - -// DisableLog disables all library log output. Logging output is disabled -// by default until UseLogger is called. -func DisableLog() { - log = btclog.Disabled -} - -// UseLogger uses a specified Logger to output package logging info. -func UseLogger(logger btclog.Logger) { - log = logger + log, _ = logger.Get(logger.SubsystemTags.MINR) } diff --git a/mining/log.go b/mining/log.go index 5e792d9af..82224c76d 100644 --- a/mining/log.go +++ b/mining/log.go @@ -6,6 +6,7 @@ package mining import ( "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/logger" ) // log is a logger that is initialized with no output filters. This @@ -13,18 +14,6 @@ import ( // requests it. var log btclog.Logger -// The default amount of logging is none. func init() { - DisableLog() -} - -// DisableLog disables all library log output. Logging output is disabled -// by default until UseLogger is called. -func DisableLog() { - log = btclog.Disabled -} - -// UseLogger uses a specified Logger to output package logging info. -func UseLogger(logger btclog.Logger) { - log = logger + log, _ = logger.Get(logger.SubsystemTags.MINR) } diff --git a/netsync/log.go b/netsync/log.go index cf12b2c70..1e7f28a6c 100644 --- a/netsync/log.go +++ b/netsync/log.go @@ -4,22 +4,16 @@ package netsync -import "github.com/btcsuite/btclog" +import ( + "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/logger" +) // log is a logger that is initialized with no output filters. This // means the package will not perform any logging by default until the caller // requests it. var log btclog.Logger -// DisableLog disables all library log output. Logging output is disabled -// by default until either UseLogger or SetLogWriter are called. -func DisableLog() { - log = btclog.Disabled -} - -// UseLogger uses a specified Logger to output package logging info. -// This should be used in preference to SetLogWriter if the caller is also -// using btclog. -func UseLogger(logger btclog.Logger) { - log = logger +func init() { + log, _ = logger.Get(logger.SubsystemTags.SYNC) } diff --git a/params.go b/params.go deleted file mode 100644 index 3fb6cf138..000000000 --- a/params.go +++ /dev/null @@ -1,74 +0,0 @@ -// 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. - -package main - -import ( - "github.com/daglabs/btcd/dagconfig" - "github.com/daglabs/btcd/wire" -) - -// activeNetParams is a pointer to the parameters specific to the -// currently active bitcoin network. -var activeNetParams = &mainNetParams - -// params is used to group parameters for various networks such as the main -// network and test networks. -type params struct { - *dagconfig.Params - rpcPort string -} - -// mainNetParams contains parameters specific to the main network -// (wire.MainNet). NOTE: The RPC port is intentionally different than the -// reference implementation because btcd does not handle wallet requests. The -// separate wallet process listens on the well-known port and forwards requests -// it does not handle on to btcd. This approach allows the wallet process -// to emulate the full reference implementation RPC API. -var mainNetParams = params{ - Params: &dagconfig.MainNetParams, - rpcPort: "8334", -} - -// regressionNetParams contains parameters specific to the regression test -// network (wire.TestNet). NOTE: The RPC port is intentionally different -// than the reference implementation - see the mainNetParams comment for -// details. -var regressionNetParams = params{ - Params: &dagconfig.RegressionNetParams, - rpcPort: "18334", -} - -// testNet3Params contains parameters specific to the test network (version 3) -// (wire.TestNet3). NOTE: The RPC port is intentionally different than the -// reference implementation - see the mainNetParams comment for details. -var testNet3Params = params{ - Params: &dagconfig.TestNet3Params, - rpcPort: "18334", -} - -// simNetParams contains parameters specific to the simulation test network -// (wire.SimNet). -var simNetParams = params{ - Params: &dagconfig.SimNetParams, - rpcPort: "18556", -} - -// netName returns the name used when referring to a bitcoin network. At the -// time of writing, btcd currently places blocks for testnet version 3 in the -// data and log directory "testnet", which does not match the Name field of the -// dagconfig parameters. This function can be used to override this directory -// name as "testnet" when the passed active network matches wire.TestNet3. -// -// A proper upgrade to move the data and log directories for this network to -// "testnet3" is planned for the future, at which point this function can be -// removed and the network parameter's name used instead. -func netName(chainParams *params) string { - switch chainParams.Net { - case wire.TestNet3: - return "testnet" - default: - return chainParams.Name - } -} diff --git a/peer/log.go b/peer/log.go index c980548e7..432013a31 100644 --- a/peer/log.go +++ b/peer/log.go @@ -11,6 +11,7 @@ import ( "github.com/btcsuite/btclog" "github.com/daglabs/btcd/dagconfig/daghash" + "github.com/daglabs/btcd/logger" "github.com/daglabs/btcd/txscript" "github.com/daglabs/btcd/wire" ) @@ -28,18 +29,7 @@ var log btclog.Logger // The default amount of logging is none. func init() { - DisableLog() -} - -// DisableLog disables all library log output. Logging output is disabled -// by default until UseLogger is called. -func DisableLog() { - log = btclog.Disabled -} - -// UseLogger uses a specified Logger to output package logging info. -func UseLogger(logger btclog.Logger) { - log = logger + log, _ = logger.Get(logger.SubsystemTags.PEER) } // LogClosure is a closure that can be printed with %v to be used to @@ -55,15 +45,6 @@ func newLogClosure(c func() string) logClosure { return logClosure(c) } -// directionString is a helper function that returns a string that represents -// the direction of a connection (inbound or outbound). -func directionString(inbound bool) string { - if inbound { - return "inbound" - } - return "outbound" -} - // formatLockTime returns a transaction lock time as a human-readable string. func formatLockTime(lockTime uint64) string { // The lock time field of a transaction is either a block height at diff --git a/peer/peer.go b/peer/peer.go index 6b9a5b5be..20808f1ac 100644 --- a/peer/peer.go +++ b/peer/peer.go @@ -21,6 +21,7 @@ import ( "github.com/daglabs/btcd/blockdag" "github.com/daglabs/btcd/dagconfig" "github.com/daglabs/btcd/dagconfig/daghash" + "github.com/daglabs/btcd/logger" "github.com/daglabs/btcd/wire" "github.com/davecgh/go-spew/spew" ) @@ -475,7 +476,7 @@ type Peer struct { // // This function is safe for concurrent access. func (p *Peer) String() string { - return fmt.Sprintf("%s (%s)", p.addr, directionString(p.inbound)) + return fmt.Sprintf("%s (%s)", p.addr, logger.DirectionString(p.inbound)) } // UpdateLastBlockHeight updates the last known block for the peer. diff --git a/server/log.go b/server/log.go new file mode 100644 index 000000000..389e89b0e --- /dev/null +++ b/server/log.go @@ -0,0 +1,24 @@ +// 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. + +package server + +import ( + "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/logger" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var srvrLog, peerLog, txmpLog, indxLog, rpcsLog, amgrLog btclog.Logger + +func init() { + srvrLog, _ = logger.Get(logger.SubsystemTags.SRVR) + peerLog, _ = logger.Get(logger.SubsystemTags.PEER) + txmpLog, _ = logger.Get(logger.SubsystemTags.TXMP) + indxLog, _ = logger.Get(logger.SubsystemTags.INDX) + rpcsLog, _ = logger.Get(logger.SubsystemTags.RPCS) + amgrLog, _ = logger.Get(logger.SubsystemTags.AMGR) +} diff --git a/server/p2p/log.go b/server/p2p/log.go new file mode 100644 index 000000000..e5bd3399b --- /dev/null +++ b/server/p2p/log.go @@ -0,0 +1,24 @@ +// 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. + +package p2p + +import ( + "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/logger" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var srvrLog, peerLog, txmpLog, indxLog, rpcsLog, amgrLog btclog.Logger + +func init() { + srvrLog, _ = logger.Get(logger.SubsystemTags.SRVR) + peerLog, _ = logger.Get(logger.SubsystemTags.PEER) + txmpLog, _ = logger.Get(logger.SubsystemTags.TXMP) + indxLog, _ = logger.Get(logger.SubsystemTags.INDX) + rpcsLog, _ = logger.Get(logger.SubsystemTags.RPCS) + amgrLog, _ = logger.Get(logger.SubsystemTags.AMGR) +} diff --git a/server.go b/server/p2p/p2p.go similarity index 79% rename from server.go rename to server/p2p/p2p.go index f02aac0c3..f2825d3d1 100644 --- a/server.go +++ b/server/p2p/p2p.go @@ -3,12 +3,11 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package main +package p2p import ( "bytes" "crypto/rand" - "crypto/tls" "encoding/binary" "errors" "fmt" @@ -25,16 +24,18 @@ import ( "github.com/daglabs/btcd/addrmgr" "github.com/daglabs/btcd/blockdag" "github.com/daglabs/btcd/blockdag/indexers" + "github.com/daglabs/btcd/config" "github.com/daglabs/btcd/connmgr" "github.com/daglabs/btcd/dagconfig" "github.com/daglabs/btcd/dagconfig/daghash" "github.com/daglabs/btcd/database" + "github.com/daglabs/btcd/logger" "github.com/daglabs/btcd/mempool" - "github.com/daglabs/btcd/mining" - "github.com/daglabs/btcd/mining/cpuminer" "github.com/daglabs/btcd/netsync" "github.com/daglabs/btcd/peer" + "github.com/daglabs/btcd/server/serverutils" "github.com/daglabs/btcd/txscript" + "github.com/daglabs/btcd/version" "github.com/daglabs/btcd/wire" "github.com/daglabs/btcutil" "github.com/daglabs/btcutil/bloom" @@ -65,7 +66,7 @@ var ( // userAgentVersion is the user agent version and is used to help // identify ourselves to other bitcoin peers. - userAgentVersion = fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch) + userAgentVersion = fmt.Sprintf("%d.%d.%d", version.AppMajor, version.AppMinor, version.AppPatch) ) // zeroHash is the zero value hash (all zeros). It is defined as a convenience. @@ -119,7 +120,7 @@ var _ net.Addr = simpleAddr{} // to all connected peers except specified excluded peers. type broadcastMsg struct { message wire.Message - excludePeers []*serverPeer + excludePeers []*Peer } // broadcastInventoryAdd is a type used to declare that the InvVect it contains @@ -149,12 +150,37 @@ type updatePeerHeightsMsg struct { originPeer *peer.Peer } +// Peer extends the peer to maintain state shared by the server and +// the blockmanager. +type Peer struct { + // The following variables must only be used atomically + FeeFilterInt int64 + + *peer.Peer + + connReq *connmgr.ConnReq + server *Server + persistent bool + continueHash *daghash.Hash + relayMtx sync.Mutex + DisableRelayTx bool + sentAddrs bool + isWhitelisted bool + filter *bloom.Filter + knownAddresses map[string]struct{} + DynamicBanScore connmgr.DynamicBanScore + quit chan struct{} + // The following chans are used to sync blockmanager and server. + txProcessed chan struct{} + blockProcessed chan struct{} +} + // peerState maintains state of inbound, persistent, outbound peers as well // as banned peers and outbound groups. type peerState struct { - inboundPeers map[int32]*serverPeer - outboundPeers map[int32]*serverPeer - persistentPeers map[int32]*serverPeer + inboundPeers map[int32]*Peer + outboundPeers map[int32]*Peer + persistentPeers map[int32]*Peer banned map[string]time.Time outboundGroups map[string]int } @@ -167,7 +193,7 @@ func (ps *peerState) Count() int { // forAllOutboundPeers is a helper function that runs closure on all outbound // peers known to peerState. -func (ps *peerState) forAllOutboundPeers(closure func(sp *serverPeer)) { +func (ps *peerState) forAllOutboundPeers(closure func(sp *Peer)) { for _, e := range ps.outboundPeers { closure(e) } @@ -178,7 +204,7 @@ func (ps *peerState) forAllOutboundPeers(closure func(sp *serverPeer)) { // forAllPeers is a helper function that runs closure on all peers known to // peerState. -func (ps *peerState) forAllPeers(closure func(sp *serverPeer)) { +func (ps *peerState) forAllPeers(closure func(sp *Peer)) { for _, e := range ps.inboundPeers { closure(e) } @@ -192,9 +218,9 @@ type cfHeaderKV struct { filterHeader daghash.Hash } -// server provides a bitcoin server for handling communications to and from +// Server provides a bitcoin server for handling communications to and from // bitcoin peers. -type server struct { +type Server struct { // The following variables must only be used atomically. // Putting the uint64s first makes them 64-bit aligned for 32-bit systems. bytesReceived uint64 // Total bytes received from all peers since start. @@ -202,79 +228,55 @@ type server struct { started int32 shutdown int32 shutdownSched int32 - startupTime int64 - chainParams *dagconfig.Params - addrManager *addrmgr.AddrManager - connManager *connmgr.ConnManager - sigCache *txscript.SigCache - rpcServer *rpcServer - syncManager *netsync.SyncManager - dag *blockdag.BlockDAG - txMemPool *mempool.TxPool - cpuMiner *cpuminer.CPUMiner + DAGParams *dagconfig.Params + addrManager *addrmgr.AddrManager + connManager *connmgr.ConnManager + SigCache *txscript.SigCache + SyncManager *netsync.SyncManager + DAG *blockdag.BlockDAG + TxMemPool *mempool.TxPool + modifyRebroadcastInv chan interface{} - newPeers chan *serverPeer - donePeers chan *serverPeer - banPeers chan *serverPeer - query chan interface{} + newPeers chan *Peer + donePeers chan *Peer + banPeers chan *Peer + Query chan interface{} relayInv chan relayMsg broadcast chan broadcastMsg peerHeightsUpdate chan updatePeerHeightsMsg wg sync.WaitGroup quit chan struct{} - nat NAT + nat serverutils.NAT db database.DB - timeSource blockdag.MedianTimeSource + TimeSource blockdag.MedianTimeSource services wire.ServiceFlag // The following fields are used for optional indexes. They will be nil // if the associated index is not enabled. These fields are set during // initial creation of the server and never changed afterwards, so they // do not need to be protected for concurrent access. - txIndex *indexers.TxIndex - addrIndex *indexers.AddrIndex - cfIndex *indexers.CfIndex + TxIndex *indexers.TxIndex + AddrIndex *indexers.AddrIndex + CfIndex *indexers.CfIndex // The fee estimator keeps track of how long transactions are left in // the mempool before they are mined into blocks. - feeEstimator *mempool.FeeEstimator + FeeEstimator *mempool.FeeEstimator // cfCheckptCaches stores a cached slice of filter headers for cfcheckpt // messages for each filter type. cfCheckptCaches map[wire.FilterType][]cfHeaderKV cfCheckptCachesMtx sync.RWMutex -} -// serverPeer extends the peer to maintain state shared by the server and -// the blockmanager. -type serverPeer struct { - // The following variables must only be used atomically - feeFilter int64 - - *peer.Peer - - connReq *connmgr.ConnReq - server *server - persistent bool - continueHash *daghash.Hash - relayMtx sync.Mutex - disableRelayTx bool - sentAddrs bool - isWhitelisted bool - filter *bloom.Filter - knownAddresses map[string]struct{} - banScore connmgr.DynamicBanScore - quit chan struct{} - // The following chans are used to sync blockmanager and server. - txProcessed chan struct{} - blockProcessed chan struct{} + notifyNewTransactions func(txns []*mempool.TxDesc) + isRPCServerActive bool } // newServerPeer returns a new serverPeer instance. The peer needs to be set by // the caller. -func newServerPeer(s *server, isPersistent bool) *serverPeer { - return &serverPeer{ +func newServerPeer(s *Server, isPersistent bool) *Peer { + return &Peer{ server: s, persistent: isPersistent, filter: bloom.LoadFilter(nil), @@ -287,39 +289,39 @@ func newServerPeer(s *server, isPersistent bool) *serverPeer { // newestBlock returns the current best block hash and height using the format // required by the configuration for the peer package. -func (sp *serverPeer) newestBlock() (*daghash.Hash, int32, error) { - dagState := sp.server.dag.GetDAGState() +func (sp *Peer) newestBlock() (*daghash.Hash, int32, error) { + dagState := sp.server.DAG.GetDAGState() return &dagState.SelectedTip.Hash, dagState.SelectedTip.Height, nil } // addKnownAddresses adds the given addresses to the set of known addresses to // the peer to prevent sending duplicate addresses. -func (sp *serverPeer) addKnownAddresses(addresses []*wire.NetAddress) { +func (sp *Peer) addKnownAddresses(addresses []*wire.NetAddress) { for _, na := range addresses { sp.knownAddresses[addrmgr.NetAddressKey(na)] = struct{}{} } } // addressKnown true if the given address is already known to the peer. -func (sp *serverPeer) addressKnown(na *wire.NetAddress) bool { +func (sp *Peer) addressKnown(na *wire.NetAddress) bool { _, exists := sp.knownAddresses[addrmgr.NetAddressKey(na)] return exists } // setDisableRelayTx toggles relaying of transactions for the given peer. // It is safe for concurrent access. -func (sp *serverPeer) setDisableRelayTx(disable bool) { +func (sp *Peer) setDisableRelayTx(disable bool) { sp.relayMtx.Lock() - sp.disableRelayTx = disable + sp.DisableRelayTx = disable sp.relayMtx.Unlock() } // relayTxDisabled returns whether or not relaying of transactions for the given // peer is disabled. // It is safe for concurrent access. -func (sp *serverPeer) relayTxDisabled() bool { +func (sp *Peer) relayTxDisabled() bool { sp.relayMtx.Lock() - isDisabled := sp.disableRelayTx + isDisabled := sp.DisableRelayTx sp.relayMtx.Unlock() return isDisabled @@ -327,7 +329,7 @@ func (sp *serverPeer) relayTxDisabled() bool { // pushAddrMsg sends an addr message to the connected peer using the provided // addresses. -func (sp *serverPeer) pushAddrMsg(addresses []*wire.NetAddress) { +func (sp *Peer) pushAddrMsg(addresses []*wire.NetAddress) { // Filter addresses already known to the peer. addrs := make([]*wire.NetAddress, 0, len(addresses)) for _, addr := range addresses { @@ -349,9 +351,9 @@ func (sp *serverPeer) pushAddrMsg(addresses []*wire.NetAddress) { // threshold, a warning is logged including the reason provided. Further, if // the score is above the ban threshold, the peer will be banned and // disconnected. -func (sp *serverPeer) addBanScore(persistent, transient uint32, reason string) { +func (sp *Peer) addBanScore(persistent, transient uint32, reason string) { // No warning is logged and no score is calculated if banning is disabled. - if cfg.DisableBanning { + if config.MainConfig().DisableBanning { return } if sp.isWhitelisted { @@ -359,22 +361,22 @@ func (sp *serverPeer) addBanScore(persistent, transient uint32, reason string) { return } - warnThreshold := cfg.BanThreshold >> 1 + warnThreshold := config.MainConfig().BanThreshold >> 1 if transient == 0 && persistent == 0 { // The score is not being increased, but a warning message is still // logged if the score is above the warn threshold. - score := sp.banScore.Int() + score := sp.DynamicBanScore.Int() if score > warnThreshold { peerLog.Warnf("Misbehaving peer %s: %s -- ban score is %d, "+ "it was not increased this time", sp, reason, score) } return } - score := sp.banScore.Increase(persistent, transient) + score := sp.DynamicBanScore.Increase(persistent, transient) if score > warnThreshold { peerLog.Warnf("Misbehaving peer %s: %s -- ban score increased to %d", sp, reason, score) - if score > cfg.BanThreshold { + if score > config.MainConfig().BanThreshold { peerLog.Warnf("Misbehaving peer %s -- banning and disconnecting", sp) sp.server.BanPeer(sp) @@ -386,13 +388,13 @@ func (sp *serverPeer) addBanScore(persistent, transient uint32, reason string) { // OnVersion is invoked when a peer receives a version bitcoin message // and is used to negotiate the protocol version details as well as kick start // the communications. -func (sp *serverPeer) OnVersion(_ *peer.Peer, msg *wire.MsgVersion) { +func (sp *Peer) OnVersion(_ *peer.Peer, msg *wire.MsgVersion) { // Add the remote peer time as a sample for creating an offset against // the local clock to keep the network time in sync. - sp.server.timeSource.AddTimeSample(sp.Addr(), msg.Timestamp) + sp.server.TimeSource.AddTimeSample(sp.Addr(), msg.Timestamp) // Signal the sync manager this peer is a new sync candidate. - sp.server.syncManager.NewPeer(sp.Peer) + sp.server.SyncManager.NewPeer(sp.Peer) // Choose whether or not to relay transactions before a filter command // is received. @@ -403,14 +405,14 @@ func (sp *serverPeer) OnVersion(_ *peer.Peer, msg *wire.MsgVersion) { // on the simulation test network since it is only intended to connect // to specified peers and actively avoids advertising and connecting to // discovered peers. - if !cfg.SimNet { + if !config.MainConfig().SimNet { addrManager := sp.server.addrManager // Outbound connections. if !sp.Inbound() { // TODO(davec): Only do this if not doing the initial block // download and the local address is routable. - if !cfg.DisableListen /* && isCurrent? */ { + if !config.MainConfig().DisableListen /* && isCurrent? */ { // Get address that best matches. lna := addrManager.GetBestLocalAddress(sp.NA()) if addrmgr.IsRoutable(lna) { @@ -442,7 +444,7 @@ func (sp *serverPeer) OnVersion(_ *peer.Peer, msg *wire.MsgVersion) { // It creates and sends an inventory message with the contents of the memory // pool up to the maximum inventory allowed per message. When the peer has a // bloom filter loaded, the contents are filtered accordingly. -func (sp *serverPeer) OnMemPool(_ *peer.Peer, msg *wire.MsgMemPool) { +func (sp *Peer) OnMemPool(_ *peer.Peer, msg *wire.MsgMemPool) { // Only allow mempool requests if the server has bloom filtering // enabled. if sp.server.services&wire.SFNodeBloom != wire.SFNodeBloom { @@ -463,7 +465,7 @@ func (sp *serverPeer) OnMemPool(_ *peer.Peer, msg *wire.MsgMemPool) { // per message. The NewMsgInvSizeHint function automatically limits // the passed hint to the maximum allowed, so it's safe to pass it // without double checking it here. - txMemPool := sp.server.txMemPool + txMemPool := sp.server.TxMemPool txDescs := txMemPool.TxDescs() invMsg := wire.NewMsgInvSizeHint(uint(len(txDescs))) @@ -490,8 +492,8 @@ func (sp *serverPeer) OnMemPool(_ *peer.Peer, msg *wire.MsgMemPool) { // until the bitcoin transaction has been fully processed. Unlock the block // handler this does not serialize all transactions through a single thread // transactions don't rely on the previous one in a linear fashion like blocks. -func (sp *serverPeer) OnTx(_ *peer.Peer, msg *wire.MsgTx) { - if cfg.BlocksOnly { +func (sp *Peer) OnTx(_ *peer.Peer, msg *wire.MsgTx) { + if config.MainConfig().BlocksOnly { peerLog.Tracef("Ignoring tx %v from %v - blocksonly enabled", msg.TxHash(), sp) return @@ -509,13 +511,13 @@ func (sp *serverPeer) OnTx(_ *peer.Peer, msg *wire.MsgTx) { // processed and known good or bad. This helps prevent a malicious peer // from queuing up a bunch of bad transactions before disconnecting (or // being disconnected) and wasting memory. - sp.server.syncManager.QueueTx(tx, sp.Peer, sp.txProcessed) + sp.server.SyncManager.QueueTx(tx, sp.Peer, sp.txProcessed) <-sp.txProcessed } // OnBlock is invoked when a peer receives a block bitcoin message. It // blocks until the bitcoin block has been fully processed. -func (sp *serverPeer) OnBlock(_ *peer.Peer, msg *wire.MsgBlock, buf []byte) { +func (sp *Peer) OnBlock(_ *peer.Peer, msg *wire.MsgBlock, buf []byte) { // Convert the raw MsgBlock to a btcutil.Block which provides some // convenience methods and things such as hash caching. block := btcutil.NewBlockFromBlockAndBytes(msg, buf) @@ -535,7 +537,7 @@ func (sp *serverPeer) OnBlock(_ *peer.Peer, msg *wire.MsgBlock, buf []byte) { // reference implementation processes blocks in the same // thread and therefore blocks further messages until // the bitcoin block has been fully processed. - sp.server.syncManager.QueueBlock(block, sp.Peer, sp.blockProcessed) + sp.server.SyncManager.QueueBlock(block, sp.Peer, sp.blockProcessed) <-sp.blockProcessed } @@ -543,10 +545,10 @@ func (sp *serverPeer) OnBlock(_ *peer.Peer, msg *wire.MsgBlock, buf []byte) { // used to examine the inventory being advertised by the remote peer and react // accordingly. We pass the message down to blockmanager which will call // QueueMessage with any appropriate responses. -func (sp *serverPeer) OnInv(_ *peer.Peer, msg *wire.MsgInv) { - if !cfg.BlocksOnly { +func (sp *Peer) OnInv(_ *peer.Peer, msg *wire.MsgInv) { + if !config.MainConfig().BlocksOnly { if len(msg.InvList) > 0 { - sp.server.syncManager.QueueInv(msg, sp.Peer) + sp.server.SyncManager.QueueInv(msg, sp.Peer) } return } @@ -572,19 +574,19 @@ func (sp *serverPeer) OnInv(_ *peer.Peer, msg *wire.MsgInv) { } if len(newInv.InvList) > 0 { - sp.server.syncManager.QueueInv(newInv, sp.Peer) + sp.server.SyncManager.QueueInv(newInv, sp.Peer) } } // OnHeaders is invoked when a peer receives a headers bitcoin // message. The message is passed down to the sync manager. -func (sp *serverPeer) OnHeaders(_ *peer.Peer, msg *wire.MsgHeaders) { - sp.server.syncManager.QueueHeaders(msg, sp.Peer) +func (sp *Peer) OnHeaders(_ *peer.Peer, msg *wire.MsgHeaders) { + sp.server.SyncManager.QueueHeaders(msg, sp.Peer) } -// handleGetData is invoked when a peer receives a getdata bitcoin message and +// OnGetData is invoked when a peer receives a getdata bitcoin message and // is used to deliver block and transaction information. -func (sp *serverPeer) OnGetData(_ *peer.Peer, msg *wire.MsgGetData) { +func (sp *Peer) OnGetData(_ *peer.Peer, msg *wire.MsgGetData) { numAdded := 0 notFound := wire.NewMsgNotFound() @@ -658,7 +660,7 @@ func (sp *serverPeer) OnGetData(_ *peer.Peer, msg *wire.MsgGetData) { // OnGetBlocks is invoked when a peer receives a getblocks bitcoin // message. -func (sp *serverPeer) OnGetBlocks(_ *peer.Peer, msg *wire.MsgGetBlocks) { +func (sp *Peer) OnGetBlocks(_ *peer.Peer, msg *wire.MsgGetBlocks) { // Find the most recent known block in the best chain based on the block // locator and fetch all of the block hashes after it until either // wire.MaxBlocksPerMsg have been fetched or the provided stop hash is @@ -669,7 +671,7 @@ func (sp *serverPeer) OnGetBlocks(_ *peer.Peer, msg *wire.MsgGetBlocks) { // over with the genesis block if unknown block locators are provided. // // This mirrors the behavior in the reference implementation. - chain := sp.server.dag + chain := sp.server.DAG hashList := chain.LocateBlocks(msg.BlockLocatorHashes, &msg.HashStop, wire.MaxBlocksPerMsg) @@ -697,9 +699,9 @@ func (sp *serverPeer) OnGetBlocks(_ *peer.Peer, msg *wire.MsgGetBlocks) { // OnGetHeaders is invoked when a peer receives a getheaders bitcoin // message. -func (sp *serverPeer) OnGetHeaders(_ *peer.Peer, msg *wire.MsgGetHeaders) { +func (sp *Peer) OnGetHeaders(_ *peer.Peer, msg *wire.MsgGetHeaders) { // Ignore getheaders requests if not in sync. - if !sp.server.syncManager.IsCurrent() { + if !sp.server.SyncManager.IsCurrent() { return } @@ -713,7 +715,7 @@ func (sp *serverPeer) OnGetHeaders(_ *peer.Peer, msg *wire.MsgGetHeaders) { // over with the genesis block if unknown block locators are provided. // // This mirrors the behavior in the reference implementation. - chain := sp.server.dag + chain := sp.server.DAG headers := chain.LocateHeaders(msg.BlockLocatorHashes, &msg.HashStop) if len(headers) == 0 { // Nothing to send. @@ -729,13 +731,13 @@ func (sp *serverPeer) OnGetHeaders(_ *peer.Peer, msg *wire.MsgGetHeaders) { } // OnGetCFilters is invoked when a peer receives a getcfilters bitcoin message. -func (sp *serverPeer) OnGetCFilters(_ *peer.Peer, msg *wire.MsgGetCFilters) { +func (sp *Peer) OnGetCFilters(_ *peer.Peer, msg *wire.MsgGetCFilters) { // Ignore getcfilters requests if not in sync. - if !sp.server.syncManager.IsCurrent() { + if !sp.server.SyncManager.IsCurrent() { return } - hashes, err := sp.server.dag.HeightToHashRange(int32(msg.StartHeight), + hashes, err := sp.server.DAG.HeightToHashRange(int32(msg.StartHeight), &msg.StopHash, wire.MaxGetCFiltersReqRange) if err != nil { peerLog.Debugf("Invalid getcfilters request: %v", err) @@ -749,7 +751,7 @@ func (sp *serverPeer) OnGetCFilters(_ *peer.Peer, msg *wire.MsgGetCFilters) { hashPtrs[i] = &hashes[i] } - filters, err := sp.server.cfIndex.FiltersByBlockHashes(hashPtrs, + filters, err := sp.server.CfIndex.FiltersByBlockHashes(hashPtrs, msg.FilterType) if err != nil { peerLog.Errorf("Error retrieving cfilters: %v", err) @@ -767,9 +769,9 @@ func (sp *serverPeer) OnGetCFilters(_ *peer.Peer, msg *wire.MsgGetCFilters) { } // OnGetCFHeaders is invoked when a peer receives a getcfheader bitcoin message. -func (sp *serverPeer) OnGetCFHeaders(_ *peer.Peer, msg *wire.MsgGetCFHeaders) { +func (sp *Peer) OnGetCFHeaders(_ *peer.Peer, msg *wire.MsgGetCFHeaders) { // Ignore getcfilterheader requests if not in sync. - if !sp.server.syncManager.IsCurrent() { + if !sp.server.SyncManager.IsCurrent() { return } @@ -784,7 +786,7 @@ func (sp *serverPeer) OnGetCFHeaders(_ *peer.Peer, msg *wire.MsgGetCFHeaders) { } // Fetch the hashes from the block index. - hashList, err := sp.server.dag.HeightToHashRange(startHeight, + hashList, err := sp.server.DAG.HeightToHashRange(startHeight, &msg.StopHash, maxResults) if err != nil { peerLog.Debugf("Invalid getcfheaders request: %v", err) @@ -806,7 +808,7 @@ func (sp *serverPeer) OnGetCFHeaders(_ *peer.Peer, msg *wire.MsgGetCFHeaders) { } // Fetch the raw filter hash bytes from the database for all blocks. - filterHashes, err := sp.server.cfIndex.FilterHashesByBlockHashes(hashPtrs, + filterHashes, err := sp.server.CfIndex.FilterHashesByBlockHashes(hashPtrs, msg.FilterType) if err != nil { peerLog.Errorf("Error retrieving cfilter hashes: %v", err) @@ -822,7 +824,7 @@ func (sp *serverPeer) OnGetCFHeaders(_ *peer.Peer, msg *wire.MsgGetCFHeaders) { // Fetch the raw committed filter header bytes from the // database. - headerBytes, err := sp.server.cfIndex.FilterHeaderByBlockHash( + headerBytes, err := sp.server.CfIndex.FilterHeaderByBlockHash( prevBlockHash, msg.FilterType) if err != nil { peerLog.Errorf("Error retrieving CF header: %v", err) @@ -869,13 +871,13 @@ func (sp *serverPeer) OnGetCFHeaders(_ *peer.Peer, msg *wire.MsgGetCFHeaders) { } // OnGetCFCheckpt is invoked when a peer receives a getcfcheckpt bitcoin message. -func (sp *serverPeer) OnGetCFCheckpt(_ *peer.Peer, msg *wire.MsgGetCFCheckpt) { +func (sp *Peer) OnGetCFCheckpt(_ *peer.Peer, msg *wire.MsgGetCFCheckpt) { // Ignore getcfcheckpt requests if not in sync. - if !sp.server.syncManager.IsCurrent() { + if !sp.server.SyncManager.IsCurrent() { return } - blockHashes, err := sp.server.dag.IntervalBlockHashes(&msg.StopHash, + blockHashes, err := sp.server.DAG.IntervalBlockHashes(&msg.StopHash, wire.CFCheckptInterval) if err != nil { peerLog.Debugf("Invalid getcfilters request: %v", err) @@ -931,7 +933,7 @@ func (sp *serverPeer) OnGetCFCheckpt(_ *peer.Peer, msg *wire.MsgGetCFCheckpt) { blockHashPtrs = append(blockHashPtrs, &blockHashes[i]) } - filterHeaders, err := sp.server.cfIndex.FilterHeadersByBlockHashes(blockHashPtrs, + filterHeaders, err := sp.server.CfIndex.FilterHeadersByBlockHashes(blockHashPtrs, msg.FilterType) if err != nil { peerLog.Errorf("Error retrieving cfilter headers: %v", err) @@ -971,7 +973,7 @@ func (sp *serverPeer) OnGetCFCheckpt(_ *peer.Peer, msg *wire.MsgGetCFCheckpt) { // allow bloom filters. Additionally, if the peer has negotiated to a protocol // version that is high enough to observe the bloom filter service support bit, // it will be banned since it is intentionally violating the protocol. -func (sp *serverPeer) enforceNodeBloomFlag(cmd string) bool { +func (sp *Peer) enforceNodeBloomFlag(cmd string) bool { if sp.server.services&wire.SFNodeBloom != wire.SFNodeBloom { // Ban the peer if the protocol version is high enough that the // peer is knowingly violating the protocol and banning is @@ -982,7 +984,7 @@ func (sp *serverPeer) enforceNodeBloomFlag(cmd string) bool { // to ensure the violation is logged and the peer is // disconnected regardless. if sp.ProtocolVersion() >= wire.BIP0111Version && - !cfg.DisableBanning { + !config.MainConfig().DisableBanning { // Disconnect the peer regardless of whether it was // banned. @@ -1006,7 +1008,7 @@ func (sp *serverPeer) enforceNodeBloomFlag(cmd string) bool { // is used by remote peers to request that no transactions which have a fee rate // lower than provided value are inventoried to them. The peer will be // disconnected if an invalid fee filter value is provided. -func (sp *serverPeer) OnFeeFilter(_ *peer.Peer, msg *wire.MsgFeeFilter) { +func (sp *Peer) OnFeeFilter(_ *peer.Peer, msg *wire.MsgFeeFilter) { // Check that the passed minimum fee is a valid amount. if msg.MinFee < 0 || msg.MinFee > btcutil.MaxSatoshi { peerLog.Debugf("Peer %v sent an invalid feefilter '%v' -- "+ @@ -1015,14 +1017,14 @@ func (sp *serverPeer) OnFeeFilter(_ *peer.Peer, msg *wire.MsgFeeFilter) { return } - atomic.StoreInt64(&sp.feeFilter, msg.MinFee) + atomic.StoreInt64(&sp.FeeFilterInt, msg.MinFee) } // OnFilterAdd is invoked when a peer receives a filteradd bitcoin // message and is used by remote peers to add data to an already loaded bloom // filter. The peer will be disconnected if a filter is not loaded when this // message is received or the server is not configured to allow bloom filters. -func (sp *serverPeer) OnFilterAdd(_ *peer.Peer, msg *wire.MsgFilterAdd) { +func (sp *Peer) OnFilterAdd(_ *peer.Peer, msg *wire.MsgFilterAdd) { // Disconnect and/or ban depending on the node bloom services flag and // negotiated protocol version. if !sp.enforceNodeBloomFlag(msg.Command()) { @@ -1043,7 +1045,7 @@ func (sp *serverPeer) OnFilterAdd(_ *peer.Peer, msg *wire.MsgFilterAdd) { // message and is used by remote peers to clear an already loaded bloom filter. // The peer will be disconnected if a filter is not loaded when this message is // received or the server is not configured to allow bloom filters. -func (sp *serverPeer) OnFilterClear(_ *peer.Peer, msg *wire.MsgFilterClear) { +func (sp *Peer) OnFilterClear(_ *peer.Peer, msg *wire.MsgFilterClear) { // Disconnect and/or ban depending on the node bloom services flag and // negotiated protocol version. if !sp.enforceNodeBloomFlag(msg.Command()) { @@ -1065,7 +1067,7 @@ func (sp *serverPeer) OnFilterClear(_ *peer.Peer, msg *wire.MsgFilterClear) { // delivering merkle blocks and associated transactions that match the filter. // The peer will be disconnected if the server is not configured to allow bloom // filters. -func (sp *serverPeer) OnFilterLoad(_ *peer.Peer, msg *wire.MsgFilterLoad) { +func (sp *Peer) OnFilterLoad(_ *peer.Peer, msg *wire.MsgFilterLoad) { // Disconnect and/or ban depending on the node bloom services flag and // negotiated protocol version. if !sp.enforceNodeBloomFlag(msg.Command()) { @@ -1080,12 +1082,12 @@ func (sp *serverPeer) OnFilterLoad(_ *peer.Peer, msg *wire.MsgFilterLoad) { // OnGetAddr is invoked when a peer receives a getaddr bitcoin message // and is used to provide the peer with known addresses from the address // manager. -func (sp *serverPeer) OnGetAddr(_ *peer.Peer, msg *wire.MsgGetAddr) { +func (sp *Peer) OnGetAddr(_ *peer.Peer, msg *wire.MsgGetAddr) { // Don't return any addresses when running on the simulation test // network. This helps prevent the network from becoming another // public test network since it will not be able to learn about other // peers that have not specifically been provided. - if cfg.SimNet { + if config.MainConfig().SimNet { return } @@ -1115,12 +1117,12 @@ func (sp *serverPeer) OnGetAddr(_ *peer.Peer, msg *wire.MsgGetAddr) { // OnAddr is invoked when a peer receives an addr bitcoin message and is // used to notify the server about advertised addresses. -func (sp *serverPeer) OnAddr(_ *peer.Peer, msg *wire.MsgAddr) { +func (sp *Peer) OnAddr(_ *peer.Peer, msg *wire.MsgAddr) { // Ignore addresses when running on the simulation test network. This // helps prevent the network from becoming another public test network // since it will not be able to learn about other peers that have not // specifically been provided. - if cfg.SimNet { + if config.MainConfig().SimNet { return } @@ -1165,13 +1167,13 @@ func (sp *serverPeer) OnAddr(_ *peer.Peer, msg *wire.MsgAddr) { // OnRead is invoked when a peer receives a message and it is used to update // the bytes received by the server. -func (sp *serverPeer) OnRead(_ *peer.Peer, bytesRead int, msg wire.Message, err error) { +func (sp *Peer) OnRead(_ *peer.Peer, bytesRead int, msg wire.Message, err error) { sp.server.AddBytesReceived(uint64(bytesRead)) } // OnWrite is invoked when a peer sends a message and it is used to update // the bytes sent by the server. -func (sp *serverPeer) OnWrite(_ *peer.Peer, bytesWritten int, msg wire.Message, err error) { +func (sp *Peer) OnWrite(_ *peer.Peer, bytesWritten int, msg wire.Message, err error) { sp.server.AddBytesSent(uint64(bytesWritten)) } @@ -1195,7 +1197,7 @@ func randomUint16Number(max uint16) uint16 { // AddRebroadcastInventory adds 'iv' to the list of inventories to be // rebroadcasted at random intervals until they show up in a block. -func (s *server) AddRebroadcastInventory(iv *wire.InvVect, data interface{}) { +func (s *Server) AddRebroadcastInventory(iv *wire.InvVect, data interface{}) { // Ignore if shutting down. if atomic.LoadInt32(&s.shutdown) != 0 { return @@ -1206,7 +1208,7 @@ func (s *server) AddRebroadcastInventory(iv *wire.InvVect, data interface{}) { // RemoveRebroadcastInventory removes 'iv' from the list of items to be // rebroadcasted if present. -func (s *server) RemoveRebroadcastInventory(iv *wire.InvVect) { +func (s *Server) RemoveRebroadcastInventory(iv *wire.InvVect) { // Ignore if shutting down. if atomic.LoadInt32(&s.shutdown) != 0 { return @@ -1215,52 +1217,24 @@ func (s *server) RemoveRebroadcastInventory(iv *wire.InvVect) { s.modifyRebroadcastInv <- broadcastInventoryDel(iv) } -// relayTransactions generates and relays inventory vectors for all of the +// RelayTransactions generates and relays inventory vectors for all of the // passed transactions to all connected peers. -func (s *server) relayTransactions(txns []*mempool.TxDesc) { +func (s *Server) RelayTransactions(txns []*mempool.TxDesc) { for _, txD := range txns { iv := wire.NewInvVect(wire.InvTypeTx, txD.Tx.Hash()) s.RelayInventory(iv, txD) } } -// AnnounceNewTransactions generates and relays inventory vectors and notifies -// both websocket and getblocktemplate long poll clients of the passed -// transactions. This function should be called whenever new transactions -// are added to the mempool. -func (s *server) AnnounceNewTransactions(txns []*mempool.TxDesc) { - // Generate and relay inventory vectors for all newly accepted - // transactions. - s.relayTransactions(txns) - - // Notify both websocket and getblocktemplate long poll clients of all - // newly accepted transactions. - if s.rpcServer != nil { - s.rpcServer.NotifyNewTransactions(txns) - } -} - -// Transaction has one confirmation on the main chain. Now we can mark it as no -// longer needing rebroadcasting. -func (s *server) TransactionConfirmed(tx *btcutil.Tx) { - // Rebroadcasting is only necessary when the RPC server is active. - if s.rpcServer == nil { - return - } - - iv := wire.NewInvVect(wire.InvTypeTx, tx.Hash()) - s.RemoveRebroadcastInventory(iv) -} - // pushTxMsg sends a tx message for the provided transaction hash to the // connected peer. An error is returned if the transaction hash is not known. -func (s *server) pushTxMsg(sp *serverPeer, hash *daghash.Hash, doneChan chan<- struct{}, +func (s *Server) pushTxMsg(sp *Peer, hash *daghash.Hash, doneChan chan<- struct{}, waitChan <-chan struct{}) error { // Attempt to fetch the requested transaction from the pool. A // call could be made to check for existence first, but simply trying // to fetch a missing transaction results in the same behavior. - tx, err := s.txMemPool.FetchTransaction(hash) + tx, err := s.TxMemPool.FetchTransaction(hash) if err != nil { peerLog.Tracef("Unable to fetch tx %v from transaction "+ "pool: %v", hash, err) @@ -1283,7 +1257,7 @@ func (s *server) pushTxMsg(sp *serverPeer, hash *daghash.Hash, doneChan chan<- s // pushBlockMsg sends a block message for the provided block hash to the // connected peer. An error is returned if the block hash is not known. -func (s *server) pushBlockMsg(sp *serverPeer, hash *daghash.Hash, doneChan chan<- struct{}, +func (s *Server) pushBlockMsg(sp *Peer, hash *daghash.Hash, doneChan chan<- struct{}, waitChan <-chan struct{}) error { // Fetch the raw block bytes from the database. @@ -1337,7 +1311,7 @@ func (s *server) pushBlockMsg(sp *serverPeer, hash *daghash.Hash, doneChan chan< // to trigger it to issue another getblocks message for the next // batch of inventory. if sendInv { - dagState := sp.server.dag.GetDAGState() + dagState := sp.server.DAG.GetDAGState() invMsg := wire.NewMsgInvSizeHint(1) iv := wire.NewInvVect(wire.InvTypeBlock, &dagState.SelectedTip.Hash) invMsg.AddInvVect(iv) @@ -1351,7 +1325,7 @@ func (s *server) pushBlockMsg(sp *serverPeer, hash *daghash.Hash, doneChan chan< // the connected peer. Since a merkle block requires the peer to have a filter // loaded, this call will simply be ignored if there is no filter loaded. An // error is returned if the block hash is not known. -func (s *server) pushMerkleBlockMsg(sp *serverPeer, hash *daghash.Hash, +func (s *Server) pushMerkleBlockMsg(sp *Peer, hash *daghash.Hash, doneChan chan<- struct{}, waitChan <-chan struct{}) error { // Do not send a response if the peer doesn't have a filter loaded. @@ -1363,7 +1337,7 @@ func (s *server) pushMerkleBlockMsg(sp *serverPeer, hash *daghash.Hash, } // Fetch the raw block bytes from the database. - blk, err := sp.server.dag.BlockByHash(hash) + blk, err := sp.server.DAG.BlockByHash(hash) if err != nil { peerLog.Tracef("Unable to fetch requested block hash %v: %v", hash, err) @@ -1409,8 +1383,8 @@ func (s *server) pushMerkleBlockMsg(sp *serverPeer, hash *daghash.Hash, // handleUpdatePeerHeight updates the heights of all peers who were known to // announce a block we recently accepted. -func (s *server) handleUpdatePeerHeights(state *peerState, umsg updatePeerHeightsMsg) { - state.forAllPeers(func(sp *serverPeer) { +func (s *Server) handleUpdatePeerHeights(state *peerState, umsg updatePeerHeightsMsg) { + state.forAllPeers(func(sp *Peer) { // The origin peer should already have the updated height. if sp.Peer == umsg.originPeer { return @@ -1437,7 +1411,7 @@ func (s *server) handleUpdatePeerHeights(state *peerState, umsg updatePeerHeight // handleAddPeerMsg deals with adding new peers. It is invoked from the // peerHandler goroutine. -func (s *server) handleAddPeerMsg(state *peerState, sp *serverPeer) bool { +func (s *Server) handleAddPeerMsg(state *peerState, sp *Peer) bool { if sp == nil { return false } @@ -1471,9 +1445,9 @@ func (s *server) handleAddPeerMsg(state *peerState, sp *serverPeer) bool { // TODO: Check for max peers from a single IP. // Limit max number of total peers. - if state.Count() >= cfg.MaxPeers { + if state.Count() >= config.MainConfig().MaxPeers { srvrLog.Infof("Max peers reached [%d] - disconnecting peer %s", - cfg.MaxPeers, sp) + config.MainConfig().MaxPeers, sp) sp.Disconnect() // TODO: how to handle permanent peers here? // they should be rescheduled. @@ -1498,8 +1472,8 @@ func (s *server) handleAddPeerMsg(state *peerState, sp *serverPeer) bool { // handleDonePeerMsg deals with peers that have signalled they are done. It is // invoked from the peerHandler goroutine. -func (s *server) handleDonePeerMsg(state *peerState, sp *serverPeer) { - var list map[int32]*serverPeer +func (s *Server) handleDonePeerMsg(state *peerState, sp *Peer) { + var list map[int32]*Peer if sp.persistent { list = state.persistentPeers } else if sp.Inbound() { @@ -1535,22 +1509,22 @@ func (s *server) handleDonePeerMsg(state *peerState, sp *serverPeer) { // handleBanPeerMsg deals with banning peers. It is invoked from the // peerHandler goroutine. -func (s *server) handleBanPeerMsg(state *peerState, sp *serverPeer) { +func (s *Server) handleBanPeerMsg(state *peerState, sp *Peer) { host, _, err := net.SplitHostPort(sp.Addr()) if err != nil { srvrLog.Debugf("can't split ban peer %s %v", sp.Addr(), err) return } - direction := directionString(sp.Inbound()) + direction := logger.DirectionString(sp.Inbound()) srvrLog.Infof("Banned peer %s (%s) for %v", host, direction, - cfg.BanDuration) - state.banned[host] = time.Now().Add(cfg.BanDuration) + config.MainConfig().BanDuration) + state.banned[host] = time.Now().Add(config.MainConfig().BanDuration) } // handleRelayInvMsg deals with relaying inventory to peers that are not already // known to have it. It is invoked from the peerHandler goroutine. -func (s *server) handleRelayInvMsg(state *peerState, msg relayMsg) { - state.forAllPeers(func(sp *serverPeer) { +func (s *Server) handleRelayInvMsg(state *peerState, msg relayMsg) { + state.forAllPeers(func(sp *Peer) { if !sp.Connected() { return } @@ -1592,7 +1566,7 @@ func (s *server) handleRelayInvMsg(state *peerState, msg relayMsg) { // Don't relay the transaction if the transaction fee-per-kb // is less than the peer's feefilter. - feeFilter := atomic.LoadInt64(&sp.feeFilter) + feeFilter := atomic.LoadInt64(&sp.FeeFilterInt) if feeFilter > 0 && txD.FeePerKB < feeFilter { return } @@ -1615,8 +1589,8 @@ func (s *server) handleRelayInvMsg(state *peerState, msg relayMsg) { // handleBroadcastMsg deals with broadcasting messages to peers. It is invoked // from the peerHandler goroutine. -func (s *server) handleBroadcastMsg(state *peerState, bmsg *broadcastMsg) { - state.forAllPeers(func(sp *serverPeer) { +func (s *Server) handleBroadcastMsg(state *peerState, bmsg *broadcastMsg) { + state.forAllPeers(func(sp *Peer) { if !sp.Connected() { return } @@ -1635,8 +1609,9 @@ type getConnCountMsg struct { reply chan int32 } -type getPeersMsg struct { - reply chan []*serverPeer +//GetPeersMsg is the message type which is used by the rpc server to get the peers list from the p2p server +type GetPeersMsg struct { + Reply chan []*Peer } type getOutboundGroup struct { @@ -1644,90 +1619,94 @@ type getOutboundGroup struct { reply chan int } -type getAddedNodesMsg struct { - reply chan []*serverPeer +//GetAddedNodesMsg is the message type which is used by the rpc server to get the list of persistent peers from the p2p server +type GetAddedNodesMsg struct { + Reply chan []*Peer } -type disconnectNodeMsg struct { - cmp func(*serverPeer) bool - reply chan error +//DisconnectNodeMsg is the message that is sent to a peer before it gets disconnected +type DisconnectNodeMsg struct { + Cmp func(*Peer) bool + Reply chan error } -type connectNodeMsg struct { - addr string - permanent bool - reply chan error +//ConnectNodeMsg is the message type which is used by the rpc server to add a peer to the p2p server +type ConnectNodeMsg struct { + Addr string + Permanent bool + Reply chan error } -type removeNodeMsg struct { - cmp func(*serverPeer) bool - reply chan error +//RemoveNodeMsg is the message type which is used by the rpc server to remove a peer from the p2p server +type RemoveNodeMsg struct { + Cmp func(*Peer) bool + Reply chan error } // handleQuery is the central handler for all queries and commands from other // goroutines related to peer state. -func (s *server) handleQuery(state *peerState, querymsg interface{}) { +func (s *Server) handleQuery(state *peerState, querymsg interface{}) { switch msg := querymsg.(type) { case getConnCountMsg: nconnected := int32(0) - state.forAllPeers(func(sp *serverPeer) { + state.forAllPeers(func(sp *Peer) { if sp.Connected() { nconnected++ } }) msg.reply <- nconnected - case getPeersMsg: - peers := make([]*serverPeer, 0, state.Count()) - state.forAllPeers(func(sp *serverPeer) { + case GetPeersMsg: + peers := make([]*Peer, 0, state.Count()) + state.forAllPeers(func(sp *Peer) { if !sp.Connected() { return } peers = append(peers, sp) }) - msg.reply <- peers + msg.Reply <- peers - case connectNodeMsg: + case ConnectNodeMsg: // TODO: duplicate oneshots? // Limit max number of total peers. - if state.Count() >= cfg.MaxPeers { - msg.reply <- errors.New("max peers reached") + if state.Count() >= config.MainConfig().MaxPeers { + msg.Reply <- errors.New("max peers reached") return } for _, peer := range state.persistentPeers { - if peer.Addr() == msg.addr { - if msg.permanent { - msg.reply <- errors.New("peer already connected") + if peer.Addr() == msg.Addr { + if msg.Permanent { + msg.Reply <- errors.New("peer already connected") } else { - msg.reply <- errors.New("peer exists as a permanent peer") + msg.Reply <- errors.New("peer exists as a permanent peer") } return } } - netAddr, err := addrStringToNetAddr(msg.addr) + netAddr, err := addrStringToNetAddr(msg.Addr) if err != nil { - msg.reply <- err + msg.Reply <- err return } // TODO: if too many, nuke a non-perm peer. go s.connManager.Connect(&connmgr.ConnReq{ Addr: netAddr, - Permanent: msg.permanent, + Permanent: msg.Permanent, }) - msg.reply <- nil - case removeNodeMsg: - found := disconnectPeer(state.persistentPeers, msg.cmp, func(sp *serverPeer) { + msg.Reply <- nil + case RemoveNodeMsg: + found := disconnectPeer(state.persistentPeers, msg.Cmp, func(sp *Peer) { // Keep group counts ok since we remove from // the list now. state.outboundGroups[addrmgr.GroupKey(sp.NA())]-- }) if found { - msg.reply <- nil + msg.Reply <- nil } else { - msg.reply <- errors.New("peer not found") + msg.Reply <- errors.New("peer not found") } case getOutboundGroup: count, ok := state.outboundGroups[msg.key] @@ -1737,24 +1716,24 @@ func (s *server) handleQuery(state *peerState, querymsg interface{}) { msg.reply <- 0 } // Request a list of the persistent (added) peers. - case getAddedNodesMsg: + case GetAddedNodesMsg: // Respond with a slice of the relevant peers. - peers := make([]*serverPeer, 0, len(state.persistentPeers)) + peers := make([]*Peer, 0, len(state.persistentPeers)) for _, sp := range state.persistentPeers { peers = append(peers, sp) } - msg.reply <- peers - case disconnectNodeMsg: + msg.Reply <- peers + case DisconnectNodeMsg: // Check inbound peers. We pass a nil callback since we don't // require any additional actions on disconnect for inbound peers. - found := disconnectPeer(state.inboundPeers, msg.cmp, nil) + found := disconnectPeer(state.inboundPeers, msg.Cmp, nil) if found { - msg.reply <- nil + msg.Reply <- nil return } // Check outbound peers. - found = disconnectPeer(state.outboundPeers, msg.cmp, func(sp *serverPeer) { + found = disconnectPeer(state.outboundPeers, msg.Cmp, func(sp *Peer) { // Keep group counts ok since we remove from // the list now. state.outboundGroups[addrmgr.GroupKey(sp.NA())]-- @@ -1764,15 +1743,15 @@ func (s *server) handleQuery(state *peerState, querymsg interface{}) { // ip:port, continue disconnecting them all until no such // peers are found. for found { - found = disconnectPeer(state.outboundPeers, msg.cmp, func(sp *serverPeer) { + found = disconnectPeer(state.outboundPeers, msg.Cmp, func(sp *Peer) { state.outboundGroups[addrmgr.GroupKey(sp.NA())]-- }) } - msg.reply <- nil + msg.Reply <- nil return } - msg.reply <- errors.New("peer not found") + msg.Reply <- errors.New("peer not found") } } @@ -1783,7 +1762,7 @@ func (s *server) handleQuery(state *peerState, querymsg interface{}) { // to be located. If the peer is found, and the passed callback: `whenFound' // isn't nil, we call it with the peer as the argument before it is removed // from the peerList, and is disconnected from the server. -func disconnectPeer(peerList map[int32]*serverPeer, compareFunc func(*serverPeer) bool, whenFound func(*serverPeer)) bool { +func disconnectPeer(peerList map[int32]*Peer, compareFunc func(*Peer) bool, whenFound func(*Peer)) bool { for addr, peer := range peerList { if compareFunc(peer) { if whenFound != nil { @@ -1801,7 +1780,7 @@ func disconnectPeer(peerList map[int32]*serverPeer, compareFunc func(*serverPeer } // newPeerConfig returns the configuration for the given serverPeer. -func newPeerConfig(sp *serverPeer) *peer.Config { +func newPeerConfig(sp *Peer) *peer.Config { return &peer.Config{ Listeners: peer.MessageListeners{ OnVersion: sp.OnVersion, @@ -1833,13 +1812,13 @@ func newPeerConfig(sp *serverPeer) *peer.Config { }, NewestBlock: sp.newestBlock, HostToNetAddress: sp.server.addrManager.HostToNetAddress, - Proxy: cfg.Proxy, + Proxy: config.MainConfig().Proxy, UserAgentName: userAgentName, UserAgentVersion: userAgentVersion, - UserAgentComments: cfg.UserAgentComments, - ChainParams: sp.server.chainParams, + UserAgentComments: config.MainConfig().UserAgentComments, + ChainParams: sp.server.DAGParams, Services: sp.server.services, - DisableRelayTx: cfg.BlocksOnly, + DisableRelayTx: config.MainConfig().BlocksOnly, ProtocolVersion: peer.MaxProtocolVersion, } } @@ -1848,7 +1827,7 @@ func newPeerConfig(sp *serverPeer) *peer.Config { // connection is established. It initializes a new inbound server peer // instance, associates it with the connection, and starts a goroutine to wait // for disconnection. -func (s *server) inboundPeerConnected(conn net.Conn) { +func (s *Server) inboundPeerConnected(conn net.Conn) { sp := newServerPeer(s, false) sp.isWhitelisted = isWhitelisted(conn.RemoteAddr()) sp.Peer = peer.NewInboundPeer(newPeerConfig(sp)) @@ -1861,7 +1840,7 @@ func (s *server) inboundPeerConnected(conn net.Conn) { // peer instance, associates it with the relevant state such as the connection // request instance and the connection itself, and finally notifies the address // manager of the attempt. -func (s *server) outboundPeerConnected(c *connmgr.ConnReq, conn net.Conn) { +func (s *Server) outboundPeerConnected(c *connmgr.ConnReq, conn net.Conn) { sp := newServerPeer(s, c.Permanent) p, err := peer.NewOutboundPeer(newPeerConfig(sp), c.Addr.String()) if err != nil { @@ -1878,19 +1857,19 @@ func (s *server) outboundPeerConnected(c *connmgr.ConnReq, conn net.Conn) { // peerDoneHandler handles peer disconnects by notifiying the server that it's // done along with other performing other desirable cleanup. -func (s *server) peerDoneHandler(sp *serverPeer) { +func (s *Server) peerDoneHandler(sp *Peer) { sp.WaitForDisconnect() s.donePeers <- sp // Only tell sync manager we are gone if we ever told it we existed. if sp.VersionKnown() { - s.syncManager.DonePeer(sp.Peer) + s.SyncManager.DonePeer(sp.Peer) // Evict any remaining orphans that were sent by the peer. - numEvicted := s.txMemPool.RemoveOrphansByTag(mempool.Tag(sp.ID())) + numEvicted := s.TxMemPool.RemoveOrphansByTag(mempool.Tag(sp.ID())) if numEvicted > 0 { txmpLog.Debugf("Evicted %d %s from peer %v (id %d)", - numEvicted, pickNoun(numEvicted, "orphan", + numEvicted, logger.PickNoun(numEvicted, "orphan", "orphans"), sp, sp.ID()) } } @@ -1900,29 +1879,29 @@ func (s *server) peerDoneHandler(sp *serverPeer) { // peerHandler is used to handle peer operations such as adding and removing // peers to and from the server, banning peers, and broadcasting messages to // peers. It must be run in a goroutine. -func (s *server) peerHandler() { +func (s *Server) peerHandler() { // Start the address manager and sync manager, both of which are needed // by peers. This is done here since their lifecycle is closely tied // to this handler and rather than adding more channels to sychronize // things, it's easier and slightly faster to simply start and stop them // in this handler. s.addrManager.Start() - s.syncManager.Start() + s.SyncManager.Start() srvrLog.Tracef("Starting peer handler") state := &peerState{ - inboundPeers: make(map[int32]*serverPeer), - persistentPeers: make(map[int32]*serverPeer), - outboundPeers: make(map[int32]*serverPeer), + inboundPeers: make(map[int32]*Peer), + persistentPeers: make(map[int32]*Peer), + outboundPeers: make(map[int32]*Peer), banned: make(map[string]time.Time), outboundGroups: make(map[string]int), } - if !cfg.DisableDNSSeed { + if !config.MainConfig().DisableDNSSeed { // Add peers discovered through DNS to the address manager. - connmgr.SeedFromDNS(activeNetParams.Params, defaultRequiredServices, - btcdLookup, func(addrs []*wire.NetAddress) { + connmgr.SeedFromDNS(config.ActiveNetParams(), defaultRequiredServices, + serverutils.BTCDLookup, func(addrs []*wire.NetAddress) { // Bitcoind uses a lookup of the dns seeder here. This // is rather strange since the values looked up by the // DNS seed lookups will vary quite a lot. @@ -1961,12 +1940,12 @@ out: case bmsg := <-s.broadcast: s.handleBroadcastMsg(state, &bmsg) - case qmsg := <-s.query: + case qmsg := <-s.Query: s.handleQuery(state, qmsg) case <-s.quit: // Disconnect all peers on server shutdown. - state.forAllPeers(func(sp *serverPeer) { + state.forAllPeers(func(sp *Peer) { srvrLog.Tracef("Shutdown peer %s", sp) sp.Disconnect() }) @@ -1975,7 +1954,7 @@ out: } s.connManager.Stop() - s.syncManager.Stop() + s.SyncManager.Stop() s.addrManager.Stop() // Drain channels before exiting so nothing is left waiting around @@ -1988,7 +1967,7 @@ cleanup: case <-s.peerHeightsUpdate: case <-s.relayInv: case <-s.broadcast: - case <-s.query: + case <-s.Query: default: break cleanup } @@ -1998,24 +1977,24 @@ cleanup: } // AddPeer adds a new peer that has already been connected to the server. -func (s *server) AddPeer(sp *serverPeer) { +func (s *Server) AddPeer(sp *Peer) { s.newPeers <- sp } // BanPeer bans a peer that has already been connected to the server by ip. -func (s *server) BanPeer(sp *serverPeer) { +func (s *Server) BanPeer(sp *Peer) { s.banPeers <- sp } // RelayInventory relays the passed inventory vector to all connected peers // that are not already known to have it. -func (s *server) RelayInventory(invVect *wire.InvVect, data interface{}) { +func (s *Server) RelayInventory(invVect *wire.InvVect, data interface{}) { s.relayInv <- relayMsg{invVect: invVect, data: data} } // BroadcastMessage sends msg to all peers currently connected to the server // except those in the passed peers to exclude. -func (s *server) BroadcastMessage(msg wire.Message, exclPeers ...*serverPeer) { +func (s *Server) BroadcastMessage(msg wire.Message, exclPeers ...*Peer) { // XXX: Need to determine if this is an alert that has already been // broadcast and refrain from broadcasting again. bmsg := broadcastMsg{message: msg, excludePeers: exclPeers} @@ -2023,37 +2002,37 @@ func (s *server) BroadcastMessage(msg wire.Message, exclPeers ...*serverPeer) { } // ConnectedCount returns the number of currently connected peers. -func (s *server) ConnectedCount() int32 { +func (s *Server) ConnectedCount() int32 { replyChan := make(chan int32) - s.query <- getConnCountMsg{reply: replyChan} + s.Query <- getConnCountMsg{reply: replyChan} return <-replyChan } // OutboundGroupCount returns the number of peers connected to the given // outbound group key. -func (s *server) OutboundGroupCount(key string) int { +func (s *Server) OutboundGroupCount(key string) int { replyChan := make(chan int) - s.query <- getOutboundGroup{key: key, reply: replyChan} + s.Query <- getOutboundGroup{key: key, reply: replyChan} return <-replyChan } // AddBytesSent adds the passed number of bytes to the total bytes sent counter // for the server. It is safe for concurrent access. -func (s *server) AddBytesSent(bytesSent uint64) { +func (s *Server) AddBytesSent(bytesSent uint64) { atomic.AddUint64(&s.bytesSent, bytesSent) } // AddBytesReceived adds the passed number of bytes to the total bytes received // counter for the server. It is safe for concurrent access. -func (s *server) AddBytesReceived(bytesReceived uint64) { +func (s *Server) AddBytesReceived(bytesReceived uint64) { atomic.AddUint64(&s.bytesReceived, bytesReceived) } // NetTotals returns the sum of all bytes received and sent across the network // for all peers. It is safe for concurrent access. -func (s *server) NetTotals() (uint64, uint64) { +func (s *Server) NetTotals() (uint64, uint64) { return atomic.LoadUint64(&s.bytesReceived), atomic.LoadUint64(&s.bytesSent) } @@ -2062,7 +2041,7 @@ func (s *server) NetTotals() (uint64, uint64) { // the latest connected main chain block, or a recognized orphan. These height // updates allow us to dynamically refresh peer heights, ensuring sync peer // selection has access to the latest block heights for each peer. -func (s *server) UpdatePeerHeights(latestBlkHash *daghash.Hash, latestHeight int32, updateSource *peer.Peer) { +func (s *Server) UpdatePeerHeights(latestBlkHash *daghash.Hash, latestHeight int32, updateSource *peer.Peer) { s.peerHeightsUpdate <- updatePeerHeightsMsg{ newHash: latestBlkHash, newHeight: latestHeight, @@ -2073,7 +2052,7 @@ func (s *server) UpdatePeerHeights(latestBlkHash *daghash.Hash, latestHeight int // rebroadcastHandler keeps track of user submitted inventories that we have // sent out but have not yet made it into a block. We periodically rebroadcast // them in case our peers restarted or otherwise lost track of them. -func (s *server) rebroadcastHandler() { +func (s *Server) rebroadcastHandler() { // Wait 5 min before first tx rebroadcast. timer := time.NewTimer(5 * time.Minute) pendingInvs := make(map[wire.InvVect]interface{}) @@ -2129,16 +2108,7 @@ cleanup: } // Start begins accepting connections from peers. -func (s *server) Start() { - // Already started? - if atomic.AddInt32(&s.started, 1) != 1 { - return - } - - srvrLog.Trace("Starting server") - - // Server startup time. Used for the uptime command for uptime calculation. - s.startupTime = time.Now().Unix() +func (s *Server) Start() { // Start the peer handler which in turn starts the address and block // managers. @@ -2150,45 +2120,25 @@ func (s *server) Start() { go s.upnpUpdateThread() } + cfg := config.MainConfig() + if !cfg.DisableRPC { s.wg.Add(1) // Start the rebroadcastHandler, which ensures user tx received by // the RPC server are rebroadcast until being included in a block. go s.rebroadcastHandler() - - s.rpcServer.Start() - } - - // Start the CPU miner if generation is enabled. - if cfg.Generate { - s.cpuMiner.Start() } } // Stop gracefully shuts down the server by stopping and disconnecting all // peers and the main listener. -func (s *server) Stop() error { - // Make sure this only happens once. - if atomic.AddInt32(&s.shutdown, 1) != 1 { - srvrLog.Infof("Server is already in the process of shutting down") - return nil - } - - srvrLog.Warnf("Server shutting down") - - // Stop the CPU miner if needed - s.cpuMiner.Stop() - - // Shutdown the RPC server if it's not disabled. - if !cfg.DisableRPC { - s.rpcServer.Stop() - } +func (s *Server) Stop() error { // Save fee estimator state in the database. s.db.Update(func(tx database.Tx) error { metadata := tx.Metadata() - metadata.Put(mempool.EstimateFeeDatabaseKey, s.feeEstimator.Save()) + metadata.Put(mempool.EstimateFeeDatabaseKey, s.FeeEstimator.Save()) return nil }) @@ -2199,14 +2149,14 @@ func (s *server) Stop() error { } // WaitForShutdown blocks until the main listener and peer handlers are stopped. -func (s *server) WaitForShutdown() { +func (s *Server) WaitForShutdown() { s.wg.Wait() } // ScheduleShutdown schedules a server shutdown after the specified duration. // It also dynamically adjusts how often to warn the server is going down based // on remaining duration. -func (s *server) ScheduleShutdown(duration time.Duration) { +func (s *Server) ScheduleShutdown(duration time.Duration) { // Don't schedule shutdown more than once. if atomic.AddInt32(&s.shutdownSched, 1) != 1 { return @@ -2243,11 +2193,11 @@ func (s *server) ScheduleShutdown(duration time.Duration) { }() } -// parseListeners determines whether each listen address is IPv4 and IPv6 and +// ParseListeners determines whether each listen address is IPv4 and IPv6 and // returns a slice of appropriate net.Addrs to listen on with TCP. It also // properly detects addresses which apply to "all interfaces" and adds the // address as both IPv4 and IPv6. -func parseListeners(addrs []string) ([]net.Addr, error) { +func ParseListeners(addrs []string) ([]net.Addr, error) { netAddrs := make([]net.Addr, 0, len(addrs)*2) for _, addr := range addrs { host, _, err := net.SplitHostPort(addr) @@ -2287,11 +2237,11 @@ func parseListeners(addrs []string) ([]net.Addr, error) { return netAddrs, nil } -func (s *server) upnpUpdateThread() { +func (s *Server) upnpUpdateThread() { // Go off immediately to prevent code duplication, thereafter we renew // lease every 15 minutes. timer := time.NewTimer(0 * time.Second) - lport, _ := strconv.ParseInt(activeNetParams.DefaultPort, 10, 16) + lport, _ := strconv.ParseInt(config.ActiveNetParams().DefaultPort, 10, 16) first := true out: for { @@ -2341,72 +2291,23 @@ out: s.wg.Done() } -// setupRPCListeners returns a slice of listeners that are configured for use -// with the RPC server depending on the configuration settings for listen -// addresses and TLS. -func setupRPCListeners() ([]net.Listener, error) { - // Setup TLS if not disabled. - listenFunc := net.Listen - if !cfg.DisableTLS { - // Generate the TLS cert and key file if both don't already - // exist. - if !fileExists(cfg.RPCKey) && !fileExists(cfg.RPCCert) { - err := genCertPair(cfg.RPCCert, cfg.RPCKey) - if err != nil { - return nil, err - } - } - keypair, err := tls.LoadX509KeyPair(cfg.RPCCert, cfg.RPCKey) - if err != nil { - return nil, err - } - - tlsConfig := tls.Config{ - Certificates: []tls.Certificate{keypair}, - MinVersion: tls.VersionTLS12, - } - - // Change the standard net.Listen function to the tls one. - listenFunc = func(net string, laddr string) (net.Listener, error) { - return tls.Listen(net, laddr, &tlsConfig) - } - } - - netAddrs, err := parseListeners(cfg.RPCListeners) - if err != nil { - return nil, err - } - - listeners := make([]net.Listener, 0, len(netAddrs)) - for _, addr := range netAddrs { - listener, err := listenFunc(addr.Network(), addr.String()) - if err != nil { - rpcsLog.Warnf("Can't listen on %s: %v", addr, err) - continue - } - listeners = append(listeners, listener) - } - - return listeners, nil -} - -// newServer returns a new btcd server configured to listen on addr for the -// bitcoin network type specified by chainParams. Use start to begin accepting +// NewServer returns a new btcd server configured to listen on addr for the +// bitcoin network type specified by dagParams. Use start to begin accepting // connections from peers. -func newServer(listenAddrs []string, db database.DB, chainParams *dagconfig.Params, interrupt <-chan struct{}) (*server, error) { +func NewServer(listenAddrs []string, db database.DB, dagParams *dagconfig.Params, interrupt <-chan struct{}, notifyNewTransactions func(txns []*mempool.TxDesc)) (*Server, error) { services := defaultServices - if cfg.NoPeerBloomFilters { + if config.MainConfig().NoPeerBloomFilters { services &^= wire.SFNodeBloom } - if cfg.NoCFilters { + if config.MainConfig().NoCFilters { services &^= wire.SFNodeCF } - amgr := addrmgr.New(cfg.DataDir, btcdLookup) + amgr := addrmgr.New(config.MainConfig().DataDir, serverutils.BTCDLookup) var listeners []net.Listener - var nat NAT - if !cfg.DisableListen { + var nat serverutils.NAT + if !config.MainConfig().DisableListen { var err error listeners, nat, err = initListeners(amgr, listenAddrs, services) if err != nil { @@ -2417,24 +2318,25 @@ func newServer(listenAddrs []string, db database.DB, chainParams *dagconfig.Para } } - s := server{ - chainParams: chainParams, - addrManager: amgr, - newPeers: make(chan *serverPeer, cfg.MaxPeers), - donePeers: make(chan *serverPeer, cfg.MaxPeers), - banPeers: make(chan *serverPeer, cfg.MaxPeers), - query: make(chan interface{}), - relayInv: make(chan relayMsg, cfg.MaxPeers), - broadcast: make(chan broadcastMsg, cfg.MaxPeers), - quit: make(chan struct{}), - modifyRebroadcastInv: make(chan interface{}), - peerHeightsUpdate: make(chan updatePeerHeightsMsg), - nat: nat, - db: db, - timeSource: blockdag.NewMedianTime(), - services: services, - sigCache: txscript.NewSigCache(cfg.SigCacheMaxSize), - cfCheckptCaches: make(map[wire.FilterType][]cfHeaderKV), + s := Server{ + DAGParams: dagParams, + addrManager: amgr, + newPeers: make(chan *Peer, config.MainConfig().MaxPeers), + donePeers: make(chan *Peer, config.MainConfig().MaxPeers), + banPeers: make(chan *Peer, config.MainConfig().MaxPeers), + Query: make(chan interface{}), + relayInv: make(chan relayMsg, config.MainConfig().MaxPeers), + broadcast: make(chan broadcastMsg, config.MainConfig().MaxPeers), + quit: make(chan struct{}), + modifyRebroadcastInv: make(chan interface{}), + peerHeightsUpdate: make(chan updatePeerHeightsMsg), + nat: nat, + db: db, + TimeSource: blockdag.NewMedianTime(), + services: services, + SigCache: txscript.NewSigCache(config.MainConfig().SigCacheMaxSize), + cfCheckptCaches: make(map[wire.FilterType][]cfHeaderKV), + notifyNewTransactions: notifyNewTransactions, } // Create the transaction and address indexes if needed. @@ -2444,29 +2346,29 @@ func newServer(listenAddrs []string, db database.DB, chainParams *dagconfig.Para // addrindex is run first, it may not have the transactions from the // current block indexed. var indexes []indexers.Indexer - if cfg.TxIndex || cfg.AddrIndex { + if config.MainConfig().TxIndex || config.MainConfig().AddrIndex { // Enable transaction index if address index is enabled since it // requires it. - if !cfg.TxIndex { + if !config.MainConfig().TxIndex { indxLog.Infof("Transaction index enabled because it " + "is required by the address index") - cfg.TxIndex = true + config.MainConfig().TxIndex = true } else { indxLog.Info("Transaction index is enabled") } - s.txIndex = indexers.NewTxIndex(db) - indexes = append(indexes, s.txIndex) + s.TxIndex = indexers.NewTxIndex(db) + indexes = append(indexes, s.TxIndex) } - if cfg.AddrIndex { + if config.MainConfig().AddrIndex { indxLog.Info("Address index is enabled") - s.addrIndex = indexers.NewAddrIndex(db, chainParams) - indexes = append(indexes, s.addrIndex) + s.AddrIndex = indexers.NewAddrIndex(db, dagParams) + indexes = append(indexes, s.AddrIndex) } - if !cfg.NoCFilters { + if !config.MainConfig().NoCFilters { indxLog.Info("cf index is enabled") - s.cfIndex = indexers.NewCfIndex(db, chainParams) - indexes = append(indexes, s.cfIndex) + s.CfIndex = indexers.NewCfIndex(db, dagParams) + indexes = append(indexes, s.CfIndex) } // Create an index manager if any of the optional indexes are enabled. @@ -2477,19 +2379,19 @@ func newServer(listenAddrs []string, db database.DB, chainParams *dagconfig.Para // Merge given checkpoints with the default ones unless they are disabled. var checkpoints []dagconfig.Checkpoint - if !cfg.DisableCheckpoints { - checkpoints = mergeCheckpoints(s.chainParams.Checkpoints, cfg.addCheckpoints) + if !config.MainConfig().DisableCheckpoints { + checkpoints = mergeCheckpoints(s.DAGParams.Checkpoints, config.MainConfig().AddCheckpoints) } // Create a new block chain instance with the appropriate configuration. var err error - s.dag, err = blockdag.New(&blockdag.Config{ + s.DAG, err = blockdag.New(&blockdag.Config{ DB: s.db, Interrupt: interrupt, - DAGParams: s.chainParams, + DAGParams: s.DAGParams, Checkpoints: checkpoints, - TimeSource: s.timeSource, - SigCache: s.sigCache, + TimeSource: s.TimeSource, + SigCache: s.SigCache, IndexManager: indexManager, }) if err != nil { @@ -2508,7 +2410,7 @@ func newServer(listenAddrs []string, db database.DB, chainParams *dagconfig.Para // If there is an error, log it and make a new fee estimator. var err error - s.feeEstimator, err = mempool.RestoreFeeEstimator(feeEstimationData) + s.FeeEstimator, err = mempool.RestoreFeeEstimator(feeEstimationData) if err != nil { peerLog.Errorf("Failed to restore fee estimator %v", err) @@ -2520,72 +2422,52 @@ func newServer(listenAddrs []string, db database.DB, chainParams *dagconfig.Para // If no feeEstimator has been found, or if the one that has been found // is behind somehow, create a new one and start over. - if s.feeEstimator == nil || s.feeEstimator.LastKnownHeight() != s.dag.GetDAGState().SelectedTip.Height { - s.feeEstimator = mempool.NewFeeEstimator( + if s.FeeEstimator == nil || s.FeeEstimator.LastKnownHeight() != s.DAG.GetDAGState().SelectedTip.Height { + s.FeeEstimator = mempool.NewFeeEstimator( mempool.DefaultEstimateFeeMaxRollback, mempool.DefaultEstimateFeeMinRegisteredBlocks) } txC := mempool.Config{ Policy: mempool.Policy{ - DisableRelayPriority: cfg.NoRelayPriority, - AcceptNonStd: cfg.RelayNonStd, - FreeTxRelayLimit: cfg.FreeTxRelayLimit, - MaxOrphanTxs: cfg.MaxOrphanTxs, - MaxOrphanTxSize: defaultMaxOrphanTxSize, + DisableRelayPriority: config.MainConfig().NoRelayPriority, + AcceptNonStd: config.MainConfig().RelayNonStd, + FreeTxRelayLimit: config.MainConfig().FreeTxRelayLimit, + MaxOrphanTxs: config.MainConfig().MaxOrphanTxs, + MaxOrphanTxSize: config.DefaultMaxOrphanTxSize, MaxSigOpsPerTx: blockdag.MaxSigOpsPerBlock / 5, - MinRelayTxFee: cfg.minRelayTxFee, + MinRelayTxFee: config.MainConfig().MinRelayTxFee, MaxTxVersion: 1, }, - ChainParams: chainParams, - FetchUtxoView: s.dag.FetchUtxoView, - BestHeight: func() int32 { return s.dag.GetDAGState().SelectedTip.Height }, - MedianTimePast: func() time.Time { return s.dag.GetDAGState().SelectedTip.MedianTime }, + ChainParams: dagParams, + FetchUtxoView: s.DAG.FetchUtxoView, + BestHeight: func() int32 { return s.DAG.GetDAGState().SelectedTip.Height }, + MedianTimePast: func() time.Time { return s.DAG.GetDAGState().SelectedTip.MedianTime }, CalcSequenceLock: func(tx *btcutil.Tx, view *blockdag.UtxoViewpoint) (*blockdag.SequenceLock, error) { - return s.dag.CalcSequenceLock(tx, view, true) + return s.DAG.CalcSequenceLock(tx, view, true) }, - IsDeploymentActive: s.dag.IsDeploymentActive, - SigCache: s.sigCache, - AddrIndex: s.addrIndex, - FeeEstimator: s.feeEstimator, + IsDeploymentActive: s.DAG.IsDeploymentActive, + SigCache: s.SigCache, + AddrIndex: s.AddrIndex, + FeeEstimator: s.FeeEstimator, } - s.txMemPool = mempool.New(&txC) + s.TxMemPool = mempool.New(&txC) - s.syncManager, err = netsync.New(&netsync.Config{ + cfg := config.MainConfig() + + s.SyncManager, err = netsync.New(&netsync.Config{ PeerNotifier: &s, - DAG: s.dag, - TxMemPool: s.txMemPool, - ChainParams: s.chainParams, + DAG: s.DAG, + TxMemPool: s.TxMemPool, + ChainParams: s.DAGParams, DisableCheckpoints: cfg.DisableCheckpoints, MaxPeers: cfg.MaxPeers, - FeeEstimator: s.feeEstimator, + FeeEstimator: s.FeeEstimator, }) if err != nil { return nil, err } - // Create the mining policy and block template generator based on the - // configuration options. - // - // NOTE: The CPU miner relies on the mempool, so the mempool has to be - // created before calling the function to create the CPU miner. - policy := mining.Policy{ - BlockMinSize: cfg.BlockMinSize, - BlockMaxSize: cfg.BlockMaxSize, - BlockPrioritySize: cfg.BlockPrioritySize, - TxMinFreeFee: cfg.minRelayTxFee, - } - blockTemplateGenerator := mining.NewBlkTmplGenerator(&policy, - s.chainParams, s.txMemPool, s.dag, s.timeSource, s.sigCache) - s.cpuMiner = cpuminer.New(&cpuminer.Config{ - ChainParams: chainParams, - BlockTemplateGenerator: blockTemplateGenerator, - MiningAddrs: cfg.miningAddrs, - ProcessBlock: s.syncManager.ProcessBlock, - ConnectedCount: s.ConnectedCount, - IsCurrent: s.syncManager.IsCurrent, - }) - // Only setup a function to return new addresses to connect to when // not running in connect-only mode. The simulation network is always // in connect-only mode since it is only intended to connect to @@ -2593,7 +2475,7 @@ func newServer(listenAddrs []string, db database.DB, chainParams *dagconfig.Para // discovered peers in order to prevent it from becoming a public test // network. var newAddressFunc func() (net.Addr, error) - if !cfg.SimNet && len(cfg.ConnectPeers) == 0 { + if !config.MainConfig().SimNet && len(config.MainConfig().ConnectPeers) == 0 { newAddressFunc = func() (net.Addr, error) { for tries := 0; tries < 100; tries++ { addr := s.addrManager.GetAddress() @@ -2620,7 +2502,7 @@ func newServer(listenAddrs []string, db database.DB, chainParams *dagconfig.Para // allow nondefault ports after 50 failed tries. if tries < 50 && fmt.Sprintf("%d", addr.NetAddress().Port) != - activeNetParams.DefaultPort { + config.ActiveNetParams().DefaultPort { continue } @@ -2634,15 +2516,15 @@ func newServer(listenAddrs []string, db database.DB, chainParams *dagconfig.Para // Create a connection manager. targetOutbound := defaultTargetOutbound - if cfg.MaxPeers < targetOutbound { - targetOutbound = cfg.MaxPeers + if config.MainConfig().MaxPeers < targetOutbound { + targetOutbound = config.MainConfig().MaxPeers } cmgr, err := connmgr.New(&connmgr.Config{ Listeners: listeners, OnAccept: s.inboundPeerConnected, RetryDuration: connectionRetryInterval, TargetOutbound: uint32(targetOutbound), - Dial: btcdDial, + Dial: serverutils.BTCDDial, OnConnection: s.outboundPeerConnected, GetNewAddress: newAddressFunc, }) @@ -2652,9 +2534,9 @@ func newServer(listenAddrs []string, db database.DB, chainParams *dagconfig.Para s.connManager = cmgr // Start up persistent peers. - permanentPeers := cfg.ConnectPeers + permanentPeers := config.MainConfig().ConnectPeers if len(permanentPeers) == 0 { - permanentPeers = cfg.AddPeers + permanentPeers = config.MainConfig().AddPeers } for _, addr := range permanentPeers { netAddr, err := addrStringToNetAddr(addr) @@ -2668,54 +2550,15 @@ func newServer(listenAddrs []string, db database.DB, chainParams *dagconfig.Para }) } - if !cfg.DisableRPC { - // Setup listeners for the configured RPC listen addresses and - // TLS settings. - rpcListeners, err := setupRPCListeners() - if err != nil { - return nil, err - } - if len(rpcListeners) == 0 { - return nil, errors.New("RPCS: No valid listen address") - } - - s.rpcServer, err = newRPCServer(&rpcserverConfig{ - Listeners: rpcListeners, - StartupTime: s.startupTime, - ConnMgr: &rpcConnManager{&s}, - SyncMgr: &rpcSyncMgr{&s, s.syncManager}, - TimeSource: s.timeSource, - DAG: s.dag, - ChainParams: chainParams, - DB: db, - TxMemPool: s.txMemPool, - Generator: blockTemplateGenerator, - CPUMiner: s.cpuMiner, - TxIndex: s.txIndex, - AddrIndex: s.addrIndex, - CfIndex: s.cfIndex, - FeeEstimator: s.feeEstimator, - }) - if err != nil { - return nil, err - } - - // Signal process shutdown when the RPC server requests it. - go func() { - <-s.rpcServer.RequestedProcessShutdown() - shutdownRequestChannel <- struct{}{} - }() - } - return &s, nil } // initListeners initializes the configured net listeners and adds any bound // addresses to the address manager. Returns the listeners and a NAT interface, // which is non-nil if UPnP is in use. -func initListeners(amgr *addrmgr.AddrManager, listenAddrs []string, services wire.ServiceFlag) ([]net.Listener, NAT, error) { +func initListeners(amgr *addrmgr.AddrManager, listenAddrs []string, services wire.ServiceFlag) ([]net.Listener, serverutils.NAT, error) { // Listen for TCP connections at the configured addresses - netAddrs, err := parseListeners(listenAddrs) + netAddrs, err := ParseListeners(listenAddrs) if err != nil { return nil, nil, err } @@ -2730,16 +2573,16 @@ func initListeners(amgr *addrmgr.AddrManager, listenAddrs []string, services wir listeners = append(listeners, listener) } - var nat NAT - if len(cfg.ExternalIPs) != 0 { - defaultPort, err := strconv.ParseUint(activeNetParams.DefaultPort, 10, 16) + var nat serverutils.NAT + if len(config.MainConfig().ExternalIPs) != 0 { + defaultPort, err := strconv.ParseUint(config.ActiveNetParams().DefaultPort, 10, 16) if err != nil { srvrLog.Errorf("Can not parse default port %s for active chain: %v", - activeNetParams.DefaultPort, err) + config.ActiveNetParams().DefaultPort, err) return nil, nil, err } - for _, sip := range cfg.ExternalIPs { + for _, sip := range config.MainConfig().ExternalIPs { eport := uint16(defaultPort) host, portstr, err := net.SplitHostPort(sip) if err != nil { @@ -2766,9 +2609,9 @@ func initListeners(amgr *addrmgr.AddrManager, listenAddrs []string, services wir } } } else { - if cfg.Upnp { + if config.MainConfig().Upnp { var err error - nat, err = Discover() + nat, err = serverutils.Discover() if err != nil { srvrLog.Warnf("Can't discover upnp: %v", err) } @@ -2814,7 +2657,7 @@ func addrStringToNetAddr(addr string) (net.Addr, error) { // Tor addresses cannot be resolved to an IP, so just return an onion // address instead. if strings.HasSuffix(host, ".onion") { - if cfg.NoOnion { + if config.MainConfig().NoOnion { return nil, errors.New("tor has been disabled") } @@ -2822,7 +2665,7 @@ func addrStringToNetAddr(addr string) (net.Addr, error) { } // Attempt to look up an IP address associated with the parsed host. - ips, err := btcdLookup(host) + ips, err := serverutils.BTCDLookup(host) if err != nil { return nil, err } @@ -2907,7 +2750,7 @@ func dynamicTickDuration(remaining time.Duration) time.Duration { // isWhitelisted returns whether the IP address is included in the whitelisted // networks and IPs. func isWhitelisted(addr net.Addr) bool { - if len(cfg.whitelists) == 0 { + if len(config.MainConfig().Whitelists) == 0 { return false } @@ -2922,7 +2765,7 @@ func isWhitelisted(addr net.Addr) bool { return false } - for _, ipnet := range cfg.whitelists { + for _, ipnet := range config.MainConfig().Whitelists { if ipnet.Contains(ip) { return true } @@ -2982,3 +2825,30 @@ func mergeCheckpoints(defaultCheckpoints, additional []dagconfig.Checkpoint) []d sort.Sort(checkpointSorter(checkpoints)) return checkpoints } + +// AnnounceNewTransactions generates and relays inventory vectors and notifies +// both websocket and getblocktemplate long poll clients of the passed +// transactions. This function should be called whenever new transactions +// are added to the mempool. +func (s *Server) AnnounceNewTransactions(txns []*mempool.TxDesc) { + // Generate and relay inventory vectors for all newly accepted + // transactions. + s.RelayTransactions(txns) + + // Notify both websocket and getblocktemplate long poll clients of all + // newly accepted transactions. + s.notifyNewTransactions(txns) +} + +// TransactionConfirmed is a function for the peerNotifier interface. +// When a transaction has one confirmation, we can mark it as no +// longer needing rebroadcasting. +func (s *Server) TransactionConfirmed(tx *btcutil.Tx) { + // Rebroadcasting is only necessary when the RPC server is active. + if config.MainConfig().DisableRPC { + return + } + + iv := wire.NewInvVect(wire.InvTypeTx, tx.Hash()) + s.RemoveRebroadcastInventory(iv) +} diff --git a/server/rpcserver/log.go b/server/rpcserver/log.go new file mode 100644 index 000000000..ae463c12e --- /dev/null +++ b/server/rpcserver/log.go @@ -0,0 +1,19 @@ +// 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. + +package rpcserver + +import ( + "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/logger" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log btclog.Logger + +func init() { + log, _ = logger.Get(logger.SubsystemTags.RPCS) +} diff --git a/rpcadapters.go b/server/rpcserver/rpcadapters.go similarity index 88% rename from rpcadapters.go rename to server/rpcserver/rpcadapters.go index 9686a4d78..07f142545 100644 --- a/rpcadapters.go +++ b/server/rpcserver/rpcadapters.go @@ -2,7 +2,7 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package main +package rpcserver import ( "sync/atomic" @@ -12,13 +12,14 @@ import ( "github.com/daglabs/btcd/mempool" "github.com/daglabs/btcd/netsync" "github.com/daglabs/btcd/peer" + "github.com/daglabs/btcd/server/p2p" "github.com/daglabs/btcd/wire" "github.com/daglabs/btcutil" ) // rpcPeer provides a peer for use with the RPC server and implements the // rpcserverPeer interface. -type rpcPeer serverPeer +type rpcPeer p2p.Peer // Ensure rpcPeer implements the rpcserverPeer interface. var _ rpcserverPeer = (*rpcPeer)(nil) @@ -31,7 +32,7 @@ func (p *rpcPeer) ToPeer() *peer.Peer { if p == nil { return nil } - return (*serverPeer)(p).Peer + return (*p2p.Peer)(p).Peer } // IsTxRelayDisabled returns whether or not the peer has disabled transaction @@ -40,7 +41,7 @@ func (p *rpcPeer) ToPeer() *peer.Peer { // This function is safe for concurrent access and is part of the rpcserverPeer // interface implementation. func (p *rpcPeer) IsTxRelayDisabled() bool { - return (*serverPeer)(p).disableRelayTx + return (*p2p.Peer)(p).DisableRelayTx } // BanScore returns the current integer value that represents how close the peer @@ -49,7 +50,7 @@ func (p *rpcPeer) IsTxRelayDisabled() bool { // This function is safe for concurrent access and is part of the rpcserverPeer // interface implementation. func (p *rpcPeer) BanScore() uint32 { - return (*serverPeer)(p).banScore.Int() + return (*p2p.Peer)(p).DynamicBanScore.Int() } // FeeFilter returns the requested current minimum fee rate for which @@ -58,13 +59,13 @@ func (p *rpcPeer) BanScore() uint32 { // This function is safe for concurrent access and is part of the rpcserverPeer // interface implementation. func (p *rpcPeer) FeeFilter() int64 { - return atomic.LoadInt64(&(*serverPeer)(p).feeFilter) + return atomic.LoadInt64(&(*p2p.Peer)(p).FeeFilterInt) } // rpcConnManager provides a connection manager for use with the RPC server and // implements the rpcserverConnManager interface. type rpcConnManager struct { - server *server + server *p2p.Server } // Ensure rpcConnManager implements the rpcserverConnManager interface. @@ -79,10 +80,10 @@ var _ rpcserverConnManager = &rpcConnManager{} // rpcserverConnManager interface implementation. func (cm *rpcConnManager) Connect(addr string, permanent bool) error { replyChan := make(chan error) - cm.server.query <- connectNodeMsg{ - addr: addr, - permanent: permanent, - reply: replyChan, + cm.server.Query <- p2p.ConnectNodeMsg{ + Addr: addr, + Permanent: permanent, + Reply: replyChan, } return <-replyChan } @@ -95,9 +96,9 @@ func (cm *rpcConnManager) Connect(addr string, permanent bool) error { // rpcserverConnManager interface implementation. func (cm *rpcConnManager) RemoveByID(id int32) error { replyChan := make(chan error) - cm.server.query <- removeNodeMsg{ - cmp: func(sp *serverPeer) bool { return sp.ID() == id }, - reply: replyChan, + cm.server.Query <- p2p.RemoveNodeMsg{ + Cmp: func(sp *p2p.Peer) bool { return sp.ID() == id }, + Reply: replyChan, } return <-replyChan } @@ -110,9 +111,9 @@ func (cm *rpcConnManager) RemoveByID(id int32) error { // rpcserverConnManager interface implementation. func (cm *rpcConnManager) RemoveByAddr(addr string) error { replyChan := make(chan error) - cm.server.query <- removeNodeMsg{ - cmp: func(sp *serverPeer) bool { return sp.Addr() == addr }, - reply: replyChan, + cm.server.Query <- p2p.RemoveNodeMsg{ + Cmp: func(sp *p2p.Peer) bool { return sp.Addr() == addr }, + Reply: replyChan, } return <-replyChan } @@ -125,9 +126,9 @@ func (cm *rpcConnManager) RemoveByAddr(addr string) error { // rpcserverConnManager interface implementation. func (cm *rpcConnManager) DisconnectByID(id int32) error { replyChan := make(chan error) - cm.server.query <- disconnectNodeMsg{ - cmp: func(sp *serverPeer) bool { return sp.ID() == id }, - reply: replyChan, + cm.server.Query <- p2p.DisconnectNodeMsg{ + Cmp: func(sp *p2p.Peer) bool { return sp.ID() == id }, + Reply: replyChan, } return <-replyChan } @@ -140,9 +141,9 @@ func (cm *rpcConnManager) DisconnectByID(id int32) error { // rpcserverConnManager interface implementation. func (cm *rpcConnManager) DisconnectByAddr(addr string) error { replyChan := make(chan error) - cm.server.query <- disconnectNodeMsg{ - cmp: func(sp *serverPeer) bool { return sp.Addr() == addr }, - reply: replyChan, + cm.server.Query <- p2p.DisconnectNodeMsg{ + Cmp: func(sp *p2p.Peer) bool { return sp.Addr() == addr }, + Reply: replyChan, } return <-replyChan } @@ -169,8 +170,8 @@ func (cm *rpcConnManager) NetTotals() (uint64, uint64) { // This function is safe for concurrent access and is part of the // rpcserverConnManager interface implementation. func (cm *rpcConnManager) ConnectedPeers() []rpcserverPeer { - replyChan := make(chan []*serverPeer) - cm.server.query <- getPeersMsg{reply: replyChan} + replyChan := make(chan []*p2p.Peer) + cm.server.Query <- p2p.GetPeersMsg{Reply: replyChan} serverPeers := <-replyChan // Convert to RPC server peers. @@ -187,8 +188,8 @@ func (cm *rpcConnManager) ConnectedPeers() []rpcserverPeer { // This function is safe for concurrent access and is part of the // rpcserverConnManager interface implementation. func (cm *rpcConnManager) PersistentPeers() []rpcserverPeer { - replyChan := make(chan []*serverPeer) - cm.server.query <- getAddedNodesMsg{reply: replyChan} + replyChan := make(chan []*p2p.Peer) + cm.server.Query <- p2p.GetAddedNodesMsg{Reply: replyChan} serverPeers := <-replyChan // Convert to generic peers. @@ -220,13 +221,13 @@ func (cm *rpcConnManager) AddRebroadcastInventory(iv *wire.InvVect, data interfa // RelayTransactions generates and relays inventory vectors for all of the // passed transactions to all connected peers. func (cm *rpcConnManager) RelayTransactions(txns []*mempool.TxDesc) { - cm.server.relayTransactions(txns) + cm.server.RelayTransactions(txns) } // rpcSyncMgr provides a block manager for use with the RPC server and // implements the rpcserverSyncManager interface. type rpcSyncMgr struct { - server *server + server *p2p.Server syncMgr *netsync.SyncManager } @@ -275,5 +276,5 @@ func (b *rpcSyncMgr) SyncPeerID() int32 { // This function is safe for concurrent access and is part of the // rpcserverSyncManager interface implementation. func (b *rpcSyncMgr) LocateHeaders(locators []*daghash.Hash, hashStop *daghash.Hash) []wire.BlockHeader { - return b.server.dag.LocateHeaders(locators, hashStop) + return b.server.DAG.LocateHeaders(locators, hashStop) } diff --git a/rpcserver.go b/server/rpcserver/rpcserver.go similarity index 91% rename from rpcserver.go rename to server/rpcserver/rpcserver.go index 504597be7..76eb79c9a 100644 --- a/rpcserver.go +++ b/server/rpcserver/rpcserver.go @@ -3,12 +3,13 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package main +package rpcserver import ( "bytes" "crypto/sha256" "crypto/subtle" + "crypto/tls" "encoding/base64" "encoding/hex" "encoding/json" @@ -20,7 +21,6 @@ import ( "math/rand" "net" "net/http" - "os" "strconv" "strings" "sync" @@ -32,16 +32,23 @@ import ( "github.com/daglabs/btcd/blockdag/indexers" "github.com/daglabs/btcd/btcec" "github.com/daglabs/btcd/btcjson" + "github.com/daglabs/btcd/config" "github.com/daglabs/btcd/dagconfig" "github.com/daglabs/btcd/dagconfig/daghash" "github.com/daglabs/btcd/database" + "github.com/daglabs/btcd/logger" "github.com/daglabs/btcd/mempool" "github.com/daglabs/btcd/mining" "github.com/daglabs/btcd/mining/cpuminer" "github.com/daglabs/btcd/peer" + "github.com/daglabs/btcd/server/p2p" + "github.com/daglabs/btcd/server/serverutils" "github.com/daglabs/btcd/txscript" + "github.com/daglabs/btcd/version" "github.com/daglabs/btcd/wire" "github.com/daglabs/btcutil" + "github.com/daglabs/btcutil/fs" + "github.com/daglabs/btcutil/network" ) // API version constants @@ -120,7 +127,7 @@ var ( } ) -type commandHandler func(*rpcServer, interface{}, <-chan struct{}) (interface{}, error) +type commandHandler func(*Server, interface{}, <-chan struct{}) (interface{}, error) // rpcHandlers maps RPC command strings to appropriate handler functions. // This is set by init because help references rpcHandlers and thus causes @@ -303,7 +310,7 @@ func internalRPCError(errStr, context string) *btcjson.RPCError { if context != "" { logStr = context + ": " + errStr } - rpcsLog.Error(logStr) + log.Error(logStr) return btcjson.NewRPCError(btcjson.ErrRPCInternal.Code, errStr) } @@ -348,22 +355,22 @@ func newGbtWorkState(timeSource blockdag.MedianTimeSource) *gbtWorkState { // handleUnimplemented is the handler for commands that should ultimately be // supported but are not yet implemented. -func handleUnimplemented(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleUnimplemented(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { return nil, ErrRPCUnimplemented } // handleAskWallet is the handler for commands that are recognized as valid, but // are unable to answer correctly since it involves wallet state. // These commands will be implemented in btcwallet. -func handleAskWallet(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleAskWallet(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { return nil, ErrRPCNoWallet } // handleAddNode handles addnode commands. -func handleAddNode(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleAddNode(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.AddNodeCmd) - addr := normalizeAddress(c.Addr, s.cfg.ChainParams.DefaultPort) + addr := network.NormalizeAddress(c.Addr, s.cfg.ChainParams.DefaultPort) var err error switch c.SubCmd { case "add": @@ -391,7 +398,7 @@ func handleAddNode(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (in } // handleNode handles node commands. -func handleNode(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleNode(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.NodeCmd) var addr string @@ -407,7 +414,7 @@ func handleNode(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (inter err = s.cfg.ConnMgr.DisconnectByID(int32(nodeID)) } else { if _, _, errP := net.SplitHostPort(c.Target); errP == nil || net.ParseIP(c.Target) != nil { - addr = normalizeAddress(c.Target, params.DefaultPort) + addr = network.NormalizeAddress(c.Target, params.DefaultPort) err = s.cfg.ConnMgr.DisconnectByAddr(addr) } else { return nil, &btcjson.RPCError{ @@ -432,7 +439,7 @@ func handleNode(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (inter err = s.cfg.ConnMgr.RemoveByID(int32(nodeID)) } else { if _, _, errP := net.SplitHostPort(c.Target); errP == nil || net.ParseIP(c.Target) != nil { - addr = normalizeAddress(c.Target, params.DefaultPort) + addr = network.NormalizeAddress(c.Target, params.DefaultPort) err = s.cfg.ConnMgr.RemoveByAddr(addr) } else { return nil, &btcjson.RPCError{ @@ -449,7 +456,7 @@ func handleNode(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (inter } case "connect": - addr = normalizeAddress(c.Target, params.DefaultPort) + addr = network.NormalizeAddress(c.Target, params.DefaultPort) // Default to temporary connections. subCmd := "temp" @@ -509,7 +516,7 @@ func messageToHex(msg wire.Message) (string, error) { } // handleCreateRawTransaction handles createrawtransaction commands. -func handleCreateRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleCreateRawTransaction(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.CreateRawTransactionCmd) // Validate the locktime, if given. @@ -614,16 +621,16 @@ func handleCreateRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan } // handleDebugLevel handles debuglevel commands. -func handleDebugLevel(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleDebugLevel(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.DebugLevelCmd) // Special show command to list supported subsystems. if c.LevelSpec == "show" { return fmt.Sprintf("Supported subsystems %v", - supportedSubsystems()), nil + logger.SupportedSubsystems()), nil } - err := parseAndSetDebugLevels(c.LevelSpec) + err := logger.ParseAndSetDebugLevels(c.LevelSpec) if err != nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInvalidParams.Code, @@ -751,7 +758,7 @@ func createTxRawResult(chainParams *dagconfig.Params, mtx *wire.MsgTx, } // handleDecodeRawTransaction handles decoderawtransaction commands. -func handleDecodeRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleDecodeRawTransaction(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.DecodeRawTransactionCmd) // Deserialize the transaction. @@ -784,7 +791,7 @@ func handleDecodeRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan } // handleDecodeScript handles decodescript commands. -func handleDecodeScript(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleDecodeScript(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.DecodeScriptCmd) // Convert the hex script to bytes. @@ -832,7 +839,7 @@ func handleDecodeScript(s *rpcServer, cmd interface{}, closeChan <-chan struct{} } // handleEstimateFee handles estimatefee commands. -func handleEstimateFee(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleEstimateFee(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.EstimateFeeCmd) if s.cfg.FeeEstimator == nil { @@ -854,10 +861,10 @@ func handleEstimateFee(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) } // handleGenerate handles generate commands. -func handleGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGenerate(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { // Respond with an error if there are no addresses to pay the // created blocks to. - if len(cfg.miningAddrs) == 0 { + if len(config.MainConfig().MiningAddrs) == 0 { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInternal.Code, Message: "No payment addresses specified " + @@ -908,7 +915,7 @@ func handleGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (i } // handleGetAddedNodeInfo handles getaddednodeinfo commands. -func handleGetAddedNodeInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetAddedNodeInfo(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetAddedNodeInfoCmd) // Retrieve a list of persistent (added) peers from the server and @@ -968,7 +975,7 @@ func handleGetAddedNodeInfo(s *rpcServer, cmd interface{}, closeChan <-chan stru default: // Do a DNS lookup for the address. If the lookup fails, just // use the host. - ips, err := btcdLookup(host) + ips, err := serverutils.BTCDLookup(host) if err != nil { ipList = make([]string, 1) ipList[0] = host @@ -987,7 +994,7 @@ func handleGetAddedNodeInfo(s *rpcServer, cmd interface{}, closeChan <-chan stru addr.Address = ip addr.Connected = "false" if ip == host && peer.Connected() { - addr.Connected = directionString(peer.Inbound()) + addr.Connected = logger.DirectionString(peer.Inbound()) } addrs = append(addrs, addr) } @@ -998,7 +1005,7 @@ func handleGetAddedNodeInfo(s *rpcServer, cmd interface{}, closeChan <-chan stru } // handleGetBestBlock implements the getbestblock command. -func handleGetBestBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetBestBlock(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { // All other "get block" commands give either the height, the // hash, or both but require the block SHA. This gets both for // the best block. @@ -1011,7 +1018,7 @@ func handleGetBestBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{} } // handleGetBestBlockHash implements the getbestblockhash command. -func handleGetBestBlockHash(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetBestBlockHash(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { dagState := s.cfg.DAG.GetDAGState() return dagState.SelectedTip.Hash.String(), nil } @@ -1030,14 +1037,14 @@ func getDifficultyRatio(bits uint32, params *dagconfig.Params) float64 { outString := difficulty.FloatString(8) diff, err := strconv.ParseFloat(outString, 64) if err != nil { - rpcsLog.Errorf("Cannot get difficulty: %v", err) + log.Errorf("Cannot get difficulty: %v", err) return 0 } return diff } // handleGetBlock implements the getblock command. -func handleGetBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetBlock(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetBlockCmd) // Load the raw block bytes from the database. @@ -1157,7 +1164,7 @@ func softForkStatus(state blockdag.ThresholdState) (string, error) { } // handleGetBlockDAGInfo implements the getblockdaginfo command. -func handleGetBlockDAGInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetBlockDAGInfo(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { // Obtain a snapshot of the current best known DAG state. We'll // populate the response to this call primarily from this snapshot. params := s.cfg.ChainParams @@ -1252,7 +1259,7 @@ func handleGetBlockDAGInfo(s *rpcServer, cmd interface{}, closeChan <-chan struc } // handleGetBlockCount implements the getblockcount command. -func handleGetBlockCount(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetBlockCount(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { dagState := s.cfg.DAG.GetDAGState() return int64(dagState.SelectedTip.Height), nil } @@ -1260,12 +1267,12 @@ func handleGetBlockCount(s *rpcServer, cmd interface{}, closeChan <-chan struct{ // handleGetBlockHash implements the getblockhash command. // This command had been (possibly temporarily) dropped. // Originally it relied on height, which no longer makes sense. -func handleGetBlockHash(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetBlockHash(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { return nil, ErrRPCUnimplemented } // handleGetBlockHeader implements the getblockheader command. -func handleGetBlockHeader(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetBlockHeader(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetBlockHeaderCmd) // Fetch the header from chain. @@ -1484,7 +1491,7 @@ func (state *gbtWorkState) templateUpdateChan(prevHash *daghash.Hash, lastGenera // addresses. // // This function MUST be called with the state locked. -func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bool) error { +func (state *gbtWorkState) updateBlockTemplate(s *Server, useCoinbaseValue bool) error { generator := s.cfg.Generator lastTxUpdate := generator.TxSource().LastUpdated() if lastTxUpdate.IsZero() { @@ -1515,7 +1522,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bo // to create their own coinbase. var payAddr btcutil.Address if !useCoinbaseValue { - payAddr = cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] + payAddr = config.MainConfig().MiningAddrs[rand.Intn(len(config.MainConfig().MiningAddrs))] } // Create a new block template that has a coinbase which anyone @@ -1547,7 +1554,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bo state.prevHash = latestHash state.minTimestamp = minTimestamp - rpcsLog.Debugf("Generated block template (timestamp %v, "+ + log.Debugf("Generated block template (timestamp %v, "+ "target %s, merkle root %s)", msgBlock.Header.Timestamp, targetDifficulty, msgBlock.Header.MerkleRoot) @@ -1570,7 +1577,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bo // returned if none have been specified. if !useCoinbaseValue && !template.ValidPayAddress { // Choose a payment address at random. - payToAddr := cfg.miningAddrs[rand.Intn(len(cfg.miningAddrs))] + payToAddr := config.MainConfig().MiningAddrs[rand.Intn(len(config.MainConfig().MiningAddrs))] // Update the block coinbase output of the template to // pay to the randomly selected payment address. @@ -1599,7 +1606,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *rpcServer, useCoinbaseValue bo generator.UpdateBlockTime(msgBlock) msgBlock.Header.Nonce = 0 - rpcsLog.Debugf("Updated block template (timestamp %v, "+ + log.Debugf("Updated block template (timestamp %v, "+ "target %s)", msgBlock.Header.Timestamp, targetDifficulty) } @@ -1754,7 +1761,7 @@ func (state *gbtWorkState) blockTemplateResult(useCoinbaseValue bool, submitOld // has passed without finding a solution. // // See https://en.bitcoin.it/wiki/BIP_0022 for more details. -func handleGetBlockTemplateLongPoll(s *rpcServer, longPollID string, useCoinbaseValue bool, closeChan <-chan struct{}) (interface{}, error) { +func handleGetBlockTemplateLongPoll(s *Server, longPollID string, useCoinbaseValue bool, closeChan <-chan struct{}) (interface{}, error) { state := s.gbtWorkState state.Lock() // The state unlock is intentionally not deferred here since it needs to @@ -1847,7 +1854,7 @@ func handleGetBlockTemplateLongPoll(s *rpcServer, longPollID string, useCoinbase // in regards to whether or not it supports creating its own coinbase (the // coinbasetxn and coinbasevalue capabilities) and modifies the returned block // template accordingly. -func handleGetBlockTemplateRequest(s *rpcServer, request *btcjson.TemplateRequest, closeChan <-chan struct{}) (interface{}, error) { +func handleGetBlockTemplateRequest(s *Server, request *btcjson.TemplateRequest, closeChan <-chan struct{}) (interface{}, error) { // Extract the relevant passed capabilities and restrict the result to // either a coinbase value or a coinbase transaction object depending on // the request. Default to only providing a coinbase value. @@ -1870,7 +1877,7 @@ func handleGetBlockTemplateRequest(s *rpcServer, request *btcjson.TemplateReques // When a coinbase transaction has been requested, respond with an error // if there are no addresses to pay the created block template to. - if !useCoinbaseValue && len(cfg.miningAddrs) == 0 { + if !useCoinbaseValue && len(config.MainConfig().MiningAddrs) == 0 { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInternal.Code, Message: "A coinbase transaction has been requested, " + @@ -1883,7 +1890,7 @@ func handleGetBlockTemplateRequest(s *rpcServer, request *btcjson.TemplateReques // way to relay a found block or receive transactions to work on. // However, allow this state when running in the regression test or // simulation test mode. - if !(cfg.RegressionTest || cfg.SimNet) && + if !(config.MainConfig().RegressionTest || config.MainConfig().SimNet) && s.cfg.ConnMgr.ConnectedCount() == 0 { return nil, &btcjson.RPCError{ @@ -2025,7 +2032,7 @@ func chainErrToGBTErrString(err error) string { // deals with block proposals. // // See https://en.bitcoin.it/wiki/BIP_0023 for more details. -func handleGetBlockTemplateProposal(s *rpcServer, request *btcjson.TemplateRequest) (interface{}, error) { +func handleGetBlockTemplateProposal(s *Server, request *btcjson.TemplateRequest) (interface{}, error) { hexData := request.Data if hexData == "" { return false, &btcjson.RPCError{ @@ -2067,14 +2074,14 @@ func handleGetBlockTemplateProposal(s *rpcServer, request *btcjson.TemplateReque if err := s.cfg.DAG.CheckConnectBlockTemplate(block); err != nil { if _, ok := err.(blockdag.RuleError); !ok { errStr := fmt.Sprintf("Failed to process block proposal: %v", err) - rpcsLog.Error(errStr) + log.Error(errStr) return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCVerify, Message: errStr, } } - rpcsLog.Infof("Rejected block proposal: %v", err) + log.Infof("Rejected block proposal: %v", err) return chainErrToGBTErrString(err), nil } @@ -2085,7 +2092,7 @@ func handleGetBlockTemplateProposal(s *rpcServer, request *btcjson.TemplateReque // // See https://en.bitcoin.it/wiki/BIP_0022 and // https://en.bitcoin.it/wiki/BIP_0023 for more details. -func handleGetBlockTemplate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetBlockTemplate(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetBlockTemplateCmd) request := c.Request @@ -2109,7 +2116,7 @@ func handleGetBlockTemplate(s *rpcServer, cmd interface{}, closeChan <-chan stru } // handleGetCFilter implements the getcfilter command. -func handleGetCFilter(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetCFilter(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { if s.cfg.CfIndex == nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCNoCFIndex, @@ -2125,7 +2132,7 @@ func handleGetCFilter(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) filterBytes, err := s.cfg.CfIndex.FilterByBlockHash(hash, c.FilterType) if err != nil { - rpcsLog.Debugf("Could not find committed filter for %v: %v", + log.Debugf("Could not find committed filter for %v: %v", hash, err) return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCBlockNotFound, @@ -2133,12 +2140,12 @@ func handleGetCFilter(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) } } - rpcsLog.Debugf("Found committed filter for %v", hash) + log.Debugf("Found committed filter for %v", hash) return hex.EncodeToString(filterBytes), nil } // handleGetCFilterHeader implements the getcfilterheader command. -func handleGetCFilterHeader(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetCFilterHeader(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { if s.cfg.CfIndex == nil { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCNoCFIndex, @@ -2154,9 +2161,9 @@ func handleGetCFilterHeader(s *rpcServer, cmd interface{}, closeChan <-chan stru headerBytes, err := s.cfg.CfIndex.FilterHeaderByBlockHash(hash, c.FilterType) if len(headerBytes) > 0 { - rpcsLog.Debugf("Found header of committed filter for %v", hash) + log.Debugf("Found header of committed filter for %v", hash) } else { - rpcsLog.Debugf("Could not find header of committed filter for %v: %v", + log.Debugf("Could not find header of committed filter for %v: %v", hash, err) return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCBlockNotFound, @@ -2169,28 +2176,28 @@ func handleGetCFilterHeader(s *rpcServer, cmd interface{}, closeChan <-chan stru } // handleGetConnectionCount implements the getconnectioncount command. -func handleGetConnectionCount(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetConnectionCount(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { return s.cfg.ConnMgr.ConnectedCount(), nil } // handleGetCurrentNet implements the getcurrentnet command. -func handleGetCurrentNet(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetCurrentNet(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { return s.cfg.ChainParams.Net, nil } // handleGetDifficulty implements the getdifficulty command. -func handleGetDifficulty(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetDifficulty(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { dagState := s.cfg.DAG.GetDAGState() return getDifficultyRatio(dagState.SelectedTip.Bits, s.cfg.ChainParams), nil } // handleGetGenerate implements the getgenerate command. -func handleGetGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetGenerate(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { return s.cfg.CPUMiner.IsMining(), nil } // handleGetHashesPerSec implements the gethashespersec command. -func handleGetHashesPerSec(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetHashesPerSec(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { return int64(s.cfg.CPUMiner.HashesPerSecond()), nil } @@ -2198,7 +2205,7 @@ func handleGetHashesPerSec(s *rpcServer, cmd interface{}, closeChan <-chan struc // // NOTE: This is a btcsuite extension originally ported from // github.com/decred/dcrd. -func handleGetHeaders(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetHeaders(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetHeadersCmd) // Fetch the requested headers from chain while respecting the provided @@ -2237,25 +2244,25 @@ func handleGetHeaders(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) // handleGetInfo implements the getinfo command. We only return the fields // that are not related to wallet functionality. -func handleGetInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetInfo(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { dagState := s.cfg.DAG.GetDAGState() ret := &btcjson.InfoDAGResult{ - Version: int32(1000000*appMajor + 10000*appMinor + 100*appPatch), + Version: int32(1000000*version.AppMajor + 10000*version.AppMinor + 100*version.AppPatch), ProtocolVersion: int32(maxProtocolVersion), Blocks: dagState.SelectedTip.Height, TimeOffset: int64(s.cfg.TimeSource.Offset().Seconds()), Connections: s.cfg.ConnMgr.ConnectedCount(), - Proxy: cfg.Proxy, + Proxy: config.MainConfig().Proxy, Difficulty: getDifficultyRatio(dagState.SelectedTip.Bits, s.cfg.ChainParams), - TestNet: cfg.TestNet3, - RelayFee: cfg.minRelayTxFee.ToBTC(), + TestNet: config.MainConfig().TestNet3, + RelayFee: config.MainConfig().MinRelayTxFee.ToBTC(), } return ret, nil } // handleGetMempoolInfo implements the getmempoolinfo command. -func handleGetMempoolInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetMempoolInfo(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { mempoolTxns := s.cfg.TxMemPool.TxDescs() var numBytes int64 @@ -2273,7 +2280,7 @@ func handleGetMempoolInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct // handleGetMiningInfo implements the getmininginfo command. We only return the // fields that are not related to wallet functionality. -func handleGetMiningInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetMiningInfo(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { // Create a default getnetworkhashps command to use defaults and make // use of the existing getnetworkhashps handler. gnhpsCmd := btcjson.NewGetNetworkHashPSCmd(nil, nil) @@ -2301,13 +2308,13 @@ func handleGetMiningInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{ HashesPerSec: int64(s.cfg.CPUMiner.HashesPerSecond()), NetworkHashPS: networkHashesPerSec, PooledTx: uint64(s.cfg.TxMemPool.Count()), - TestNet: cfg.TestNet3, + TestNet: config.MainConfig().TestNet3, } return &result, nil } // handleGetNetTotals implements the getnettotals command. -func handleGetNetTotals(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetNetTotals(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { totalBytesRecv, totalBytesSent := s.cfg.ConnMgr.NetTotals() reply := &btcjson.GetNetTotalsResult{ TotalBytesRecv: totalBytesRecv, @@ -2320,12 +2327,12 @@ func handleGetNetTotals(s *rpcServer, cmd interface{}, closeChan <-chan struct{} // handleGetNetworkHashPS implements the getnetworkhashps command. // This command had been (possibly temporarily) dropped. // Originally it relied on height, which no longer makes sense. -func handleGetNetworkHashPS(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetNetworkHashPS(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { return nil, ErrRPCUnimplemented } // handleGetPeerInfo implements the getpeerinfo command. -func handleGetPeerInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetPeerInfo(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { peers := s.cfg.ConnMgr.ConnectedPeers() syncPeerID := s.cfg.SyncMgr.SyncPeerID() infos := make([]*btcjson.GetPeerInfoResult, 0, len(peers)) @@ -2364,7 +2371,7 @@ func handleGetPeerInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) } // handleGetRawMempool implements the getrawmempool command. -func handleGetRawMempool(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetRawMempool(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetRawMempoolCmd) mp := s.cfg.TxMemPool @@ -2384,7 +2391,7 @@ func handleGetRawMempool(s *rpcServer, cmd interface{}, closeChan <-chan struct{ } // handleGetRawTransaction implements the getrawtransaction command. -func handleGetRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetRawTransaction(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetRawTransactionCmd) // Convert the provided transaction hash hex to a Hash. @@ -2503,7 +2510,7 @@ func handleGetRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan str } // handleGetTxOut handles gettxout commands. -func handleGetTxOut(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleGetTxOut(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.GetTxOutCmd) // Convert the provided transaction hash hex to a Hash. @@ -2609,7 +2616,7 @@ func handleGetTxOut(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (i } // handleHelp implements the help command. -func handleHelp(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleHelp(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.HelpCmd) // Provide a usage overview of all commands when no specific command @@ -2648,7 +2655,7 @@ func handleHelp(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (inter } // handlePing implements the ping command. -func handlePing(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handlePing(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { // Ask server to ping \o_ nonce, err := wire.RandomUint64() if err != nil { @@ -2676,7 +2683,7 @@ type retrievedTx struct { // fetchInputTxos fetches the outpoints from all transactions referenced by the // inputs to the passed transaction by checking the transaction mempool first // then the transaction index for those already mined into blocks. -func fetchInputTxos(s *rpcServer, tx *wire.MsgTx) (map[wire.OutPoint]wire.TxOut, error) { +func fetchInputTxos(s *Server, tx *wire.MsgTx) (map[wire.OutPoint]wire.TxOut, error) { mp := s.cfg.TxMemPool originOutputs := make(map[wire.OutPoint]wire.TxOut) for txInIndex, txIn := range tx.TxIn { @@ -2741,7 +2748,7 @@ func fetchInputTxos(s *rpcServer, tx *wire.MsgTx) (map[wire.OutPoint]wire.TxOut, // createVinListPrevOut returns a slice of JSON objects for the inputs of the // passed transaction. -func createVinListPrevOut(s *rpcServer, mtx *wire.MsgTx, chainParams *dagconfig.Params, vinExtra bool, filterAddrMap map[string]struct{}) ([]btcjson.VinPrevOut, error) { +func createVinListPrevOut(s *Server, mtx *wire.MsgTx, chainParams *dagconfig.Params, vinExtra bool, filterAddrMap map[string]struct{}) ([]btcjson.VinPrevOut, error) { // Coinbase transactions only have a single txin by definition. if blockdag.IsCoinBaseTx(mtx) { // Only include the transaction if the filter map is empty @@ -2859,7 +2866,7 @@ func createVinListPrevOut(s *rpcServer, mtx *wire.MsgTx, chainParams *dagconfig. // fetchMempoolTxnsForAddress queries the address index for all unconfirmed // transactions that involve the provided address. The results will be limited // by the number to skip and the number requested. -func fetchMempoolTxnsForAddress(s *rpcServer, addr btcutil.Address, numToSkip, numRequested uint32) ([]*btcutil.Tx, uint32) { +func fetchMempoolTxnsForAddress(s *Server, addr btcutil.Address, numToSkip, numRequested uint32) ([]*btcutil.Tx, uint32) { // There are no entries to return when there are less available than the // number being skipped. mpTxns := s.cfg.AddrIndex.UnconfirmedTxnsForAddress(addr) @@ -2878,7 +2885,7 @@ func fetchMempoolTxnsForAddress(s *rpcServer, addr btcutil.Address, numToSkip, n } // handleSearchRawTransactions implements the searchrawtransactions command. -func handleSearchRawTransactions(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleSearchRawTransactions(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { // Respond with an error if the address index is not enabled. addrIndex := s.cfg.AddrIndex if addrIndex == nil { @@ -3142,7 +3149,7 @@ func handleSearchRawTransactions(s *rpcServer, cmd interface{}, closeChan <-chan } // handleSendRawTransaction implements the sendrawtransaction command. -func handleSendRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleSendRawTransaction(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.SendRawTransactionCmd) // Deserialize and send off to tx relay hexStr := c.HexTx @@ -3173,10 +3180,10 @@ func handleSendRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan st // error is returned to the client with the deserialization // error code (to match bitcoind behavior). if _, ok := err.(mempool.RuleError); ok { - rpcsLog.Debugf("Rejected transaction %v: %v", tx.Hash(), + log.Debugf("Rejected transaction %v: %v", tx.Hash(), err) } else { - rpcsLog.Errorf("Failed to process transaction %v: %v", + log.Errorf("Failed to process transaction %v: %v", tx.Hash(), err) } return nil, &btcjson.RPCError{ @@ -3219,7 +3226,7 @@ func handleSendRawTransaction(s *rpcServer, cmd interface{}, closeChan <-chan st } // handleSetGenerate implements the setgenerate command. -func handleSetGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleSetGenerate(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.SetGenerateCmd) // Disable generation regardless of the provided generate flag if the @@ -3239,7 +3246,7 @@ func handleSetGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) } else { // Respond with an error if there are no addresses to pay the // created blocks to. - if len(cfg.miningAddrs) == 0 { + if len(config.MainConfig().MiningAddrs) == 0 { return nil, &btcjson.RPCError{ Code: btcjson.ErrRPCInternal.Code, Message: "No payment addresses specified " + @@ -3255,7 +3262,7 @@ func handleSetGenerate(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) } // handleStop implements the stop command. -func handleStop(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleStop(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { select { case s.requestProcessShutdown <- struct{}{}: default: @@ -3264,7 +3271,7 @@ func handleStop(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (inter } // handleSubmitBlock implements the submitblock command. -func handleSubmitBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleSubmitBlock(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.SubmitBlockCmd) // Deserialize the submitted block. @@ -3292,17 +3299,17 @@ func handleSubmitBlock(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) return fmt.Sprintf("rejected: %s", err.Error()), nil } - rpcsLog.Infof("Accepted block %s via submitblock", block.Hash()) + log.Infof("Accepted block %s via submitblock", block.Hash()) return nil, nil } // handleUptime implements the uptime command. -func handleUptime(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleUptime(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { return time.Now().Unix() - s.cfg.StartupTime, nil } // handleValidateAddress implements the validateaddress command. -func handleValidateAddress(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleValidateAddress(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.ValidateAddressCmd) result := btcjson.ValidateAddressResult{} @@ -3318,13 +3325,13 @@ func handleValidateAddress(s *rpcServer, cmd interface{}, closeChan <-chan struc return result, nil } -func verifyDAG(s *rpcServer, level, depth int32) error { +func verifyDAG(s *Server, level, depth int32) error { dagState := s.cfg.DAG.GetDAGState() finishHeight := dagState.SelectedTip.Height - depth if finishHeight < 0 { finishHeight = 0 } - rpcsLog.Infof("Verifying chain for %d blocks at level %d", + log.Infof("Verifying chain for %d blocks at level %d", dagState.SelectedTip.Height-finishHeight, level) currentHash := &dagState.SelectedTip.Hash @@ -3332,7 +3339,7 @@ func verifyDAG(s *rpcServer, level, depth int32) error { // Level 0 just looks up the block. block, err := s.cfg.DAG.BlockByHash(currentHash) if err != nil { - rpcsLog.Errorf("Verify is unable to fetch block at "+ + log.Errorf("Verify is unable to fetch block at "+ "height %d: %v", height, err) return err } @@ -3342,7 +3349,7 @@ func verifyDAG(s *rpcServer, level, depth int32) error { err := blockdag.CheckBlockSanity(block, s.cfg.ChainParams.PowLimit, s.cfg.TimeSource) if err != nil { - rpcsLog.Errorf("Verify is unable to validate "+ + log.Errorf("Verify is unable to validate "+ "block at hash %v height %d: %v", block.Hash(), height, err) return err @@ -3351,13 +3358,13 @@ func verifyDAG(s *rpcServer, level, depth int32) error { currentHash = block.MsgBlock().Header.SelectedPrevBlock() } - rpcsLog.Infof("Chain verify completed successfully") + log.Infof("Chain verify completed successfully") return nil } // handleVerifyDAG implements the verifydag command. -func handleVerifyDAG(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleVerifyDAG(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.VerifyDAGCmd) var checkLevel, checkDepth int32 @@ -3373,7 +3380,7 @@ func handleVerifyDAG(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) ( } // handleVerifyMessage implements the verifymessage command. -func handleVerifyMessage(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleVerifyMessage(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { c := cmd.(*btcjson.VerifyMessageCmd) // Decode the provided address. @@ -3438,7 +3445,7 @@ func handleVerifyMessage(s *rpcServer, cmd interface{}, closeChan <-chan struct{ // handleVersion implements the version command. // // NOTE: This is a btcsuite extension ported from github.com/decred/dcrd. -func handleVersion(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { +func handleVersion(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { result := map[string]btcjson.VersionResult{ "btcdjsonrpcapi": { VersionString: jsonrpcSemverString, @@ -3450,8 +3457,8 @@ func handleVersion(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (in return result, nil } -// rpcServer provides a concurrent safe RPC server to a chain server. -type rpcServer struct { +// Server provides a concurrent safe RPC server to a chain server. +type Server struct { started int32 shutdown int32 cfg rpcserverConfig @@ -3471,7 +3478,7 @@ type rpcServer struct { // httpStatusLine returns a response Status-Line (RFC 2616 Section 6.1) // for the given request and response status code. This function was lifted and // adapted from the standard library HTTP server code since it's not exported. -func (s *rpcServer) httpStatusLine(req *http.Request, code int) string { +func (s *Server) httpStatusLine(req *http.Request, code int) string { // Fast path: key := code proto11 := req.ProtoAtLeast(1, 1) @@ -3508,7 +3515,7 @@ func (s *rpcServer) httpStatusLine(req *http.Request, code int) string { // writeHTTPResponseHeaders writes the necessary response headers prior to // writing an HTTP body given a request to use for protocol negotiation, headers // to write, a status code, and a writer. -func (s *rpcServer) writeHTTPResponseHeaders(req *http.Request, headers http.Header, code int, w io.Writer) error { +func (s *Server) writeHTTPResponseHeaders(req *http.Request, headers http.Header, code int, w io.Writer) error { _, err := io.WriteString(w, s.httpStatusLine(req, code)) if err != nil { return err @@ -3524,16 +3531,16 @@ func (s *rpcServer) writeHTTPResponseHeaders(req *http.Request, headers http.Hea } // Stop is used by server.go to stop the rpc listener. -func (s *rpcServer) Stop() error { +func (s *Server) Stop() error { if atomic.AddInt32(&s.shutdown, 1) != 1 { - rpcsLog.Infof("RPC server is already in the process of shutting down") + log.Infof("RPC server is already in the process of shutting down") return nil } - rpcsLog.Warnf("RPC server shutting down") + log.Warnf("RPC server shutting down") for _, listener := range s.cfg.Listeners { err := listener.Close() if err != nil { - rpcsLog.Errorf("Problem shutting down rpc: %v", err) + log.Errorf("Problem shutting down rpc: %v", err) return err } } @@ -3541,21 +3548,21 @@ func (s *rpcServer) Stop() error { s.ntfnMgr.WaitForShutdown() close(s.quit) s.wg.Wait() - rpcsLog.Infof("RPC server shutdown complete") + log.Infof("RPC server shutdown complete") return nil } // RequestedProcessShutdown returns a channel that is sent to when an authorized // RPC client requests the process to shutdown. If the request can not be read // immediately, it is dropped. -func (s *rpcServer) RequestedProcessShutdown() <-chan struct{} { +func (s *Server) RequestedProcessShutdown() <-chan struct{} { return s.requestProcessShutdown } // NotifyNewTransactions notifies both websocket and getblocktemplate long // poll clients of the passed transactions. This function should be called // whenever new transactions are added to the mempool. -func (s *rpcServer) NotifyNewTransactions(txns []*mempool.TxDesc) { +func (s *Server) NotifyNewTransactions(txns []*mempool.TxDesc) { for _, txD := range txns { // Notify websocket clients about mempool transactions. s.ntfnMgr.NotifyMempoolTx(txD.Tx, true) @@ -3570,10 +3577,10 @@ func (s *rpcServer) NotifyNewTransactions(txns []*mempool.TxDesc) { // adding another client would exceed the maximum allow RPC clients. // // This function is safe for concurrent access. -func (s *rpcServer) limitConnections(w http.ResponseWriter, remoteAddr string) bool { - if int(atomic.LoadInt32(&s.numClients)+1) > cfg.RPCMaxClients { - rpcsLog.Infof("Max RPC clients exceeded [%d] - "+ - "disconnecting client %s", cfg.RPCMaxClients, +func (s *Server) limitConnections(w http.ResponseWriter, remoteAddr string) bool { + if int(atomic.LoadInt32(&s.numClients)+1) > config.MainConfig().RPCMaxClients { + log.Infof("Max RPC clients exceeded [%d] - "+ + "disconnecting client %s", config.MainConfig().RPCMaxClients, remoteAddr) http.Error(w, "503 Too busy. Try again later.", http.StatusServiceUnavailable) @@ -3587,7 +3594,7 @@ func (s *rpcServer) limitConnections(w http.ResponseWriter, remoteAddr string) b // limits and are tracked separately. // // This function is safe for concurrent access. -func (s *rpcServer) incrementClients() { +func (s *Server) incrementClients() { atomic.AddInt32(&s.numClients, 1) } @@ -3596,7 +3603,7 @@ func (s *rpcServer) incrementClients() { // limits and are tracked separately. // // This function is safe for concurrent access. -func (s *rpcServer) decrementClients() { +func (s *Server) decrementClients() { atomic.AddInt32(&s.numClients, -1) } @@ -3611,11 +3618,11 @@ func (s *rpcServer) decrementClients() { // the second bool return value specifies whether the user can change the state // of the server (true) or whether the user is limited (false). The second is // always false if the first is. -func (s *rpcServer) checkAuth(r *http.Request, require bool) (bool, bool, error) { +func (s *Server) checkAuth(r *http.Request, require bool) (bool, bool, error) { authhdr := r.Header["Authorization"] if len(authhdr) <= 0 { if require { - rpcsLog.Warnf("RPC authentication failure from %s", + log.Warnf("RPC authentication failure from %s", r.RemoteAddr) return false, false, errors.New("auth failure") } @@ -3639,7 +3646,7 @@ func (s *rpcServer) checkAuth(r *http.Request, require bool) (bool, bool, error) } // Request's auth doesn't match either user - rpcsLog.Warnf("RPC authentication failure from %s", r.RemoteAddr) + log.Warnf("RPC authentication failure from %s", r.RemoteAddr) return false, false, errors.New("auth failure") } @@ -3657,7 +3664,7 @@ type parsedRPCCmd struct { // command and runs the appropriate handler to reply to the command. Any // commands which are not recognized or not implemented will return an error // suitable for use in replies. -func (s *rpcServer) standardCmdResult(cmd *parsedRPCCmd, closeChan <-chan struct{}) (interface{}, error) { +func (s *Server) standardCmdResult(cmd *parsedRPCCmd, closeChan <-chan struct{}) (interface{}, error) { handler, ok := rpcHandlers[cmd.method] if ok { goto handled @@ -3726,7 +3733,7 @@ func createMarshalledReply(id, result interface{}, replyErr error) ([]byte, erro } // jsonRPCRead handles reading and responding to RPC messages. -func (s *rpcServer) jsonRPCRead(w http.ResponseWriter, r *http.Request, isAdmin bool) { +func (s *Server) jsonRPCRead(w http.ResponseWriter, r *http.Request, isAdmin bool) { if atomic.LoadInt32(&s.shutdown) != 0 { return } @@ -3750,14 +3757,14 @@ func (s *rpcServer) jsonRPCRead(w http.ResponseWriter, r *http.Request, isAdmin hj, ok := w.(http.Hijacker) if !ok { errMsg := "webserver doesn't support hijacking" - rpcsLog.Warnf(errMsg) + log.Warnf(errMsg) errCode := http.StatusInternalServerError http.Error(w, strconv.Itoa(errCode)+" "+errMsg, errCode) return } conn, buf, err := hj.Hijack() if err != nil { - rpcsLog.Warnf("Failed to hijack HTTP connection: %v", err) + log.Warnf("Failed to hijack HTTP connection: %v", err) errCode := http.StatusInternalServerError http.Error(w, strconv.Itoa(errCode)+" "+err.Error(), errCode) return @@ -3796,7 +3803,7 @@ func (s *rpcServer) jsonRPCRead(w http.ResponseWriter, r *http.Request, isAdmin // // RPC quirks can be enabled by the user to avoid compatibility issues // with software relying on Core's behavior. - if request.ID == nil && !(cfg.RPCQuirks && request.Jsonrpc == "") { + if request.ID == nil && !(config.MainConfig().RPCQuirks && request.Jsonrpc == "") { return } @@ -3839,23 +3846,23 @@ func (s *rpcServer) jsonRPCRead(w http.ResponseWriter, r *http.Request, isAdmin // Marshal the response. msg, err := createMarshalledReply(responseID, result, jsonErr) if err != nil { - rpcsLog.Errorf("Failed to marshal reply: %v", err) + log.Errorf("Failed to marshal reply: %v", err) return } // Write the response. err = s.writeHTTPResponseHeaders(r, w.Header(), http.StatusOK, buf) if err != nil { - rpcsLog.Error(err) + log.Error(err) return } if _, err := buf.Write(msg); err != nil { - rpcsLog.Errorf("Failed to write marshalled reply: %v", err) + log.Errorf("Failed to write marshalled reply: %v", err) } // Terminate with newline to maintain compatibility with Bitcoin Core. if err := buf.WriteByte('\n'); err != nil { - rpcsLog.Errorf("Failed to append terminating newline to reply: %v", err) + log.Errorf("Failed to append terminating newline to reply: %v", err) } } @@ -3866,12 +3873,12 @@ func jsonAuthFail(w http.ResponseWriter) { } // Start is used by server.go to start the rpc listener. -func (s *rpcServer) Start() { +func (s *Server) Start() { if atomic.AddInt32(&s.started, 1) != 1 { return } - rpcsLog.Trace("Starting RPC server") + log.Trace("Starting RPC server") rpcServeMux := http.NewServeMux() httpServer := &http.Server{ Handler: rpcServeMux, @@ -3916,7 +3923,7 @@ func (s *rpcServer) Start() { ws, err := websocket.Upgrade(w, r, nil, 0, 0) if err != nil { if _, ok := err.(websocket.HandshakeError); !ok { - rpcsLog.Errorf("Unexpected websocket error: %v", + log.Errorf("Unexpected websocket error: %v", err) } http.Error(w, "400 Bad Request.", http.StatusBadRequest) @@ -3928,9 +3935,9 @@ func (s *rpcServer) Start() { for _, listener := range s.cfg.Listeners { s.wg.Add(1) go func(listener net.Listener) { - rpcsLog.Infof("RPC server listening on %s", listener.Addr()) + log.Infof("RPC server listening on %s", listener.Addr()) httpServer.Serve(listener) - rpcsLog.Tracef("RPC listener done for %s", listener.Addr()) + log.Tracef("RPC listener done for %s", listener.Addr()) s.wg.Done() }(listener) } @@ -3938,30 +3945,6 @@ func (s *rpcServer) Start() { s.ntfnMgr.Start() } -// genCertPair generates a key/cert pair to the paths provided. -func genCertPair(certFile, keyFile string) error { - rpcsLog.Infof("Generating TLS certificates...") - - org := "btcd autogenerated cert" - validUntil := time.Now().Add(10 * 365 * 24 * time.Hour) - cert, key, err := btcutil.NewTLSCertPair(org, validUntil, nil) - if err != nil { - return err - } - - // Write cert and key files. - if err = ioutil.WriteFile(certFile, cert, 0666); err != nil { - return err - } - if err = ioutil.WriteFile(keyFile, key, 0600); err != nil { - os.Remove(certFile) - return err - } - - rpcsLog.Infof("Done generating TLS certificates") - return nil -} - // rpcserverPeer represents a peer for use with the RPC server. // // The interface contract requires that all of these methods are safe for @@ -4121,23 +4104,104 @@ type rpcserverConfig struct { FeeEstimator *mempool.FeeEstimator } -// newRPCServer returns a new instance of the rpcServer struct. -func newRPCServer(config *rpcserverConfig) (*rpcServer, error) { - rpc := rpcServer{ - cfg: *config, +// setupRPCListeners returns a slice of listeners that are configured for use +// with the RPC server depending on the configuration settings for listen +// addresses and TLS. +func setupRPCListeners() ([]net.Listener, error) { + // Setup TLS if not disabled. + listenFunc := net.Listen + if !config.MainConfig().DisableTLS { + // Generate the TLS cert and key file if both don't already + // exist. + if !fs.FileExists(config.MainConfig().RPCKey) && !fs.FileExists(config.MainConfig().RPCCert) { + err := serverutils.GenCertPair(config.MainConfig().RPCCert, config.MainConfig().RPCKey) + if err != nil { + return nil, err + } + } + keypair, err := tls.LoadX509KeyPair(config.MainConfig().RPCCert, config.MainConfig().RPCKey) + if err != nil { + return nil, err + } + + tlsConfig := tls.Config{ + Certificates: []tls.Certificate{keypair}, + MinVersion: tls.VersionTLS12, + } + + // Change the standard net.Listen function to the tls one. + listenFunc = func(net string, laddr string) (net.Listener, error) { + return tls.Listen(net, laddr, &tlsConfig) + } + } + + netAddrs, err := p2p.ParseListeners(config.MainConfig().RPCListeners) + if err != nil { + return nil, err + } + + listeners := make([]net.Listener, 0, len(netAddrs)) + for _, addr := range netAddrs { + listener, err := listenFunc(addr.Network(), addr.String()) + if err != nil { + log.Warnf("Can't listen on %s: %v", addr, err) + continue + } + listeners = append(listeners, listener) + } + + return listeners, nil +} + +// NewRPCServer returns a new instance of the rpcServer struct. +func NewRPCServer( + startupTime int64, + p2pServer *p2p.Server, + db database.DB, + blockTemplateGenerator *mining.BlkTmplGenerator, + cpuminer *cpuminer.CPUMiner, + +) (*Server, error) { + // Setup listeners for the configured RPC listen addresses and + // TLS settings. + rpcListeners, err := setupRPCListeners() + if err != nil { + return nil, err + } + if len(rpcListeners) == 0 { + return nil, errors.New("RPCS: No valid listen address") + } + cfg := &rpcserverConfig{ + Listeners: rpcListeners, + StartupTime: startupTime, + ConnMgr: &rpcConnManager{p2pServer}, + SyncMgr: &rpcSyncMgr{p2pServer, p2pServer.SyncManager}, + TimeSource: p2pServer.TimeSource, + ChainParams: p2pServer.DAGParams, + DB: db, + TxMemPool: p2pServer.TxMemPool, + Generator: blockTemplateGenerator, + CPUMiner: cpuminer, + TxIndex: p2pServer.TxIndex, + AddrIndex: p2pServer.AddrIndex, + CfIndex: p2pServer.CfIndex, + FeeEstimator: p2pServer.FeeEstimator, + } + rpc := Server{ + cfg: *cfg, statusLines: make(map[int]string), - gbtWorkState: newGbtWorkState(config.TimeSource), + gbtWorkState: newGbtWorkState(cfg.TimeSource), helpCacher: newHelpCacher(), requestProcessShutdown: make(chan struct{}), quit: make(chan int), } - if cfg.RPCUser != "" && cfg.RPCPass != "" { - login := cfg.RPCUser + ":" + cfg.RPCPass + if config.MainConfig().RPCUser != "" && config.MainConfig().RPCPass != "" { + login := config.MainConfig().RPCUser + ":" + config.MainConfig().RPCPass auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) rpc.authsha = sha256.Sum256([]byte(auth)) } - if cfg.RPCLimitUser != "" && cfg.RPCLimitPass != "" { - login := cfg.RPCLimitUser + ":" + cfg.RPCLimitPass + if config.MainConfig().RPCLimitUser != "" && config.MainConfig().RPCLimitPass != "" { + login := config.MainConfig().RPCLimitUser + ":" + config.MainConfig().RPCLimitPass auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) rpc.limitauthsha = sha256.Sum256([]byte(auth)) } @@ -4149,12 +4213,12 @@ func newRPCServer(config *rpcserverConfig) (*rpcServer, error) { // Callback for notifications from blockchain. It notifies clients that are // long polling for changes or subscribed to websockets notifications. -func (s *rpcServer) handleBlockchainNotification(notification *blockdag.Notification) { +func (s *Server) handleBlockchainNotification(notification *blockdag.Notification) { switch notification.Type { case blockdag.NTBlockAccepted: block, ok := notification.Data.(*btcutil.Block) if !ok { - rpcsLog.Warnf("Chain accepted notification is not a block.") + log.Warnf("Chain accepted notification is not a block.") break } @@ -4166,7 +4230,7 @@ func (s *rpcServer) handleBlockchainNotification(notification *blockdag.Notifica case blockdag.NTBlockConnected: block, ok := notification.Data.(*btcutil.Block) if !ok { - rpcsLog.Warnf("Chain connected notification is not a block.") + log.Warnf("Chain connected notification is not a block.") break } @@ -4176,7 +4240,7 @@ func (s *rpcServer) handleBlockchainNotification(notification *blockdag.Notifica case blockdag.NTBlockDisconnected: block, ok := notification.Data.(*btcutil.Block) if !ok { - rpcsLog.Warnf("Chain disconnected notification is not a block.") + log.Warnf("Chain disconnected notification is not a block.") break } diff --git a/rpcserverhelp.go b/server/rpcserver/rpcserverhelp.go similarity index 99% rename from rpcserverhelp.go rename to server/rpcserver/rpcserverhelp.go index 8a59a0218..0e3695a5c 100644 --- a/rpcserverhelp.go +++ b/server/rpcserver/rpcserverhelp.go @@ -3,7 +3,7 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package main +package rpcserver import ( "errors" diff --git a/rpcserverhelp_test.go b/server/rpcserver/rpcserverhelp_test.go similarity index 99% rename from rpcserverhelp_test.go rename to server/rpcserver/rpcserverhelp_test.go index c56a5f74f..2fb754ae3 100644 --- a/rpcserverhelp_test.go +++ b/server/rpcserver/rpcserverhelp_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package main +package rpcserver import ( "testing" diff --git a/rpcwebsocket.go b/server/rpcserver/rpcwebsocket.go similarity index 96% rename from rpcwebsocket.go rename to server/rpcserver/rpcwebsocket.go index 29c302bdc..79007b2cd 100644 --- a/rpcwebsocket.go +++ b/server/rpcserver/rpcwebsocket.go @@ -3,7 +3,7 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package main +package rpcserver import ( "bytes" @@ -21,14 +21,15 @@ import ( "golang.org/x/crypto/ripemd160" + "github.com/btcsuite/websocket" "github.com/daglabs/btcd/blockdag" "github.com/daglabs/btcd/btcjson" + "github.com/daglabs/btcd/config" "github.com/daglabs/btcd/dagconfig" "github.com/daglabs/btcd/dagconfig/daghash" "github.com/daglabs/btcd/txscript" "github.com/daglabs/btcd/wire" "github.com/daglabs/btcutil" - "github.com/btcsuite/websocket" ) const ( @@ -81,7 +82,7 @@ var wsHandlersBeforeInit = map[string]wsCommandHandler{ // must be run in a separate goroutine. It should be invoked from the websocket // server handler which runs each new connection in a new goroutine thereby // satisfying the requirement. -func (s *rpcServer) WebsocketHandler(conn *websocket.Conn, remoteAddr string, +func (s *Server) WebsocketHandler(conn *websocket.Conn, remoteAddr string, authenticated bool, isAdmin bool) { // Clear the read deadline that was set before the websocket hijacked @@ -89,10 +90,10 @@ func (s *rpcServer) WebsocketHandler(conn *websocket.Conn, remoteAddr string, conn.SetReadDeadline(timeZeroVal) // Limit max number of websocket clients. - rpcsLog.Infof("New websocket client %s", remoteAddr) - if s.ntfnMgr.NumClients()+1 > cfg.RPCMaxWebsockets { - rpcsLog.Infof("Max websocket clients exceeded [%d] - "+ - "disconnecting client %s", cfg.RPCMaxWebsockets, + log.Infof("New websocket client %s", remoteAddr) + if s.ntfnMgr.NumClients()+1 > config.MainConfig().RPCMaxWebsockets { + log.Infof("Max websocket clients exceeded [%d] - "+ + "disconnecting client %s", config.MainConfig().RPCMaxWebsockets, remoteAddr) conn.Close() return @@ -103,7 +104,7 @@ func (s *rpcServer) WebsocketHandler(conn *websocket.Conn, remoteAddr string, // disconnected), remove it and any notifications it registered for. client, err := newWebsocketClient(s, conn, remoteAddr, authenticated, isAdmin) if err != nil { - rpcsLog.Errorf("Failed to serve client %s: %v", remoteAddr, err) + log.Errorf("Failed to serve client %s: %v", remoteAddr, err) conn.Close() return } @@ -111,7 +112,7 @@ func (s *rpcServer) WebsocketHandler(conn *websocket.Conn, remoteAddr string, client.Start() client.WaitForShutdown() s.ntfnMgr.RemoveClient(client) - rpcsLog.Infof("Disconnected websocket client %s", remoteAddr) + log.Infof("Disconnected websocket client %s", remoteAddr) } // wsNotificationManager is a connection and notification manager used for @@ -124,7 +125,7 @@ func (s *rpcServer) WebsocketHandler(conn *websocket.Conn, remoteAddr string, // track of all connected websocket clients. type wsNotificationManager struct { // server is the RPC server the notification manager is associated with. - server *rpcServer + server *Server // queueNotification queues a notification for handling. queueNotification chan interface{} @@ -585,7 +586,7 @@ out: delete(txNotifications, wsc.quit) default: - rpcsLog.Warn("Unhandled notification type") + log.Warn("Unhandled notification type") } case m.numClients <- len(clients): @@ -695,7 +696,7 @@ func (*wsNotificationManager) notifyBlockConnected(clients map[chan struct{}]*ws block.MsgBlock().Header.Timestamp.Unix()) marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn) if err != nil { - rpcsLog.Errorf("Failed to marshal block connected notification: "+ + log.Errorf("Failed to marshal block connected notification: "+ "%v", err) return } @@ -719,7 +720,7 @@ func (*wsNotificationManager) notifyBlockDisconnected(clients map[chan struct{}] block.Height(), block.MsgBlock().Header.Timestamp.Unix()) marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn) if err != nil { - rpcsLog.Errorf("Failed to marshal block disconnected "+ + log.Errorf("Failed to marshal block disconnected "+ "notification: %v", err) return } @@ -738,7 +739,7 @@ func (m *wsNotificationManager) notifyFilteredBlockConnected(clients map[chan st var w bytes.Buffer err := block.MsgBlock().Header.Serialize(&w) if err != nil { - rpcsLog.Errorf("Failed to serialize header for filtered block "+ + log.Errorf("Failed to serialize header for filtered block "+ "connected notification: %v", err) return } @@ -765,7 +766,7 @@ func (m *wsNotificationManager) notifyFilteredBlockConnected(clients map[chan st // Marshal and queue notification. marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn) if err != nil { - rpcsLog.Errorf("Failed to marshal filtered block "+ + log.Errorf("Failed to marshal filtered block "+ "connected notification: %v", err) return } @@ -788,7 +789,7 @@ func (*wsNotificationManager) notifyFilteredBlockDisconnected(clients map[chan s var w bytes.Buffer err := block.MsgBlock().Header.Serialize(&w) if err != nil { - rpcsLog.Errorf("Failed to serialize header for filtered block "+ + log.Errorf("Failed to serialize header for filtered block "+ "disconnected notification: %v", err) return } @@ -796,7 +797,7 @@ func (*wsNotificationManager) notifyFilteredBlockDisconnected(clients map[chan s hex.EncodeToString(w.Bytes())) marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn) if err != nil { - rpcsLog.Errorf("Failed to marshal filtered block disconnected "+ + log.Errorf("Failed to marshal filtered block disconnected "+ "notification: %v", err) return } @@ -831,7 +832,7 @@ func (m *wsNotificationManager) notifyForNewTx(clients map[chan struct{}]*wsClie ntfn := btcjson.NewTxAcceptedNtfn(txHashStr, btcutil.Amount(amount).ToBTC()) marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn) if err != nil { - rpcsLog.Errorf("Failed to marshal tx notification: %s", err.Error()) + log.Errorf("Failed to marshal tx notification: %s", err.Error()) return } @@ -855,7 +856,7 @@ func (m *wsNotificationManager) notifyForNewTx(clients map[chan struct{}]*wsClie marshalledJSONVerbose, err = btcjson.MarshalCmd(nil, verboseNtfn) if err != nil { - rpcsLog.Errorf("Failed to marshal verbose tx "+ + log.Errorf("Failed to marshal verbose tx "+ "notification: %s", err.Error()) return } @@ -904,7 +905,7 @@ func (m *wsNotificationManager) addSpentRequests(opMap map[wire.OutPoint]map[cha for _, op := range ops { spend := m.server.cfg.TxMemPool.CheckSpend(*op) if spend != nil { - rpcsLog.Debugf("Found existing mempool spend for "+ + log.Debugf("Found existing mempool spend for "+ "outpoint<%v>: %v", op, spend.Hash()) spends[*spend.Hash()] = spend } @@ -938,7 +939,7 @@ func (*wsNotificationManager) removeSpentRequest(ops map[wire.OutPoint]map[chan // Remove the client from the list to notify. notifyMap, ok := ops[*op] if !ok { - rpcsLog.Warnf("Attempt to remove nonexistent spent request "+ + log.Warnf("Attempt to remove nonexistent spent request "+ "for websocket client %s", wsc.addr) return } @@ -1016,7 +1017,7 @@ func (m *wsNotificationManager) notifyForTxOuts(ops map[wire.OutPoint]map[chan s marshalledJSON, err := btcjson.MarshalCmd(nil, ntfn) if err != nil { - rpcsLog.Errorf("Failed to marshal processedtx notification: %v", err) + log.Errorf("Failed to marshal processedtx notification: %v", err) continue } @@ -1047,7 +1048,7 @@ func (m *wsNotificationManager) notifyRelevantTxAccepted(tx *btcutil.Tx, n := btcjson.NewRelevantTxAcceptedNtfn(txHexString(tx.MsgTx())) marshalled, err := btcjson.MarshalCmd(nil, n) if err != nil { - rpcsLog.Errorf("Failed to marshal notification: %v", err) + log.Errorf("Failed to marshal notification: %v", err) return } for quitChan := range clientsToNotify { @@ -1092,7 +1093,7 @@ func (m *wsNotificationManager) notifyForTxIns(ops map[wire.OutPoint]map[chan st } marshalledJSON, err := newRedeemingTxNotification(txHex, tx.Index(), block) if err != nil { - rpcsLog.Warnf("Failed to marshal redeemingtx notification: %v", err) + log.Warnf("Failed to marshal redeemingtx notification: %v", err) continue } for wscQuit, wsc := range cmap { @@ -1161,7 +1162,7 @@ func (*wsNotificationManager) removeAddrRequest(addrs map[string]map[chan struct // Remove the client from the list to notify. cmap, ok := addrs[addr] if !ok { - rpcsLog.Warnf("Attempt to remove nonexistent addr request "+ + log.Warnf("Attempt to remove nonexistent addr request "+ "<%s> for websocket client %s", addr, wsc.addr) return } @@ -1210,7 +1211,7 @@ func (m *wsNotificationManager) Shutdown() { // newWsNotificationManager returns a new notification manager ready for use. // See wsNotificationManager for more details. -func newWsNotificationManager(server *rpcServer) *wsNotificationManager { +func newWsNotificationManager(server *Server) *wsNotificationManager { return &wsNotificationManager{ server: server, queueNotification: make(chan interface{}), @@ -1246,7 +1247,7 @@ type wsClient struct { sync.Mutex // server is the RPC server that is servicing the client. - server *rpcServer + server *Server // conn is the underlying websocket connection. conn *websocket.Conn @@ -1315,7 +1316,7 @@ out: if err != nil { // Log the error if it's not due to disconnecting. if err != io.EOF { - rpcsLog.Errorf("Websocket receive error from "+ + log.Errorf("Websocket receive error from "+ "%s: %v", c.addr, err) } break out @@ -1334,7 +1335,7 @@ out: } reply, err := createMarshalledReply(nil, nil, jsonErr) if err != nil { - rpcsLog.Errorf("Failed to marshal parse failure "+ + log.Errorf("Failed to marshal parse failure "+ "reply: %v", err) continue } @@ -1360,7 +1361,7 @@ out: // // RPC quirks can be enabled by the user to avoid compatibility issues // with software relying on Core's behavior. - if request.ID == nil && !(cfg.RPCQuirks && request.Jsonrpc == "") { + if request.ID == nil && !(config.MainConfig().RPCQuirks && request.Jsonrpc == "") { if !c.authenticated { break out } @@ -1375,14 +1376,14 @@ out: reply, err := createMarshalledReply(cmd.id, nil, cmd.err) if err != nil { - rpcsLog.Errorf("Failed to marshal parse failure "+ + log.Errorf("Failed to marshal parse failure "+ "reply: %v", err) continue } c.SendMessage(reply, nil) continue } - rpcsLog.Debugf("Received command <%s> from %s", cmd.method, c.addr) + log.Debugf("Received command <%s> from %s", cmd.method, c.addr) // Check auth. The client is immediately disconnected if the // first request of an unauthentiated websocket client is not @@ -1391,11 +1392,11 @@ out: // authentication credentials are provided in the request. switch authCmd, ok := cmd.cmd.(*btcjson.AuthenticateCmd); { case c.authenticated && ok: - rpcsLog.Warnf("Websocket client %s is already authenticated", + log.Warnf("Websocket client %s is already authenticated", c.addr) break out case !c.authenticated && !ok: - rpcsLog.Warnf("Unauthenticated websocket message " + + log.Warnf("Unauthenticated websocket message " + "received") break out case !c.authenticated: @@ -1406,7 +1407,7 @@ out: cmp := subtle.ConstantTimeCompare(authSha[:], c.server.authsha[:]) limitcmp := subtle.ConstantTimeCompare(authSha[:], c.server.limitauthsha[:]) if cmp != 1 && limitcmp != 1 { - rpcsLog.Warnf("Auth failure.") + log.Warnf("Auth failure.") break out } c.authenticated = true @@ -1415,7 +1416,7 @@ out: // Marshal and send response. reply, err := createMarshalledReply(cmd.id, nil, nil) if err != nil { - rpcsLog.Errorf("Failed to marshal authenticate reply: "+ + log.Errorf("Failed to marshal authenticate reply: "+ "%v", err.Error()) continue } @@ -1434,7 +1435,7 @@ out: // Marshal and send response. reply, err := createMarshalledReply(request.ID, nil, jsonErr) if err != nil { - rpcsLog.Errorf("Failed to marshal parse failure "+ + log.Errorf("Failed to marshal parse failure "+ "reply: %v", err) continue } @@ -1473,7 +1474,7 @@ out: // Ensure the connection is closed. c.Disconnect() c.wg.Done() - rpcsLog.Tracef("Websocket client input handler done for %s", c.addr) + log.Tracef("Websocket client input handler done for %s", c.addr) } // serviceRequest services a parsed RPC request by looking up and executing the @@ -1495,7 +1496,7 @@ func (c *wsClient) serviceRequest(r *parsedRPCCmd) { } reply, err := createMarshalledReply(r.id, result, err) if err != nil { - rpcsLog.Errorf("Failed to marshal reply for <%s> "+ + log.Errorf("Failed to marshal reply for <%s> "+ "command: %v", r.method, err) return } @@ -1571,7 +1572,7 @@ cleanup: } } c.wg.Done() - rpcsLog.Tracef("Websocket client notification queue handler done "+ + log.Tracef("Websocket client notification queue handler done "+ "for %s", c.addr) } @@ -1614,7 +1615,7 @@ cleanup: } } c.wg.Done() - rpcsLog.Tracef("Websocket client output handler done for %s", c.addr) + log.Tracef("Websocket client output handler done for %s", c.addr) } // SendMessage sends the passed json to the websocket client. It is backed @@ -1677,7 +1678,7 @@ func (c *wsClient) Disconnect() { return } - rpcsLog.Tracef("Disconnecting websocket client %s", c.addr) + log.Tracef("Disconnecting websocket client %s", c.addr) close(c.quit) c.conn.Close() c.disconnected = true @@ -1685,7 +1686,7 @@ func (c *wsClient) Disconnect() { // Start begins processing input and output messages. func (c *wsClient) Start() { - rpcsLog.Tracef("Starting websocket client %s", c.addr) + log.Tracef("Starting websocket client %s", c.addr) // Start processing input and output. c.wg.Add(3) @@ -1706,7 +1707,7 @@ func (c *wsClient) WaitForShutdown() { // returned client is ready to start. Once started, the client will process // incoming and outgoing messages in separate goroutines complete with queuing // and asynchrous handling for long-running operations. -func newWebsocketClient(server *rpcServer, conn *websocket.Conn, +func newWebsocketClient(server *Server, conn *websocket.Conn, remoteAddr string, authenticated bool, isAdmin bool) (*wsClient, error) { sessionID, err := wire.RandomUint64() @@ -1723,7 +1724,7 @@ func newWebsocketClient(server *rpcServer, conn *websocket.Conn, server: server, addrRequests: make(map[string]struct{}), spentRequests: make(map[wire.OutPoint]struct{}), - serviceRequestSem: makeSemaphore(cfg.RPCMaxConcurrentReqs), + serviceRequestSem: makeSemaphore(config.MainConfig().RPCMaxConcurrentReqs), ntfnChan: make(chan []byte, 1), // nonblocking sync sendChan: make(chan wsResponse, websocketSendBufferSize), quit: make(chan struct{}), diff --git a/server/server.go b/server/server.go new file mode 100644 index 000000000..013c575fa --- /dev/null +++ b/server/server.go @@ -0,0 +1,146 @@ +package server + +import ( + "sync/atomic" + "time" + + "github.com/daglabs/btcd/config" + "github.com/daglabs/btcd/dagconfig" + "github.com/daglabs/btcd/database" + "github.com/daglabs/btcd/mempool" + "github.com/daglabs/btcd/mining" + "github.com/daglabs/btcd/mining/cpuminer" + "github.com/daglabs/btcd/server/p2p" + "github.com/daglabs/btcd/server/rpcserver" + "github.com/daglabs/btcd/signal" +) + +// Server is a wrapper for p2p server and rpc server +type Server struct { + rpcServer *rpcserver.Server + p2pServer *p2p.Server + cpuminer *cpuminer.CPUMiner + startupTime int64 + + started, shutdown int32 +} + +// Start begins accepting connections from peers. +func (s *Server) Start() { + // Already started? + if atomic.AddInt32(&s.started, 1) != 1 { + return + } + + srvrLog.Trace("Starting server") + + // Server startup time. Used for the uptime command for uptime calculation. + s.startupTime = time.Now().Unix() + + s.p2pServer.Start() + + // Start the CPU miner if generation is enabled. + cfg := config.MainConfig() + if cfg.Generate { + s.cpuminer.Start() + } + + if !cfg.DisableRPC { + + s.rpcServer.Start() + } +} + +// Stop gracefully shuts down the server by stopping and disconnecting all +// peers and the main listener. +func (s *Server) Stop() error { + // Make sure this only happens once. + if atomic.AddInt32(&s.shutdown, 1) != 1 { + srvrLog.Infof("Server is already in the process of shutting down") + return nil + } + + srvrLog.Warnf("Server shutting down") + + // Stop the CPU miner if needed + s.cpuminer.Stop() + + s.p2pServer.Stop() + + // Shutdown the RPC server if it's not disabled. + if !config.MainConfig().DisableRPC { + s.rpcServer.Stop() + } + + return nil +} + +// NewServer returns a new btcd server configured to listen on addr for the +// bitcoin network type specified by chainParams. Use start to begin accepting +// connections from peers. +func NewServer(listenAddrs []string, db database.DB, dagParams *dagconfig.Params, interrupt <-chan struct{}) (*Server, error) { + s := &Server{} + var err error + notifyNewTransactions := func(txns []*mempool.TxDesc) { + // Notify both websocket and getblocktemplate long poll clients of all + // newly accepted transactions. + if s.rpcServer != nil { + s.rpcServer.NotifyNewTransactions(txns) + } + } + s.p2pServer, err = p2p.NewServer(listenAddrs, db, dagParams, interrupt, notifyNewTransactions) + if err != nil { + return nil, err + } + + cfg := config.MainConfig() + + // Create the mining policy and block template generator based on the + // configuration options. + // + // NOTE: The CPU miner relies on the mempool, so the mempool has to be + // created before calling the function to create the CPU miner. + policy := mining.Policy{ + BlockMinSize: cfg.BlockMinSize, + BlockMaxSize: cfg.BlockMaxSize, + BlockPrioritySize: cfg.BlockPrioritySize, + TxMinFreeFee: cfg.MinRelayTxFee, + } + blockTemplateGenerator := mining.NewBlkTmplGenerator(&policy, + s.p2pServer.DAGParams, s.p2pServer.TxMemPool, s.p2pServer.DAG, s.p2pServer.TimeSource, s.p2pServer.SigCache) + s.cpuminer = cpuminer.New(&cpuminer.Config{ + ChainParams: dagParams, + BlockTemplateGenerator: blockTemplateGenerator, + MiningAddrs: cfg.MiningAddrs, + ProcessBlock: s.p2pServer.SyncManager.ProcessBlock, + ConnectedCount: s.p2pServer.ConnectedCount, + IsCurrent: s.p2pServer.SyncManager.IsCurrent, + }) + + if !cfg.DisableRPC { + + s.rpcServer, err = rpcserver.NewRPCServer( + s.startupTime, + s.p2pServer, + db, + blockTemplateGenerator, + s.cpuminer, + ) + if err != nil { + return nil, err + } + + // Signal process shutdown when the RPC server requests it. + go func() { + <-s.rpcServer.RequestedProcessShutdown() + signal.ShutdownRequestChannel <- struct{}{} + }() + } + + return s, nil +} + +// WaitForShutdown blocks until the main listener and peer handlers are stopped. +func (s *Server) WaitForShutdown() { + s.p2pServer.WaitForShutdown() +} diff --git a/server/serverutils/log.go b/server/serverutils/log.go new file mode 100644 index 000000000..cd025fff0 --- /dev/null +++ b/server/serverutils/log.go @@ -0,0 +1,21 @@ +// 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. + +package serverutils + +import ( + "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/logger" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var srvLog, peerLog, rpcsLog btclog.Logger + +func init() { + srvLog, _ = logger.Get(logger.SubsystemTags.SRVR) + peerLog, _ = logger.Get(logger.SubsystemTags.PEER) + rpcsLog, _ = logger.Get(logger.SubsystemTags.RPCS) +} diff --git a/upnp.go b/server/serverutils/upnp.go similarity index 99% rename from upnp.go rename to server/serverutils/upnp.go index a1f37e650..812163e17 100644 --- a/upnp.go +++ b/server/serverutils/upnp.go @@ -1,4 +1,4 @@ -package main +package serverutils // Upnp code taken from Taipei Torrent license is below: // Copyright (c) 2010 Jack Palevich. All rights reserved. diff --git a/server/serverutils/utils.go b/server/serverutils/utils.go new file mode 100644 index 000000000..6c1f0ef09 --- /dev/null +++ b/server/serverutils/utils.go @@ -0,0 +1,86 @@ +package serverutils + +import ( + "fmt" + "io/ioutil" + "net" + "os" + "strings" + "sync" + "time" + + "github.com/daglabs/btcd/config" + "github.com/daglabs/btcd/connmgr" + "github.com/daglabs/btcd/peer" + "github.com/daglabs/btcutil" +) + +// Peer extends the peer to maintain state shared by the server and +// the blockmanager. +type Peer struct { + *peer.Peer + + // The following variables must only be used atomically + FeeFilter int64 + + relayMtx sync.Mutex + DynamicBanScore connmgr.DynamicBanScore + quit chan struct{} + DisableRelayTx bool + + // The following chans are used to sync blockmanager and server. + txProcessed chan struct{} + blockProcessed chan struct{} +} + +// BTCDLookup resolves the IP of the given host using the correct DNS lookup +// function depending on the configuration options. For example, addresses will +// be resolved using tor when the --proxy flag was specified unless --noonion +// was also specified in which case the normal system DNS resolver will be used. +// +// Any attempt to resolve a tor address (.onion) will return an error since they +// are not intended to be resolved outside of the tor proxy. +func BTCDLookup(host string) ([]net.IP, error) { + if strings.HasSuffix(host, ".onion") { + return nil, fmt.Errorf("attempt to resolve tor address %s", host) + } + + return config.MainConfig().Lookup(host) +} + +// GenCertPair generates a key/cert pair to the paths provided. +func GenCertPair(certFile, keyFile string) error { + rpcsLog.Infof("Generating TLS certificates...") + + org := "btcd autogenerated cert" + validUntil := time.Now().Add(10 * 365 * 24 * time.Hour) + cert, key, err := btcutil.NewTLSCertPair(org, validUntil, nil) + if err != nil { + return err + } + + // Write cert and key files. + if err = ioutil.WriteFile(certFile, cert, 0666); err != nil { + return err + } + if err = ioutil.WriteFile(keyFile, key, 0600); err != nil { + os.Remove(certFile) + return err + } + + rpcsLog.Infof("Done generating TLS certificates") + return nil +} + +// 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(addr net.Addr) (net.Conn, error) { + if strings.Contains(addr.String(), ".onion:") { + return config.MainConfig().OnionDial(addr.Network(), addr.String(), + config.DefaultConnectTimeout) + } + return config.MainConfig().Dial(addr.Network(), addr.String(), config.DefaultConnectTimeout) +} diff --git a/service_windows.go b/service_windows.go index 8101ae651..8fba132e1 100644 --- a/service_windows.go +++ b/service_windows.go @@ -13,6 +13,10 @@ import ( "github.com/btcsuite/winsvc/eventlog" "github.com/btcsuite/winsvc/mgr" "github.com/btcsuite/winsvc/svc" + "github.com/daglabs/btcd/signal" + "github.com/daglabs/btcd/config" + "github.com/daglabs/btcd/version" + "github.com/daglabs/btcd/server" ) const ( @@ -34,10 +38,10 @@ var elog *eventlog.Log // logServiceStartOfDay logs information about btcd when the main server has // been started to the Windows event log. -func logServiceStartOfDay(srvr *server) { +func logServiceStartOfDay() { var message string - message += fmt.Sprintf("Version %s\n", version()) - message += fmt.Sprintf("Configuration directory: %s\n", defaultHomeDir) + message += fmt.Sprintf("Version %s\n", version.Version()) + message += fmt.Sprintf("Configuration directory: %s\n", config.DefaultHomeDir) message += fmt.Sprintf("Configuration file: %s\n", cfg.ConfigFile) message += fmt.Sprintf("Data directory: %s\n", cfg.DataDir) @@ -62,7 +66,7 @@ func (s *btcdService) Execute(args []string, r <-chan svc.ChangeRequest, changes // doneChan. serverChan is notified with the main server instance once // it is started so it can be gracefully stopped. doneChan := make(chan error) - serverChan := make(chan *server) + serverChan := make(chan *server.Server) go func() { err := btcdMain(serverChan) doneChan <- err @@ -70,8 +74,6 @@ func (s *btcdService) Execute(args []string, r <-chan svc.ChangeRequest, changes // Service is now started. changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted} - - var mainServer *server loop: for { select { @@ -86,16 +88,15 @@ loop: changes <- svc.Status{State: svc.StopPending} // Signal the main function to exit. - shutdownRequestChannel <- struct{}{} + signal.ShutdownRequestChannel <- struct{}{} default: elog.Error(1, fmt.Sprintf("Unexpected control "+ "request #%d.", c)) } - case srvr := <-serverChan: - mainServer = srvr - logServiceStartOfDay(mainServer) + case <-serverChan: + logServiceStartOfDay() case err := <-doneChan: if err != nil { @@ -302,6 +303,6 @@ func serviceMain() (bool, error) { // Set windows specific functions to real functions. func init() { - runServiceCommand = performServiceCommand + config.RunServiceCommand = performServiceCommand winServiceMain = serviceMain } diff --git a/signal/log.go b/signal/log.go new file mode 100644 index 000000000..d9ebb4619 --- /dev/null +++ b/signal/log.go @@ -0,0 +1,19 @@ +// 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. + +package signal + +import ( + "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/logger" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var btcdLog btclog.Logger + +func init() { + btcdLog, _ = logger.Get(logger.SubsystemTags.BTCD) +} diff --git a/signal.go b/signal/signal.go similarity index 76% rename from signal.go rename to signal/signal.go index 5e60783ea..a132551c9 100644 --- a/signal.go +++ b/signal/signal.go @@ -2,25 +2,25 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package main +package signal import ( "os" "os/signal" ) -// shutdownRequestChannel is used to initiate shutdown from one of the +// ShutdownRequestChannel is used to initiate shutdown from one of the // subsystems using the same code paths as when an interrupt signal is received. -var shutdownRequestChannel = make(chan struct{}) +var ShutdownRequestChannel = make(chan struct{}) // interruptSignals defines the default signals to catch in order to do a proper // shutdown. This may be modified during init depending on the platform. var interruptSignals = []os.Signal{os.Interrupt} -// interruptListener listens for OS Signals such as SIGINT (Ctrl+C) and shutdown +// InterruptListener listens for OS Signals such as SIGINT (Ctrl+C) and shutdown // requests from shutdownRequestChannel. It returns a channel that is closed // when either signal is received. -func interruptListener() <-chan struct{} { +func InterruptListener() <-chan struct{} { c := make(chan struct{}) go func() { interruptChannel := make(chan os.Signal, 1) @@ -33,7 +33,7 @@ func interruptListener() <-chan struct{} { btcdLog.Infof("Received signal (%s). Shutting down...", sig) - case <-shutdownRequestChannel: + case <-ShutdownRequestChannel: btcdLog.Info("Shutdown requested. Shutting down...") } close(c) @@ -47,7 +47,7 @@ func interruptListener() <-chan struct{} { btcdLog.Infof("Received signal (%s). Already "+ "shutting down...", sig) - case <-shutdownRequestChannel: + case <-ShutdownRequestChannel: btcdLog.Info("Shutdown requested. Already " + "shutting down...") } @@ -57,10 +57,10 @@ func interruptListener() <-chan struct{} { return c } -// interruptRequested returns true when the channel returned by -// interruptListener was closed. This simplifies early shutdown slightly since +// InterruptRequested returns true when the channel returned by +// InterruptListener was closed. This simplifies early shutdown slightly since // the caller can just use an if statement instead of a select. -func interruptRequested(interrupted <-chan struct{}) bool { +func InterruptRequested(interrupted <-chan struct{}) bool { select { case <-interrupted: return true diff --git a/signalsigterm.go b/signal/signalsigterm.go similarity index 95% rename from signalsigterm.go rename to signal/signalsigterm.go index 831655010..c6fb425c6 100644 --- a/signalsigterm.go +++ b/signal/signalsigterm.go @@ -4,7 +4,7 @@ // +build darwin dragonfly freebsd linux netbsd openbsd solaris -package main +package signal import ( "os" diff --git a/txscript/log.go b/txscript/log.go index 86ab4088e..92f097bb7 100644 --- a/txscript/log.go +++ b/txscript/log.go @@ -6,6 +6,7 @@ package txscript import ( "github.com/btcsuite/btclog" + "github.com/daglabs/btcd/logger" ) // log is a logger that is initialized with no output filters. This @@ -13,20 +14,8 @@ import ( // requests it. var log btclog.Logger -// The default amount of logging is none. func init() { - DisableLog() -} - -// DisableLog disables all library log output. Logging output is disabled -// by default until UseLogger is called. -func DisableLog() { - log = btclog.Disabled -} - -// UseLogger uses a specified Logger to output package logging info. -func UseLogger(logger btclog.Logger) { - log = logger + log, _ = logger.Get(logger.SubsystemTags.SCRP) } // LogClosure is a closure that can be printed with %v to be used to diff --git a/version.go b/version/version.go similarity index 90% rename from version.go rename to version/version.go index 192f4a7f5..d22dcd6dc 100644 --- a/version.go +++ b/version/version.go @@ -2,7 +2,7 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package main +package version import ( "bytes" @@ -16,9 +16,9 @@ const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr // These constants define the application version and follow the semantic // versioning 2.0.0 spec (http://semver.org/). const ( - appMajor uint = 0 - appMinor uint = 12 - appPatch uint = 0 + AppMajor uint = 0 + AppMinor uint = 12 + AppPatch uint = 0 // appPreRelease MUST only contain characters from semanticAlphabet // per the semantic versioning spec. @@ -30,11 +30,11 @@ const ( // contain characters from semanticAlphabet per the semantic versioning spec. var appBuild string -// version returns the application version as a properly formed string per the +// Version returns the application version as a properly formed string per the // semantic versioning 2.0.0 spec (http://semver.org/). -func version() string { +func Version() string { // Start with the major, minor, and patch versions. - version := fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch) + version := fmt.Sprintf("%d.%d.%d", AppMajor, AppMinor, AppPatch) // Append pre-release version if there is one. The hyphen called for // by the semantic versioning spec is automatically appended and should