Reject reused or old timestamps. (#215)

* Reject reused or old timestamps.

* Refine error mesage
This commit is contained in:
Jamie Holdstock 2020-12-27 15:22:17 +00:00 committed by GitHub
parent 057b89e2f2
commit fe286a5d1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 39 additions and 3 deletions

View File

@ -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"
}

View File

@ -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(),