multi: Display expired/missed tickets on webpage.
Revoked ticket count is replaced by separate counts for expired/missed tickets. /vspinfo API response remains unchanged.
This commit is contained in:
parent
fc1f7f2955
commit
54e243526e
@ -308,13 +308,14 @@ func (vdb *VspDatabase) Size() (uint64, error) {
|
||||
return size, err
|
||||
}
|
||||
|
||||
// CountTickets returns the total number of voted, revoked, and currently voting
|
||||
// tickets. This func iterates over every ticket so should be used sparingly.
|
||||
func (vdb *VspDatabase) CountTickets() (int64, int64, int64, error) {
|
||||
// CountTickets returns the total number of voted, expired, missed, and
|
||||
// currently voting tickets. This func iterates over every ticket so should be
|
||||
// used sparingly.
|
||||
func (vdb *VspDatabase) CountTickets() (int64, int64, int64, int64, error) {
|
||||
vdb.ticketsMtx.RLock()
|
||||
defer vdb.ticketsMtx.RUnlock()
|
||||
|
||||
var voting, voted, revoked int64
|
||||
var voting, voted, expired, missed int64
|
||||
err := vdb.db.View(func(tx *bolt.Tx) error {
|
||||
ticketBkt := tx.Bucket(vspBktK).Bucket(ticketBktK)
|
||||
|
||||
@ -325,8 +326,15 @@ func (vdb *VspDatabase) CountTickets() (int64, int64, int64, error) {
|
||||
switch TicketOutcome(tBkt.Get(outcomeK)) {
|
||||
case Voted:
|
||||
voted++
|
||||
case Revoked, Expired, Missed:
|
||||
revoked++
|
||||
case Expired:
|
||||
expired++
|
||||
case Missed:
|
||||
missed++
|
||||
case Revoked:
|
||||
// There shouldn't be any revoked tickets in the db, they
|
||||
// should have been updated to expired/missed. Give benefit
|
||||
// of doubt to VSP admin and count these as expired.
|
||||
expired++
|
||||
default:
|
||||
voting++
|
||||
}
|
||||
@ -336,7 +344,7 @@ func (vdb *VspDatabase) CountTickets() (int64, int64, int64, error) {
|
||||
})
|
||||
})
|
||||
|
||||
return voting, voted, revoked, err
|
||||
return voting, voted, expired, missed, err
|
||||
}
|
||||
|
||||
// GetUnconfirmedTickets returns tickets which are not yet confirmed.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020-2022 The Decred developers
|
||||
// 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.
|
||||
|
||||
@ -228,8 +228,8 @@ func testFilterTickets(t *testing.T) {
|
||||
}
|
||||
|
||||
func testCountTickets(t *testing.T) {
|
||||
count := func(test string, expectedVoting, expectedVoted, expectedRevoked int64) {
|
||||
voting, voted, revoked, err := db.CountTickets()
|
||||
count := func(test string, expectedVoting, expectedVoted, expectedExpired, expectedMissed int64) {
|
||||
voting, voted, expired, missed, err := db.CountTickets()
|
||||
if err != nil {
|
||||
t.Fatalf("error counting tickets: %v", err)
|
||||
}
|
||||
@ -242,14 +242,18 @@ func testCountTickets(t *testing.T) {
|
||||
t.Fatalf("test %s: expected %d voted tickets, got %d",
|
||||
test, expectedVoted, voted)
|
||||
}
|
||||
if revoked != expectedRevoked {
|
||||
t.Fatalf("test %s: expected %d revoked tickets, got %d",
|
||||
test, expectedRevoked, revoked)
|
||||
if expired != expectedExpired {
|
||||
t.Fatalf("test %s: expected %d expired tickets, got %d",
|
||||
test, expectedExpired, expired)
|
||||
}
|
||||
if missed != expectedMissed {
|
||||
t.Fatalf("test %s: expected %d missed tickets, got %d",
|
||||
test, expectedMissed, missed)
|
||||
}
|
||||
}
|
||||
|
||||
// Initial counts should all be zero.
|
||||
count("empty db", 0, 0, 0)
|
||||
count("empty db", 0, 0, 0, 0)
|
||||
|
||||
// Insert a ticket with non-confirmed fee into the database.
|
||||
// This should not be counted.
|
||||
@ -260,7 +264,7 @@ func testCountTickets(t *testing.T) {
|
||||
t.Fatalf("error storing ticket in database: %v", err)
|
||||
}
|
||||
|
||||
count("unconfirmed fee", 0, 0, 0)
|
||||
count("unconfirmed fee", 0, 0, 0, 0)
|
||||
|
||||
// Insert a ticket with confirmed fee into the database.
|
||||
// This should be counted.
|
||||
@ -271,7 +275,7 @@ func testCountTickets(t *testing.T) {
|
||||
t.Fatalf("error storing ticket in database: %v", err)
|
||||
}
|
||||
|
||||
count("confirmed fee", 1, 0, 0)
|
||||
count("confirmed fee", 1, 0, 0, 0)
|
||||
|
||||
// Insert a voted ticket into the database.
|
||||
// This should be counted.
|
||||
@ -283,17 +287,41 @@ func testCountTickets(t *testing.T) {
|
||||
t.Fatalf("error storing ticket in database: %v", err)
|
||||
}
|
||||
|
||||
count("voted", 1, 1, 0)
|
||||
count("voted", 1, 1, 0, 0)
|
||||
|
||||
// Insert a revoked ticket into the database.
|
||||
// Insert an expired ticket into the database.
|
||||
// This should be counted.
|
||||
ticket4 := exampleTicket()
|
||||
ticket4.FeeTxStatus = FeeConfirmed
|
||||
ticket4.Outcome = Revoked
|
||||
ticket4.Outcome = Expired
|
||||
err = db.InsertNewTicket(ticket4)
|
||||
if err != nil {
|
||||
t.Fatalf("error storing ticket in database: %v", err)
|
||||
}
|
||||
|
||||
count("revoked", 1, 1, 1)
|
||||
count("expired", 1, 1, 1, 0)
|
||||
|
||||
// Insert a missed ticket into the database.
|
||||
// This should be counted.
|
||||
ticket5 := exampleTicket()
|
||||
ticket5.FeeTxStatus = FeeConfirmed
|
||||
ticket5.Outcome = Missed
|
||||
err = db.InsertNewTicket(ticket5)
|
||||
if err != nil {
|
||||
t.Fatalf("error storing ticket in database: %v", err)
|
||||
}
|
||||
|
||||
count("missed", 1, 1, 1, 1)
|
||||
|
||||
// Insert a revoked ticket into the database.
|
||||
// This should be counted as expired.
|
||||
ticket6 := exampleTicket()
|
||||
ticket6.FeeTxStatus = FeeConfirmed
|
||||
ticket6.Outcome = Revoked
|
||||
err = db.InsertNewTicket(ticket6)
|
||||
if err != nil {
|
||||
t.Fatalf("error storing ticket in database: %v", err)
|
||||
}
|
||||
|
||||
count("revoked", 1, 1, 2, 1)
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020-2022 The Decred developers
|
||||
// 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.
|
||||
|
||||
@ -31,12 +31,14 @@ type cacheData struct {
|
||||
DatabaseSize string
|
||||
Voting int64
|
||||
Voted int64
|
||||
Revoked int64
|
||||
Expired int64
|
||||
Missed int64
|
||||
VotingWalletsOnline int64
|
||||
TotalVotingWallets int64
|
||||
BlockHeight uint32
|
||||
NetworkProportion float32
|
||||
RevokedProportion float32
|
||||
ExpiredProportion float32
|
||||
MissedProportion float32
|
||||
}
|
||||
|
||||
func (c *cache) getData() cacheData {
|
||||
@ -66,8 +68,8 @@ func (c *cache) update(db *database.VspDatabase, dcrd rpc.DcrdConnect,
|
||||
return err
|
||||
}
|
||||
|
||||
// Get latest counts of voting, voted and revoked tickets.
|
||||
voting, voted, revoked, err := db.CountTickets()
|
||||
// Get latest counts of voting, voted, expired and missed tickets.
|
||||
voting, voted, expired, missed, err := db.CountTickets()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -104,17 +106,20 @@ func (c *cache) update(db *database.VspDatabase, dcrd rpc.DcrdConnect,
|
||||
c.data.Voted = voted
|
||||
c.data.TotalVotingWallets = int64(len(clients) + len(failedConnections))
|
||||
c.data.VotingWalletsOnline = int64(len(clients))
|
||||
c.data.Revoked = revoked
|
||||
c.data.Expired = expired
|
||||
c.data.Missed = missed
|
||||
c.data.BlockHeight = bestBlock.Height
|
||||
c.data.NetworkProportion = float32(voting) / float32(bestBlock.PoolSize)
|
||||
|
||||
total := voted + revoked
|
||||
total := voted + expired + missed
|
||||
|
||||
// Prevent dividing by zero when pool has no voted/revoked tickets.
|
||||
// Prevent dividing by zero when pool has no voted/expired/missed tickets.
|
||||
if total == 0 {
|
||||
c.data.RevokedProportion = 0
|
||||
c.data.ExpiredProportion = 0
|
||||
c.data.MissedProportion = 0
|
||||
} else {
|
||||
c.data.RevokedProportion = float32(revoked) / float32(total)
|
||||
c.data.ExpiredProportion = float32(expired) / float32(total)
|
||||
c.data.MissedProportion = float32(missed) / float32(total)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@ -13,10 +13,18 @@
|
||||
</div>
|
||||
|
||||
<div class="col-6 col-sm-4 col-lg-2 py-3">
|
||||
<div class="stat-title">Revoked tickets</div>
|
||||
<div class="stat-title">Expired tickets</div>
|
||||
<div class="stat-value">
|
||||
{{ comma .WebApiCache.Revoked }}
|
||||
<span class="text-muted">({{ float32ToPercent .WebApiCache.RevokedProportion }})</span>
|
||||
{{ comma .WebApiCache.Expired }}
|
||||
<span class="text-muted">({{ float32ToPercent .WebApiCache.ExpiredProportion }})</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6 col-sm-4 col-lg-2 py-3">
|
||||
<div class="stat-title">Missed tickets</div>
|
||||
<div class="stat-value">
|
||||
{{ comma .WebApiCache.Missed }}
|
||||
<span class="text-muted">({{ float32ToPercent .WebApiCache.MissedProportion }})</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2020-2022 The Decred developers
|
||||
// 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.
|
||||
|
||||
@ -28,7 +28,7 @@ func (s *Server) vspInfo(c *gin.Context) {
|
||||
Voted: cachedStats.Voted,
|
||||
TotalVotingWallets: cachedStats.TotalVotingWallets,
|
||||
VotingWalletsOnline: cachedStats.VotingWalletsOnline,
|
||||
Revoked: cachedStats.Revoked,
|
||||
Revoked: cachedStats.Expired + cachedStats.Missed,
|
||||
BlockHeight: cachedStats.BlockHeight,
|
||||
NetworkProportion: cachedStats.NetworkProportion,
|
||||
}, c)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user