From a67de8a024d982217e2d4c8f6a06769ca3c42080 Mon Sep 17 00:00:00 2001 From: David Hill Date: Mon, 5 Oct 2020 09:30:42 +0000 Subject: [PATCH] rpc: optimize TicketInfo (#189) --- background/background.go | 61 +++++++++++++++++++++++++++++++++------- database/ticket.go | 1 + rpc/dcrwallet.go | 12 ++++---- 3 files changed, 58 insertions(+), 16 deletions(-) diff --git a/background/background.go b/background/background.go index 257c9da..2c55a0c 100644 --- a/background/background.go +++ b/background/background.go @@ -8,6 +8,7 @@ import ( "context" "encoding/json" "errors" + "strings" "sync" "time" @@ -106,6 +107,7 @@ func blockConnected() { } if tktTx.Confirmations >= requiredConfs { + ticket.PurchaseHeight = tktTx.BlockHeight ticket.Confirmed = true err = db.UpdateTicket(ticket) if err != nil { @@ -208,9 +210,19 @@ func blockConnected() { for agenda, choice := range ticket.VoteChoices { err = walletClient.SetVoteChoice(agenda, choice, ticket.Hash) if err != nil { - log.Errorf("%s: dcrwallet.SetVoteChoice error (wallet=%s, ticketHash=%s): %v", - funcName, walletClient.String(), ticket.Hash, err) - continue + if strings.Contains(err.Error(), "no agenda with ID") { + log.Warnf("%s: Removing invalid agenda from ticket vote choices (ticketHash=%s, agenda=%s)", + funcName, ticket.Hash, agenda) + delete(ticket.VoteChoices, agenda) + err = db.UpdateTicket(ticket) + if err != nil { + log.Errorf("%s: db.UpdateTicket error, failed to remove invalid agenda (ticketHash=%s): %v", + funcName, ticket.Hash, err) + } + } else { + log.Errorf("%s: dcrwallet.SetVoteChoice error (wallet=%s, ticketHash=%s): %v", + funcName, walletClient.String(), ticket.Hash, err) + } } } log.Infof("%s: Ticket added to voting wallet (wallet=%s, ticketHash=%s)", @@ -233,7 +245,10 @@ func blockConnected() { continue } - ticketInfo, err := walletClient.TicketInfo() + // Find the oldest block height from confirmed tickets. + oldestHeight := findOldestHeight(dbTickets) + + ticketInfo, err := walletClient.TicketInfo(oldestHeight) if err != nil { log.Errorf("%s: dcrwallet.TicketInfo failed (wallet=%s): %v", funcName, walletClient.String(), err) @@ -401,10 +416,13 @@ func checkWalletConsistency() { return } + // Find the oldest block height from confirmed tickets. + oldestHeight := findOldestHeight(votableTickets) + // Iterate over each wallet and add any missing tickets. for _, walletClient := range walletClients { // Get all tickets the wallet is aware of. - walletTickets, err := walletClient.TicketInfo() + walletTickets, err := walletClient.TicketInfo(oldestHeight) if err != nil { log.Errorf("%s: dcrwallet.TicketInfo failed (wallet=%s): %v", funcName, walletClient.String(), err) @@ -462,7 +480,7 @@ func checkWalletConsistency() { for _, walletClient := range walletClients { // Get all tickets the wallet is aware of. - walletTickets, err := walletClient.TicketInfo() + walletTickets, err := walletClient.TicketInfo(oldestHeight) if err != nil { log.Errorf("%s: dcrwallet.TicketInfo failed (wallet=%s): %v", funcName, walletClient.String(), err) @@ -500,12 +518,35 @@ func checkWalletConsistency() { // choice. err = walletClient.SetVoteChoice(dbAgenda, dbChoice, dbTicket.Hash) if err != nil { - log.Errorf("%s: dcrwallet.SetVoteChoice error (wallet=%s, ticketHash=%s): %v", - funcName, walletClient.String(), dbTicket.Hash, err) - continue + if strings.Contains(err.Error(), "no agenda with ID") { + log.Warnf("%s: Removing invalid agenda from ticket vote choices (ticketHash=%s, agenda=%s)", + funcName, dbTicket.Hash, dbAgenda) + delete(dbTicket.VoteChoices, dbAgenda) + err = db.UpdateTicket(dbTicket) + if err != nil { + log.Errorf("%s: db.UpdateTicket error, failed to remove invalid agenda (ticketHash=%s): %v", + funcName, dbTicket.Hash, err) + } + } else { + log.Errorf("%s: dcrwallet.SetVoteChoice error (wallet=%s, ticketHash=%s): %v", + funcName, walletClient.String(), dbTicket.Hash, err) + } } } } - } } + +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 +} diff --git a/database/ticket.go b/database/ticket.go index dd64aa7..242d099 100644 --- a/database/ticket.go +++ b/database/ticket.go @@ -43,6 +43,7 @@ const ( // deliberately kept short because they are duplicated many times in the db. type Ticket struct { Hash string `json:"hsh"` + PurchaseHeight int64 `json:"phgt"` CommitmentAddress string `json:"cmtaddr"` FeeAddressIndex uint32 `json:"faddridx"` FeeAddress string `json:"faddr"` diff --git a/rpc/dcrwallet.go b/rpc/dcrwallet.go index 007edf6..44d6e34 100644 --- a/rpc/dcrwallet.go +++ b/rpc/dcrwallet.go @@ -181,22 +181,22 @@ func (c *WalletRPC) SetVoteChoice(agenda, choice, ticketHash string) error { return c.Call(c.ctx, "setvotechoice", nil, agenda, choice, ticketHash) } -// GetBestBlockHeight uses getbestblock RPC to query the height of the best +// GetBestBlockHeight uses getblockcount RPC to query the height of the best // block known by the dcrwallet instance. func (c *WalletRPC) GetBestBlockHeight() (int64, error) { - var block dcrdtypes.GetBestBlockResult - err := c.Call(c.ctx, "getbestblock", &block) + var height int64 + err := c.Call(c.ctx, "getblockcount", &height) if err != nil { return 0, err } - return block.Height, nil + return height, nil } // TicketInfo uses ticketinfo RPC to retrieve a detailed list of all tickets // known by this dcrwallet instance. -func (c *WalletRPC) TicketInfo() (map[string]*wallettypes.TicketInfoResult, error) { +func (c *WalletRPC) TicketInfo(startHeight int64) (map[string]*wallettypes.TicketInfoResult, error) { var result []*wallettypes.TicketInfoResult - err := c.Call(c.ctx, "ticketinfo", &result) + err := c.Call(c.ctx, "ticketinfo", &result, startHeight) if err != nil { return nil, err }