Storing references to the database/dcrd/dcrwallet clients inside the cache struct means they don't need to be passed in every time the cache is updated.
133 lines
3.2 KiB
Go
133 lines
3.2 KiB
Go
// Copyright (c) 2020-2023 The Decred developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package webapi
|
|
|
|
import (
|
|
"errors"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/decred/slog"
|
|
"github.com/decred/vspd/database"
|
|
"github.com/decred/vspd/rpc"
|
|
"github.com/dustin/go-humanize"
|
|
)
|
|
|
|
// cache is used to store values which are commonly used by the API, so
|
|
// repeated web requests don't repeatedly trigger DB or RPC calls.
|
|
type cache struct {
|
|
// data is the cached data.
|
|
data cacheData
|
|
// mtx must be held to read/write cache data.
|
|
mtx sync.RWMutex
|
|
|
|
log slog.Logger
|
|
db *database.VspDatabase
|
|
dcrd rpc.DcrdConnect
|
|
wallets rpc.WalletConnect
|
|
}
|
|
|
|
type cacheData struct {
|
|
UpdateTime string
|
|
PubKey string
|
|
DatabaseSize string
|
|
Voting int64
|
|
Voted int64
|
|
Expired int64
|
|
Missed int64
|
|
VotingWalletsOnline int64
|
|
TotalVotingWallets int64
|
|
BlockHeight uint32
|
|
NetworkProportion float32
|
|
ExpiredProportion float32
|
|
MissedProportion float32
|
|
}
|
|
|
|
func (c *cache) getData() cacheData {
|
|
c.mtx.RLock()
|
|
defer c.mtx.RUnlock()
|
|
|
|
return c.data
|
|
}
|
|
|
|
// newCache creates a new cache and initializes it with static values.
|
|
func newCache(signPubKey string, log slog.Logger, db *database.VspDatabase,
|
|
dcrd rpc.DcrdConnect, wallets rpc.WalletConnect) *cache {
|
|
return &cache{
|
|
data: cacheData{
|
|
PubKey: signPubKey,
|
|
},
|
|
log: log,
|
|
db: db,
|
|
dcrd: dcrd,
|
|
wallets: wallets,
|
|
}
|
|
}
|
|
|
|
// update will use the provided database and RPC connections to update the
|
|
// dynamic values in the cache.
|
|
func (c *cache) update() error {
|
|
dbSize, err := c.db.Size()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Get latest counts of voting, voted, expired and missed tickets.
|
|
voting, voted, expired, missed, err := c.db.CountTickets()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Get latest best block height.
|
|
dcrdClient, _, err := c.dcrd.Client()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
bestBlock, err := dcrdClient.GetBestBlockHeader()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if bestBlock.PoolSize == 0 {
|
|
return errors.New("dcr node reports a network ticket pool size of zero")
|
|
}
|
|
|
|
clients, failedConnections := c.wallets.Clients()
|
|
if len(clients) == 0 {
|
|
c.log.Error("Could not connect to any wallets")
|
|
} else if len(failedConnections) > 0 {
|
|
c.log.Errorf("Failed to connect to %d wallet(s), proceeding with only %d",
|
|
len(failedConnections), len(clients))
|
|
}
|
|
|
|
c.mtx.Lock()
|
|
defer c.mtx.Unlock()
|
|
|
|
c.data.UpdateTime = dateTime(time.Now().Unix())
|
|
c.data.DatabaseSize = humanize.Bytes(dbSize)
|
|
c.data.Voting = voting
|
|
c.data.Voted = voted
|
|
c.data.TotalVotingWallets = int64(len(clients) + len(failedConnections))
|
|
c.data.VotingWalletsOnline = int64(len(clients))
|
|
c.data.Expired = expired
|
|
c.data.Missed = missed
|
|
c.data.BlockHeight = bestBlock.Height
|
|
c.data.NetworkProportion = float32(voting) / float32(bestBlock.PoolSize)
|
|
|
|
total := voted + expired + missed
|
|
|
|
// Prevent dividing by zero when pool has no voted/expired/missed tickets.
|
|
if total == 0 {
|
|
c.data.ExpiredProportion = 0
|
|
c.data.MissedProportion = 0
|
|
} else {
|
|
c.data.ExpiredProportion = float32(expired) / float32(total)
|
|
c.data.MissedProportion = float32(missed) / float32(total)
|
|
}
|
|
|
|
return nil
|
|
}
|