Wallet consistency checks & setting ticket outcome

This commit is contained in:
jholdstock 2020-07-27 17:04:58 +01:00 committed by David Hill
parent 8c3cab7942
commit 9d503e67ae
15 changed files with 367 additions and 75 deletions

View File

@ -27,6 +27,8 @@ var (
type NotificationHandler struct{} type NotificationHandler struct{}
const ( const (
// consistencyInterval is the time period between wallet consistency checks.
consistencyInterval = 30 * time.Minute
// requiredConfs is the number of confirmations required to consider a // requiredConfs is the number of confirmations required to consider a
// ticket purchase or a fee transaction to be final. // ticket purchase or a fee transaction to be final.
requiredConfs = 6 requiredConfs = 6
@ -69,7 +71,7 @@ func blockConnected() {
return return
} }
// Step 1/3: Update the database with any tickets which now have 6+ // Step 1/4: Update the database with any tickets which now have 6+
// confirmations. // confirmations.
unconfirmed, err := db.GetUnconfirmedTickets() unconfirmed, err := db.GetUnconfirmedTickets()
@ -106,15 +108,16 @@ func blockConnected() {
ticket.Confirmed = true ticket.Confirmed = true
err = db.UpdateTicket(ticket) err = db.UpdateTicket(ticket)
if err != nil { if err != nil {
log.Errorf("%s: db.UpdateTicket error (ticketHash=%s): %v", funcName, ticket.Hash, err) log.Errorf("%s: db.UpdateTicket error, failed to set ticket as confirmed (ticketHash=%s): %v",
funcName, ticket.Hash, err)
continue continue
} }
log.Debugf("%s: Ticket confirmed (ticketHash=%s)", funcName, ticket.Hash) log.Infof("%s: Ticket confirmed (ticketHash=%s)", funcName, ticket.Hash)
} }
} }
// Step 2/3: Broadcast fee tx for tickets which are confirmed. // Step 2/4: Broadcast fee tx for tickets which are confirmed.
pending, err := db.GetPendingFees() pending, err := db.GetPendingFees()
if err != nil { if err != nil {
@ -128,36 +131,28 @@ func blockConnected() {
funcName, ticket.Hash, err) funcName, ticket.Hash, err)
ticket.FeeTxStatus = database.FeeError ticket.FeeTxStatus = database.FeeError
} else { } else {
log.Debugf("%s: Fee tx broadcast for ticket (ticketHash=%s, feeHash=%s)", log.Infof("%s: Fee tx broadcast for ticket (ticketHash=%s, feeHash=%s)",
funcName, ticket.Hash, ticket.FeeTxHash) funcName, ticket.Hash, ticket.FeeTxHash)
ticket.FeeTxStatus = database.FeeBroadcast ticket.FeeTxStatus = database.FeeBroadcast
} }
err = db.UpdateTicket(ticket) err = db.UpdateTicket(ticket)
if err != nil { if err != nil {
log.Errorf("%s: db.UpdateTicket error (ticketHash=%s): %v", funcName, ticket.Hash, err) log.Errorf("%s: db.UpdateTicket error, failed to set fee tx as broadcast (ticketHash=%s): %v",
funcName, ticket.Hash, err)
} }
} }
// Step 3/3: Add tickets with confirmed fees to voting wallets. // Step 3/4: Add tickets with confirmed fees to voting wallets.
unconfirmedFees, err := db.GetUnconfirmedFees() unconfirmedFees, err := db.GetUnconfirmedFees()
if err != nil { if err != nil {
log.Errorf("%s: db.GetUnconfirmedFees error: %v", funcName, err) log.Errorf("%s: db.GetUnconfirmedFees error: %v", funcName, err)
// If this fails, there is nothing more we can do. Return.
return
}
// If there are no confirmed fees, there is nothing more to do. Return.
if len(unconfirmedFees) == 0 {
return
} }
walletClients, failedConnections := walletRPC.Clients(ctx, netParams) walletClients, failedConnections := walletRPC.Clients(ctx, netParams)
if len(walletClients) == 0 { if len(walletClients) == 0 {
// If no wallet clients, there is nothing more we can do. Return.
log.Errorf("%s: Could not connect to any wallets", funcName) log.Errorf("%s: Could not connect to any wallets", funcName)
return
} }
if len(failedConnections) > 0 { if len(failedConnections) > 0 {
log.Errorf("%s: Failed to connect to %d wallet(s), proceeding with only %d", log.Errorf("%s: Failed to connect to %d wallet(s), proceeding with only %d",
@ -178,10 +173,11 @@ func blockConnected() {
ticket.FeeTxStatus = database.FeeConfirmed ticket.FeeTxStatus = database.FeeConfirmed
err = db.UpdateTicket(ticket) err = db.UpdateTicket(ticket)
if err != nil { if err != nil {
log.Errorf("%s: db.UpdateTicket error (ticketHash=%s): %v", funcName, ticket.Hash, err) log.Errorf("%s: db.UpdateTicket error, failed to set fee tx as confirmed (ticketHash=%s): %v",
return funcName, ticket.Hash, err)
continue
} }
log.Debugf("%s: Fee tx confirmed (ticketHash=%s)", funcName, ticket.Hash) log.Infof("%s: Fee tx confirmed (ticketHash=%s)", funcName, ticket.Hash)
// Add ticket to the voting wallet. // Add ticket to the voting wallet.
@ -208,11 +204,63 @@ func blockConnected() {
continue continue
} }
} }
log.Debugf("%s: Ticket added to voting wallet (wallet=%s, ticketHash=%s)", log.Infof("%s: Ticket added to voting wallet (wallet=%s, ticketHash=%s)",
funcName, walletClient.String(), ticket.Hash) funcName, walletClient.String(), ticket.Hash)
} }
} }
} }
// Step 4/4: Set ticket outcome in database if any tickets are voted/revoked.
// Ticket status needs to be checked on every wallet. This is because only
// one of the voting wallets will actually succeed in voting/revoking
// tickets (the others will get errors like "tx already exists"). Only the
// successful wallet will have the most up-to-date ticket status, the others
// will be outdated.
for _, walletClient := range walletClients {
dbTickets, err := db.GetVotableTickets()
if err != nil {
log.Errorf("%s: db.GetVotableTickets failed: %v", funcName, err)
continue
}
ticketInfo, err := walletClient.TicketInfo()
if err != nil {
log.Errorf("%s: dcrwallet.TicketInfo failed (wallet=%s): %v",
funcName, walletClient.String(), err)
continue
}
for _, dbTicket := range dbTickets {
tInfo, ok := ticketInfo[dbTicket.Hash]
if !ok {
log.Warnf("%s: TicketInfo response did not include expected ticket (wallet=%s, ticketHash=%s)",
funcName, walletClient.String(), dbTicket.Hash)
continue
}
switch tInfo.Status {
case "revoked":
dbTicket.Outcome = database.Revoked
case "voted":
dbTicket.Outcome = database.Voted
default:
// Skip to next ticket.
continue
}
err = db.UpdateTicket(dbTicket)
if err != nil {
log.Errorf("%s: db.UpdateTicket error, failed to set ticket outcome (ticketHash=%s): %v",
funcName, dbTicket.Hash, err)
continue
}
log.Infof("%s: Ticket no longer votable: outcome=%s, ticketHash=%s", funcName,
dbTicket.Outcome, dbTicket.Hash)
}
}
} }
func (n *NotificationHandler) Close() error { func (n *NotificationHandler) Close() error {
@ -260,6 +308,24 @@ func Start(c context.Context, wg *sync.WaitGroup, vdb *database.VspDatabase, drp
// while vspd was shut down. // while vspd was shut down.
blockConnected() blockConnected()
// Run voting wallet consistency check now to ensure all wallets are up to
// date.
checkWalletConsistency()
// Run voting wallet consistency check periodically.
go func() {
ticker := time.NewTicker(consistencyInterval)
for {
select {
case <-ctx.Done():
ticker.Stop()
return
case <-ticker.C:
checkWalletConsistency()
}
}
}()
// Loop forever attempting to create a connection to the dcrd server for // Loop forever attempting to create a connection to the dcrd server for
// notifications. // notifications.
go func() { go func() {
@ -280,3 +346,145 @@ func Start(c context.Context, wg *sync.WaitGroup, vdb *database.VspDatabase, drp
} }
}() }()
} }
// checkWalletConsistency will retrieve all votable tickets from the database
// and ensure they are all added to voting wallets with the correct vote
// choices.
func checkWalletConsistency() {
funcName := "checkWalletConsistency"
log.Info("Checking voting wallet consistency")
dcrdClient, err := dcrdRPC.Client(ctx, netParams)
if err != nil {
log.Errorf("%s: %v", funcName, err)
return
}
walletClients, failedConnections := walletRPC.Clients(ctx, netParams)
if len(walletClients) == 0 {
log.Errorf("%s: Could not connect to any wallets", funcName)
return
}
if len(failedConnections) > 0 {
log.Errorf("%s: Failed to connect to %d wallet(s), proceeding with only %d",
funcName, len(failedConnections), len(walletClients))
}
// Step 1/2: Check all tickets are added to all voting wallets.
votableTickets, err := db.GetVotableTickets()
if err != nil {
log.Errorf("%s: db.GetVotableTickets failed: %v", funcName, err)
return
}
// 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()
if err != nil {
log.Errorf("%s: dcrwallet.TicketInfo failed (wallet=%s): %v",
funcName, walletClient.String(), err)
continue
}
// If missing tickets are added, set a flag and keep track of the
// earliest purchase height.
var added bool
var minHeight int64
for _, dbTicket := range votableTickets {
// If wallet already knows this ticket, skip to the next one.
_, exists := walletTickets[dbTicket.Hash]
if exists {
continue
}
log.Debugf("%s: Adding missing ticket (wallet=%s, ticketHash=%s)",
funcName, walletClient.String(), dbTicket.Hash)
rawTicket, err := dcrdClient.GetRawTransaction(dbTicket.Hash)
if err != nil {
log.Errorf("%s: dcrd.GetRawTransaction error: %v", funcName, err)
continue
}
err = walletClient.AddTicketForVoting(dbTicket.VotingWIF, rawTicket.BlockHash, rawTicket.Hex)
if err != nil {
log.Errorf("%s: dcrwallet.AddTicketForVoting error (wallet=%s, ticketHash=%s): %v",
funcName, walletClient.String(), dbTicket.Hash, err)
continue
}
added = true
if minHeight == 0 || minHeight > rawTicket.BlockHeight {
minHeight = rawTicket.BlockHeight
}
}
// Perform a rescan if any missing tickets were added to this wallet.
if added {
log.Infof("%s: Performing a rescan on wallet %s (fromHeight=%d)",
funcName, walletClient.String(), minHeight)
err = walletClient.RescanFrom(minHeight)
if err != nil {
log.Errorf("%s: dcrwallet.RescanFrom failed (wallet=%s): %v",
funcName, walletClient.String(), err)
continue
}
}
}
// Step 2/2: Ensure vote choices are set correctly for all tickets on
// all wallets.
for _, walletClient := range walletClients {
// Get all tickets the wallet is aware of.
walletTickets, err := walletClient.TicketInfo()
if err != nil {
log.Errorf("%s: dcrwallet.TicketInfo failed (wallet=%s): %v",
funcName, walletClient.String(), err)
continue
}
for _, dbTicket := range votableTickets {
// All tickets should be added to all wallets at this point, so log
// a warning if any are still missing.
walletTicket, exists := walletTickets[dbTicket.Hash]
if !exists {
log.Warnf("%s: Ticket missing from voting wallet (wallet=%s, ticketHash=%s)",
funcName, walletClient.String, dbTicket.Hash)
continue
}
// Check if vote choices match
for dbAgenda, dbChoice := range dbTicket.VoteChoices {
match := false
for _, walletChoice := range walletTicket.Choices {
if walletChoice.AgendaID == dbAgenda && walletChoice.ChoiceID == dbChoice {
match = true
}
}
// Skip to next agenda if db and wallet are matching.
if match {
continue
}
log.Debugf("%s: Updating incorrect vote choices (wallet=%s, agenda=%s, ticketHash=%s)",
funcName, walletClient.String(), dbAgenda, dbTicket.Hash)
// If db and wallet are not matching, update wallet with correct
// 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
}
}
}
}
}

