Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display expired/missed tickets on webpage #417

Merged
merged 3 commits into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions database/ticket.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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++
}
Expand All @@ -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.
Expand Down
54 changes: 41 additions & 13 deletions database/ticket_test.go
Original file line number Diff line number Diff line change
@@ -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.

Expand Down Expand Up @@ -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)
}
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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)
}
34 changes: 18 additions & 16 deletions webapi/cache.go
Original file line number Diff line number Diff line change
@@ -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.

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -104,20 +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)

// Prevent dividing by zero when pool has no voted tickets.
switch voted {
case 0:
if revoked == 0 {
c.data.RevokedProportion = 0
} else {
c.data.RevokedProportion = 1
}
default:
c.data.RevokedProportion = float32(revoked) / float32(voted)
total := voted + expired + missed

// Prevent dividing by zero when pool has no voted/expired/missed tickets.
if total == 0 {
c.data.ExpiredProportion = 0
c.data.MissedProportion = 0
} else {
c.data.ExpiredProportion = float32(expired) / float32(total)
c.data.MissedProportion = float32(missed) / float32(total)
}

return nil
Expand Down
7 changes: 7 additions & 0 deletions webapi/templates/homepage.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ <h4 class="alert-heading mb-3">
</div>
{{ end }}

{{ if not (eq .WebApiCfg.NetParams.Name "mainnet") }}
<div class="alert alert-warning mb-3">
This Voting Service Provider is running on testnet.
Visit <a href="https://decred.org/vsp/" class="alert-link" target="_blank" rel="noopener noreferrer">decred.org</a> to find a list of mainnet VSPs.
</div>
{{ end }}

<h1>VSP Overview</h1>

<p class="pt-1 pb-2">A Voting Service Provider (VSP) maintains a pool of always-online voting wallets,
Expand Down
17 changes: 10 additions & 7 deletions webapi/templates/vsp-stats.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,24 @@
</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">VSP Fee</div>
<div class="stat-value">{{ .WebApiCfg.VSPFee }}%</div>
<div class="stat-title">Missed tickets</div>
<div class="stat-value">
{{ comma .WebApiCache.Missed }}
<span class="text-muted">({{ float32ToPercent .WebApiCache.MissedProportion }})</span>
</div>
</div>

<div class="col-6 col-sm-4 col-lg-2 py-3">
<div class="stat-title">Network</div>
<div class="stat-value">{{ .WebApiCfg.NetParams.Name }}</div>
<div class="stat-title">VSP Fee</div>
<div class="stat-value">{{ .WebApiCfg.VSPFee }}%</div>
</div>

<div class="col-6 col-sm-4 col-lg-2 py-3">
Expand Down
4 changes: 2 additions & 2 deletions webapi/vspinfo.go
Original file line number Diff line number Diff line change
@@ -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.

Expand Down Expand Up @@ -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)
Expand Down
Loading