Include ticket hex in feeaddress request.
This commit is contained in:
parent
8b049204eb
commit
1131e15ff1
@ -68,7 +68,8 @@ for the specified ticket.
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"timestamp":1590509066,
|
"timestamp":1590509066,
|
||||||
"tickethash":"484a68f7148e55d05f0b64a29fe7b148572cb5272d1ce2438cf15466d347f4f4"
|
"tickethash":"1b9f5dc3b4872c47f66b148b0633647458123d72a0f0623a90890cc51a668737",
|
||||||
|
"tickethex":"0100000001a8...bfa6e4bf9c5ec1"
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package webapi
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@ -12,12 +13,18 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
|
"github.com/jrick/wsrpc/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ticketHashRequest struct {
|
type ticketHashRequest struct {
|
||||||
TicketHash string `json:"tickethash" binding:"required"`
|
TicketHash string `json:"tickethash" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ticketRequest struct {
|
||||||
|
TicketHex string `json:"tickethex" binding:"required"`
|
||||||
|
TicketHash string `json:"tickethash" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
// withSession middleware adds a gorilla session to the request context for
|
// withSession middleware adds a gorilla session to the request context for
|
||||||
// downstream handlers to make use of. Sessions are used by admin pages to
|
// downstream handlers to make use of. Sessions are used by admin pages to
|
||||||
// maintain authentication status.
|
// maintain authentication status.
|
||||||
@ -101,6 +108,81 @@ func withWalletClients(wallets rpc.WalletConnect) gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensureTicketBroadcast will parse ticket hash and ticket hex from the request
|
||||||
|
// body, and ensure the local dcrd instance can retrieve information about that
|
||||||
|
// ticket. If no info can be found, the ticket hex will be broadcast.
|
||||||
|
func ensureTicketBroadcast() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// Read request bytes and then replace the request reader for
|
||||||
|
// downstream handlers to use.
|
||||||
|
reqBytes, err := ioutil.ReadAll(c.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Error reading request from %s: %v", c.ClientIP(), err)
|
||||||
|
sendErrorWithMsg(err.Error(), errBadRequest, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Request.Body.Close()
|
||||||
|
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(reqBytes))
|
||||||
|
|
||||||
|
// Parse request and ensure ticket hash and hex are included.
|
||||||
|
var request ticketRequest
|
||||||
|
if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
|
||||||
|
log.Warnf("Bad request from %s: %v", c.ClientIP(), err)
|
||||||
|
sendErrorWithMsg(err.Error(), errBadRequest, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the provided hex is a valid ticket.
|
||||||
|
msgTx, err := decodeTransaction(request.TicketHex)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("decodeTransaction error: %v", err)
|
||||||
|
sendErrorWithMsg("cannot decode ticket hex", errBadRequest, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = isValidTicket(msgTx)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Invalid ticket from %s: %v", c.ClientIP(), err)
|
||||||
|
sendError(errInvalidTicket, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure hex matches hash.
|
||||||
|
if msgTx.TxHash().String() != request.TicketHash {
|
||||||
|
log.Warnf("Ticket hex/hash mismatch from %s", c.ClientIP())
|
||||||
|
sendErrorWithMsg("ticket hex does not match hash", errBadRequest, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dcrdClient := c.MustGet("DcrdClient").(*rpc.DcrdRPC)
|
||||||
|
|
||||||
|
// Use GetRawTransaction to check if local dcrd already knows this
|
||||||
|
// ticket.
|
||||||
|
_, err = dcrdClient.GetRawTransaction(request.TicketHash)
|
||||||
|
if err == nil {
|
||||||
|
// No error means dcrd knows the ticket, we are done here.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrNoTxInfo means local dcrd is not aware of the ticket. We have the
|
||||||
|
// hex, so we can broadcast it here.
|
||||||
|
var e *wsrpc.Error
|
||||||
|
if errors.As(err, &e) && e.Code == rpc.ErrNoTxInfo {
|
||||||
|
log.Debugf("Broadcasting ticket with hash %s", request.TicketHash)
|
||||||
|
err = dcrdClient.SendRawTransaction(request.TicketHex)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("SendRawTransaction error: %v", err)
|
||||||
|
sendError(errInternalError, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Errorf("GetRawTransaction error: %v", err)
|
||||||
|
sendError(errInternalError, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// vspAuth middleware reads the request body and extracts the ticket hash. The
|
// vspAuth middleware reads the request body and extracts the ticket hash. The
|
||||||
// commitment address for the ticket is retrieved from the database if it is
|
// commitment address for the ticket is retrieved from the database if it is
|
||||||
// known, or it is retrieved from the chain if not.
|
// known, or it is retrieved from the chain if not.
|
||||||
|
|||||||
@ -11,6 +11,7 @@ type vspInfoResponse struct {
|
|||||||
type FeeAddressRequest struct {
|
type FeeAddressRequest struct {
|
||||||
Timestamp int64 `json:"timestamp" binding:"required"`
|
Timestamp int64 `json:"timestamp" binding:"required"`
|
||||||
TicketHash string `json:"tickethash" binding:"required"`
|
TicketHash string `json:"tickethash" binding:"required"`
|
||||||
|
TicketHex string `json:"tickethex" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type feeAddressResponse struct {
|
type feeAddressResponse struct {
|
||||||
|
|||||||
@ -183,21 +183,22 @@ func router(debugMode bool, cookieSecret []byte, dcrd rpc.DcrdConnect, wallets r
|
|||||||
// Serve static web resources
|
// Serve static web resources
|
||||||
router.Static("/public", "webapi/public/")
|
router.Static("/public", "webapi/public/")
|
||||||
|
|
||||||
// These routes have no extra middleware. They can be accessed by anybody.
|
|
||||||
router.GET("", homepage)
|
|
||||||
router.GET("/api/vspinfo", vspInfo)
|
|
||||||
|
|
||||||
// These API routes access dcrd and they need authentication.
|
|
||||||
feeOnly := router.Group("/api").Use(
|
|
||||||
withDcrdClient(dcrd), vspAuth(),
|
|
||||||
)
|
|
||||||
feeOnly.POST("/feeaddress", feeAddress)
|
|
||||||
feeOnly.GET("/ticketstatus", ticketStatus)
|
|
||||||
feeOnly.POST("/payfee", payFee)
|
|
||||||
|
|
||||||
// Create a cookie store for persisting admin session information.
|
// Create a cookie store for persisting admin session information.
|
||||||
cookieStore := sessions.NewCookieStore(cookieSecret)
|
cookieStore := sessions.NewCookieStore(cookieSecret)
|
||||||
|
|
||||||
|
// API routes.
|
||||||
|
|
||||||
|
api := router.Group("/api")
|
||||||
|
api.GET("/vspinfo", vspInfo)
|
||||||
|
api.POST("/feeaddress", withDcrdClient(dcrd), ensureTicketBroadcast(), vspAuth(), feeAddress)
|
||||||
|
api.GET("/ticketstatus", withDcrdClient(dcrd), vspAuth(), ticketStatus)
|
||||||
|
api.POST("/payfee", withDcrdClient(dcrd), vspAuth(), payFee)
|
||||||
|
api.POST("/setvotechoices", withDcrdClient(dcrd), withWalletClients(wallets), vspAuth(), setVoteChoices)
|
||||||
|
|
||||||
|
// Website routes.
|
||||||
|
|
||||||
|
router.GET("", homepage)
|
||||||
|
|
||||||
login := router.Group("/admin").Use(
|
login := router.Group("/admin").Use(
|
||||||
withSession(cookieStore),
|
withSession(cookieStore),
|
||||||
)
|
)
|
||||||
@ -211,13 +212,6 @@ func router(debugMode bool, cookieSecret []byte, dcrd rpc.DcrdConnect, wallets r
|
|||||||
admin.GET("/backup", downloadDatabaseBackup)
|
admin.GET("/backup", downloadDatabaseBackup)
|
||||||
admin.POST("/logout", adminLogout)
|
admin.POST("/logout", adminLogout)
|
||||||
|
|
||||||
// These API routes access dcrd and the voting wallets, and they need
|
|
||||||
// authentication.
|
|
||||||
both := router.Group("/api").Use(
|
|
||||||
withDcrdClient(dcrd), withWalletClients(wallets), vspAuth(),
|
|
||||||
)
|
|
||||||
both.POST("/setvotechoices", setVoteChoices)
|
|
||||||
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user