Remove global cache variable. (#341)
* Remove global cache variable. Rather than maintaining cached data in a global variable, instantiate a cache struct and keep it in the `Server` struct. * Store net params in RPC clients. This means net params only need to be supplied once at startup, and also removes a global instance of net params in `background.go`.
This commit is contained in:
parent
78abc59e97
commit
78bb28056c
@ -12,7 +12,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/v3"
|
||||
"github.com/decred/vspd/database"
|
||||
"github.com/decred/vspd/rpc"
|
||||
"github.com/jrick/wsrpc/v2"
|
||||
@ -22,7 +21,6 @@ var (
|
||||
db *database.VspDatabase
|
||||
dcrdRPC rpc.DcrdConnect
|
||||
walletRPC rpc.WalletConnect
|
||||
netParams *chaincfg.Params
|
||||
notifierClosed chan struct{}
|
||||
)
|
||||
|
||||
@ -76,7 +74,7 @@ func blockConnected() {
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
dcrdClient, _, err := dcrdRPC.Client(ctx, netParams)
|
||||
dcrdClient, _, err := dcrdRPC.Client(ctx)
|
||||
if err != nil {
|
||||
log.Errorf("%s: %v", funcName, err)
|
||||
return
|
||||
@ -170,7 +168,7 @@ func blockConnected() {
|
||||
log.Errorf("%s: db.GetUnconfirmedFees error: %v", funcName, err)
|
||||
}
|
||||
|
||||
walletClients, failedConnections := walletRPC.Clients(ctx, netParams)
|
||||
walletClients, failedConnections := walletRPC.Clients(ctx)
|
||||
if len(walletClients) == 0 {
|
||||
log.Errorf("%s: Could not connect to any wallets", funcName)
|
||||
return
|
||||
@ -333,7 +331,7 @@ func blockConnected() {
|
||||
func connectNotifier(shutdownCtx context.Context, dcrdWithNotifs rpc.DcrdConnect) error {
|
||||
notifierClosed = make(chan struct{})
|
||||
|
||||
dcrdClient, _, err := dcrdWithNotifs.Client(shutdownCtx, netParams)
|
||||
dcrdClient, _, err := dcrdWithNotifs.Client(shutdownCtx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -361,12 +359,11 @@ func connectNotifier(shutdownCtx context.Context, dcrdWithNotifs rpc.DcrdConnect
|
||||
}
|
||||
|
||||
func Start(shutdownCtx context.Context, wg *sync.WaitGroup, vdb *database.VspDatabase, drpc rpc.DcrdConnect,
|
||||
dcrdWithNotif rpc.DcrdConnect, wrpc rpc.WalletConnect, p *chaincfg.Params) {
|
||||
dcrdWithNotif rpc.DcrdConnect, wrpc rpc.WalletConnect) {
|
||||
|
||||
db = vdb
|
||||
dcrdRPC = drpc
|
||||
walletRPC = wrpc
|
||||
netParams = p
|
||||
|
||||
// Run the block connected handler now to catch up with any blocks mined
|
||||
// while vspd was shut down.
|
||||
@ -428,13 +425,13 @@ func checkWalletConsistency() {
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
dcrdClient, _, err := dcrdRPC.Client(ctx, netParams)
|
||||
dcrdClient, _, err := dcrdRPC.Client(ctx)
|
||||
if err != nil {
|
||||
log.Errorf("%s: %v", funcName, err)
|
||||
return
|
||||
}
|
||||
|
||||
walletClients, failedConnections := walletRPC.Clients(ctx, netParams)
|
||||
walletClients, failedConnections := walletRPC.Clients(ctx)
|
||||
if len(walletClients) == 0 {
|
||||
log.Errorf("%s: Could not connect to any wallets", funcName)
|
||||
return
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020-2021 The Decred developers
|
||||
// Copyright (c) 2020-2022 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
@ -16,7 +16,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/v3"
|
||||
"github.com/decred/vspd/rpc"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
@ -386,7 +385,7 @@ func (vdb *VspDatabase) BackupDB(w http.ResponseWriter) error {
|
||||
|
||||
// CheckIntegrity will ensure that all data in the database is present and up to
|
||||
// date.
|
||||
func (vdb *VspDatabase) CheckIntegrity(ctx context.Context, params *chaincfg.Params, dcrd rpc.DcrdConnect) error {
|
||||
func (vdb *VspDatabase) CheckIntegrity(ctx context.Context, dcrd rpc.DcrdConnect) error {
|
||||
|
||||
// Ensure all confirmed tickets have a purchase height.
|
||||
// This is necessary because of an old bug which, in some circumstances,
|
||||
@ -401,7 +400,7 @@ func (vdb *VspDatabase) CheckIntegrity(ctx context.Context, params *chaincfg.Par
|
||||
return nil
|
||||
}
|
||||
|
||||
dcrdClient, _, err := dcrd.Client(ctx, params)
|
||||
dcrdClient, _, err := dcrd.Client(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
56
rpc/dcrd.go
56
rpc/dcrd.go
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020 The Decred developers
|
||||
// Copyright (c) 2020-2022 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
@ -41,45 +41,53 @@ type DcrdRPC struct {
|
||||
}
|
||||
|
||||
type DcrdConnect struct {
|
||||
*client
|
||||
client *client
|
||||
params *chaincfg.Params
|
||||
}
|
||||
|
||||
func SetupDcrd(user, pass, addr string, cert []byte, n wsrpc.Notifier) DcrdConnect {
|
||||
return DcrdConnect{setup(user, pass, addr, cert, n)}
|
||||
func SetupDcrd(user, pass, addr string, cert []byte, n wsrpc.Notifier, params *chaincfg.Params) DcrdConnect {
|
||||
return DcrdConnect{
|
||||
client: setup(user, pass, addr, cert, n),
|
||||
params: params,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DcrdConnect) Close() {
|
||||
d.client.Close()
|
||||
}
|
||||
|
||||
// Client creates a new DcrdRPC client instance. Returns an error if dialing
|
||||
// dcrd fails or if dcrd is misconfigured.
|
||||
func (d *DcrdConnect) Client(ctx context.Context, netParams *chaincfg.Params) (*DcrdRPC, string, error) {
|
||||
c, newConnection, err := d.dial(ctx)
|
||||
func (d *DcrdConnect) Client(ctx context.Context) (*DcrdRPC, string, error) {
|
||||
c, newConnection, err := d.client.dial(ctx)
|
||||
if err != nil {
|
||||
return nil, d.addr, fmt.Errorf("dcrd connection error: %w", err)
|
||||
return nil, d.client.addr, fmt.Errorf("dcrd connection error: %w", err)
|
||||
}
|
||||
|
||||
// If this is a reused connection, we don't need to validate the dcrd config
|
||||
// again.
|
||||
if !newConnection {
|
||||
return &DcrdRPC{c, ctx}, d.addr, nil
|
||||
return &DcrdRPC{c, ctx}, d.client.addr, nil
|
||||
}
|
||||
|
||||
// Verify dcrd is at the required api version.
|
||||
var verMap map[string]dcrdtypes.VersionResult
|
||||
err = c.Call(ctx, "version", &verMap)
|
||||
if err != nil {
|
||||
d.Close()
|
||||
return nil, d.addr, fmt.Errorf("dcrd version check failed: %w", err)
|
||||
d.client.Close()
|
||||
return nil, d.client.addr, fmt.Errorf("dcrd version check failed: %w", err)
|
||||
}
|
||||
|
||||
ver, exists := verMap["dcrdjsonrpcapi"]
|
||||
if !exists {
|
||||
d.Close()
|
||||
return nil, d.addr, fmt.Errorf("dcrd version response missing 'dcrdjsonrpcapi'")
|
||||
d.client.Close()
|
||||
return nil, d.client.addr, fmt.Errorf("dcrd version response missing 'dcrdjsonrpcapi'")
|
||||
}
|
||||
|
||||
sVer := semver{ver.Major, ver.Minor, ver.Patch}
|
||||
if !semverCompatible(requiredDcrdVersion, sVer) {
|
||||
d.Close()
|
||||
return nil, d.addr, fmt.Errorf("dcrd has incompatible JSON-RPC version: got %s, expected %s",
|
||||
d.client.Close()
|
||||
return nil, d.client.addr, fmt.Errorf("dcrd has incompatible JSON-RPC version: got %s, expected %s",
|
||||
sVer, requiredDcrdVersion)
|
||||
}
|
||||
|
||||
@ -87,27 +95,27 @@ func (d *DcrdConnect) Client(ctx context.Context, netParams *chaincfg.Params) (*
|
||||
var netID wire.CurrencyNet
|
||||
err = c.Call(ctx, "getcurrentnet", &netID)
|
||||
if err != nil {
|
||||
d.Close()
|
||||
return nil, d.addr, fmt.Errorf("dcrd getcurrentnet check failed: %w", err)
|
||||
d.client.Close()
|
||||
return nil, d.client.addr, fmt.Errorf("dcrd getcurrentnet check failed: %w", err)
|
||||
}
|
||||
if netID != netParams.Net {
|
||||
d.Close()
|
||||
return nil, d.addr, fmt.Errorf("dcrd running on %s, expected %s", netID, netParams.Net)
|
||||
if netID != d.params.Net {
|
||||
d.client.Close()
|
||||
return nil, d.client.addr, fmt.Errorf("dcrd running on %s, expected %s", netID, d.params.Net)
|
||||
}
|
||||
|
||||
// Verify dcrd has tx index enabled (required for getrawtransaction).
|
||||
var info dcrdtypes.InfoChainResult
|
||||
err = c.Call(ctx, "getinfo", &info)
|
||||
if err != nil {
|
||||
d.Close()
|
||||
return nil, d.addr, fmt.Errorf("dcrd getinfo check failed: %w", err)
|
||||
d.client.Close()
|
||||
return nil, d.client.addr, fmt.Errorf("dcrd getinfo check failed: %w", err)
|
||||
}
|
||||
if !info.TxIndex {
|
||||
d.Close()
|
||||
return nil, d.addr, errors.New("dcrd does not have transaction index enabled (--txindex)")
|
||||
d.client.Close()
|
||||
return nil, d.client.addr, errors.New("dcrd does not have transaction index enabled (--txindex)")
|
||||
}
|
||||
|
||||
return &DcrdRPC{c, ctx}, d.addr, nil
|
||||
return &DcrdRPC{c, ctx}, d.client.addr, nil
|
||||
}
|
||||
|
||||
// GetRawTransaction uses getrawtransaction RPC to retrieve details about the
|
||||
|
||||
@ -25,32 +25,38 @@ type WalletRPC struct {
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
type WalletConnect []*client
|
||||
|
||||
func SetupWallet(user, pass, addrs []string, cert [][]byte) WalletConnect {
|
||||
walletConnect := make(WalletConnect, len(addrs))
|
||||
|
||||
for i := 0; i < len(addrs); i++ {
|
||||
walletConnect[i] = setup(user[i], pass[i], addrs[i], cert[i], nil)
|
||||
type WalletConnect struct {
|
||||
clients []*client
|
||||
params *chaincfg.Params
|
||||
}
|
||||
|
||||
return walletConnect
|
||||
func SetupWallet(user, pass, addrs []string, cert [][]byte, params *chaincfg.Params) WalletConnect {
|
||||
clients := make([]*client, len(addrs))
|
||||
|
||||
for i := 0; i < len(addrs); i++ {
|
||||
clients[i] = setup(user[i], pass[i], addrs[i], cert[i], nil)
|
||||
}
|
||||
|
||||
return WalletConnect{
|
||||
clients: clients,
|
||||
params: params,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WalletConnect) Close() {
|
||||
for _, connect := range []*client(*w) {
|
||||
connect.Close()
|
||||
for _, client := range w.clients {
|
||||
client.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// Clients loops over each wallet and tries to establish a connection. It
|
||||
// increments a count of failed connections if a connection cannot be
|
||||
// established, or if the wallet is misconfigured.
|
||||
func (w *WalletConnect) Clients(ctx context.Context, netParams *chaincfg.Params) ([]*WalletRPC, []string) {
|
||||
func (w *WalletConnect) Clients(ctx context.Context) ([]*WalletRPC, []string) {
|
||||
walletClients := make([]*WalletRPC, 0)
|
||||
failedConnections := make([]string, 0)
|
||||
|
||||
for _, connect := range []*client(*w) {
|
||||
for _, connect := range w.clients {
|
||||
|
||||
c, newConnection, err := connect.dial(ctx)
|
||||
if err != nil {
|
||||
@ -103,9 +109,9 @@ func (w *WalletConnect) Clients(ctx context.Context, netParams *chaincfg.Params)
|
||||
connect.Close()
|
||||
continue
|
||||
}
|
||||
if netID != netParams.Net {
|
||||
if netID != w.params.Net {
|
||||
log.Errorf("dcrwallet on wrong network (wallet=%s): running on %s, expected %s",
|
||||
c.String(), netID, netParams.Net)
|
||||
c.String(), netID, w.params.Net)
|
||||
failedConnections = append(failedConnections, connect.addr)
|
||||
connect.Close()
|
||||
continue
|
||||
|
||||
12
vspd.go
12
vspd.go
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020 The Decred developers
|
||||
// Copyright (c) 2020-2022 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
@ -84,15 +84,15 @@ func run(ctx context.Context) error {
|
||||
|
||||
// Create RPC client for local dcrd instance (used for broadcasting and
|
||||
// checking the status of fee transactions).
|
||||
dcrd := rpc.SetupDcrd(cfg.DcrdUser, cfg.DcrdPass, cfg.DcrdHost, cfg.dcrdCert, nil)
|
||||
dcrd := rpc.SetupDcrd(cfg.DcrdUser, cfg.DcrdPass, cfg.DcrdHost, cfg.dcrdCert, nil, cfg.netParams.Params)
|
||||
defer dcrd.Close()
|
||||
|
||||
// Create RPC client for remote dcrwallet instance (used for voting).
|
||||
wallets := rpc.SetupWallet(cfg.walletUsers, cfg.walletPasswords, cfg.walletHosts, cfg.walletCerts)
|
||||
wallets := rpc.SetupWallet(cfg.walletUsers, cfg.walletPasswords, cfg.walletHosts, cfg.walletCerts, cfg.netParams.Params)
|
||||
defer wallets.Close()
|
||||
|
||||
// Ensure all data in database is present and up-to-date.
|
||||
err = db.CheckIntegrity(ctx, cfg.netParams.Params, dcrd)
|
||||
err = db.CheckIntegrity(ctx, dcrd)
|
||||
if err != nil {
|
||||
// vspd should still start if this fails, so just log an error.
|
||||
log.Errorf("Could not check database integrity: %v", err)
|
||||
@ -124,12 +124,12 @@ func run(ctx context.Context) error {
|
||||
// Create a dcrd client with a blockconnected notification handler.
|
||||
notifHandler := background.NotificationHandler{ShutdownWg: &shutdownWg}
|
||||
dcrdWithNotifs := rpc.SetupDcrd(cfg.DcrdUser, cfg.DcrdPass,
|
||||
cfg.DcrdHost, cfg.dcrdCert, ¬ifHandler)
|
||||
cfg.DcrdHost, cfg.dcrdCert, ¬ifHandler, cfg.netParams.Params)
|
||||
defer dcrdWithNotifs.Close()
|
||||
|
||||
// Start background process which will continually attempt to reconnect to
|
||||
// dcrd if the connection drops.
|
||||
background.Start(ctx, &shutdownWg, db, dcrd, dcrdWithNotifs, wallets, cfg.netParams.Params)
|
||||
background.Start(ctx, &shutdownWg, db, dcrd, dcrdWithNotifs, wallets)
|
||||
|
||||
// Wait for shutdown tasks to complete before running deferred tasks and
|
||||
// returning.
|
||||
|
||||
@ -143,7 +143,7 @@ func (s *Server) statusJSON(c *gin.Context) {
|
||||
// adminPage is the handler for "GET /admin".
|
||||
func (s *Server) adminPage(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "admin.html", gin.H{
|
||||
"WebApiCache": getCache(),
|
||||
"WebApiCache": s.cache.getData(),
|
||||
"WebApiCfg": s.cfg,
|
||||
"WalletStatus": walletStatus(c),
|
||||
"DcrdStatus": dcrdStatus(c),
|
||||
@ -185,7 +185,7 @@ func (s *Server) ticketSearch(c *gin.Context) {
|
||||
VoteChanges: voteChanges,
|
||||
MaxVoteChanges: s.cfg.MaxVoteChangeRecords,
|
||||
},
|
||||
"WebApiCache": getCache(),
|
||||
"WebApiCache": s.cache.getData(),
|
||||
"WebApiCfg": s.cfg,
|
||||
"WalletStatus": walletStatus(c),
|
||||
"DcrdStatus": dcrdStatus(c),
|
||||
@ -200,7 +200,7 @@ func (s *Server) adminLogin(c *gin.Context) {
|
||||
if password != s.cfg.AdminPass {
|
||||
log.Warnf("Failed login attempt from %s", c.ClientIP())
|
||||
c.HTML(http.StatusUnauthorized, "login.html", gin.H{
|
||||
"WebApiCache": getCache(),
|
||||
"WebApiCache": s.cache.getData(),
|
||||
"WebApiCfg": s.cfg,
|
||||
"IncorrectPassword": true,
|
||||
})
|
||||
|
||||
@ -10,15 +10,21 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/v3"
|
||||
"github.com/decred/vspd/database"
|
||||
"github.com/decred/vspd/rpc"
|
||||
"github.com/dustin/go-humanize"
|
||||
)
|
||||
|
||||
// apiCache is used to cache values which are commonly used by the API, so
|
||||
// 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 apiCache struct {
|
||||
type cache struct {
|
||||
// data is the cached data.
|
||||
data cacheData
|
||||
// mtx must be held to read/write cache data.
|
||||
mtx sync.RWMutex
|
||||
}
|
||||
|
||||
type cacheData struct {
|
||||
UpdateTime string
|
||||
PubKey string
|
||||
DatabaseSize string
|
||||
@ -32,31 +38,26 @@ type apiCache struct {
|
||||
RevokedProportion float32
|
||||
}
|
||||
|
||||
var cacheMtx sync.RWMutex
|
||||
var cache apiCache
|
||||
func (c *cache) getData() cacheData {
|
||||
c.mtx.RLock()
|
||||
defer c.mtx.RUnlock()
|
||||
|
||||
func getCache() apiCache {
|
||||
cacheMtx.RLock()
|
||||
defer cacheMtx.RUnlock()
|
||||
|
||||
return cache
|
||||
return c.data
|
||||
}
|
||||
|
||||
// initCache creates the struct which holds the cached VSP stats, and
|
||||
// initializes it with static values.
|
||||
func initCache(signPubKey string) {
|
||||
cacheMtx.Lock()
|
||||
defer cacheMtx.Unlock()
|
||||
|
||||
cache = apiCache{
|
||||
// newCache creates a new cache and initializes it with static values.
|
||||
func newCache(signPubKey string) *cache {
|
||||
return &cache{
|
||||
data: cacheData{
|
||||
PubKey: signPubKey,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// updateCache updates the dynamic values in the cache (ticket counts and best
|
||||
// block height).
|
||||
func updateCache(ctx context.Context, db *database.VspDatabase,
|
||||
dcrd rpc.DcrdConnect, netParams *chaincfg.Params, wallets rpc.WalletConnect) error {
|
||||
// update will use the provided database and RPC connections to update the
|
||||
// dynamic values in the cache.
|
||||
func (c *cache) update(ctx context.Context, db *database.VspDatabase,
|
||||
dcrd rpc.DcrdConnect, wallets rpc.WalletConnect) error {
|
||||
|
||||
dbSize, err := db.Size()
|
||||
if err != nil {
|
||||
@ -70,7 +71,7 @@ func updateCache(ctx context.Context, db *database.VspDatabase,
|
||||
}
|
||||
|
||||
// Get latest best block height.
|
||||
dcrdClient, _, err := dcrd.Client(ctx, netParams)
|
||||
dcrdClient, _, err := dcrd.Client(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -84,7 +85,7 @@ func updateCache(ctx context.Context, db *database.VspDatabase,
|
||||
return errors.New("dcr node reports a network ticket pool size of zero")
|
||||
}
|
||||
|
||||
clients, failedConnections := wallets.Clients(ctx, netParams)
|
||||
clients, failedConnections := wallets.Clients(ctx)
|
||||
if len(clients) == 0 {
|
||||
log.Error("Could not connect to any wallets")
|
||||
} else if len(failedConnections) > 0 {
|
||||
@ -92,29 +93,29 @@ func updateCache(ctx context.Context, db *database.VspDatabase,
|
||||
len(failedConnections), len(clients))
|
||||
}
|
||||
|
||||
cacheMtx.Lock()
|
||||
defer cacheMtx.Unlock()
|
||||
c.mtx.Lock()
|
||||
defer c.mtx.Unlock()
|
||||
|
||||
cache.UpdateTime = dateTime(time.Now().Unix())
|
||||
cache.DatabaseSize = humanize.Bytes(dbSize)
|
||||
cache.Voting = voting
|
||||
cache.Voted = voted
|
||||
cache.TotalVotingWallets = int64(len(clients) + len(failedConnections))
|
||||
cache.VotingWalletsOnline = int64(len(clients))
|
||||
cache.Revoked = revoked
|
||||
cache.BlockHeight = bestBlock.Height
|
||||
cache.NetworkProportion = float32(voting) / float32(bestBlock.PoolSize)
|
||||
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.Revoked = revoked
|
||||
c.data.BlockHeight = bestBlock.Height
|
||||
c.data.NetworkProportion = float32(voting) / float32(bestBlock.PoolSize)
|
||||
|
||||
// Prevent dividing by zero when pool has no voted tickets.
|
||||
switch voted {
|
||||
case 0:
|
||||
if revoked == 0 {
|
||||
cache.RevokedProportion = 0
|
||||
c.data.RevokedProportion = 0
|
||||
} else {
|
||||
cache.RevokedProportion = 1
|
||||
c.data.RevokedProportion = 1
|
||||
}
|
||||
default:
|
||||
cache.RevokedProportion = float32(revoked) / float32(voted)
|
||||
c.data.RevokedProportion = float32(revoked) / float32(voted)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@ -12,7 +12,7 @@ import (
|
||||
|
||||
func (s *Server) homepage(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "homepage.html", gin.H{
|
||||
"WebApiCache": getCache(),
|
||||
"WebApiCache": s.cache.getData(),
|
||||
"WebApiCfg": s.cfg,
|
||||
})
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ func (s *Server) requireAdmin(c *gin.Context) {
|
||||
|
||||
if admin == nil {
|
||||
c.HTML(http.StatusUnauthorized, "login.html", gin.H{
|
||||
"WebApiCache": getCache(),
|
||||
"WebApiCache": s.cache.getData(),
|
||||
"WebApiCfg": s.cfg,
|
||||
})
|
||||
c.Abort()
|
||||
@ -69,9 +69,9 @@ func (s *Server) requireAdmin(c *gin.Context) {
|
||||
|
||||
// withDcrdClient middleware adds a dcrd client to the request context for
|
||||
// downstream handlers to make use of.
|
||||
func withDcrdClient(dcrd rpc.DcrdConnect, cfg Config) gin.HandlerFunc {
|
||||
func withDcrdClient(dcrd rpc.DcrdConnect) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
client, hostname, err := dcrd.Client(c, cfg.NetParams)
|
||||
client, hostname, err := dcrd.Client(c)
|
||||
// Don't handle the error here, add it to the context and let downstream
|
||||
// handlers decide what to do with it.
|
||||
c.Set(dcrdKey, client)
|
||||
@ -83,9 +83,9 @@ func withDcrdClient(dcrd rpc.DcrdConnect, cfg Config) gin.HandlerFunc {
|
||||
// withWalletClients middleware attempts to add voting wallet clients to the
|
||||
// request context for downstream handlers to make use of. Downstream handlers
|
||||
// must handle the case where no wallet clients are connected.
|
||||
func withWalletClients(wallets rpc.WalletConnect, cfg Config) gin.HandlerFunc {
|
||||
func withWalletClients(wallets rpc.WalletConnect) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
clients, failedConnections := wallets.Clients(c, cfg.NetParams)
|
||||
clients, failedConnections := wallets.Clients(c)
|
||||
if len(clients) == 0 {
|
||||
log.Error("Could not connect to any wallets")
|
||||
} else if len(failedConnections) > 0 {
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
|
||||
// vspInfo is the handler for "GET /api/v3/vspinfo".
|
||||
func (s *Server) vspInfo(c *gin.Context) {
|
||||
cachedStats := getCache()
|
||||
cachedStats := s.cache.getData()
|
||||
s.sendJSONResponse(vspInfoResponse{
|
||||
APIVersions: []int64{3},
|
||||
Timestamp: time.Now().Unix(),
|
||||
|
||||
@ -67,6 +67,7 @@ type Server struct {
|
||||
cfg Config
|
||||
db *database.VspDatabase
|
||||
addrGen *addressGenerator
|
||||
cache *cache
|
||||
signPrivKey ed25519.PrivateKey
|
||||
signPubKey ed25519.PublicKey
|
||||
}
|
||||
@ -88,8 +89,8 @@ func Start(ctx context.Context, requestShutdown func(), shutdownWg *sync.WaitGro
|
||||
}
|
||||
|
||||
// Populate cached VSP stats before starting webserver.
|
||||
initCache(base64.StdEncoding.EncodeToString(s.signPubKey))
|
||||
err = updateCache(ctx, vdb, dcrd, config.NetParams, wallets)
|
||||
s.cache = newCache(base64.StdEncoding.EncodeToString(s.signPubKey))
|
||||
err = s.cache.update(ctx, vdb, dcrd, wallets)
|
||||
if err != nil {
|
||||
log.Errorf("Could not initialize VSP stats cache: %v", err)
|
||||
}
|
||||
@ -174,7 +175,7 @@ func Start(ctx context.Context, requestShutdown func(), shutdownWg *sync.WaitGro
|
||||
shutdownWg.Done()
|
||||
return
|
||||
case <-time.After(refresh):
|
||||
err := updateCache(ctx, vdb, dcrd, config.NetParams, wallets)
|
||||
err := s.cache.update(ctx, vdb, dcrd, wallets)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to update cached VSP stats: %v", err)
|
||||
}
|
||||
@ -230,11 +231,11 @@ func (s *Server) router(cookieSecret []byte, dcrd rpc.DcrdConnect, wallets rpc.W
|
||||
|
||||
api := router.Group("/api/v3")
|
||||
api.GET("/vspinfo", s.vspInfo)
|
||||
api.POST("/setaltsignaddr", withDcrdClient(dcrd, s.cfg), s.broadcastTicket, s.vspAuth, s.setAltSignAddr)
|
||||
api.POST("/feeaddress", withDcrdClient(dcrd, s.cfg), s.broadcastTicket, s.vspAuth, s.feeAddress)
|
||||
api.POST("/ticketstatus", withDcrdClient(dcrd, s.cfg), s.vspAuth, s.ticketStatus)
|
||||
api.POST("/payfee", withDcrdClient(dcrd, s.cfg), s.vspAuth, s.payFee)
|
||||
api.POST("/setvotechoices", withDcrdClient(dcrd, s.cfg), withWalletClients(wallets, s.cfg), s.vspAuth, s.setVoteChoices)
|
||||
api.POST("/setaltsignaddr", withDcrdClient(dcrd), s.broadcastTicket, s.vspAuth, s.setAltSignAddr)
|
||||
api.POST("/feeaddress", withDcrdClient(dcrd), s.broadcastTicket, s.vspAuth, s.feeAddress)
|
||||
api.POST("/ticketstatus", withDcrdClient(dcrd), s.vspAuth, s.ticketStatus)
|
||||
api.POST("/payfee", withDcrdClient(dcrd), s.vspAuth, s.payFee)
|
||||
api.POST("/setvotechoices", withDcrdClient(dcrd), withWalletClients(wallets), s.vspAuth, s.setVoteChoices)
|
||||
|
||||
// Website routes.
|
||||
|
||||
@ -246,16 +247,16 @@ func (s *Server) router(cookieSecret []byte, dcrd rpc.DcrdConnect, wallets rpc.W
|
||||
login.POST("", s.adminLogin)
|
||||
|
||||
admin := router.Group("/admin").Use(
|
||||
withWalletClients(wallets, s.cfg), withSession(cookieStore), s.requireAdmin,
|
||||
withWalletClients(wallets), withSession(cookieStore), s.requireAdmin,
|
||||
)
|
||||
admin.GET("", withDcrdClient(dcrd, s.cfg), s.adminPage)
|
||||
admin.POST("/ticket", withDcrdClient(dcrd, s.cfg), s.ticketSearch)
|
||||
admin.GET("", withDcrdClient(dcrd), s.adminPage)
|
||||
admin.POST("/ticket", withDcrdClient(dcrd), s.ticketSearch)
|
||||
admin.GET("/backup", s.downloadDatabaseBackup)
|
||||
admin.POST("/logout", s.adminLogout)
|
||||
|
||||
// Require Basic HTTP Auth on /admin/status endpoint.
|
||||
basic := router.Group("/admin").Use(
|
||||
withDcrdClient(dcrd, s.cfg), withWalletClients(wallets, s.cfg), gin.BasicAuth(gin.Accounts{
|
||||
withDcrdClient(dcrd), withWalletClients(wallets), gin.BasicAuth(gin.Accounts{
|
||||
"admin": s.cfg.AdminPass,
|
||||
}),
|
||||
)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user