View File

@ -25,6 +25,16 @@ const (
FeeError FeeStatus = "error" FeeError FeeStatus = "error"
) )
// TicketOutcome describes the reason a ticket is no longer votable.
type TicketOutcome string
const (
// Ticket has been revoked, either because it was missed or it expired.
Revoked TicketOutcome = "revoked"
// Ticket has already voted.
Voted TicketOutcome = "voted"
)
// Ticket is serialized to json and stored in bbolt db. The json keys are // Ticket is serialized to json and stored in bbolt db. The json keys are
// deliberately kept short because they are duplicated many times in the db. // deliberately kept short because they are duplicated many times in the db.
type Ticket struct { type Ticket struct {
@ -51,6 +61,10 @@ type Ticket struct {
// FeeTxStatus indicates the current state of the fee transaction. // FeeTxStatus indicates the current state of the fee transaction.
FeeTxStatus FeeStatus `json:"fsts"` FeeTxStatus FeeStatus `json:"fsts"`
// Outcome is set once a ticket is either voted or revoked. An empty outcome
// indicates that a ticket is still votable.
Outcome TicketOutcome `json:"otcme"`
} }
func (t *Ticket) FeeExpired() bool { func (t *Ticket) FeeExpired() bool {
@ -170,16 +184,15 @@ func (vdb *VspDatabase) GetTicketByHash(ticketHash string) (Ticket, bool, error)
return ticket, found, err return ticket, found, err
} }
func (vdb *VspDatabase) CountTickets() (int, int, error) { func (vdb *VspDatabase) CountTickets() (int64, int64, int64, error) {
defer vdb.ticketsMtx.RUnlock() defer vdb.ticketsMtx.RUnlock()
vdb.ticketsMtx.RLock() vdb.ticketsMtx.RLock()
var total, feePaid int var voting, voted, revoked int64
err := vdb.db.View(func(tx *bolt.Tx) error { err := vdb.db.View(func(tx *bolt.Tx) error {
ticketBkt := tx.Bucket(vspBktK).Bucket(ticketBktK) ticketBkt := tx.Bucket(vspBktK).Bucket(ticketBktK)
return ticketBkt.ForEach(func(k, v []byte) error { return ticketBkt.ForEach(func(k, v []byte) error {
total++
var ticket Ticket var ticket Ticket
err := json.Unmarshal(v, &ticket) err := json.Unmarshal(v, &ticket)
if err != nil { if err != nil {
@ -187,14 +200,21 @@ func (vdb *VspDatabase) CountTickets() (int, int, error) {
} }
if ticket.FeeTxStatus == FeeConfirmed { if ticket.FeeTxStatus == FeeConfirmed {
feePaid++ switch ticket.Outcome {
case Voted:
voted++
case Revoked:
revoked++
default:
voting++
}
} }
return nil return nil
}) })
}) })
return total, feePaid, err return voting, voted, revoked, err
} }
// GetUnconfirmedTickets returns tickets which are not yet confirmed. // GetUnconfirmedTickets returns tickets which are not yet confirmed.
@ -229,6 +249,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) {
return vdb.filterTickets(func(t Ticket) bool {
return t.FeeTxStatus == FeeConfirmed && t.Outcome == ""
})
}
// filterTickets accepts a filter function and returns all tickets from the // filterTickets accepts a filter function and returns all tickets from the
// database which match the filter. // database which match the filter.
// //

View File

@ -50,7 +50,10 @@ when a VSP is closed will result in an error.
"feepercentage":3.0, "feepercentage":3.0,
"vspclosed":false, "vspclosed":false,
"network":"testnet3", "network":"testnet3",
"vspdversion":"1.0.0-pre" "vspdversion":"1.0.0-pre",
"voting":10,
"voted":25,
"revoked":3
} }
``` ```

