[DEV-82] Take RPC Server out of btcd package

* [DEV-82] break down main to packages

* [DEV-82] separate rpcserver and server

* [DEV-82] Fixed Windows-related code that failed to compile.

* [DEV-82] remove params.go and use only dagconfig.Params

* [DEV-82] fix log.go license
This commit is contained in:
Ori Newman 2018-08-12 16:24:43 +03:00 committed by stasatdaglabs
parent 713b01c69d
commit 56fb7f09c1
41 changed files with 1534 additions and 1423 deletions

View File

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

View File

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

View File

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

View File

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

35
btcd.go
View File

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

View File

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

View File

@ -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 '<height>:<hash>' 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 = &regressionNetParams
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
}

View File

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

19
config/log.go Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

162
log.go
View File

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

277
logger/logger.go Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

24
server/log.go Normal file
View File

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

24
server/p2p/log.go Normal file
View File

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

File diff suppressed because it is too large Load Diff

19
server/rpcserver/log.go Normal file
View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -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{}),

146
server/server.go Normal file
View File

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

21
server/serverutils/log.go Normal file
View File

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

View File

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

View File

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

View File

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

19
signal/log.go Normal file
View File

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

View File

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

View File

@ -4,7 +4,7 @@
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package main
package signal
import (
"os"

View File

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

View File

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