Add network proportion to homepage and /vspinfo (and revoked proportion) (#264)
* Add network proportion to homepage and /vspinfo. Proportion is calculated using the number of tickets currently registered with the VSP, divided by the total size of the network ticket pool as reported by `getblockheader`. The value will only ever be an estimate because: - it's possible for a single ticket to be added to multiple VSPs. - vspd does not distinguish between immature and live tickets, whereas `getblockheader` only reports live tickets. - `getblockheader` is reporting the size of the ticket pool as of the previous block, not the current block. * xaur suggestions * Show missed ticket %, not just the raw number.
This commit is contained in:
parent
978b78e745
commit
9867f78385
@ -54,7 +54,8 @@ when a VSP is closed will result in an error.
|
||||
"voting":10,
|
||||
"voted":25,
|
||||
"revoked":3,
|
||||
"blockheight":623212
|
||||
"blockheight":623212,
|
||||
"estimatednetworkproportion":0.048478414
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
11
rpc/dcrd.go
11
rpc/dcrd.go
@ -224,17 +224,6 @@ func (c *DcrdRPC) CanTicketVote(rawTx *dcrdtypes.TxRawResult, ticketHash string,
|
||||
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
|
||||
// blockconnected JSON-RPC notification.
|
||||
func ParseBlockConnectedNotification(params json.RawMessage) (*wire.BlockHeader, error) {
|
||||
|
||||
@ -53,3 +53,7 @@ func indentJSON(input string) template.HTML {
|
||||
func atomsToDCR(atoms int64) string {
|
||||
return dcrutil.Amount(atoms).String()
|
||||
}
|
||||
|
||||
func float32ToPercent(input float32) string {
|
||||
return fmt.Sprintf("%.2f%%", input*100)
|
||||
}
|
||||
|
||||
@ -21,18 +21,20 @@ import (
|
||||
// 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 {
|
||||
PubKey string
|
||||
Voting int64
|
||||
Voted int64
|
||||
Revoked int64
|
||||
VSPFee float64
|
||||
Network string
|
||||
UpdateTime string
|
||||
SupportEmail string
|
||||
VspClosed bool
|
||||
Debug bool
|
||||
Designation string
|
||||
BlockHeight int64
|
||||
PubKey string
|
||||
Voting int64
|
||||
Voted int64
|
||||
Revoked int64
|
||||
VSPFee float64
|
||||
Network string
|
||||
UpdateTime string
|
||||
SupportEmail string
|
||||
VspClosed bool
|
||||
Debug bool
|
||||
Designation string
|
||||
BlockHeight uint32
|
||||
NetworkProportion float32
|
||||
RevokedProportion float32
|
||||
}
|
||||
|
||||
var statsMtx sync.RWMutex
|
||||
@ -80,7 +82,7 @@ func updateVSPStats(ctx context.Context, db *database.VspDatabase,
|
||||
return err
|
||||
}
|
||||
|
||||
blockHeight, err := dcrdClient.GetBestBlockHeight()
|
||||
bestBlock, err := dcrdClient.GetBestBlockHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -92,7 +94,9 @@ func updateVSPStats(ctx context.Context, db *database.VspDatabase,
|
||||
stats.Voting = voting
|
||||
stats.Voted = voted
|
||||
stats.Revoked = revoked
|
||||
stats.BlockHeight = blockHeight
|
||||
stats.BlockHeight = bestBlock.Height
|
||||
stats.NetworkProportion = float32(voting) / float32(bestBlock.PoolSize)
|
||||
stats.RevokedProportion = float32(revoked) / float32(voted)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -53,6 +53,11 @@ body {
|
||||
color: #091440;
|
||||
}
|
||||
|
||||
.vsp-stats .stat-value .text-muted{
|
||||
font-size: 18px;
|
||||
color: #8997A5;
|
||||
}
|
||||
|
||||
footer {
|
||||
flex-shrink: 0;
|
||||
font-size: 0.8rem;
|
||||
|
||||
@ -14,7 +14,10 @@
|
||||
|
||||
<div class="col-6 col-sm-4 col-lg-2 py-3">
|
||||
<div class="stat-title">Revoked tickets</div>
|
||||
<div class="stat-value">{{ .Revoked }}</div>
|
||||
<div class="stat-value">
|
||||
{{ .Revoked }}
|
||||
<span class="text-muted">({{ float32ToPercent .RevokedProportion }})</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6 col-sm-4 col-lg-2 py-3">
|
||||
@ -27,6 +30,11 @@
|
||||
<div class="stat-value">{{ .Network }}</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6 col-sm-4 col-lg-2 py-3">
|
||||
<div class="stat-title">Network Proportion</div>
|
||||
<div class="stat-value">{{ float32ToPercent .NetworkProportion }}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{ end }}
|
||||
|
||||
@ -5,17 +5,18 @@
|
||||
package webapi
|
||||
|
||||
type vspInfoResponse struct {
|
||||
APIVersions []int64 `json:"apiversions"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
PubKey []byte `json:"pubkey"`
|
||||
FeePercentage float64 `json:"feepercentage"`
|
||||
VspClosed bool `json:"vspclosed"`
|
||||
Network string `json:"network"`
|
||||
VspdVersion string `json:"vspdversion"`
|
||||
Voting int64 `json:"voting"`
|
||||
Voted int64 `json:"voted"`
|
||||
Revoked int64 `json:"revoked"`
|
||||
BlockHeight int64 `json:"blockheight"`
|
||||
APIVersions []int64 `json:"apiversions"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
PubKey []byte `json:"pubkey"`
|
||||
FeePercentage float64 `json:"feepercentage"`
|
||||
VspClosed bool `json:"vspclosed"`
|
||||
Network string `json:"network"`
|
||||
VspdVersion string `json:"vspdversion"`
|
||||
Voting int64 `json:"voting"`
|
||||
Voted int64 `json:"voted"`
|
||||
Revoked int64 `json:"revoked"`
|
||||
BlockHeight uint32 `json:"blockheight"`
|
||||
NetworkProportion float32 `json:"estimatednetworkproportion"`
|
||||
}
|
||||
|
||||
type feeAddressRequest struct {
|
||||
|
||||
@ -15,16 +15,17 @@ import (
|
||||
func vspInfo(c *gin.Context) {
|
||||
cachedStats := getVSPStats()
|
||||
sendJSONResponse(vspInfoResponse{
|
||||
APIVersions: []int64{3},
|
||||
Timestamp: time.Now().Unix(),
|
||||
PubKey: signPubKey,
|
||||
FeePercentage: cfg.VSPFee,
|
||||
Network: cfg.NetParams.Name,
|
||||
VspClosed: cfg.VspClosed,
|
||||
VspdVersion: version.String(),
|
||||
Voting: cachedStats.Voting,
|
||||
Voted: cachedStats.Voted,
|
||||
Revoked: cachedStats.Revoked,
|
||||
BlockHeight: cachedStats.BlockHeight,
|
||||
APIVersions: []int64{3},
|
||||
Timestamp: time.Now().Unix(),
|
||||
PubKey: signPubKey,
|
||||
FeePercentage: cfg.VSPFee,
|
||||
Network: cfg.NetParams.Name,
|
||||
VspClosed: cfg.VspClosed,
|
||||
VspdVersion: version.String(),
|
||||
Voting: cachedStats.Voting,
|
||||
Voted: cachedStats.Voted,
|
||||
Revoked: cachedStats.Revoked,
|
||||
BlockHeight: cachedStats.BlockHeight,
|
||||
NetworkProportion: cachedStats.NetworkProportion,
|
||||
}, c)
|
||||
}
|
||||
|
||||
@ -177,13 +177,14 @@ func router(debugMode bool, cookieSecret []byte, dcrd rpc.DcrdConnect, wallets r
|
||||
|
||||
// Add custom functions for use in templates.
|
||||
router.SetFuncMap(template.FuncMap{
|
||||
"txURL": txURL(cfg.BlockExplorerURL),
|
||||
"addressURL": addressURL(cfg.BlockExplorerURL),
|
||||
"blockURL": blockURL(cfg.BlockExplorerURL),
|
||||
"dateTime": dateTime,
|
||||
"stripWss": stripWss,
|
||||
"indentJSON": indentJSON,
|
||||
"atomsToDCR": atomsToDCR,
|
||||
"txURL": txURL(cfg.BlockExplorerURL),
|
||||
"addressURL": addressURL(cfg.BlockExplorerURL),
|
||||
"blockURL": blockURL(cfg.BlockExplorerURL),
|
||||
"dateTime": dateTime,
|
||||
"stripWss": stripWss,
|
||||
"indentJSON": indentJSON,
|
||||
"atomsToDCR": atomsToDCR,
|
||||
"float32ToPercent": float32ToPercent,
|
||||
})
|
||||
|
||||
router.LoadHTMLGlob("webapi/templates/*.html")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user