Reject reused or old timestamps. (#215)
* Reject reused or old timestamps. * Refine error mesage
This commit is contained in:
parent
057b89e2f2
commit
fe286a5d1a
@ -26,6 +26,7 @@ const (
|
||||
errCannotBroadcastTicket
|
||||
errCannotBroadcastFee
|
||||
errCannotBroadcastFeeUnknownOutputs
|
||||
errInvalidTimestamp
|
||||
)
|
||||
|
||||
// httpStatus maps application error codes to HTTP status codes.
|
||||
@ -65,6 +66,8 @@ func (e apiError) httpStatus() int {
|
||||
return http.StatusInternalServerError
|
||||
case errCannotBroadcastFeeUnknownOutputs:
|
||||
return http.StatusPreconditionRequired
|
||||
case errInvalidTimestamp:
|
||||
return http.StatusBadRequest
|
||||
default:
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
@ -107,6 +110,8 @@ func (e apiError) defaultMessage() string {
|
||||
return "fee transaction could not be broadcast"
|
||||
case errCannotBroadcastFeeUnknownOutputs:
|
||||
return "fee transaction could not be broadcast due to unknown outputs"
|
||||
case errInvalidTimestamp:
|
||||
return "old or reused timestamp"
|
||||
default:
|
||||
return "unknown error"
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
package webapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@ -14,6 +15,10 @@ import (
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
)
|
||||
|
||||
type timestampRequest struct {
|
||||
Timestamp int64 `json:"timestamp" binding:"required"`
|
||||
}
|
||||
|
||||
// setVoteChoices is the handler for "POST /api/v3/setvotechoices".
|
||||
func setVoteChoices(c *gin.Context) {
|
||||
const funcName = "setVoteChoices"
|
||||
@ -60,8 +65,36 @@ func setVoteChoices(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Return an error if this request has a timestamp older than any previous
|
||||
// vote change requests. This is to prevent requests from being replayed.
|
||||
previousChanges, err := db.GetVoteChanges(ticket.Hash)
|
||||
if err != nil {
|
||||
log.Errorf("%s: db.GetVoteChanges error (ticketHash=%s): %v",
|
||||
funcName, ticket.Hash, err)
|
||||
sendError(errInternalError, c)
|
||||
return
|
||||
}
|
||||
|
||||
for _, change := range previousChanges {
|
||||
var prevReq timestampRequest
|
||||
err := json.Unmarshal([]byte(change.Request), &prevReq)
|
||||
if err != nil {
|
||||
log.Errorf("%s: Could not unmarshal vote change record (ticketHash=%s): %v",
|
||||
funcName, ticket.Hash, err)
|
||||
sendError(errInternalError, c)
|
||||
return
|
||||
}
|
||||
|
||||
if request.Timestamp <= prevReq.Timestamp {
|
||||
log.Warnf("%s: Request uses invalid timestamp (ticketHash=%s): %v",
|
||||
funcName, ticket.Hash, err)
|
||||
sendError(errInvalidTimestamp, c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
voteChoices := request.VoteChoices
|
||||
err := isValidVoteChoices(cfg.NetParams, currentVoteVersion(cfg.NetParams), voteChoices)
|
||||
err = isValidVoteChoices(cfg.NetParams, currentVoteVersion(cfg.NetParams), voteChoices)
|
||||
if err != nil {
|
||||
log.Warnf("%s: Invalid vote choices (clientIP=%s, ticketHash=%s): %v",
|
||||
funcName, c.ClientIP(), ticket.Hash, err)
|
||||
@ -98,8 +131,6 @@ func setVoteChoices(c *gin.Context) {
|
||||
|
||||
log.Debugf("%s: Vote choices updated (ticketHash=%s)", funcName, ticket.Hash)
|
||||
|
||||
// TODO: DB - error if given timestamp is older than any previous requests
|
||||
|
||||
// Send success response to client.
|
||||
resp, respSig := sendJSONResponse(setVoteChoicesResponse{
|
||||
Timestamp: time.Now().Unix(),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user