Return parsable error codes instead of just a string.

Also
- Check if VSP is closed before /payfee
This commit is contained in:
jholdstock 2020-06-06 11:27:12 +01:00 committed by David Hill
parent b50615bc6f
commit 9f48bae78e
8 changed files with 169 additions and 62 deletions

View File

@ -4,8 +4,16 @@
- Success responses use HTTP status 200 and a JSON encoded body. - Success responses use HTTP status 200 and a JSON encoded body.
- Error responses use either HTTP status 500 or 400, and a JSON encoded error - Error responses use HTTP status 500 to indicate a server error or 400 to
in the body, e.g. `{"error":"Description"}`. indiciate a client error, and will include a JSON body describing the error.
For example:
```json
{"code": 9, "message":"invalid vote choices"}
```
A full list of error codes can be looked up in
[webapi/errors.go](../webapi/errors.go)
- Requests which reference specific tickets need to be properly signed as - Requests which reference specific tickets need to be properly signed as
described in [two-way-accountability.md](./two-way-accountability.md). described in [two-way-accountability.md](./two-way-accountability.md).

84
webapi/errors.go Normal file
View File

@ -0,0 +1,84 @@
package webapi
import "net/http"
type apiError int
const (
errBadRequest apiError = iota
errInternalError
errVspClosed
errFeeAlreadyReceived
errInvalidFeeTx
errFeeTooSmall
errUnknownTicket
errTicketCannotVote
errFeeExpired
errInvalidVoteChoices
errBadSignature
errInvalidPrivKey
)
// httpStatus maps application error codes to HTTP status codes.
func (e apiError) httpStatus() int {
switch e {
case errBadRequest:
return http.StatusBadRequest
case errInternalError:
return http.StatusInternalServerError
case errVspClosed:
return http.StatusBadRequest
case errFeeAlreadyReceived:
return http.StatusBadRequest
case errInvalidFeeTx:
return http.StatusBadRequest
case errFeeTooSmall:
return http.StatusBadRequest
case errUnknownTicket:
return http.StatusBadRequest
case errTicketCannotVote:
return http.StatusBadRequest
case errFeeExpired:
return http.StatusBadRequest
case errInvalidVoteChoices:
return http.StatusBadRequest
case errBadSignature:
return http.StatusBadRequest
case errInvalidPrivKey:
return http.StatusBadRequest
default:
return http.StatusInternalServerError
}
}
// defaultMessage returns a descriptive error string for a given error code.
func (e apiError) defaultMessage() string {
switch e {
case errBadRequest:
return "bad request"
case errInternalError:
return "internal error"
case errVspClosed:
return "vsp is closed"
case errFeeAlreadyReceived:
return "fee tx already received"
case errInvalidFeeTx:
return "invalid fee transaction"
case errFeeTooSmall:
return "fee too small"
case errUnknownTicket:
return "unknown ticket"
case errTicketCannotVote:
return "ticket not eligible to vote"
case errFeeExpired:
return "fee has expired"
case errInvalidVoteChoices:
return "invalid vote choices"
case errBadSignature:
return "bad request signature"
case errInvalidPrivKey:
return "invalid private key"
default:
return "unknown error"
}
}

View File

