Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions background/background.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,14 +379,12 @@ func Start(shutdownCtx context.Context, wg *sync.WaitGroup, vdb *database.VspDat
// Run voting wallet consistency check periodically.
wg.Add(1)
go func() {
ticker := time.NewTicker(consistencyInterval)
consistencyLoop:
for {
select {
case <-shutdownCtx.Done():
ticker.Stop()
break consistencyLoop
case <-ticker.C:
case <-time.After(consistencyInterval):
checkWalletConsistency()
}
}
Expand Down
15 changes: 10 additions & 5 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,25 @@ import (
"decred.org/dcrwallet/v2/wallet/txrules"
"github.com/decred/dcrd/dcrutil/v4"
"github.com/decred/dcrd/hdkeychain/v3"
"github.com/decred/slog"
"github.com/decred/vspd/database"
"github.com/decred/vspd/version"
flags "github.com/jessevdk/go-flags"
)

const appName = "vspd"

var (
defaultListen = ":8800"
defaultLogLevel = "debug"
defaultLogLevel = slog.LevelDebug.String()
defaultMaxLogSize = int64(10)
defaultLogsToKeep = 20
defaultVSPFee = 3.0
defaultNetwork = "testnet"
defaultHomeDir = dcrutil.AppDataDir("vspd", false)
defaultConfigFilename = "vspd.conf"
defaultHomeDir = dcrutil.AppDataDir(appName, false)
defaultConfigFilename = fmt.Sprintf("%s.conf", appName)
defaultLogFilename = fmt.Sprintf("%s.log", appName)
defaultDBFilename = fmt.Sprintf("%s.db", appName)
defaultConfigFile = filepath.Join(defaultHomeDir, defaultConfigFilename)
defaultDcrdHost = "127.0.0.1"
defaultWalletHost = "127.0.0.1"
Expand Down Expand Up @@ -403,11 +408,11 @@ func loadConfig() (*config, error) {

// Initialize loggers and log rotation.
logDir := filepath.Join(cfg.HomeDir, "logs", cfg.netParams.Name)
initLogRotator(filepath.Join(logDir, "vspd.log"), cfg.MaxLogSize, cfg.LogsToKeep)
initLogRotator(filepath.Join(logDir, defaultLogFilename), cfg.MaxLogSize, cfg.LogsToKeep)
setLogLevels(cfg.LogLevel)

// Set the database path
cfg.dbPath = filepath.Join(dataDir, "vspd.db")
cfg.dbPath = filepath.Join(dataDir, defaultDBFilename)

// If xpub has been provided, create a new database and exit.
if cfg.FeeXPub != "" {
Expand Down
39 changes: 2 additions & 37 deletions database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"context"
"crypto/ed25519"
"crypto/rand"
"encoding/binary"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -89,38 +88,6 @@ func writeHotBackupFile(db *bolt.DB) error {
return err
}

func int64ToBytes(i int64) []byte {
bytes := make([]byte, 8)
binary.LittleEndian.PutUint64(bytes, uint64(i))
return bytes
}

func bytesToInt64(bytes []byte) int64 {
return int64(binary.LittleEndian.Uint64(bytes))
}

func uint32ToBytes(i uint32) []byte {
bytes := make([]byte, 4)
binary.LittleEndian.PutUint32(bytes, i)
return bytes
}

func bytesToUint32(bytes []byte) uint32 {
return binary.LittleEndian.Uint32(bytes)
}

func bytesToBool(bytes []byte) bool {
return bytes[0] == 1
}

func boolToBytes(b bool) []byte {
if b {
return []byte{1}
}

return []byte{0}
}

// CreateNew intializes a new bbolt database with all of the necessary vspd
// buckets, and inserts:
// - the provided extended pubkey (to be used for deriving fee addresses).
Expand Down Expand Up @@ -235,19 +202,17 @@ func Open(ctx context.Context, shutdownWg *sync.WaitGroup, dbFile string, backup
return nil, fmt.Errorf("upgrade failed: %w", err)
}

// Start a ticker to update the backup file at the specified interval.
// Periodically update the database backup file.
shutdownWg.Add(1)
go func() {
ticker := time.NewTicker(backupInterval)
for {
select {
case <-ticker.C:
case <-time.After(backupInterval):
err := writeHotBackupFile(db)
if err != nil {
log.Errorf("Failed to write database backup: %v", err)
}
case <-ctx.Done():
ticker.Stop()
shutdownWg.Done()
return
}
Expand Down
33 changes: 33 additions & 0 deletions database/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package database

import (
"encoding/binary"
"encoding/json"
)

Expand All @@ -26,3 +27,35 @@ func bytesToStringMap(bytes []byte) (map[string]string, error) {

return stringMap, nil
}

func int64ToBytes(i int64) []byte {
bytes := make([]byte, 8)
binary.LittleEndian.PutUint64(bytes, uint64(i))
return bytes
}

func bytesToInt64(bytes []byte) int64 {
return int64(binary.LittleEndian.Uint64(bytes))
}

func uint32ToBytes(i uint32) []byte {
bytes := make([]byte, 4)
binary.LittleEndian.PutUint32(bytes, i)
return bytes
}

func bytesToUint32(bytes []byte) uint32 {
return binary.LittleEndian.Uint32(bytes)
}

func bytesToBool(bytes []byte) bool {
return bytes[0] == 1
}

func boolToBytes(b bool) []byte {
if b {
return []byte{1}
}

return []byte{0}
}
4 changes: 2 additions & 2 deletions vspd.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func run(ctx context.Context) error {
}

// Show version at startup.
log.Infof("Version %s (Go version %s %s/%s)", version.String(), runtime.Version(),
log.Criticalf("Version %s (Go version %s %s/%s)", version.String(), runtime.Version(),
runtime.GOOS, runtime.GOARCH)

if cfg.VspClosed {
Expand All @@ -62,7 +62,7 @@ func run(ctx context.Context) error {

// WaitGroup for services to signal when they have shutdown cleanly.
var shutdownWg sync.WaitGroup
defer log.Info("Shutdown complete")
defer log.Criticalf("Shutdown complete")

// Open database.
db, err := database.Open(ctx, &shutdownWg, cfg.dbPath, cfg.BackupInterval, maxVoteChangeRecords)
Expand Down
20 changes: 7 additions & 13 deletions webapi/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func validateSignature(hash, commitmentAddress, signature, message string,
// first check if we have an alternate sign address for this ticket.
altSigData, err := db.AltSignAddrData(hash)
if err != nil {
return fmt.Errorf("db.AltSignAddrData failed: %v", err)
return fmt.Errorf("db.AltSignAddrData failed: %w", err)
}

// If we have no alternate sign address, or if validating with the
Expand Down Expand Up @@ -162,7 +162,7 @@ func validateTicketHash(hash string) error {
}
_, err := chainhash.NewHashFromStr(hash)
if err != nil {
return fmt.Errorf("invalid hash: %v", err)
return fmt.Errorf("invalid hash: %w", err)

}

Expand All @@ -172,31 +172,25 @@ func validateTicketHash(hash string) error {
// getCommitmentAddress gets the commitment address of the provided ticket hash
// from the chain.
func getCommitmentAddress(hash string, dcrdClient *rpc.DcrdRPC, params *chaincfg.Params) (string, error) {
var commitmentAddress string
resp, err := dcrdClient.GetRawTransaction(hash)
if err != nil {
return commitmentAddress, fmt.Errorf("dcrd.GetRawTransaction for ticket failed: %v", err)

return "", fmt.Errorf("dcrd.GetRawTransaction for ticket failed: %w", err)
}

msgTx, err := decodeTransaction(resp.Hex)
if err != nil {
return commitmentAddress, fmt.Errorf("Failed to decode ticket hex: %v", err)

return "", fmt.Errorf("failed to decode ticket hex: %w", err)
}

err = isValidTicket(msgTx)
if err != nil {
return commitmentAddress, fmt.Errorf("Invalid ticket: %w", errInvalidTicket)

return "", fmt.Errorf("invalid ticket: %w", errInvalidTicket)
}

addr, err := stake.AddrFromSStxPkScrCommitment(msgTx.TxOut[1].PkScript, params)
if err != nil {
return commitmentAddress, fmt.Errorf("AddrFromSStxPkScrCommitment error: %v", err)

return "", fmt.Errorf("AddrFromSStxPkScrCommitment error: %w", err)
}

commitmentAddress = addr.String()
return commitmentAddress, nil
return addr.String(), nil
}
15 changes: 10 additions & 5 deletions webapi/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ func (s *Server) broadcastTicket(c *gin.Context) {
// Ensure the provided ticket hex is a valid ticket.
msgTx, err := decodeTransaction(request.TicketHex)
if err != nil {
log.Errorf("%s: Failed to decode ticket hex (ticketHash=%s): %v", funcName, request.TicketHash, err)
log.Errorf("%s: Failed to decode ticket hex (ticketHash=%s): %v",
funcName, request.TicketHash, err)
s.sendErrorWithMsg("cannot decode ticket hex", errBadRequest, c)
return
}
Expand Down Expand Up @@ -297,7 +298,6 @@ func (s *Server) vspAuth(c *gin.Context) {

// If the ticket was found in the database, we already know its
// commitment address. Otherwise we need to get it from the chain.
var commitmentAddress string
dcrdClient := c.MustGet(dcrdKey).(*rpc.DcrdRPC)
dcrdErr := c.MustGet(dcrdErrorKey)
if dcrdErr != nil {
Expand All @@ -306,34 +306,39 @@ func (s *Server) vspAuth(c *gin.Context) {
return
}

var commitmentAddress string
if ticketFound {
commitmentAddress = ticket.CommitmentAddress
} else {
commitmentAddress, err = getCommitmentAddress(hash, dcrdClient, s.cfg.NetParams)
if err != nil {
log.Errorf("%s: Failed to get commitment address (clientIP=%s, ticketHash=%s): %v",
funcName, c.ClientIP(), hash, err)

var apiErr *apiError
if errors.Is(err, apiErr) {
s.sendError(errInvalidTicket, c)
} else {
s.sendError(errInternalError, c)
}
log.Errorf("%s: (clientIP: %s, ticketHash: %s): %v", funcName, c.ClientIP(), hash, err)

return
}
}

// Ensure a signature is provided.
signature := c.GetHeader("VSP-Client-Signature")
if signature == "" {
log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
log.Warnf("%s: No VSP-Client-Signature header (clientIP=%s)", funcName, c.ClientIP())
s.sendErrorWithMsg("no VSP-Client-Signature header", errBadRequest, c)
return
}

// Validate request signature to ensure ticket ownership.
err = validateSignature(hash, commitmentAddress, signature, string(reqBytes), s.db, s.cfg.NetParams)
if err != nil {
log.Errorf("%s: Bad signature (clientIP=%s, ticketHash=%s): %v", funcName, err)
log.Errorf("%s: Couldn't validate signature (clientIP=%s, ticketHash=%s): %v",
funcName, c.ClientIP(), hash, err)
s.sendError(errBadSignature, c)
return
}
Expand Down
6 changes: 2 additions & 4 deletions webapi/webapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func Start(ctx context.Context, requestShutdown func(), shutdownWg *sync.WaitGro
}
}()

// Use a ticker to update cached VSP stats.
// Periodically update cached VSP stats.
var refresh time.Duration
if s.cfg.Debug {
refresh = 1 * time.Second
Expand All @@ -168,14 +168,12 @@ func Start(ctx context.Context, requestShutdown func(), shutdownWg *sync.WaitGro
}
shutdownWg.Add(1)
go func() {
ticker := time.NewTicker(refresh)
for {
select {
case <-ctx.Done():
ticker.Stop()
shutdownWg.Done()
return
case <-ticker.C:
case <-time.After(refresh):
err := updateCache(ctx, vdb, dcrd, config.NetParams, wallets)
if err != nil {
log.Errorf("Failed to update cached VSP stats: %v", err)
Expand Down