6
go.mod
View File

@ -3,9 +3,9 @@ module github.com/decred/vspd
go 1.13 go 1.13
require ( require (
decred.org/dcrwallet v1.2.3-0.20200519180100-f1aa4c354e05 decred.org/dcrwallet v1.2.3-0.20200727154839-096e3bee25f2
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200616182840-3baf1f590cb1 github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200616182840-3baf1f590cb1
github.com/decred/dcrd/blockchain/v3 v3.0.0-20200311044114-143c1884e4c8 github.com/decred/dcrd/blockchain/v3 v3.0.0-20200608124004-b2f67c2dc475
github.com/decred/dcrd/chaincfg/chainhash v1.0.2 github.com/decred/dcrd/chaincfg/chainhash v1.0.2
github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200616182840-3baf1f590cb1 github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200616182840-3baf1f590cb1
github.com/decred/dcrd/dcrec v1.0.0 github.com/decred/dcrd/dcrec v1.0.0
@ -17,7 +17,7 @@ require (
github.com/decred/slog v1.0.0 github.com/decred/slog v1.0.0
github.com/gin-gonic/gin v1.6.3 github.com/gin-gonic/gin v1.6.3
github.com/gorilla/sessions v1.2.0 github.com/gorilla/sessions v1.2.0
github.com/jessevdk/go-flags v1.4.0 github.com/jessevdk/go-flags v1.4.1-0.20200711081900-c17162fe8fd7
github.com/jrick/bitset v1.0.0 github.com/jrick/bitset v1.0.0
github.com/jrick/logrotate v1.0.0 github.com/jrick/logrotate v1.0.0
github.com/jrick/wsrpc/v2 v2.3.3 github.com/jrick/wsrpc/v2 v2.3.3

41
go.sum
View File

@ -1,7 +1,8 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
decred.org/cspp v0.3.0 h1:2AkSsWzA7HIMZImfw0gT82Gdp8OXIM4NsBn7vna22uE=
decred.org/cspp v0.3.0/go.mod h1:UygjYilC94dER3BEU65Zzyoqy9ngJfWCD2rdJqvUs2A= decred.org/cspp v0.3.0/go.mod h1:UygjYilC94dER3BEU65Zzyoqy9ngJfWCD2rdJqvUs2A=
decred.org/dcrwallet v1.2.3-0.20200519180100-f1aa4c354e05 h1:xongFmW2UgEOGu4zQ4VcQFduExKVBa+dC4aLRQLCCnQ= decred.org/dcrwallet v1.2.3-0.20200727154839-096e3bee25f2 h1:iwsnZPdoo1ownS4T2asE3rZAwkDwfufQ+LanREPsFBY=
decred.org/dcrwallet v1.2.3-0.20200519180100-f1aa4c354e05/go.mod h1:V6pzOHJuuWZaUPUZZL2kiyx9Co3lVD0DRqDXJTWA+3c= decred.org/dcrwallet v1.2.3-0.20200727154839-096e3bee25f2/go.mod h1:3cZSQAgJzWXeSKMxWuyAPjxcheBYOB5fwTzD5exhvgs=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI=
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
@ -20,16 +21,18 @@ github.com/decred/base58 v1.0.1/go.mod h1:H2ENcsJjye1G7CbRa67kV9OFaui0LGr56ntKKo
github.com/decred/base58 v1.0.2/go.mod h1:pXP9cXCfM2sFLb2viz2FNIdeMWmZDBKG3ZBYbiSM78E= github.com/decred/base58 v1.0.2/go.mod h1:pXP9cXCfM2sFLb2viz2FNIdeMWmZDBKG3ZBYbiSM78E=
github.com/decred/base58 v1.0.3 h1:KGZuh8d1WEMIrK0leQRM47W85KqCAdl2N+uagbctdDI= github.com/decred/base58 v1.0.3 h1:KGZuh8d1WEMIrK0leQRM47W85KqCAdl2N+uagbctdDI=
github.com/decred/base58 v1.0.3/go.mod h1:pXP9cXCfM2sFLb2viz2FNIdeMWmZDBKG3ZBYbiSM78E= github.com/decred/base58 v1.0.3/go.mod h1:pXP9cXCfM2sFLb2viz2FNIdeMWmZDBKG3ZBYbiSM78E=
github.com/decred/dcrd/addrmgr v1.1.0 h1:VQkn1qmafZypfN2u7yi7J/girwz4ZDicquo7JzsoxdQ=
github.com/decred/dcrd/addrmgr v1.1.0/go.mod h1:exghL+0+QeVvO4MXezWJ1C2tcpBn3ngfuP6S1R+adB8= github.com/decred/dcrd/addrmgr v1.1.0/go.mod h1:exghL+0+QeVvO4MXezWJ1C2tcpBn3ngfuP6S1R+adB8=
github.com/decred/dcrd/blockchain/stake/v2 v2.0.2/go.mod h1:o2TT/l/YFdrt15waUdlZ3g90zfSwlA0WgQqHV9UGJF4= github.com/decred/dcrd/blockchain/stake/v2 v2.0.2/go.mod h1:o2TT/l/YFdrt15waUdlZ3g90zfSwlA0WgQqHV9UGJF4=
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:aDL94kcVJfaaJP+acWUJrlK7g7xEOqTSiFe6bSN3yRQ= github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:aDL94kcVJfaaJP+acWUJrlK7g7xEOqTSiFe6bSN3yRQ=
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:4zE60yDWlfCDtmqnyP5o1k1K0oyhNn3Tvqo6F93/+RU= github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200608124004-b2f67c2dc475/go.mod h1:4zE60yDWlfCDtmqnyP5o1k1K0oyhNn3Tvqo6F93/+RU=
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200616182840-3baf1f590cb1 h1:7fg/+PpoT/ecAnA/uvccRQQk7+JZ7gSOgcIHHqqZtIM= github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200616182840-3baf1f590cb1 h1:7fg/+PpoT/ecAnA/uvccRQQk7+JZ7gSOgcIHHqqZtIM=
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200616182840-3baf1f590cb1/go.mod h1:1e94ovQXEcOjIn7BRzkXpswA7pWQXqB2el5l0w0Srf8= github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200616182840-3baf1f590cb1/go.mod h1:1e94ovQXEcOjIn7BRzkXpswA7pWQXqB2el5l0w0Srf8=
github.com/decred/dcrd/blockchain/standalone v1.1.0 h1:yclvVGEY09Gf8A4GSAo+NCtL1dW2TYJ4OKp4+g0ICI0= github.com/decred/dcrd/blockchain/standalone v1.1.0 h1:yclvVGEY09Gf8A4GSAo+NCtL1dW2TYJ4OKp4+g0ICI0=
github.com/decred/dcrd/blockchain/standalone v1.1.0/go.mod h1:6K8ZgzlWM1Kz2TwXbrtiAvfvIwfAmlzrtpA7CVPCUPE= github.com/decred/dcrd/blockchain/standalone v1.1.0/go.mod h1:6K8ZgzlWM1Kz2TwXbrtiAvfvIwfAmlzrtpA7CVPCUPE=
github.com/decred/dcrd/blockchain/v3 v3.0.0-20200311044114-143c1884e4c8 h1:I3psccIeKb9eld+TNd69SgUOy6940uflH/J3aLM2ctU= github.com/decred/dcrd/blockchain/v3 v3.0.0-20200608124004-b2f67c2dc475 h1:4VxMHgkwn9YTglLQyp7fvuP2/TWqBrvonjaOJComPIs=
github.com/decred/dcrd/blockchain/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:R9rIXU8kEJVC9Z4LAlh9bo9hiT3a+ihys3mCrz4PVao= github.com/decred/dcrd/blockchain/v3 v3.0.0-20200608124004-b2f67c2dc475/go.mod h1:Jh6HF5q9YQHjV+0PHcwCWL7NCYj0LFyjnKlzPFO8/Zc=
github.com/decred/dcrd/certgen v1.1.0 h1:lAPE2OLYdYeXDCaji/+KC53j7/s7wF7RVGeQbXK//XA=
github.com/decred/dcrd/certgen v1.1.0/go.mod h1:ivkPLChfjdAgFh7ZQOtl6kJRqVkfrCq67dlq3AbZBQE= github.com/decred/dcrd/certgen v1.1.0/go.mod h1:ivkPLChfjdAgFh7ZQOtl6kJRqVkfrCq67dlq3AbZBQE=
github.com/decred/dcrd/chaincfg/chainhash v1.0.2 h1:rt5Vlq/jM3ZawwiacWjPa+smINyLRN07EO0cNBV6DGU= github.com/decred/dcrd/chaincfg/chainhash v1.0.2 h1:rt5Vlq/jM3ZawwiacWjPa+smINyLRN07EO0cNBV6DGU=
github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60= github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60=
@ -39,10 +42,11 @@ github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200214194519-928737b3e580/go.mod h1:
github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200215015031-3283587e6add/go.mod h1:v4oyBPQ/ZstYCV7+B0y6HogFByW76xTjr+72fOm66Y8= github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200215015031-3283587e6add/go.mod h1:v4oyBPQ/ZstYCV7+B0y6HogFByW76xTjr+72fOm66Y8=
github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200215023918-6247af01d5e3/go.mod h1:v4oyBPQ/ZstYCV7+B0y6HogFByW76xTjr+72fOm66Y8= github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200215023918-6247af01d5e3/go.mod h1:v4oyBPQ/ZstYCV7+B0y6HogFByW76xTjr+72fOm66Y8=
github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:v4oyBPQ/ZstYCV7+B0y6HogFByW76xTjr+72fOm66Y8= github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:v4oyBPQ/ZstYCV7+B0y6HogFByW76xTjr+72fOm66Y8=
github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:v4oyBPQ/ZstYCV7+B0y6HogFByW76xTjr+72fOm66Y8= github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200608124004-b2f67c2dc475/go.mod h1:OHbKBa6UZZOXCU1Y8f9Ta3O+GShto7nB1O0nuEutKq4=
github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200616182840-3baf1f590cb1 h1:IfBqlTemNgX3Yax/lBBQSxDiVin1IitIXY6zegtXMps= github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200616182840-3baf1f590cb1 h1:IfBqlTemNgX3Yax/lBBQSxDiVin1IitIXY6zegtXMps=
github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200616182840-3baf1f590cb1/go.mod h1:OHbKBa6UZZOXCU1Y8f9Ta3O+GShto7nB1O0nuEutKq4= github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200616182840-3baf1f590cb1/go.mod h1:OHbKBa6UZZOXCU1Y8f9Ta3O+GShto7nB1O0nuEutKq4=
github.com/decred/dcrd/connmgr/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:mvIMJsrOEngogmVrq+tdbPIZchHVgGnVBZeNwj1cW6E= github.com/decred/dcrd/connmgr/v3 v3.0.0-20200608124004-b2f67c2dc475 h1:jOEkyTB8KqKrYRNS4PZUs13tf7UbiWspsNYix+eKbis=
github.com/decred/dcrd/connmgr/v3 v3.0.0-20200608124004-b2f67c2dc475/go.mod h1:UWFfZ1MbPzBgNA3bRCkF0woOlXkv1EIFEkwD+mdUW5Y=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/crypto/ripemd160 v1.0.0 h1:MciTnR4NfBqDFRFjFkrn8WPLP4Vo7t6ww6ghfn6wcXQ= github.com/decred/dcrd/crypto/ripemd160 v1.0.0 h1:MciTnR4NfBqDFRFjFkrn8WPLP4Vo7t6ww6ghfn6wcXQ=
@ -59,8 +63,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0-20200214194519-928737b3e580/go.
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0-20200215015031-3283587e6add/go.mod h1:Ej0/gOv8NpFfaczyXGndw7eRMJFVhmY2faSeyxztSUw= github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0-20200215015031-3283587e6add/go.mod h1:Ej0/gOv8NpFfaczyXGndw7eRMJFVhmY2faSeyxztSUw=
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0-20200215023918-6247af01d5e3/go.mod h1:Ej0/gOv8NpFfaczyXGndw7eRMJFVhmY2faSeyxztSUw= github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0-20200215023918-6247af01d5e3/go.mod h1:Ej0/gOv8NpFfaczyXGndw7eRMJFVhmY2faSeyxztSUw=
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:Ej0/gOv8NpFfaczyXGndw7eRMJFVhmY2faSeyxztSUw= github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:Ej0/gOv8NpFfaczyXGndw7eRMJFVhmY2faSeyxztSUw=
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0-20200421213827-b60c60ffe98b h1:5/9ZtxOJ2scrLjNte19jKnN2n43WNj01+RGPp/j8L6Q= github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0-20200608124004-b2f67c2dc475 h1:N4p2A5SPMXm97Vc8LazxchwudeE5GDs6S0WxwcrRmog=
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0-20200421213827-b60c60ffe98b/go.mod h1:J70FGZSbzsjecRTiTzER+3f1KZLNaXkuv+yeFTKoxM8= github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0-20200608124004-b2f67c2dc475/go.mod h1:J70FGZSbzsjecRTiTzER+3f1KZLNaXkuv+yeFTKoxM8=
github.com/decred/dcrd/dcrjson/v3 v3.0.1 h1:b9cpplNJG+nutE2jS8K/BtSGIJihEQHhFjFAsvJF/iI= github.com/decred/dcrd/dcrjson/v3 v3.0.1 h1:b9cpplNJG+nutE2jS8K/BtSGIJihEQHhFjFAsvJF/iI=
github.com/decred/dcrd/dcrjson/v3 v3.0.1/go.mod h1:fnTHev/ABGp8IxFudDhjGi9ghLiXRff1qZz/wvq12Mg= github.com/decred/dcrd/dcrjson/v3 v3.0.1/go.mod h1:fnTHev/ABGp8IxFudDhjGi9ghLiXRff1qZz/wvq12Mg=
github.com/decred/dcrd/dcrutil/v2 v2.0.1 h1:aL+c7o7Q66HV1gIif+XkNYo9DeorN3l01Vns8mh0mqs= github.com/decred/dcrd/dcrutil/v2 v2.0.1 h1:aL+c7o7Q66HV1gIif+XkNYo9DeorN3l01Vns8mh0mqs=
@ -68,13 +72,13 @@ github.com/decred/dcrd/dcrutil/v2 v2.0.1/go.mod h1:JdEgF6eh0TTohPeiqDxqDSikTSvAc
github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200215015031-3283587e6add/go.mod h1:CibwaHcCfz1sedFseBYKt+1hSbqnWC4Oe95DM8dAOlA= github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200215015031-3283587e6add/go.mod h1:CibwaHcCfz1sedFseBYKt+1hSbqnWC4Oe95DM8dAOlA=
github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200215023918-6247af01d5e3/go.mod h1:48ZLpNNrRIYfqYxmvzMgOZrnTZUU3aTJveWtamCkOxo= github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200215023918-6247af01d5e3/go.mod h1:48ZLpNNrRIYfqYxmvzMgOZrnTZUU3aTJveWtamCkOxo=
github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:jFxEd2LWDLvrWlrIiyx9ZGTQjvoFHZ0OVfBdyIX7jSw= github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:jFxEd2LWDLvrWlrIiyx9ZGTQjvoFHZ0OVfBdyIX7jSw=
github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:/CDBC1SOXKrmihavgXviaTr6eVZSAWKQqEbRmacDxgg= github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200608124004-b2f67c2dc475/go.mod h1:WyoYp6FRgNAQL33CdcpvSnKcujH8wMzIRBSMCg64Egw=
github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200616182840-3baf1f590cb1 h1:uiMnZy+YBJt0ZzTXHk0TZgIPTlOGgrIYrRoFET0kUwg= github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200616182840-3baf1f590cb1 h1:uiMnZy+YBJt0ZzTXHk0TZgIPTlOGgrIYrRoFET0kUwg=
github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200616182840-3baf1f590cb1/go.mod h1:WyoYp6FRgNAQL33CdcpvSnKcujH8wMzIRBSMCg64Egw= github.com/decred/dcrd/dcrutil/v3 v3.0.0-20200616182840-3baf1f590cb1/go.mod h1:WyoYp6FRgNAQL33CdcpvSnKcujH8wMzIRBSMCg64Egw=
github.com/decred/dcrd/gcs/v2 v2.0.0/go.mod h1:3XjKcrtvB+r2ezhIsyNCLk6dRnXRJVyYmsd1P3SkU3o= github.com/decred/dcrd/gcs/v2 v2.0.0/go.mod h1:3XjKcrtvB+r2ezhIsyNCLk6dRnXRJVyYmsd1P3SkU3o=
github.com/decred/dcrd/gcs/v2 v2.0.2-0.20200312171759-0a8cc56a776e h1:tBOk2P8F9JyRUSp0iRTs4nYEBro1FKBDIbg/UualLWw= github.com/decred/dcrd/gcs/v2 v2.0.2-0.20200608124004-b2f67c2dc475 h1:5Qd0LWsOKVAmZyFRI/enaIsPpD8NsIPSxYwJ7uywMqo=
github.com/decred/dcrd/gcs/v2 v2.0.2-0.20200312171759-0a8cc56a776e/go.mod h1:JJGd1m0DrFgV4J2J8HKNB9YVkM06ewQHT6iINis39Z4= github.com/decred/dcrd/gcs/v2 v2.0.2-0.20200608124004-b2f67c2dc475/go.mod h1:JJGd1m0DrFgV4J2J8HKNB9YVkM06ewQHT6iINis39Z4=
github.com/decred/dcrd/hdkeychain/v3 v3.0.0-20200421213827-b60c60ffe98b/go.mod h1:qKN0WzeSEEZ4fUBsTwKzOPkLP7GqSM6jBUm5Auq9mrM= github.com/decred/dcrd/hdkeychain/v3 v3.0.0-20200608124004-b2f67c2dc475/go.mod h1:OQQKlU+hzvOHVZfUJq1iqQ5IfyycGaSPm1lmqMOyMaQ=
github.com/decred/dcrd/hdkeychain/v3 v3.0.0-20200616182840-3baf1f590cb1 h1:kKZVUO9qWWWq5mJuKy9jiBPUMT6lFyY9MX1xOIaQn5c= github.com/decred/dcrd/hdkeychain/v3 v3.0.0-20200616182840-3baf1f590cb1 h1:kKZVUO9qWWWq5mJuKy9jiBPUMT6lFyY9MX1xOIaQn5c=
github.com/decred/dcrd/hdkeychain/v3 v3.0.0-20200616182840-3baf1f590cb1/go.mod h1:OQQKlU+hzvOHVZfUJq1iqQ5IfyycGaSPm1lmqMOyMaQ= github.com/decred/dcrd/hdkeychain/v3 v3.0.0-20200616182840-3baf1f590cb1/go.mod h1:OQQKlU+hzvOHVZfUJq1iqQ5IfyycGaSPm1lmqMOyMaQ=
github.com/decred/dcrd/rpc/jsonrpc/types/v2 v2.0.0/go.mod h1:c5S+PtQWNIA2aUakgrLhrlopkMadcOv51dWhCEdo49c= github.com/decred/dcrd/rpc/jsonrpc/types/v2 v2.0.0/go.mod h1:c5S+PtQWNIA2aUakgrLhrlopkMadcOv51dWhCEdo49c=
@ -83,11 +87,12 @@ github.com/decred/dcrd/rpc/jsonrpc/types/v2 v2.0.1-0.20200527025017-6fc98347d984
github.com/decred/dcrd/txscript/v2 v2.1.0/go.mod h1:XaJAVrZU4NWRx4UEzTiDAs86op1m8GRJLz24SDBKOi0= github.com/decred/dcrd/txscript/v2 v2.1.0/go.mod h1:XaJAVrZU4NWRx4UEzTiDAs86op1m8GRJLz24SDBKOi0=
github.com/decred/dcrd/txscript/v3 v3.0.0-20200215023918-6247af01d5e3/go.mod h1:ATMA8K0SOo+M9Wdbr6dMnAd8qICJi6pXjGLlKsJc99E= github.com/decred/dcrd/txscript/v3 v3.0.0-20200215023918-6247af01d5e3/go.mod h1:ATMA8K0SOo+M9Wdbr6dMnAd8qICJi6pXjGLlKsJc99E=
github.com/decred/dcrd/txscript/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:KsDS7McU1yFaCYR9LCIwk6YnE15YN3wJUDxhKdFqlsc= github.com/decred/dcrd/txscript/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:KsDS7McU1yFaCYR9LCIwk6YnE15YN3wJUDxhKdFqlsc=
github.com/decred/dcrd/txscript/v3 v3.0.0-20200421213827-b60c60ffe98b/go.mod h1:vrm3R/AesmA9slTf0rFcwhD0SduAJAWxocyaWVi8dM0= github.com/decred/dcrd/txscript/v3 v3.0.0-20200608124004-b2f67c2dc475/go.mod h1:vrm3R/AesmA9slTf0rFcwhD0SduAJAWxocyaWVi8dM0=
github.com/decred/dcrd/txscript/v3 v3.0.0-20200616182840-3baf1f590cb1 h1:ZvPldwEQn42X4Wr2YkaOLNOkKMgtW8hS1mlRHnYfcQc= github.com/decred/dcrd/txscript/v3 v3.0.0-20200616182840-3baf1f590cb1 h1:ZvPldwEQn42X4Wr2YkaOLNOkKMgtW8hS1mlRHnYfcQc=
github.com/decred/dcrd/txscript/v3 v3.0.0-20200616182840-3baf1f590cb1/go.mod h1:vrm3R/AesmA9slTf0rFcwhD0SduAJAWxocyaWVi8dM0= github.com/decred/dcrd/txscript/v3 v3.0.0-20200616182840-3baf1f590cb1/go.mod h1:vrm3R/AesmA9slTf0rFcwhD0SduAJAWxocyaWVi8dM0=
github.com/decred/dcrd/wire v1.3.0 h1:X76I2/a8esUmxXmFpJpAvXEi014IA4twgwcOBeIS8lE= github.com/decred/dcrd/wire v1.3.0 h1:X76I2/a8esUmxXmFpJpAvXEi014IA4twgwcOBeIS8lE=
github.com/decred/dcrd/wire v1.3.0/go.mod h1:fnKGlUY2IBuqnpxx5dYRU5Oiq392OBqAuVjRVSkIoXM= github.com/decred/dcrd/wire v1.3.0/go.mod h1:fnKGlUY2IBuqnpxx5dYRU5Oiq392OBqAuVjRVSkIoXM=
github.com/decred/go-socks v1.1.0 h1:dnENcc0KIqQo3HSXdgboXAHgqsCIutkqq6ntQjYtm2U=
github.com/decred/go-socks v1.1.0/go.mod h1:sDhHqkZH0X4JjSa02oYOGhcGHYp12FsY1jQ/meV8md0= github.com/decred/go-socks v1.1.0/go.mod h1:sDhHqkZH0X4JjSa02oYOGhcGHYp12FsY1jQ/meV8md0=
github.com/decred/slog v1.0.0 h1:Dl+W8O6/JH6n2xIFN2p3DNjCmjYwvrXsjlSJTQQ4MhE= github.com/decred/slog v1.0.0 h1:Dl+W8O6/JH6n2xIFN2p3DNjCmjYwvrXsjlSJTQQ4MhE=
github.com/decred/slog v1.0.0/go.mod h1:zR98rEZHSnbZ4WHZtO0iqmSZjDLKhkXfrPTZQKtAonQ= github.com/decred/slog v1.0.0/go.mod h1:zR98rEZHSnbZ4WHZtO0iqmSZjDLKhkXfrPTZQKtAonQ=
@ -121,10 +126,14 @@ github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYb
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.1-0.20200711081900-c17162fe8fd7 h1:Ug59miTxVKVg5Oi2S5uHlKOIV5jBx4Hb2u0jIxxDaSs=
github.com/jessevdk/go-flags v1.4.1-0.20200711081900-c17162fe8fd7/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jrick/bitset v1.0.0 h1:Ws0PXV3PwXqWK2n7Vz6idCdrV/9OrBXgHEJi27ZB9Dw= github.com/jrick/bitset v1.0.0 h1:Ws0PXV3PwXqWK2n7Vz6idCdrV/9OrBXgHEJi27ZB9Dw=
github.com/jrick/bitset v1.0.0/go.mod h1:ZOYB5Uvkla7wIEY4FEssPVi3IQXa02arznRaYaAEPe4= github.com/jrick/bitset v1.0.0/go.mod h1:ZOYB5Uvkla7wIEY4FEssPVi3IQXa02arznRaYaAEPe4=
github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI=
@ -157,11 +166,11 @@ github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg=
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -201,9 +210,11 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -15,7 +15,7 @@ import (
) )
const ( const (
requiredDcrdVersion = "6.1.1" requiredDcrdVersion = "6.1.2"
) )
// These error codes are defined in dcrd/dcrjson. They are copied here so we // These error codes are defined in dcrd/dcrjson. They are copied here so we

View File

@ -164,3 +164,24 @@ func (c *WalletRPC) GetBestBlockHeight() (int64, error) {
} }
return block.Height, nil return block.Height, nil
} }
func (c *WalletRPC) TicketInfo() (map[string]*wallettypes.TicketInfoResult, error) {
var result []*wallettypes.TicketInfoResult
err := c.Call(c.ctx, "ticketinfo", &result)
if err != nil {
return nil, err
}
// For easier access later on, store the tickets in a map using their hash
// as the key.
tickets := make(map[string]*wallettypes.TicketInfoResult, len(result))
for _, t := range result {
tickets[t.Hash] = t
}
return tickets, err
}
func (c *WalletRPC) RescanFrom(fromHeight int64) error {
return c.Call(c.ctx, "rescanwallet", nil, fromHeight)
}

