add new helper functions
This commit is contained in:
parent
860e50130e
commit
4d4f9c8ca0
@ -15,7 +15,7 @@ import (
|
||||
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||
"github.com/decred/dcrd/dcrutil/v4"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/decred/vspd/rpc"
|
||||
)
|
||||
|
||||
func currentVoteVersion(params *chaincfg.Params) uint32 {
|
||||
@ -105,16 +105,22 @@ func validPolicyOption(policy string) error {
|
||||
}
|
||||
}
|
||||
|
||||
func validateSignature(reqBytes []byte, commitmentAddress string, c *gin.Context) error {
|
||||
// Ensure a signature is provided.
|
||||
signature := c.GetHeader("VSP-Client-Signature")
|
||||
if signature == "" {
|
||||
return errors.New("no VSP-Client-Signature header")
|
||||
}
|
||||
func validateSignature(hash, commitmentAddress, signature, message string) error {
|
||||
firstErr := dcrutil.VerifyMessage(commitmentAddress, signature, message, cfg.NetParams)
|
||||
if firstErr != nil {
|
||||
// Don't return an error straight away if sig validation fails -
|
||||
// first check if we have an alternate sign address for this ticket.
|
||||
altSigData, err := db.AltSignAddrData(hash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("db.AltSignAddrData failed: %v", err)
|
||||
}
|
||||
|
||||
// If we have no alternate sign address, or if validating with the
|
||||
// alt sign addr fails, return an error to the client.
|
||||
if altSigData == nil || dcrutil.VerifyMessage(altSigData.AltSignAddr, signature, message, cfg.NetParams) != nil {
|
||||
return fmt.Errorf("bad signature")
|
||||
}
|
||||
|
||||
err := dcrutil.VerifyMessage(commitmentAddress, signature, string(reqBytes), cfg.NetParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -141,3 +147,52 @@ func isValidTicket(tx *wire.MsgTx) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateTicketHash ensures the provided ticket hash is a valid ticket hash.
|
||||
// A ticket hash should be 64 chars (MaxHashStringSize) and should parse into
|
||||
// a chainhash.Hash without error.
|
||||
func validateTicketHash(hash string) error {
|
||||
if len(hash) != chainhash.MaxHashStringSize {
|
||||
return fmt.Errorf("incorrect hash length: got %d, expected %d", len(hash), chainhash.MaxHashStringSize)
|
||||
|
||||
}
|
||||
_, err := chainhash.NewHashFromStr(hash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid hash: %v", err)
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getCommitmentAddress gets the commitment address of the provided ticket hash
|
||||
// from the chain.
|
||||
func getCommitmentAddress(hash string, dcrdClient *rpc.DcrdRPC) (string, error) {
|
||||
var commitmentAddress string
|
||||
resp, err := dcrdClient.GetRawTransaction(hash)
|
||||
if err != nil {
|
||||
return commitmentAddress, fmt.Errorf("dcrd.GetRawTransaction for ticket failed: %v", err)
|
||||
|
||||
}
|
||||
|
||||
msgTx, err := decodeTransaction(resp.Hex)
|
||||
if err != nil {
|
||||
return commitmentAddress, fmt.Errorf("Failed to decode ticket hex: %v", err)
|
||||
|
||||
}
|
||||
|
||||
err = isValidTicket(msgTx)
|
||||
if err != nil {
|
||||
return commitmentAddress, fmt.Errorf("Invalid ticket: %w", errInvalidTicket)
|
||||
|
||||
}
|
||||
|
||||
addr, err := stake.AddrFromSStxPkScrCommitment(msgTx.TxOut[1].PkScript, cfg.NetParams)
|
||||
if err != nil {
|
||||
return commitmentAddress, fmt.Errorf("AddrFromSStxPkScrCommitment error: %v", err)
|
||||
|
||||
}
|
||||
|
||||
commitmentAddress = addr.String()
|
||||
return commitmentAddress, nil
|
||||
}
|
||||
|
||||
@ -11,8 +11,6 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/decred/dcrd/blockchain/stake/v4"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/vspd/rpc"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
@ -287,17 +285,9 @@ func vspAuth() gin.HandlerFunc {
|
||||
hash := request.TicketHash
|
||||
|
||||
// Before hitting the db or any RPC, ensure this is a valid ticket hash.
|
||||
// A ticket hash should be 64 chars (MaxHashStringSize) and should parse
|
||||
// into a chainhash.Hash without error.
|
||||
if len(hash) != chainhash.MaxHashStringSize {
|
||||
log.Errorf("%s: Incorrect hash length (clientIP=%s): got %d, expected %d",
|
||||
funcName, c.ClientIP(), len(hash), chainhash.MaxHashStringSize)
|
||||
sendErrorWithMsg("invalid ticket hash", errBadRequest, c)
|
||||
return
|
||||
}
|
||||
_, err = chainhash.NewHashFromStr(hash)
|
||||
err = validateTicketHash(hash)
|
||||
if err != nil {
|
||||
log.Errorf("%s: Invalid hash (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
||||
log.Errorf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
||||
sendErrorWithMsg("invalid ticket hash", errBadRequest, c)
|
||||
return
|
||||
}
|
||||
@ -313,67 +303,44 @@ func vspAuth() gin.HandlerFunc {
|
||||
// If the ticket was found in the database, we already know its
|
||||
// commitment address. Otherwise we need to get it from the chain.
|
||||
var commitmentAddress string
|
||||
dcrdClient := c.MustGet(dcrdKey).(*rpc.DcrdRPC)
|
||||
dcrdErr := c.MustGet(dcrdErrorKey)
|
||||
if dcrdErr != nil {
|
||||
log.Errorf("%s: could not get dcrd client: %v", funcName, dcrdErr.(error))
|
||||
sendError(errInternalError, c)
|
||||
return
|
||||
}
|
||||
|
||||
if ticketFound {
|
||||
commitmentAddress = ticket.CommitmentAddress
|
||||
} else {
|
||||
dcrdClient := c.MustGet(dcrdKey).(*rpc.DcrdRPC)
|
||||
dcrdErr := c.MustGet(dcrdErrorKey)
|
||||
if dcrdErr != nil {
|
||||
log.Errorf("%s: could not get dcrd client: %v", funcName, dcrdErr.(error))
|
||||
sendError(errInternalError, c)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := dcrdClient.GetRawTransaction(hash)
|
||||
commitmentAddress, err = getCommitmentAddress(hash, dcrdClient)
|
||||
if err != nil {
|
||||
log.Errorf("%s: dcrd.GetRawTransaction for ticket failed (ticketHash=%s): %v", funcName, hash, err)
|
||||
sendError(errInternalError, c)
|
||||
var apiErr *apiError
|
||||
if errors.Is(err, apiErr) {
|
||||
sendError(errInvalidTicket, c)
|
||||
} else {
|
||||
sendError(errInternalError, c)
|
||||
}
|
||||
log.Errorf("%s: (clientIP: %s, ticketHash: %s): %v", funcName, c.ClientIP(), hash, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
msgTx, err := decodeTransaction(resp.Hex)
|
||||
if err != nil {
|
||||
log.Errorf("%s: Failed to decode ticket hex (ticketHash=%s): %v", funcName, ticket.Hash, err)
|
||||
sendError(errInternalError, c)
|
||||
return
|
||||
}
|
||||
|
||||
err = isValidTicket(msgTx)
|
||||
if err != nil {
|
||||
log.Warnf("%s: Invalid ticket (clientIP=%s, ticketHash=%s): %v", funcName, c.ClientIP(), hash, err)
|
||||
sendError(errInvalidTicket, c)
|
||||
return
|
||||
}
|
||||
|
||||
addr, err := stake.AddrFromSStxPkScrCommitment(msgTx.TxOut[1].PkScript, cfg.NetParams)
|
||||
if err != nil {
|
||||
log.Errorf("%s: AddrFromSStxPkScrCommitment error (ticketHash=%s): %v", funcName, hash, err)
|
||||
sendError(errInternalError, c)
|
||||
return
|
||||
}
|
||||
|
||||
commitmentAddress = addr.String()
|
||||
// Ensure a signature is provided.
|
||||
signature := c.GetHeader("VSP-Client-Signature")
|
||||
if signature == "" {
|
||||
log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
||||
sendErrorWithMsg("no VSP-Client-Signature header", errBadRequest, c)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate request signature to ensure ticket ownership.
|
||||
err = validateSignature(reqBytes, commitmentAddress, c)
|
||||
err = validateSignature(hash, commitmentAddress, signature, string(reqBytes))
|
||||
if err != nil {
|
||||
// Don't return an error straight away if sig validation fails -
|
||||
// first check if we have an alternate sign address for this ticket.
|
||||
altSigData, err := db.AltSignAddrData(hash)
|
||||
if err != nil {
|
||||
log.Errorf("%s: db.AltSignAddrData failed (ticketHash=%s): %v", funcName, hash, err)
|
||||
sendError(errInternalError, c)
|
||||
return
|
||||
}
|
||||
|
||||
// If we have no alternate sign address, or if validating with the
|
||||
// alt sign addr fails, return an error to the client.
|
||||
if altSigData == nil || validateSignature(reqBytes, altSigData.AltSignAddr, c) != nil {
|
||||
log.Warnf("%s: Bad signature (clientIP=%s, ticketHash=%s)", funcName, c.ClientIP(), hash)
|
||||
sendError(errBadSignature, c)
|
||||
return
|
||||
}
|
||||
log.Errorf("%s: Bad signature (clientIP=%s, ticketHash=%s): %v", funcName, err)
|
||||
sendError(errBadSignature, c)
|
||||
return
|
||||
}
|
||||
|
||||
// Add ticket information to context so downstream handlers don't need
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user