Add decred logging and config (#8)

This commit is contained in:
Jamie Holdstock 2020-05-14 19:02:45 +01:00 committed by GitHub
parent 48f7a584ac
commit 57dfc1ed6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 457 additions and 100 deletions

264
config.go Normal file
View 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
}

View File

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

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

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

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

View File

@ -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
View 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",
}

View File

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