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
|
errCannotBroadcastTicket
|
||||||
errCannotBroadcastFee
|
errCannotBroadcastFee
|
||||||
errCannotBroadcastFeeUnknownOutputs
|
errCannotBroadcastFeeUnknownOutputs
|
||||||
|
errInvalidTimestamp
|
||||||
)
|
)
|
||||||
|
|
||||||
// httpStatus maps application error codes to HTTP status codes.
|
// httpStatus maps application error codes to HTTP status codes.
|
||||||
@ -65,6 +66,8 @@ func (e apiError) httpStatus() int {
|
|||||||
return http.StatusInternalServerError
|
return http.StatusInternalServerError
|
||||||
case errCannotBroadcastFeeUnknownOutputs:
|
case errCannotBroadcastFeeUnknownOutputs:
|
||||||
return http.StatusPreconditionRequired
|
return http.StatusPreconditionRequired
|
||||||
|
case errInvalidTimestamp:
|
||||||
|
return http.StatusBadRequest
|
||||||
default:
|
default:
|
||||||
return http.StatusInternalServerError
|
return http.StatusInternalServerError
|
||||||
}
|
}
|
||||||
@ -107,6 +110,8 @@ func (e apiError) defaultMessage() string {
|
|||||||
return "fee transaction could not be broadcast"
|
return "fee transaction could not be broadcast"
|
||||||
case errCannotBroadcastFeeUnknownOutputs:
|
case errCannotBroadcastFeeUnknownOutputs:
|
||||||
return "fee transaction could not be broadcast due to unknown outputs"
|
return "fee transaction could not be broadcast due to unknown outputs"
|
||||||
|
case errInvalidTimestamp:
|
||||||
|
return "old or reused timestamp"
|
||||||
default:
|
default:
|
||||||
return "unknown error"
|
return "unknown error"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
package webapi
|
package webapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -14,6 +15,10 @@ import (
|
|||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type timestampRequest struct {
|
||||||
|
Timestamp int64 `json:"timestamp" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
// setVoteChoices is the handler for "POST /api/v3/setvotechoices".
|
// setVoteChoices is the handler for "POST /api/v3/setvotechoices".
|
||||||
func setVoteChoices(c *gin.Context) {
|
func setVoteChoices(c *gin.Context) {
|
||||||
const funcName = "setVoteChoices"
|
const funcName = "setVoteChoices"
|
||||||
@ -60,8 +65,36 @@ func setVoteChoices(c *gin.Context) {
|
|||||||
return
|
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
|
voteChoices := request.VoteChoices
|
||||||
err := isValidVoteChoices(cfg.NetParams, currentVoteVersion(cfg.NetParams), voteChoices)
|
err = isValidVoteChoices(cfg.NetParams, currentVoteVersion(cfg.NetParams), voteChoices)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("%s: Invalid vote choices (clientIP=%s, ticketHash=%s): %v",
|
log.Warnf("%s: Invalid vote choices (clientIP=%s, ticketHash=%s): %v",
|
||||||
funcName, c.ClientIP(), ticket.Hash, err)
|
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)
|
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.
|
// Send success response to client.
|
||||||
resp, respSig := sendJSONResponse(setVoteChoicesResponse{
|
resp, respSig := sendJSONResponse(setVoteChoicesResponse{
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: time.Now().Unix(),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user