database: Add TicketList struct.

This enables a func to find the earliest purchase height to be reused in
multiple packages.
This commit is contained in:
jholdstock 2023-08-18 09:45:15 +01:00 committed by Jamie Holdstock
parent 8b50fe619a
commit bca2a32a30
2 changed files with 30 additions and 25 deletions

View File

@ -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.
@ -239,7 +239,7 @@ func blockConnected(dcrdRPC rpc.DcrdConnect, walletRPC rpc.WalletConnect, db *da
}
// Find the oldest block height from confirmed tickets.
oldestHeight := findOldestHeight(votableTickets)
oldestHeight := votableTickets.EarliestPurchaseHeight()
ticketInfo, err := walletClient.TicketInfo(oldestHeight)
if err != nil {
@ -319,7 +319,7 @@ func checkWalletConsistency(dcrdRPC rpc.DcrdConnect, walletRPC rpc.WalletConnect
}
// Find the oldest block height from confirmed tickets.
oldestHeight := findOldestHeight(votableTickets)
oldestHeight := votableTickets.EarliestPurchaseHeight()
// Iterate over each wallet and add any missing tickets.
for _, walletClient := range walletClients {
@ -440,17 +440,3 @@ func checkWalletConsistency(dcrdRPC rpc.DcrdConnect, walletRPC rpc.WalletConnect
}
}
}
func findOldestHeight(tickets []database.Ticket) int64 {
var oldestHeight int64
for _, ticket := range tickets {
// skip unconfirmed tickets
if ticket.PurchaseHeight == 0 {
continue
}
if oldestHeight == 0 || oldestHeight > ticket.PurchaseHeight {
oldestHeight = ticket.PurchaseHeight
}
}
return oldestHeight
}

View File

@ -91,6 +91,25 @@ type Ticket struct {
Outcome TicketOutcome
}
type TicketList []Ticket
// EarliestPurchaseHeight returns the lowest non-zero purchase height in the
// list of tickets. Zero will be returned if the list is empty, or if every
// ticket in the list has zero purchase height.
func (t TicketList) EarliestPurchaseHeight() int64 {
var oldestHeight int64
for _, ticket := range t {
// Skip unconfirmed tickets.
if ticket.PurchaseHeight == 0 {
continue
}
if oldestHeight == 0 || oldestHeight > ticket.PurchaseHeight {
oldestHeight = ticket.PurchaseHeight
}
}
return oldestHeight
}
func (t *Ticket) FeeExpired() bool {
now := time.Now()
return now.After(time.Unix(t.FeeExpiration, 0))
@ -315,7 +334,7 @@ func (vdb *VspDatabase) CountTickets() (int64, int64, int64, error) {
}
// GetUnconfirmedTickets returns tickets which are not yet confirmed.
func (vdb *VspDatabase) GetUnconfirmedTickets() ([]Ticket, error) {
func (vdb *VspDatabase) GetUnconfirmedTickets() (TicketList, error) {
vdb.ticketsMtx.RLock()
defer vdb.ticketsMtx.RUnlock()
@ -326,7 +345,7 @@ func (vdb *VspDatabase) GetUnconfirmedTickets() ([]Ticket, error) {
// GetPendingFees returns tickets which are confirmed and have a fee tx which is
// not yet broadcast.
func (vdb *VspDatabase) GetPendingFees() ([]Ticket, error) {
func (vdb *VspDatabase) GetPendingFees() (TicketList, error) {
vdb.ticketsMtx.RLock()
defer vdb.ticketsMtx.RUnlock()
@ -337,7 +356,7 @@ func (vdb *VspDatabase) GetPendingFees() ([]Ticket, error) {
// GetUnconfirmedFees returns tickets with a fee tx that is broadcast but not
// confirmed yet.
func (vdb *VspDatabase) GetUnconfirmedFees() ([]Ticket, error) {
func (vdb *VspDatabase) GetUnconfirmedFees() (TicketList, error) {
vdb.ticketsMtx.RLock()
defer vdb.ticketsMtx.RUnlock()
@ -348,14 +367,14 @@ func (vdb *VspDatabase) GetUnconfirmedFees() ([]Ticket, error) {
// GetVotableTickets returns tickets with a confirmed fee tx and no outcome (ie.
// not expired/voted/missed).
func (vdb *VspDatabase) GetVotableTickets() ([]Ticket, error) {
func (vdb *VspDatabase) GetVotableTickets() (TicketList, error) {
return vdb.filterTickets(func(t *bolt.Bucket) bool {
return FeeStatus(t.Get(feeTxStatusK)) == FeeConfirmed && TicketOutcome(t.Get(outcomeK)) == ""
})
}
// GetVotedTickets returns tickets with a confirmed fee tx and outcome == voted.
func (vdb *VspDatabase) GetVotedTickets() ([]Ticket, error) {
func (vdb *VspDatabase) GetVotedTickets() (TicketList, error) {
return vdb.filterTickets(func(t *bolt.Bucket) bool {
return FeeStatus(t.Get(feeTxStatusK)) == FeeConfirmed && TicketOutcome(t.Get(outcomeK)) == Voted
})
@ -363,7 +382,7 @@ func (vdb *VspDatabase) GetVotedTickets() ([]Ticket, error) {
// GetMissingPurchaseHeight returns tickets which are confirmed but do not have
// a purchase height.
func (vdb *VspDatabase) GetMissingPurchaseHeight() ([]Ticket, error) {
func (vdb *VspDatabase) GetMissingPurchaseHeight() (TicketList, error) {
return vdb.filterTickets(func(t *bolt.Bucket) bool {
return bytesToBool(t.Get(confirmedK)) && bytesToInt64(t.Get(purchaseHeightK)) == 0
})
@ -373,8 +392,8 @@ func (vdb *VspDatabase) GetMissingPurchaseHeight() ([]Ticket, error) {
// database which match the filter.
//
// This function must be called with the lock held.
func (vdb *VspDatabase) filterTickets(filter func(*bolt.Bucket) bool) ([]Ticket, error) {
var tickets []Ticket
func (vdb *VspDatabase) filterTickets(filter func(*bolt.Bucket) bool) (TicketList, error) {
var tickets TicketList
err := vdb.db.View(func(tx *bolt.Tx) error {
ticketBkt := tx.Bucket(vspBktK).Bucket(ticketBktK)