Include best block height in /vspinfo response (#254)

This commit is contained in:
Jamie Holdstock 2021-05-15 03:09:03 +01:00 committed by GitHub
parent 0936e090a9
commit ef472ffe5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 67 additions and 20 deletions

View File

@ -243,7 +243,7 @@ func loadConfig() (*config, error) {
flags.IniIncludeComments|flags.IniIncludeDefaults) flags.IniIncludeComments|flags.IniIncludeDefaults)
if err != nil { if err != nil {
return nil, fmt.Errorf("error creating a default "+ return nil, fmt.Errorf("error creating a default "+
"config file: %v", err) "config file: %w", err)
} }
fmt.Printf("Config file with default values written to %s\n", defaultConfigFile) fmt.Printf("Config file with default values written to %s\n", defaultConfigFile)

View File

@ -53,7 +53,8 @@ when a VSP is closed will result in an error.
"vspdversion":"1.0.0-pre", "vspdversion":"1.0.0-pre",
"voting":10, "voting":10,
"voted":25, "voted":25,
"revoked":3 "revoked":3,
"blockheight":623212
} }
``` ```

View File

@ -224,6 +224,17 @@ func (c *DcrdRPC) CanTicketVote(rawTx *dcrdtypes.TxRawResult, ticketHash string,
return live, nil return live, nil
} }
// GetBestBlockHeight uses getblockcount RPC to query the height of the best
// block known by the dcrd instance.
func (c *DcrdRPC) GetBestBlockHeight() (int64, error) {
var height int64
err := c.Call(c.ctx, "getblockcount", &height)
if err != nil {
return 0, err
}
return height, nil
}
// ParseBlockConnectedNotification extracts the block header from a // ParseBlockConnectedNotification extracts the block header from a
// blockconnected JSON-RPC notification. // blockconnected JSON-RPC notification.
func ParseBlockConnectedNotification(params json.RawMessage) (*wire.BlockHeader, error) { func ParseBlockConnectedNotification(params json.RawMessage) (*wire.BlockHeader, error) {

View File

@ -5,16 +5,21 @@
package webapi package webapi
import ( import (
"context"
"encoding/base64" "encoding/base64"
"net/http" "net/http"
"sync" "sync"
"time" "time"
"github.com/decred/dcrd/chaincfg/v3"
"github.com/decred/vspd/database" "github.com/decred/vspd/database"
"github.com/decred/vspd/rpc"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
// vspStats is used to cache values which are commonly used by the API, so
// repeated web requests don't repeatedly trigger DB or RPC calls.
type vspStats struct { type vspStats struct {
PubKey string PubKey string
Voting int64 Voting int64
@ -27,6 +32,7 @@ type vspStats struct {
VspClosed bool VspClosed bool
Debug bool Debug bool
Designation string Designation string
BlockHeight int64
} }
var statsMtx sync.RWMutex var statsMtx sync.RWMutex
@ -39,28 +45,54 @@ func getVSPStats() *vspStats {
return stats return stats
} }
func updateVSPStats(db *database.VspDatabase, cfg Config) error { // initVSPStats creates the struct which holds the cached VSP stats, and
// initializes it with static values.
func initVSPStats() {
statsMtx.Lock()
defer statsMtx.Unlock()
stats = &vspStats{
PubKey: base64.StdEncoding.EncodeToString(signPubKey),
VSPFee: cfg.VSPFee,
Network: cfg.NetParams.Name,
SupportEmail: cfg.SupportEmail,
VspClosed: cfg.VspClosed,
Debug: cfg.Debug,
Designation: cfg.Designation,
}
}
// updateVSPStats updates the dynamic values in the cached VSP stats (ticket
// counts and best block height).
func updateVSPStats(ctx context.Context, db *database.VspDatabase,
dcrd rpc.DcrdConnect, netParams *chaincfg.Params) error {
// Update counts of voting, voted and revoked tickets.
voting, voted, revoked, err := db.CountTickets() voting, voted, revoked, err := db.CountTickets()
if err != nil { if err != nil {
return err return err
} }
// Update best block height.
dcrdClient, err := dcrd.Client(ctx, netParams)
if err != nil {
return err
}
blockHeight, err := dcrdClient.GetBestBlockHeight()
if err != nil {
return err
}
statsMtx.Lock() statsMtx.Lock()
defer statsMtx.Unlock() defer statsMtx.Unlock()
stats = &vspStats{ stats.UpdateTime = dateTime(time.Now().Unix())
PubKey: base64.StdEncoding.EncodeToString(signPubKey), stats.Voting = voting
Voting: voting, stats.Voted = voted
Voted: voted, stats.Revoked = revoked
Revoked: revoked, stats.BlockHeight = blockHeight
VSPFee: cfg.VSPFee,
Network: cfg.NetParams.Name,
UpdateTime: dateTime(time.Now().Unix()),
SupportEmail: cfg.SupportEmail,
VspClosed: cfg.VspClosed,
Debug: cfg.Debug,
Designation: cfg.Designation,
}
return nil return nil
} }

View File

@ -15,6 +15,7 @@ type vspInfoResponse struct {
Voting int64 `json:"voting"` Voting int64 `json:"voting"`
Voted int64 `json:"voted"` Voted int64 `json:"voted"`
Revoked int64 `json:"revoked"` Revoked int64 `json:"revoked"`
BlockHeight int64 `json:"blockheight"`
} }
type feeAddressRequest struct { type feeAddressRequest struct {

View File

@ -25,5 +25,6 @@ func vspInfo(c *gin.Context) {
Voting: cachedStats.Voting, Voting: cachedStats.Voting,
Voted: cachedStats.Voted, Voted: cachedStats.Voted,
Revoked: cachedStats.Revoked, Revoked: cachedStats.Revoked,
BlockHeight: cachedStats.BlockHeight,
}, c) }, c)
} }

View File

@ -69,9 +69,10 @@ func Start(ctx context.Context, requestShutdownChan chan struct{}, shutdownWg *s
} }
// Populate cached VSP stats before starting webserver. // Populate cached VSP stats before starting webserver.
err = updateVSPStats(vdb, config) initVSPStats()
err = updateVSPStats(ctx, vdb, dcrd, config.NetParams)
if err != nil { if err != nil {
return fmt.Errorf("could not initialize VSP stats cache: %w", err) log.Errorf("Could not initialize VSP stats cache: %v", err)
} }
// Get the last used address index and the feeXpub from the database, and // Get the last used address index and the feeXpub from the database, and
@ -139,7 +140,7 @@ func Start(ctx context.Context, requestShutdownChan chan struct{}, shutdownWg *s
} }
}() }()
// Use a ticker to update template data. // Use a ticker to update cached VSP stats.
var refresh time.Duration var refresh time.Duration
if cfg.Debug { if cfg.Debug {
refresh = 1 * time.Second refresh = 1 * time.Second
@ -156,7 +157,7 @@ func Start(ctx context.Context, requestShutdownChan chan struct{}, shutdownWg *s
shutdownWg.Done() shutdownWg.Done()
return return
case <-ticker.C: case <-ticker.C:
err = updateVSPStats(db, cfg) err = updateVSPStats(ctx, vdb, dcrd, config.NetParams)
if err != nil { if err != nil {
log.Errorf("Failed to update cached VSP stats: %v", err) log.Errorf("Failed to update cached VSP stats: %v", err)
} }