View File

@ -131,7 +131,8 @@ func feeAddress(c *gin.Context) {
err = db.UpdateTicket(ticket) err = db.UpdateTicket(ticket)
if err != nil { if err != nil {
log.Errorf("%s: db.UpdateTicket failed (ticketHash=%s): %v", funcName, ticket.Hash, err) log.Errorf("%s: db.UpdateTicket error, failed to update fee expiry (ticketHash=%s): %v",
funcName, ticket.Hash, err)
sendError(errInternalError, c) sendError(errInternalError, c)
return return
} }

View File

@ -12,16 +12,17 @@ import (
) )
type vspStats struct { type vspStats struct {
PubKey string PubKey string
TotalTickets int Voting int64
FeeConfirmedTickets int Voted int64
VSPFee float64 Revoked int64
Network string VSPFee float64
UpdateTime string Network string
SupportEmail string UpdateTime string
VspClosed bool SupportEmail string
Debug bool VspClosed bool
Designation string Debug bool
Designation string
} }
var statsMtx sync.RWMutex var statsMtx sync.RWMutex
@ -35,7 +36,7 @@ func getVSPStats() *vspStats {
} }
func updateVSPStats(db *database.VspDatabase, cfg Config) error { func updateVSPStats(db *database.VspDatabase, cfg Config) error {
total, feeConfirmed, err := db.CountTickets() voting, voted, revoked, err := db.CountTickets()
if err != nil { if err != nil {
return err return err
} }
@ -44,16 +45,17 @@ func updateVSPStats(db *database.VspDatabase, cfg Config) error {
defer statsMtx.Unlock() defer statsMtx.Unlock()
stats = &vspStats{ stats = &vspStats{
PubKey: base64.StdEncoding.EncodeToString(signPubKey), PubKey: base64.StdEncoding.EncodeToString(signPubKey),
TotalTickets: total, Voting: voting,
FeeConfirmedTickets: feeConfirmed, Voted: voted,
VSPFee: cfg.VSPFee, Revoked: revoked,
Network: cfg.NetParams.Name, VSPFee: cfg.VSPFee,
UpdateTime: time.Now().Format("Mon Jan _2 15:04:05 2006"), Network: cfg.NetParams.Name,
SupportEmail: cfg.SupportEmail, UpdateTime: time.Now().Format("Mon Jan _2 15:04:05 2006"),
VspClosed: cfg.VspClosed, SupportEmail: cfg.SupportEmail,
Debug: cfg.Debug, VspClosed: cfg.VspClosed,
Designation: cfg.Designation, Debug: cfg.Debug,
Designation: cfg.Designation,
} }
return nil return nil

View File

@ -212,7 +212,8 @@ findAddress:
err = db.UpdateTicket(ticket) err = db.UpdateTicket(ticket)
if err != nil { if err != nil {
log.Errorf("%s: InsertTicket failed (ticketHash=%s): %v", funcName, ticket.Hash, err) log.Errorf("%s: db.UpdateTicket error, failed to set fee tx (ticketHash=%s): %v",
funcName, ticket.Hash, err)
sendError(errInternalError, c) sendError(errInternalError, c)
return return
} }
@ -230,7 +231,8 @@ findAddress:
err = db.UpdateTicket(ticket) err = db.UpdateTicket(ticket)
if err != nil { if err != nil {
log.Errorf("%s: db.UpdateTicket failed (ticketHash=%s): %v", funcName, ticket.Hash, err) log.Errorf("%s: db.UpdateTicket error, failed to set fee tx error (ticketHash=%s): %v",
funcName, ticket.Hash, err)
} }
sendErrorWithMsg("could not broadcast fee transaction", errInvalidFeeTx, c) sendErrorWithMsg("could not broadcast fee transaction", errInvalidFeeTx, c)
@ -241,7 +243,8 @@ findAddress:
err = db.UpdateTicket(ticket) err = db.UpdateTicket(ticket)
if err != nil { if err != nil {
log.Errorf("%s: db.UpdateTicket failed (ticketHash=%s): %v", funcName, ticket.Hash, err) log.Errorf("%s: db.UpdateTicket error, failed to set fee tx as broadcast (ticketHash=%s): %v",
funcName, ticket.Hash, err)
sendError(errInternalError, c) sendError(errInternalError, c)
return return
} }

View File

@ -51,7 +51,8 @@ func setVoteChoices(c *gin.Context) {
ticket.VoteChoices = voteChoices ticket.VoteChoices = voteChoices
err = db.UpdateTicket(ticket) err = db.UpdateTicket(ticket)
if err != nil { if err != nil {
log.Errorf("%s: db.UpdateTicket error (ticketHash=%s): %v", funcName, ticket.Hash, err) log.Errorf("%s: db.UpdateTicket error, failed to set vote choices (ticketHash=%s): %v",
funcName, ticket.Hash, err)
sendError(errInternalError, c) sendError(errInternalError, c)
return return
} }

View File

@ -148,6 +148,8 @@
<td>{{ .FeeTxStatus }}</td> <td>{{ .FeeTxStatus }}</td>
</tr> </tr>
<tr> <tr>
<th>Ticket Outcome</th>
<td>{{ .Outcome }}</td>
</tr> </tr>
</table> </table>
{{ end }} {{ end }}

View File

@ -3,13 +3,18 @@
<div class="row vsp-stats"> <div class="row vsp-stats">
<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">Total tickets</div> <div class="stat-title">Live tickets</div>
<div class="stat-value" id="vsp-hash-rate">{{ .TotalTickets }}</div> <div class="stat-value">{{ .Voting }}</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">
<div class="stat-title">Fee confirmed tickets</div> <div class="stat-title">Voted tickets</div>
<div class="stat-value" id="last-work-height">{{ .FeeConfirmedTickets }}</div> <div class="stat-value">{{ .Voted }}</div>
</div>
<div class="col-6 col-sm-4 col-lg-2 py-3">
<div class="stat-title">Revoked tickets</div>
<div class="stat-value">{{ .Revoked }}</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">

View File

@ -8,6 +8,9 @@ type vspInfoResponse struct {
VspClosed bool `json:"vspclosed"` VspClosed bool `json:"vspclosed"`
Network string `json:"network"` Network string `json:"network"`
VspdVersion string `json:"vspdversion"` VspdVersion string `json:"vspdversion"`
Voting int64 `json:"voting"`
Voted int64 `json:"voted"`
Revoked int64 `json:"revoked"`
} }
type FeeAddressRequest struct { type FeeAddressRequest struct {

View File

@ -9,6 +9,7 @@ import (
// vspInfo is the handler for "GET /api/v3/vspinfo". // vspInfo is the handler for "GET /api/v3/vspinfo".
func vspInfo(c *gin.Context) { func vspInfo(c *gin.Context) {
cachedStats := getVSPStats()
sendJSONResponse(vspInfoResponse{ sendJSONResponse(vspInfoResponse{
APIVersions: []int64{3}, APIVersions: []int64{3},
Timestamp: time.Now().Unix(), Timestamp: time.Now().Unix(),
@ -17,5 +18,8 @@ func vspInfo(c *gin.Context) {
Network: cfg.NetParams.Name, Network: cfg.NetParams.Name,
VspClosed: cfg.VspClosed, VspClosed: cfg.VspClosed,
VspdVersion: version.String(), VspdVersion: version.String(),
Voting: cachedStats.Voting,
Voted: cachedStats.Voted,
Revoked: cachedStats.Revoked,
}, c) }, c)
} }