@ -1,7 +1,6 @@
package webapi package webapi
import ( import (
"net/http"
"sync" "sync"
"time" "time"
@ -70,14 +69,14 @@ func feeAddress(c *gin.Context) {
dcrdClient := c.MustGet("DcrdClient").(*rpc.DcrdRPC) dcrdClient := c.MustGet("DcrdClient").(*rpc.DcrdRPC)
if cfg.VspClosed { if cfg.VspClosed {
sendErrorResponse("pool is not accepting new tickets", http.StatusBadRequest, c) sendError(errVspClosed, c)
return return
} }
var feeAddressRequest FeeAddressRequest var feeAddressRequest FeeAddressRequest
if err := binding.JSON.BindBody(rawRequest, &feeAddressRequest); err != nil { if err := binding.JSON.BindBody(rawRequest, &feeAddressRequest); err != nil {
log.Warnf("Bad feeaddress request from %s: %v", c.ClientIP(), err) log.Warnf("Bad feeaddress request from %s: %v", c.ClientIP(), err)
sendErrorResponse(err.Error(), http.StatusBadRequest, c) sendErrorWithMsg(err.Error(), errBadRequest, c)
return return
} }
@ -86,7 +85,7 @@ func feeAddress(c *gin.Context) {
// Respond early if we already have the fee tx for this ticket. // Respond early if we already have the fee tx for this ticket.
if ticket.FeeTxHex != "" { if ticket.FeeTxHex != "" {
log.Warnf("Fee tx already received from %s: ticketHash=%s", c.ClientIP(), ticket.Hash) log.Warnf("Fee tx already received from %s: ticketHash=%s", c.ClientIP(), ticket.Hash)
sendErrorResponse("fee tx already received", http.StatusBadRequest, c) sendError(errFeeAlreadyReceived, c)
return return
} }
@ -94,7 +93,7 @@ func feeAddress(c *gin.Context) {
rawTicket, err := dcrdClient.GetRawTransaction(ticketHash) rawTicket, err := dcrdClient.GetRawTransaction(ticketHash)
if err != nil { if err != nil {
log.Errorf("Could not retrieve tx %s for %s: %v", ticketHash, c.ClientIP(), err) log.Errorf("Could not retrieve tx %s for %s: %v", ticketHash, c.ClientIP(), err)
sendErrorResponse(err.Error(), http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
@ -102,12 +101,12 @@ func feeAddress(c *gin.Context) {
canVote, err := dcrdClient.CanTicketVote(rawTicket, ticketHash, cfg.NetParams) canVote, err := dcrdClient.CanTicketVote(rawTicket, ticketHash, cfg.NetParams)
if err != nil { if err != nil {
log.Errorf("canTicketVote error: %v", err) log.Errorf("canTicketVote error: %v", err)
sendErrorResponse("error validating ticket", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
if !canVote { if !canVote {
log.Warnf("Unvotable ticket %s from %s", ticketHash, c.ClientIP()) log.Warnf("Unvotable ticket %s from %s", ticketHash, c.ClientIP())
sendErrorResponse("ticket not eligible to vote", http.StatusBadRequest, c) sendError(errTicketCannotVote, c)
return return
} }
@ -120,7 +119,7 @@ func feeAddress(c *gin.Context) {
newFee, err := getCurrentFee(dcrdClient) newFee, err := getCurrentFee(dcrdClient)
if err != nil { if err != nil {
log.Errorf("getCurrentFee error: %v", err) log.Errorf("getCurrentFee error: %v", err)
sendErrorResponse("fee error", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
ticket.FeeExpiration = now.Add(feeAddressExpiration).Unix() ticket.FeeExpiration = now.Add(feeAddressExpiration).Unix()
@ -129,7 +128,7 @@ func feeAddress(c *gin.Context) {
err = db.UpdateTicket(ticket) err = db.UpdateTicket(ticket)
if err != nil { if err != nil {
log.Errorf("UpdateTicket error: %v", err) log.Errorf("UpdateTicket error: %v", err)
sendErrorResponse("database error", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
log.Debugf("Expired fee updated for ticket: newFeeAmt=%f, ticketHash=%s", log.Debugf("Expired fee updated for ticket: newFeeAmt=%f, ticketHash=%s",
@ -152,7 +151,7 @@ func feeAddress(c *gin.Context) {
fee, err := getCurrentFee(dcrdClient) fee, err := getCurrentFee(dcrdClient)
if err != nil { if err != nil {
log.Errorf("getCurrentFee error: %v", err) log.Errorf("getCurrentFee error: %v", err)
sendErrorResponse("fee error", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
@ -180,7 +179,7 @@ func feeAddress(c *gin.Context) {
err = db.InsertNewTicket(dbTicket) err = db.InsertNewTicket(dbTicket)
if err != nil { if err != nil {
log.Errorf("InsertTicket error: %v", err) log.Errorf("InsertTicket error: %v", err)
sendErrorResponse("database error", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }

View File

@ -54,7 +54,7 @@ func withDcrdClient() gin.HandlerFunc {
client, err := dcrd.Client(c, cfg.NetParams) client, err := dcrd.Client(c, cfg.NetParams)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
sendErrorResponse("dcrd RPC error", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
@ -69,7 +69,7 @@ func withWalletClients() gin.HandlerFunc {
clients, failedConnections := wallets.Clients(c, cfg.NetParams) clients, failedConnections := wallets.Clients(c, cfg.NetParams)
if len(clients) == 0 { if len(clients) == 0 {
log.Error("Could not connect to any wallets") log.Error("Could not connect to any wallets")
sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
if failedConnections > 0 { if failedConnections > 0 {
@ -93,7 +93,7 @@ func vspAuth() gin.HandlerFunc {
reqBytes, err := c.GetRawData() reqBytes, err := c.GetRawData()
if err != nil { if err != nil {
log.Warnf("Error reading request from %s: %v", c.ClientIP(), err) log.Warnf("Error reading request from %s: %v", c.ClientIP(), err)
sendErrorResponse(err.Error(), http.StatusBadRequest, c) sendErrorWithMsg(err.Error(), errBadRequest, c)
return return
} }
@ -104,7 +104,7 @@ func vspAuth() gin.HandlerFunc {
var request ticketHashRequest var request ticketHashRequest
if err := binding.JSON.BindBody(reqBytes, &request); err != nil { if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
log.Warnf("Bad request from %s: %v", c.ClientIP(), err) log.Warnf("Bad request from %s: %v", c.ClientIP(), err)
sendErrorResponse(err.Error(), http.StatusBadRequest, c) sendErrorWithMsg(err.Error(), errBadRequest, c)
return return
} }
hash := request.TicketHash hash := request.TicketHash
@ -113,7 +113,7 @@ func vspAuth() gin.HandlerFunc {
ticket, ticketFound, err := db.GetTicketByHash(hash) ticket, ticketFound, err := db.GetTicketByHash(hash)
if err != nil { if err != nil {
log.Errorf("GetTicketByHash error: %v", err) log.Errorf("GetTicketByHash error: %v", err)
sendErrorResponse("database error", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
@ -127,7 +127,7 @@ func vspAuth() gin.HandlerFunc {
commitmentAddress, err = dcrdClient.GetTicketCommitmentAddress(hash, cfg.NetParams) commitmentAddress, err = dcrdClient.GetTicketCommitmentAddress(hash, cfg.NetParams)
if err != nil { if err != nil {
log.Errorf("GetTicketCommitmentAddress error: %v", err) log.Errorf("GetTicketCommitmentAddress error: %v", err)
sendErrorResponse(err.Error(), http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
} }
@ -136,7 +136,7 @@ func vspAuth() gin.HandlerFunc {
err = validateSignature(reqBytes, commitmentAddress, c) err = validateSignature(reqBytes, commitmentAddress, c)
if err != nil { if err != nil {
log.Warnf("Bad signature from %s: %v", c.ClientIP(), err) log.Warnf("Bad signature from %s: %v", c.ClientIP(), err)
sendErrorResponse("bad signature", http.StatusBadRequest, c) sendError(errBadSignature, c)
return return
} }

View File

@ -2,7 +2,6 @@ package webapi
import ( import (
"encoding/hex" "encoding/hex"
"net/http"
"time" "time"
"github.com/decred/dcrd/dcrec" "github.com/decred/dcrd/dcrec"
@ -24,23 +23,28 @@ func payFee(c *gin.Context) {
knownTicket := c.MustGet("KnownTicket").(bool) knownTicket := c.MustGet("KnownTicket").(bool)
dcrdClient := c.MustGet("DcrdClient").(*rpc.DcrdRPC) dcrdClient := c.MustGet("DcrdClient").(*rpc.DcrdRPC)
if cfg.VspClosed {
sendError(errVspClosed, c)
return
}
if !knownTicket { if !knownTicket {
log.Warnf("Invalid ticket from %s", c.ClientIP()) log.Warnf("Unknown ticket from %s", c.ClientIP())
sendErrorResponse("invalid ticket", http.StatusBadRequest, c) sendError(errUnknownTicket, c)
return return
} }
var payFeeRequest PayFeeRequest var payFeeRequest PayFeeRequest
if err := binding.JSON.BindBody(rawRequest, &payFeeRequest); err != nil { if err := binding.JSON.BindBody(rawRequest, &payFeeRequest); err != nil {
log.Warnf("Bad payfee request from %s: %v", c.ClientIP(), err) log.Warnf("Bad payfee request from %s: %v", c.ClientIP(), err)
sendErrorResponse(err.Error(), http.StatusBadRequest, c) sendErrorWithMsg(err.Error(), errBadRequest, c)
return return
} }
// Respond early if we already have the fee tx for this ticket. // Respond early if we already have the fee tx for this ticket.
if ticket.FeeTxHex != "" { if ticket.FeeTxHex != "" {
log.Warnf("Fee tx already received from %s: ticketHash=%s", c.ClientIP(), ticket.Hash) log.Warnf("Fee tx already received from %s: ticketHash=%s", c.ClientIP(), ticket.Hash)
sendErrorResponse("fee tx already received", http.StatusBadRequest, c) sendError(errFeeAlreadyReceived, c)
return return
} }
@ -48,7 +52,7 @@ func payFee(c *gin.Context) {
rawTicket, err := dcrdClient.GetRawTransaction(ticket.Hash) rawTicket, err := dcrdClient.GetRawTransaction(ticket.Hash)
if err != nil { if err != nil {
log.Errorf("Could not retrieve tx %s for %s: %v", ticket.Hash, c.ClientIP(), err) log.Errorf("Could not retrieve tx %s for %s: %v", ticket.Hash, c.ClientIP(), err)
sendErrorResponse(err.Error(), http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
@ -56,19 +60,19 @@ func payFee(c *gin.Context) {
canVote, err := dcrdClient.CanTicketVote(rawTicket, ticket.Hash, cfg.NetParams) canVote, err := dcrdClient.CanTicketVote(rawTicket, ticket.Hash, cfg.NetParams)
if err != nil { if err != nil {
log.Errorf("canTicketVote error: %v", err) log.Errorf("canTicketVote error: %v", err)
sendErrorResponse("error validating ticket", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
if !canVote { if !canVote {
log.Warnf("Unvotable ticket %s from %s", ticket.Hash, c.ClientIP()) log.Warnf("Unvotable ticket %s from %s", ticket.Hash, c.ClientIP())
sendErrorResponse("ticket not eligible to vote", http.StatusBadRequest, c) sendError(errTicketCannotVote, c)
return return
} }
// Respond early if the fee for this ticket is expired. // Respond early if the fee for this ticket is expired.
if ticket.FeeExpired() { if ticket.FeeExpired() {
log.Warnf("Expired payfee request from %s", c.ClientIP()) log.Warnf("Expired payfee request from %s", c.ClientIP())
sendErrorResponse("fee has expired", http.StatusBadRequest, c) sendError(errFeeExpired, c)
return return
} }
@ -77,7 +81,7 @@ func payFee(c *gin.Context) {
votingWIF, err := dcrutil.DecodeWIF(votingKey, cfg.NetParams.PrivateKeyID) votingWIF, err := dcrutil.DecodeWIF(votingKey, cfg.NetParams.PrivateKeyID)
if err != nil { if err != nil {
log.Warnf("Failed to decode WIF: %v", err) log.Warnf("Failed to decode WIF: %v", err)
sendErrorResponse("error decoding WIF", http.StatusBadRequest, c) sendError(errInvalidPrivKey, c)
return return
} }
@ -86,7 +90,7 @@ func payFee(c *gin.Context) {
err = isValidVoteChoices(cfg.NetParams, currentVoteVersion(cfg.NetParams), voteChoices) err = isValidVoteChoices(cfg.NetParams, currentVoteVersion(cfg.NetParams), voteChoices)
if err != nil { if err != nil {
log.Warnf("Invalid votechoices from %s: %v", c.ClientIP(), err) log.Warnf("Invalid votechoices from %s: %v", c.ClientIP(), err)
sendErrorResponse(err.Error(), http.StatusBadRequest, c) sendErrorWithMsg(err.Error(), errInvalidVoteChoices, c)
return return
} }
@ -94,14 +98,14 @@ func payFee(c *gin.Context) {
feeTxBytes, err := hex.DecodeString(payFeeRequest.FeeTx) feeTxBytes, err := hex.DecodeString(payFeeRequest.FeeTx)
if err != nil { if err != nil {
log.Warnf("Failed to decode tx: %v", err) log.Warnf("Failed to decode tx: %v", err)
sendErrorResponse("failed to decode transaction", http.StatusBadRequest, c) sendError(errInvalidFeeTx, c)
return return
} }
feeTx := wire.NewMsgTx() feeTx := wire.NewMsgTx()
if err = feeTx.FromBytes(feeTxBytes); err != nil { if err = feeTx.FromBytes(feeTxBytes); err != nil {
log.Warnf("Failed to deserialize tx: %v", err) log.Warnf("Failed to deserialize tx: %v", err)
sendErrorResponse("unable to deserialize transaction", http.StatusBadRequest, c) sendError(errInvalidFeeTx, c)
return return
} }
@ -113,14 +117,14 @@ func payFee(c *gin.Context) {
findAddress: findAddress:
for _, txOut := range feeTx.TxOut { for _, txOut := range feeTx.TxOut {
if txOut.Version != scriptVersion { if txOut.Version != scriptVersion {
sendErrorResponse("invalid script version", http.StatusBadRequest, c) sendErrorWithMsg("invalid script version", errInvalidFeeTx, c)
return return
} }
_, addresses, _, err := txscript.ExtractPkScriptAddrs(scriptVersion, _, addresses, _, err := txscript.ExtractPkScriptAddrs(scriptVersion,
txOut.PkScript, cfg.NetParams) txOut.PkScript, cfg.NetParams)
if err != nil { if err != nil {
log.Errorf("Extract PK error: %v", err) log.Errorf("Extract PK error: %v", err)
sendErrorResponse("extract PK error", http.StatusBadRequest, c) sendError(errInternalError, c)
return return
} }
for _, addr := range addresses { for _, addr := range addresses {
@ -133,7 +137,7 @@ findAddress:
if feePaid == 0 { if feePaid == 0 {
log.Warnf("FeeTx for ticket %s did not include any payments for address %s", ticket.Hash, ticket.FeeAddress) log.Warnf("FeeTx for ticket %s did not include any payments for address %s", ticket.Hash, ticket.FeeAddress)
sendErrorResponse("feetx did not include any payments for fee address", http.StatusBadRequest, c) sendErrorWithMsg("feetx did not include any payments for fee address", errInvalidFeeTx, c)
return return
} }
@ -141,7 +145,7 @@ findAddress:
dcrec.STEcdsaSecp256k1) dcrec.STEcdsaSecp256k1)
if err != nil { if err != nil {
log.Errorf("NewAddressPubKeyHash: %v", err) log.Errorf("NewAddressPubKeyHash: %v", err)
sendErrorResponse("failed to deserialize voting wif", http.StatusInternalServerError, c) sendError(errInvalidPrivKey, c)
return return
} }
@ -149,13 +153,13 @@ findAddress:
ticketBytes, err := hex.DecodeString(rawTicket.Hex) ticketBytes, err := hex.DecodeString(rawTicket.Hex)
if err != nil { if err != nil {
log.Warnf("Failed to decode tx: %v", err) log.Warnf("Failed to decode tx: %v", err)
sendErrorResponse("failed to decode transaction", http.StatusBadRequest, c) sendError(errInternalError, c)
return return
} }
ticketTx := wire.NewMsgTx() ticketTx := wire.NewMsgTx()
if err = ticketTx.FromBytes(ticketBytes); err != nil { if err = ticketTx.FromBytes(ticketBytes); err != nil {
log.Errorf("Failed to deserialize tx: %v", err) log.Errorf("Failed to deserialize tx: %v", err)
sendErrorResponse("unable to deserialize transaction", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
@ -163,12 +167,12 @@ findAddress:
_, votingAddr, _, err := txscript.ExtractPkScriptAddrs(scriptVersion, ticketTx.TxOut[0].PkScript, cfg.NetParams) _, votingAddr, _, err := txscript.ExtractPkScriptAddrs(scriptVersion, ticketTx.TxOut[0].PkScript, cfg.NetParams)
if err != nil { if err != nil {
log.Errorf("ExtractPK error: %v", err) log.Errorf("ExtractPK error: %v", err)
sendErrorResponse("extract PK error", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
if len(votingAddr) == 0 { if len(votingAddr) == 0 {
log.Error("No voting address found for ticket %s", ticket.Hash) log.Error("No voting address found for ticket %s", ticket.Hash)
sendErrorResponse("no voting address found", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
@ -176,21 +180,21 @@ findAddress:
if votingAddr[0].Address() != wifAddr.Address() { if votingAddr[0].Address() != wifAddr.Address() {
log.Warnf("Voting address does not match provided private key: "+ log.Warnf("Voting address does not match provided private key: "+
"votingAddr=%+v, wifAddr=%+v", votingAddr[0], wifAddr) "votingAddr=%+v, wifAddr=%+v", votingAddr[0], wifAddr)
sendErrorResponse("voting address does not match provided private key", sendErrorWithMsg("voting address does not match provided private key",
http.StatusBadRequest, c) errInvalidPrivKey, c)
return return
} }
minFee, err := dcrutil.NewAmount(ticket.FeeAmount) minFee, err := dcrutil.NewAmount(ticket.FeeAmount)
if err != nil { if err != nil {
log.Errorf("dcrutil.NewAmount: %v", err) log.Errorf("dcrutil.NewAmount: %v", err)
sendErrorResponse("fee error", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
if feePaid < minFee { if feePaid < minFee {
log.Warnf("Fee too small from %s: was %v, expected %v", c.ClientIP(), feePaid, minFee) log.Warnf("Fee too small from %s: was %v, expected %v", c.ClientIP(), feePaid, minFee)
sendErrorResponse("fee too small", http.StatusInternalServerError, c) sendError(errFeeTooSmall, c)
return return
} }
@ -205,7 +209,7 @@ findAddress:
err = db.UpdateTicket(ticket) err = db.UpdateTicket(ticket)
if err != nil { if err != nil {
log.Errorf("InsertTicket failed: %v", err) log.Errorf("InsertTicket failed: %v", err)
sendErrorResponse("database error", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
@ -216,7 +220,7 @@ findAddress:
feeTxHash, err := dcrdClient.SendRawTransaction(payFeeRequest.FeeTx) feeTxHash, err := dcrdClient.SendRawTransaction(payFeeRequest.FeeTx)
if err != nil { if err != nil {
log.Errorf("SendRawTransaction failed: %v", err) log.Errorf("SendRawTransaction failed: %v", err)
sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
ticket.FeeTxHash = feeTxHash ticket.FeeTxHash = feeTxHash
@ -224,7 +228,7 @@ findAddress:
err = db.UpdateTicket(ticket) err = db.UpdateTicket(ticket)
if err != nil { if err != nil {
log.Errorf("InsertTicket failed: %v", err) log.Errorf("InsertTicket failed: %v", err)
sendErrorResponse("database error", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }

View File

@ -1,7 +1,6 @@
package webapi package webapi
import ( import (
"net/http"
"time" "time"
"github.com/decred/vspd/database" "github.com/decred/vspd/database"
@ -20,8 +19,8 @@ func setVoteChoices(c *gin.Context) {
walletClients := c.MustGet("WalletClients").([]*rpc.WalletRPC) walletClients := c.MustGet("WalletClients").([]*rpc.WalletRPC)
if !knownTicket { if !knownTicket {
log.Warnf("Invalid ticket from %s", c.ClientIP()) log.Warnf("Unknown ticket from %s", c.ClientIP())
sendErrorResponse("invalid ticket", http.StatusBadRequest, c) sendError(errUnknownTicket, c)
return return
} }
@ -30,7 +29,7 @@ func setVoteChoices(c *gin.Context) {
var setVoteChoicesRequest SetVoteChoicesRequest var setVoteChoicesRequest SetVoteChoicesRequest
if err := binding.JSON.BindBody(rawRequest, &setVoteChoicesRequest); err != nil { if err := binding.JSON.BindBody(rawRequest, &setVoteChoicesRequest); err != nil {
log.Warnf("Bad setvotechoices request from %s: %v", c.ClientIP(), err) log.Warnf("Bad setvotechoices request from %s: %v", c.ClientIP(), err)
sendErrorResponse(err.Error(), http.StatusBadRequest, c) sendErrorWithMsg(err.Error(), errBadRequest, c)
return return
} }
@ -38,7 +37,7 @@ func setVoteChoices(c *gin.Context) {
err := isValidVoteChoices(cfg.NetParams, currentVoteVersion(cfg.NetParams), voteChoices) err := isValidVoteChoices(cfg.NetParams, currentVoteVersion(cfg.NetParams), voteChoices)
if err != nil { if err != nil {
log.Warnf("Invalid votechoices from %s: %v", c.ClientIP(), err) log.Warnf("Invalid votechoices from %s: %v", c.ClientIP(), err)
sendErrorResponse(err.Error(), http.StatusBadRequest, c) sendErrorWithMsg(err.Error(), errInvalidVoteChoices, c)
return return
} }
@ -48,7 +47,7 @@ func setVoteChoices(c *gin.Context) {
err = db.UpdateTicket(ticket) err = db.UpdateTicket(ticket)
if err != nil { if err != nil {
log.Errorf("UpdateTicket error: %v", err) log.Errorf("UpdateTicket error: %v", err)
sendErrorResponse("database error", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }

View File

@ -1,7 +1,6 @@
package webapi package webapi
import ( import (
"net/http"
"time" "time"
"github.com/decred/vspd/database" "github.com/decred/vspd/database"
@ -18,15 +17,15 @@ func ticketStatus(c *gin.Context) {
knownTicket := c.MustGet("KnownTicket").(bool) knownTicket := c.MustGet("KnownTicket").(bool)
if !knownTicket { if !knownTicket {
log.Warnf("Invalid ticket from %s", c.ClientIP()) log.Warnf("Unknown ticket from %s", c.ClientIP())
sendErrorResponse("invalid ticket", http.StatusBadRequest, c) sendError(errUnknownTicket, c)
return return
} }
var ticketStatusRequest TicketStatusRequest var ticketStatusRequest TicketStatusRequest
if err := binding.JSON.BindBody(rawRequest, &ticketStatusRequest); err != nil { if err := binding.JSON.BindBody(rawRequest, &ticketStatusRequest); err != nil {
log.Warnf("Bad ticketstatus request from %s: %v", c.ClientIP(), err) log.Warnf("Bad ticketstatus request from %s: %v", c.ClientIP(), err)
sendErrorResponse(err.Error(), http.StatusBadRequest, c) sendErrorWithMsg(err.Error(), errBadRequest, c)
return return
} }

View File

@ -221,7 +221,7 @@ func sendJSONResponse(resp interface{}, c *gin.Context) {
dec, err := json.Marshal(resp) dec, err := json.Marshal(resp)
if err != nil { if err != nil {
log.Errorf("JSON marshal error: %v", err) log.Errorf("JSON marshal error: %v", err)
sendErrorResponse("failed to marshal json", http.StatusInternalServerError, c) sendError(errInternalError, c)
return return
} }
@ -231,8 +231,22 @@ func sendJSONResponse(resp interface{}, c *gin.Context) {
c.AbortWithStatusJSON(http.StatusOK, resp) c.AbortWithStatusJSON(http.StatusOK, resp)
} }
func sendErrorResponse(errMsg string, code int, c *gin.Context) { // sendError returns an error response to the client using the default error
resp := gin.H{"error": errMsg} // message.
func sendError(e apiError, c *gin.Context) {
msg := e.defaultMessage()
sendErrorWithMsg(msg, e, c)
}
// sendErrorWithMsg returns an error response to the client using the provided
// error message.
func sendErrorWithMsg(msg string, e apiError, c *gin.Context) {
status := e.httpStatus()
resp := gin.H{
"code": int(e),
"message": msg,
}
// Try to sign the error response. If it fails, send it without a signature. // Try to sign the error response. If it fails, send it without a signature.
dec, err := json.Marshal(resp) dec, err := json.Marshal(resp)
@ -243,5 +257,5 @@ func sendErrorResponse(errMsg string, code int, c *gin.Context) {
c.Writer.Header().Set("VSP-Server-Signature", hex.EncodeToString(sig)) c.Writer.Header().Set("VSP-Server-Signature", hex.EncodeToString(sig))
} }
c.AbortWithStatusJSON(code, resp) c.AbortWithStatusJSON(status, resp)
} }