Add decred logging and config (#8)
This commit is contained in:
parent
48f7a584ac
commit
57dfc1ed6d
264
config.go
Normal file
264
config.go
Normal file
@ -0,0 +1,264 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/decred/dcrd/dcrutil/v3"
|
||||
flags "github.com/jessevdk/go-flags"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultListen = ":3000"
|
||||
defaultLogLevel = "debug"
|
||||
defaultVSPFee = 0.01
|
||||
defaultNetwork = "testnet"
|
||||
defaultHomeDir = dcrutil.AppDataDir("dcrvsp", false)
|
||||
defaultConfigFilename = "dcrvsp.conf"
|
||||
defaultConfigFile = filepath.Join(defaultHomeDir, defaultConfigFilename)
|
||||
)
|
||||
|
||||
// config defines the configuration options for the VSP.
|
||||
type config struct {
|
||||
Listen string `long:"listen" ini-name:"listen" description:"The ip:port to listen for API requests."`
|
||||
LogLevel string `long:"loglevel" ini-name:"loglevel" description:"Logging level." choice:"trace" choice:"debug" choice:"info" choice:"warn" choice:"error" choice:"critical"`
|
||||
Network string `long:"network" ini-name:"network" description:"Decred network to use." choice:"testnet" choice:"mainnet" choice:"simnet"`
|
||||
VSPFee float64 `long:"vspfee" ini-name:"vspfee" description:"The fee percentage charged for VSP use. eg. 0.01 (1%), 0.05 (5%)."`
|
||||
HomeDir string `long:"homedir" ini-name:"homedir" no-ini:"true" description:"Path to application home directory. Used for storing VSP database and logs."`
|
||||
ConfigFile string `long:"configfile" ini-name:"configfile" no-ini:"true" description:"Path to configuration file."`
|
||||
|
||||
signKey ed25519.PrivateKey
|
||||
pubKey ed25519.PublicKey
|
||||
dbPath string
|
||||
netParams *netParams
|
||||
}
|
||||
|
||||
// fileExists reports whether the named file or directory exists.
|
||||
func fileExists(name string) bool {
|
||||
if _, err := os.Stat(name); os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// cleanAndExpandPath expands environment variables and leading ~ in the
|
||||
// passed path, cleans the result, and returns it.
|
||||
func cleanAndExpandPath(path string) string {
|
||||
// Nothing to do when no path is given.
|
||||
if path == "" {
|
||||
return path
|
||||
}
|
||||
|
||||
// NOTE: The os.ExpandEnv doesn't work with Windows cmd.exe-style
|
||||
// %VARIABLE%, but the variables can still be expanded via POSIX-style
|
||||
// $VARIABLE.
|
||||
path = os.ExpandEnv(path)
|
||||
|
||||
if !strings.HasPrefix(path, "~") {
|
||||
return filepath.Clean(path)
|
||||
}
|
||||
|
||||
// Expand initial ~ to the current user's home directory, or ~otheruser
|
||||
// to otheruser's home directory. On Windows, both forward and backward
|
||||
// slashes can be used.
|
||||
path = path[1:]
|
||||
|
||||
var pathSeparators string
|
||||
if runtime.GOOS == "windows" {
|
||||
pathSeparators = string(os.PathSeparator) + "/"
|
||||
} else {
|
||||
pathSeparators = string(os.PathSeparator)
|
||||
}
|
||||
|
||||
userName := ""
|
||||
if i := strings.IndexAny(path, pathSeparators); i != -1 {
|
||||
userName = path[:i]
|
||||
path = path[i:]
|
||||
}
|
||||
|
||||
homeDir := ""
|
||||
var u *user.User
|
||||
var err error
|
||||
if userName == "" {
|
||||
u, err = user.Current()
|
||||
} else {
|
||||
u, err = user.Lookup(userName)
|
||||
}
|
||||
if err == nil {
|
||||
homeDir = u.HomeDir
|
||||
}
|
||||
// Fallback to CWD if user lookup fails or user has no home directory.
|
||||
if homeDir == "" {
|
||||
homeDir = "."
|
||||
}
|
||||
|
||||
return filepath.Join(homeDir, path)
|
||||
}
|
||||
|
||||
// loadConfig initializes and parses the config using a config file and command
|
||||
// line options.
|
||||
//
|
||||
// The configuration proceeds as follows:
|
||||
// 1) Start with a default config with sane settings
|
||||
// 2) Pre-parse the command line to check for an alternative config file
|
||||
// 3) Load configuration file overwriting defaults with any specified options
|
||||
// 4) Parse CLI options and overwrite/add any specified options
|
||||
//
|
||||
// The above results in dcrvsp functioning properly without any config settings
|
||||
// while still allowing the user to override settings with config files and
|
||||
// command line options. Command line options always take precedence.
|
||||
func loadConfig() (*config, error) {
|
||||
|
||||
// Default config.
|
||||
cfg := config{
|
||||
Listen: defaultListen,
|
||||
LogLevel: defaultLogLevel,
|
||||
Network: defaultNetwork,
|
||||
VSPFee: defaultVSPFee,
|
||||
HomeDir: defaultHomeDir,
|
||||
ConfigFile: defaultConfigFile,
|
||||
}
|
||||
|
||||
// Pre-parse the command line options to see if an alternative config
|
||||
// file or the version flag was specified. Any errors aside from the
|
||||
// help message error can be ignored here since they will be caught by
|
||||
// the final parse below.
|
||||
preCfg := cfg
|
||||
|
||||
preParser := flags.NewParser(&preCfg, flags.HelpFlag)
|
||||
|
||||
_, err := preParser.Parse()
|
||||
if err != nil {
|
||||
if e, ok := err.(*flags.Error); ok && e.Type != flags.ErrHelp {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
} else if ok && e.Type == flags.ErrHelp {
|
||||
fmt.Fprintln(os.Stdout, err)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
appName := filepath.Base(os.Args[0])
|
||||
appName = strings.TrimSuffix(appName, filepath.Ext(appName))
|
||||
usageMessage := fmt.Sprintf("Use %s -h to show usage", appName)
|
||||
|
||||
// Update the home directory if specified on CLI. Since the home
|
||||
// directory is updated, other variables need to be updated to
|
||||
// reflect the new changes.
|
||||
if preCfg.HomeDir != "" {
|
||||
cfg.HomeDir, _ = filepath.Abs(preCfg.HomeDir)
|
||||
|
||||
if preCfg.ConfigFile == defaultConfigFile {
|
||||
defaultConfigFile = filepath.Join(cfg.HomeDir, defaultConfigFilename)
|
||||
preCfg.ConfigFile = defaultConfigFile
|
||||
cfg.ConfigFile = defaultConfigFile
|
||||
} else {
|
||||
cfg.ConfigFile = preCfg.ConfigFile
|
||||
}
|
||||
}
|
||||
|
||||
// Create the home directory if it doesn't already exist.
|
||||
funcName := "loadConfig"
|
||||
err = os.MkdirAll(cfg.HomeDir, 0700)
|
||||
if err != nil {
|
||||
str := "%s: failed to create home directory: %v"
|
||||
err := fmt.Errorf(str, funcName, err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create a default config file when one does not exist and the user did
|
||||
// not specify an override.
|
||||
if preCfg.ConfigFile == defaultConfigFile && !fileExists(preCfg.ConfigFile) {
|
||||
preIni := flags.NewIniParser(preParser)
|
||||
err = preIni.WriteFile(preCfg.ConfigFile,
|
||||
flags.IniIncludeComments|flags.IniIncludeDefaults)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating a default "+
|
||||
"config file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Load additional config from file.
|
||||
parser := flags.NewParser(&preCfg, flags.Default)
|
||||
|
||||
err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error parsing config file: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Parse command line options again to ensure they take precedence.
|
||||
_, err = parser.Parse()
|
||||
if err != nil {
|
||||
if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
|
||||
fmt.Fprintln(os.Stderr, usageMessage)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set the active network.
|
||||
switch cfg.Network {
|
||||
case "testnet":
|
||||
cfg.netParams = &testNet3Params
|
||||
case "mainnet":
|
||||
cfg.netParams = &mainNetParams
|
||||
case "simnet":
|
||||
cfg.netParams = &simNetParams
|
||||
}
|
||||
|
||||
// Create the data directory.
|
||||
dataDir := filepath.Join(cfg.HomeDir, "data", cfg.netParams.Name)
|
||||
err = os.MkdirAll(dataDir, 0700)
|
||||
if err != nil {
|
||||
str := "%s: failed to create data directory: %v"
|
||||
err := fmt.Errorf(str, funcName, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Initialize loggers and log rotation.
|
||||
logDir := filepath.Join(cfg.HomeDir, "logs", cfg.netParams.Name)
|
||||
initLogRotator(filepath.Join(logDir, "dcrvsp.log"))
|
||||
setLogLevels(cfg.LogLevel)
|
||||
|
||||
// Set the database path
|
||||
cfg.dbPath = filepath.Join(dataDir, "vsp.db")
|
||||
|
||||
// Set pubKey/signKey. Read from seed file if it exists, otherwise generate
|
||||
// one.
|
||||
seedPath := filepath.Join(cfg.HomeDir, "sign.seed")
|
||||
seed, err := ioutil.ReadFile(seedPath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, errors.New("seedPath does not exist")
|
||||
}
|
||||
|
||||
_, cfg.signKey, err = ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate signing key: %v", err)
|
||||
}
|
||||
err = ioutil.WriteFile(seedPath, cfg.signKey.Seed(), 0400)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to save signing key: %v", err)
|
||||
}
|
||||
} else {
|
||||
cfg.signKey = ed25519.NewKeyFromSeed(seed)
|
||||
}
|
||||
|
||||
// Derive pubKey from signKey
|
||||
pubKey, ok := cfg.signKey.Public().(ed25519.PublicKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to cast signing key: %T", pubKey)
|
||||
}
|
||||
cfg.pubKey = pubKey
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
@ -34,25 +34,11 @@ func New(dbFile string) (*VspDatabase, error) {
|
||||
return nil, fmt.Errorf("unable to open db file: %v", err)
|
||||
}
|
||||
|
||||
err = createBuckets(db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &VspDatabase{db: db}, nil
|
||||
}
|
||||
|
||||
// Close releases all database resources. It will block waiting for any open
|
||||
// transactions to finish before closing the database and returning.
|
||||
func (vdb *VspDatabase) Close() error {
|
||||
return vdb.db.Close()
|
||||
}
|
||||
|
||||
// createBuckets creates all storage buckets of the VSP if they don't already
|
||||
// exist.
|
||||
func createBuckets(db *bolt.DB) error {
|
||||
return db.Update(func(tx *bolt.Tx) error {
|
||||
// Create all storage buckets of the VSP if they don't already exist.
|
||||
var newDB bool
|
||||
err = db.Update(func(tx *bolt.Tx) error {
|
||||
if tx.Bucket(vspBktK) == nil {
|
||||
newDB = true
|
||||
// Create parent bucket.
|
||||
vspBkt, err := tx.CreateBucket(vspBktK)
|
||||
if err != nil {
|
||||
@ -76,4 +62,23 @@ func createBuckets(db *bolt.DB) error {
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if newDB {
|
||||
log.Debugf("Created new database %s", dbFile)
|
||||
} else {
|
||||
log.Debugf("Using existing database %s", dbFile)
|
||||
}
|
||||
|
||||
return &VspDatabase{db: db}, nil
|
||||
}
|
||||
|
||||
// Close releases all database resources. It will block waiting for any open
|
||||
// transactions to finish before closing the database and returning.
|
||||
func (vdb *VspDatabase) Close() error {
|
||||
log.Debug("Closing database")
|
||||
return vdb.db.Close()
|
||||
}
|
||||
|
||||
26
database/log.go
Normal file
26
database/log.go
Normal file
@ -0,0 +1,26 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/decred/slog"
|
||||
)
|
||||
|
||||
// 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 slog.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 = slog.Disabled
|
||||
}
|
||||
|
||||
// UseLogger uses a specified Logger to output package logging info.
|
||||
func UseLogger(logger slog.Logger) {
|
||||
log = logger
|
||||
}
|
||||
7
go.mod
7
go.mod
@ -4,7 +4,7 @@ go 1.13
|
||||
|
||||
require (
|
||||
decred.org/dcrwallet v1.2.3-0.20200507155221-397dd551e317
|
||||
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200311044114-143c1884e4c8
|
||||
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200511175520-d08cb3f72b3b
|
||||
github.com/decred/dcrd/chaincfg/chainhash v1.0.2
|
||||
github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200511175520-d08cb3f72b3b
|
||||
github.com/decred/dcrd/dcrec v1.0.0
|
||||
@ -12,9 +12,10 @@ require (
|
||||
github.com/decred/dcrd/rpc/jsonrpc/types/v2 v2.0.0
|
||||
github.com/decred/dcrd/txscript/v3 v3.0.0-20200511175520-d08cb3f72b3b
|
||||
github.com/decred/dcrd/wire v1.3.0
|
||||
github.com/decred/slog v1.0.0
|
||||
github.com/gin-gonic/gin v1.6.3
|
||||
github.com/jessevdk/go-flags v1.4.0
|
||||
github.com/jrick/logrotate v1.0.0
|
||||
github.com/jrick/wsrpc/v2 v2.3.3
|
||||
github.com/kr/pretty v0.2.0 // indirect
|
||||
go.etcd.io/bbolt v1.3.4
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
)
|
||||
|
||||
19
go.sum
19
go.sum
@ -10,7 +10,6 @@ github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
|
||||
github.com/decred/base58 v1.0.1/go.mod h1:H2ENcsJjye1G7CbRa67kV9OFaui0LGr56ntKKoY5g9c=
|
||||
@ -19,8 +18,9 @@ github.com/decred/base58 v1.0.2/go.mod h1:pXP9cXCfM2sFLb2viz2FNIdeMWmZDBKG3ZBYbi
|
||||
github.com/decred/dcrd/addrmgr v1.1.0/go.mod h1:exghL+0+QeVvO4MXezWJ1C2tcpBn3ngfuP6S1R+adB8=
|
||||
github.com/decred/dcrd/blockchain/stake/v2 v2.0.2/go.mod h1:o2TT/l/YFdrt15waUdlZ3g90zfSwlA0WgQqHV9UGJF4=
|
||||
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:aDL94kcVJfaaJP+acWUJrlK7g7xEOqTSiFe6bSN3yRQ=
|
||||
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200311044114-143c1884e4c8 h1:6oEo1yQYyfnT9qCERrLWMi9BlDzVBeyl011ssIAVQ3w=
|
||||
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:4zE60yDWlfCDtmqnyP5o1k1K0oyhNn3Tvqo6F93/+RU=
|
||||
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200511175520-d08cb3f72b3b h1:8ChbBKdGbsfAUVWwqUzZIbGHg1z0YpFrVokpNETpal0=
|
||||
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200511175520-d08cb3f72b3b/go.mod h1:4zE60yDWlfCDtmqnyP5o1k1K0oyhNn3Tvqo6F93/+RU=
|
||||
github.com/decred/dcrd/blockchain/standalone v1.1.0 h1:yclvVGEY09Gf8A4GSAo+NCtL1dW2TYJ4OKp4+g0ICI0=
|
||||
github.com/decred/dcrd/blockchain/standalone v1.1.0/go.mod h1:6K8ZgzlWM1Kz2TwXbrtiAvfvIwfAmlzrtpA7CVPCUPE=
|
||||
github.com/decred/dcrd/blockchain/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:R9rIXU8kEJVC9Z4LAlh9bo9hiT3a+ihys3mCrz4PVao=
|
||||
@ -85,7 +85,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
@ -104,35 +103,27 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jrick/bitset v1.0.0/go.mod h1:ZOYB5Uvkla7wIEY4FEssPVi3IQXa02arznRaYaAEPe4=
|
||||
github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/jrick/wsrpc/v2 v2.3.2/go.mod h1:XPYs8BnRWl99lCvXRM5SLpZmTPqWpSOPkDIqYTwDPfU=
|
||||
github.com/jrick/wsrpc/v2 v2.3.3 h1:cGM2YUPrG8crjXFWw3b6IMcwqYHJMkteLqEb/WlDSP4=
|
||||
github.com/jrick/wsrpc/v2 v2.3.3/go.mod h1:XPYs8BnRWl99lCvXRM5SLpZmTPqWpSOPkDIqYTwDPfU=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
@ -183,8 +174,6 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
99
log.go
Normal file
99
log.go
Normal file
@ -0,0 +1,99 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/decred/slog"
|
||||
"github.com/jrick/logrotate/rotator"
|
||||
|
||||
"github.com/jholdstock/dcrvsp/database"
|
||||
)
|
||||
|
||||
// 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)
|
||||
return logRotator.Write(p)
|
||||
}
|
||||
|
||||
// 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 = slog.NewBackend(logWriter{})
|
||||
|
||||
// logRotator is one of the logging outputs. It should be closed on
|
||||
// application shutdown.
|
||||
logRotator *rotator.Rotator
|
||||
|
||||
vspLog = backendLog.Logger("VSP")
|
||||
dbLog = backendLog.Logger("DB")
|
||||
)
|
||||
|
||||
// Initialize package-global logger variables.
|
||||
func init() {
|
||||
database.UseLogger(dbLog)
|
||||
}
|
||||
|
||||
// subsystemLoggers maps each subsystem identifier to its associated logger.
|
||||
var subsystemLoggers = map[string]slog.Logger{
|
||||
"VSP": vspLog,
|
||||
"DB": dbLog,
|
||||
}
|
||||
|
||||
// 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, _ := slog.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)
|
||||
}
|
||||
}
|
||||
67
main.go
67
main.go
@ -1,81 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/v3"
|
||||
"github.com/jholdstock/dcrvsp/database"
|
||||
"github.com/jrick/wsrpc/v2"
|
||||
)
|
||||
|
||||
const listen = ":3000"
|
||||
var cfg *config
|
||||
|
||||
type Config struct {
|
||||
signKey ed25519.PrivateKey
|
||||
pubKey ed25519.PublicKey
|
||||
poolFees float64
|
||||
netParams *chaincfg.Params
|
||||
dbFile string
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
|
||||
// Database with stubbed methods
|
||||
var db *database.VspDatabase
|
||||
|
||||
var nodeConnection *wsrpc.Client
|
||||
|
||||
func initConfig() (*Config, error) {
|
||||
homePath := "~/.dcrvsp"
|
||||
|
||||
seedPath := filepath.Join(homePath, "sign.seed")
|
||||
seed, err := ioutil.ReadFile(seedPath)
|
||||
var signKey ed25519.PrivateKey
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, errors.New("seedPath does not exist")
|
||||
}
|
||||
|
||||
_, signKey, err = ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate signing key: %v", err)
|
||||
}
|
||||
err = ioutil.WriteFile(seedPath, signKey.Seed(), 0400)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to save signing key: %v", err)
|
||||
}
|
||||
} else {
|
||||
signKey = ed25519.NewKeyFromSeed(seed)
|
||||
}
|
||||
|
||||
pubKey, ok := signKey.Public().(ed25519.PublicKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to cast signing key: %T", pubKey)
|
||||
}
|
||||
|
||||
return &Config{
|
||||
netParams: chaincfg.TestNet3Params(),
|
||||
dbFile: filepath.Join(homePath, "database.db"),
|
||||
pubKey: pubKey,
|
||||
poolFees: 0.1,
|
||||
signKey: signKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg, err := initConfig()
|
||||
var err error
|
||||
cfg, err := loadConfig()
|
||||
if err != nil {
|
||||
log.Fatalf("config error: %v", err)
|
||||
}
|
||||
|
||||
db, err = database.New(cfg.dbFile)
|
||||
db, err = database.New(cfg.dbPath)
|
||||
if err != nil {
|
||||
log.Fatalf("database error: %v", err)
|
||||
}
|
||||
@ -83,6 +28,6 @@ func main() {
|
||||
defer db.Close()
|
||||
|
||||
// Start HTTP server
|
||||
log.Printf("Listening on %s", listen)
|
||||
log.Print(newRouter().Run(listen))
|
||||
log.Printf("Listening on %s", cfg.Listen)
|
||||
log.Print(newRouter().Run(cfg.Listen))
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ func pubKey(c *gin.Context) {
|
||||
func fee(c *gin.Context) {
|
||||
sendJSONResponse(feeResponse{
|
||||
Timestamp: time.Now().Unix(),
|
||||
Fee: cfg.poolFees,
|
||||
Fee: cfg.VSPFee,
|
||||
}, http.StatusOK, c)
|
||||
}
|
||||
|
||||
@ -273,7 +273,7 @@ findAddress:
|
||||
return
|
||||
}
|
||||
|
||||
minFee := txrules.StakePoolTicketFee(sDiff, relayFee, int32(feeEntry.BlockHeight), cfg.poolFees, cfg.netParams)
|
||||
minFee := txrules.StakePoolTicketFee(sDiff, relayFee, int32(feeEntry.BlockHeight), cfg.VSPFee, cfg.netParams.Params)
|
||||
if feeAmount < minFee {
|
||||
fmt.Printf("too cheap: %v %v", feeAmount, minFee)
|
||||
c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("dont get cheap on me, dodgson (sent:%v required:%v)", feeAmount, minFee))
|
||||
|
||||
29
params.go
Normal file
29
params.go
Normal file
@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/decred/dcrd/chaincfg/v3"
|
||||
)
|
||||
|
||||
type netParams struct {
|
||||
*chaincfg.Params
|
||||
DcrdRPCServerPort string
|
||||
WalletRPCServerPort string
|
||||
}
|
||||
|
||||
var mainNetParams = netParams{
|
||||
Params: chaincfg.MainNetParams(),
|
||||
DcrdRPCServerPort: "9109",
|
||||
WalletRPCServerPort: "9111",
|
||||
}
|
||||
|
||||
var testNet3Params = netParams{
|
||||
Params: chaincfg.TestNet3Params(),
|
||||
DcrdRPCServerPort: "19109",
|
||||
WalletRPCServerPort: "19111",
|
||||
}
|
||||
|
||||
var simNetParams = netParams{
|
||||
Params: chaincfg.SimNetParams(),
|
||||
DcrdRPCServerPort: "19556",
|
||||
WalletRPCServerPort: "19558",
|
||||
}
|
||||
@ -24,7 +24,6 @@ golangci-lint run --disable-all --deadline=10m \
|
||||
--enable=gosimple \
|
||||
--enable=unconvert \
|
||||
--enable=ineffassign \
|
||||
--enable=staticcheck \
|
||||
--enable=structcheck \
|
||||
--enable=goimports \
|
||||
--enable=misspell \
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user