Skip to content

Commit 4d4f9c8

Browse files
add new helper functions
1 parent 860e501 commit 4d4f9c8

2 files changed

Lines changed: 94 additions & 72 deletions

File tree

webapi/helpers.go

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
"github.com/decred/dcrd/dcrec/secp256k1/v4"
1616
"github.com/decred/dcrd/dcrutil/v4"
1717
"github.com/decred/dcrd/wire"
18-
"github.com/gin-gonic/gin"
18+
"github.com/decred/vspd/rpc"
1919
)
2020

2121
func currentVoteVersion(params *chaincfg.Params) uint32 {
@@ -105,16 +105,22 @@ func validPolicyOption(policy string) error {
105105
}
106106
}
107107

108-
func validateSignature(reqBytes []byte, commitmentAddress string, c *gin.Context) error {
109-
// Ensure a signature is provided.
110-
signature := c.GetHeader("VSP-Client-Signature")
111-
if signature == "" {
112-
return errors.New("no VSP-Client-Signature header")
113-
}
108+
func validateSignature(hash, commitmentAddress, signature, message string) error {
109+
firstErr := dcrutil.VerifyMessage(commitmentAddress, signature, message, cfg.NetParams)
110+
if firstErr != nil {
111+
// Don't return an error straight away if sig validation fails -
112+
// first check if we have an alternate sign address for this ticket.
113+
altSigData, err := db.AltSignAddrData(hash)
114+
if err != nil {
115+
return fmt.Errorf("db.AltSignAddrData failed: %v", err)
116+
}
117+
118+
// If we have no alternate sign address, or if validating with the
119+
// alt sign addr fails, return an error to the client.
120+
if altSigData == nil || dcrutil.VerifyMessage(altSigData.AltSignAddr, signature, message, cfg.NetParams) != nil {
121+
return fmt.Errorf("bad signature")
122+
}
114123

115-
err := dcrutil.VerifyMessage(commitmentAddress, signature, string(reqBytes), cfg.NetParams)
116-
if err != nil {
117-
return err
118124
}
119125
return nil
120126
}
@@ -141,3 +147,52 @@ func isValidTicket(tx *wire.MsgTx) error {
141147

142148
return nil
143149
}
150+
151+
// validateTicketHash ensures the provided ticket hash is a valid ticket hash.
152+
// A ticket hash should be 64 chars (MaxHashStringSize) and should parse into
153+
// a chainhash.Hash without error.
154+
func validateTicketHash(hash string) error {
155+
if len(hash) != chainhash.MaxHashStringSize {
156+
return fmt.Errorf("incorrect hash length: got %d, expected %d", len(hash), chainhash.MaxHashStringSize)
157+
158+
}
159+
_, err := chainhash.NewHashFromStr(hash)
160+
if err != nil {
161+
return fmt.Errorf("invalid hash: %v", err)
162+
163+
}
164+
165+
return nil
166+
}
167+
168+
// getCommitmentAddress gets the commitment address of the provided ticket hash
169+
// from the chain.
170+
func getCommitmentAddress(hash string, dcrdClient *rpc.DcrdRPC) (string, error) {
171+
var commitmentAddress string
172+
resp, err := dcrdClient.GetRawTransaction(hash)
173+
if err != nil {
174+
return commitmentAddress, fmt.Errorf("dcrd.GetRawTransaction for ticket failed: %v", err)
175+
176+
}
177+
178+
msgTx, err := decodeTransaction(resp.Hex)
179+
if err != nil {
180+
return commitmentAddress, fmt.Errorf("Failed to decode ticket hex: %v", err)
181+
182+
}
183+
184+
err = isValidTicket(msgTx)
185+
if err != nil {
186+
return commitmentAddress, fmt.Errorf("Invalid ticket: %w", errInvalidTicket)
187+
188+
}
189+
190+
addr, err := stake.AddrFromSStxPkScrCommitment(msgTx.TxOut[1].PkScript, cfg.NetParams)
191+
if err != nil {
192+
return commitmentAddress, fmt.Errorf("AddrFromSStxPkScrCommitment error: %v", err)
193+
194+
}
195+
196+
commitmentAddress = addr.String()
197+
return commitmentAddress, nil
198+
}

webapi/middleware.go

Lines changed: 29 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import (
1111
"net/http"
1212
"strings"
1313

14-
"github.com/decred/dcrd/blockchain/stake/v4"
15-
"github.com/decred/dcrd/chaincfg/chainhash"
1614
"github.com/decred/vspd/rpc"
1715
"github.com/gin-gonic/gin"
1816
"github.com/gin-gonic/gin/binding"
@@ -287,17 +285,9 @@ func vspAuth() gin.HandlerFunc {
287285
hash := request.TicketHash
288286

289287
// Before hitting the db or any RPC, ensure this is a valid ticket hash.
290-
// A ticket hash should be 64 chars (MaxHashStringSize) and should parse
291-
// into a chainhash.Hash without error.
292-
if len(hash) != chainhash.MaxHashStringSize {
293-
log.Errorf("%s: Incorrect hash length (clientIP=%s): got %d, expected %d",
294-
funcName, c.ClientIP(), len(hash), chainhash.MaxHashStringSize)
295-
sendErrorWithMsg("invalid ticket hash", errBadRequest, c)
296-
return
297-
}
298-
_, err = chainhash.NewHashFromStr(hash)
288+
err = validateTicketHash(hash)
299289
if err != nil {
300-
log.Errorf("%s: Invalid hash (clientIP=%s): %v", funcName, c.ClientIP(), err)
290+
log.Errorf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
301291
sendErrorWithMsg("invalid ticket hash", errBadRequest, c)
302292
return
303293
}
@@ -313,67 +303,44 @@ func vspAuth() gin.HandlerFunc {
313303
// If the ticket was found in the database, we already know its
314304
// commitment address. Otherwise we need to get it from the chain.
315305
var commitmentAddress string
306+
dcrdClient := c.MustGet(dcrdKey).(*rpc.DcrdRPC)
307+
dcrdErr := c.MustGet(dcrdErrorKey)
308+
if dcrdErr != nil {
309+
log.Errorf("%s: could not get dcrd client: %v", funcName, dcrdErr.(error))
310+
sendError(errInternalError, c)
311+
return
312+
}
313+
316314
if ticketFound {
317315
commitmentAddress = ticket.CommitmentAddress
318316
} else {
319-
dcrdClient := c.MustGet(dcrdKey).(*rpc.DcrdRPC)
320-
dcrdErr := c.MustGet(dcrdErrorKey)
321-
if dcrdErr != nil {
322-
log.Errorf("%s: could not get dcrd client: %v", funcName, dcrdErr.(error))
323-
sendError(errInternalError, c)
324-
return
325-
}
326-
327-
resp, err := dcrdClient.GetRawTransaction(hash)
317+
commitmentAddress, err = getCommitmentAddress(hash, dcrdClient)
328318
if err != nil {
329-
log.Errorf("%s: dcrd.GetRawTransaction for ticket failed (ticketHash=%s): %v", funcName, hash, err)
330-
sendError(errInternalError, c)
331-
return
332-
}
333-
334-
msgTx, err := decodeTransaction(resp.Hex)
335-
if err != nil {
336-
log.Errorf("%s: Failed to decode ticket hex (ticketHash=%s): %v", funcName, ticket.Hash, err)
337-
sendError(errInternalError, c)
338-
return
339-
}
340-
341-
err = isValidTicket(msgTx)
342-
if err != nil {
343-
log.Warnf("%s: Invalid ticket (clientIP=%s, ticketHash=%s): %v", funcName, c.ClientIP(), hash, err)
344-
sendError(errInvalidTicket, c)
345-
return
346-
}
347-
348-
addr, err := stake.AddrFromSStxPkScrCommitment(msgTx.TxOut[1].PkScript, cfg.NetParams)
349-
if err != nil {
350-
log.Errorf("%s: AddrFromSStxPkScrCommitment error (ticketHash=%s): %v", funcName, hash, err)
351-
sendError(errInternalError, c)
319+
var apiErr *apiError
320+
if errors.Is(err, apiErr) {
321+
sendError(errInvalidTicket, c)
322+
} else {
323+
sendError(errInternalError, c)
324+
}
325+
log.Errorf("%s: (clientIP: %s, ticketHash: %s): %v", funcName, c.ClientIP(), hash, err)
352326
return
353327
}
328+
}
354329

355-
commitmentAddress = addr.String()
330+
// Ensure a signature is provided.
331+
signature := c.GetHeader("VSP-Client-Signature")
332+
if signature == "" {
333+
log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
334+
sendErrorWithMsg("no VSP-Client-Signature header", errBadRequest, c)
335+
return
356336
}
357337

358338
// Validate request signature to ensure ticket ownership.
359-
err = validateSignature(reqBytes, commitmentAddress, c)
339+
err = validateSignature(hash, commitmentAddress, signature, string(reqBytes))
360340
if err != nil {
361-
// Don't return an error straight away if sig validation fails -
362-
// first check if we have an alternate sign address for this ticket.
363-
altSigData, err := db.AltSignAddrData(hash)
364-
if err != nil {
365-
log.Errorf("%s: db.AltSignAddrData failed (ticketHash=%s): %v", funcName, hash, err)
366-
sendError(errInternalError, c)
367-
return
368-
}
369-
370-
// If we have no alternate sign address, or if validating with the
371-
// alt sign addr fails, return an error to the client.
372-
if altSigData == nil || validateSignature(reqBytes, altSigData.AltSignAddr, c) != nil {
373-
log.Warnf("%s: Bad signature (clientIP=%s, ticketHash=%s)", funcName, c.ClientIP(), hash)
374-
sendError(errBadSignature, c)
375-
return
376-
}
341+
log.Errorf("%s: Bad signature (clientIP=%s, ticketHash=%s): %v", funcName, err)
342+
sendError(errBadSignature, c)
343+
return
377344
}
378345

379346
// Add ticket information to context so downstream handlers don't need

0 commit comments

Comments
 (0)