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:
Jamie Holdstock 2021-06-08 21:47:07 +08:00 committed by GitHub
parent 978b78e745
commit 9867f78385
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 70 additions and 56 deletions

View File

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

View File

@ -224,17 +224,6 @@ 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

@ -53,3 +53,7 @@ func indentJSON(input string) template.HTML {
func atomsToDCR(atoms int64) string { func atomsToDCR(atoms int64) string {
return dcrutil.Amount(atoms).String() return dcrutil.Amount(atoms).String()
} }
func float32ToPercent(input float32) string {
return fmt.Sprintf("%.2f%%", input*100)
}

View File

@ -32,7 +32,9 @@ type vspStats struct {
VspClosed bool VspClosed bool
Debug bool Debug bool
Designation string Designation string
BlockHeight int64 BlockHeight uint32
NetworkProportion float32
RevokedProportion float32
} }
var statsMtx sync.RWMutex var statsMtx sync.RWMutex
@ -80,7 +82,7 @@ func updateVSPStats(ctx context.Context, db *database.VspDatabase,
return err return err
} }
blockHeight, err := dcrdClient.GetBestBlockHeight() bestBlock, err := dcrdClient.GetBestBlockHeader()
if err != nil { if err != nil {
return err return err
} }
@ -92,7 +94,9 @@ func updateVSPStats(ctx context.Context, db *database.VspDatabase,
stats.Voting = voting stats.Voting = voting
stats.Voted = voted stats.Voted = voted
stats.Revoked = revoked 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 return nil
} }

View File

@ -53,6 +53,11 @@ body {
color: #091440; color: #091440;
} }
.vsp-stats .stat-value .text-muted{
font-size: 18px;
color: #8997A5;
}
footer { footer {
flex-shrink: 0; flex-shrink: 0;
font-size: 0.8rem; font-size: 0.8rem;

View File

@ -14,7 +14,10 @@
<div class="col-6 col-sm-4 col-lg-2 py-3"> <div class="col-6 col-sm-4 col-lg-2 py-3">
<div class="stat-title">Revoked tickets</div> <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>
<div class="col-6 col-sm-4 col-lg-2 py-3"> <div class="col-6 col-sm-4 col-lg-2 py-3">
@ -27,6 +30,11 @@
<div class="stat-value">{{ .Network }}</div> <div class="stat-value">{{ .Network }}</div>
</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> </div>
{{ end }} {{ end }}

View File

@ -15,7 +15,8 @@ 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"` BlockHeight uint32 `json:"blockheight"`
NetworkProportion float32 `json:"estimatednetworkproportion"`
} }
type feeAddressRequest struct { type feeAddressRequest struct {

View File

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

View File

@ -184,6 +184,7 @@ func router(debugMode bool, cookieSecret []byte, dcrd rpc.DcrdConnect, wallets r
"stripWss": stripWss, "stripWss": stripWss,
"indentJSON": indentJSON, "indentJSON": indentJSON,
"atomsToDCR": atomsToDCR, "atomsToDCR": atomsToDCR,
"float32ToPercent": float32ToPercent,
}) })
router.LoadHTMLGlob("webapi/templates/*.html") router.LoadHTMLGlob("webapi/templates/*.html")