127 lines
4.1 KiB
Go
127 lines
4.1 KiB
Go
package webapi
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/gin-gonic/gin/binding"
|
|
"github.com/jholdstock/vspd/rpc"
|
|
)
|
|
|
|
type ticketHashRequest struct {
|
|
TicketHash string `json:"tickethash" binding:"required"`
|
|
}
|
|
|
|
// withDcrdClient middleware adds a dcrd client to the request
|
|
// context for downstream handlers to make use of.
|
|
func withDcrdClient() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
dcrdConn, err := dcrdConnect()
|
|
if err != nil {
|
|
log.Errorf("dcrd connection error: %v", err)
|
|
sendErrorResponse("dcrd RPC error", http.StatusInternalServerError, c)
|
|
return
|
|
}
|
|
dcrdClient, err := rpc.DcrdClient(c, dcrdConn, cfg.NetParams)
|
|
if err != nil {
|
|
log.Errorf("dcrd client error: %v", err)
|
|
sendErrorResponse("dcrd RPC error", http.StatusInternalServerError, c)
|
|
return
|
|
}
|
|
|
|
c.Set("DcrdClient", dcrdClient)
|
|
}
|
|
}
|
|
|
|
// withWalletClient middleware adds a voting wallet client to the request
|
|
// context for downstream handlers to make use of.
|
|
func withWalletClient() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
walletClient := make([]*rpc.WalletRPC, len(walletConnect))
|
|
for i := 0; i < len(walletConnect); i++ {
|
|
walletConn, err := walletConnect[i]()
|
|
if err != nil {
|
|
log.Errorf("dcrwallet '%s' connection error: %v", err)
|
|
sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c)
|
|
return
|
|
}
|
|
walletClient[i], err = rpc.WalletClient(c, walletConn, cfg.NetParams)
|
|
if err != nil {
|
|
log.Errorf("dcrwallet '%s' client error: %v", walletClient[i].String(), err)
|
|
sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c)
|
|
return
|
|
}
|
|
}
|
|
c.Set("WalletClient", walletClient)
|
|
}
|
|
}
|
|
|
|
// 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
|
|
// known, or it is retrieved from the chain if not.
|
|
// The middleware errors out if the VSP-Client-Signature header of the request
|
|
// does not contain the request body signed with the commitment address.
|
|
// Ticket information is added to the request context for downstream handlers to
|
|
// use.
|
|
func vspAuth() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// Read request bytes.
|
|
reqBytes, err := c.GetRawData()
|
|
if err != nil {
|
|
log.Warnf("Error reading request from %s: %v", c.ClientIP(), err)
|
|
sendErrorResponse(err.Error(), http.StatusBadRequest, c)
|
|
return
|
|
}
|
|
|
|
// Add raw request to context for downstream handlers to use.
|
|
c.Set("RawRequest", reqBytes)
|
|
|
|
// Parse request and ensure there is a ticket hash included.
|
|
var request ticketHashRequest
|
|
if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
|
|
log.Warnf("Bad request from %s: %v", c.ClientIP(), err)
|
|
sendErrorResponse(err.Error(), http.StatusBadRequest, c)
|
|
return
|
|
}
|
|
hash := request.TicketHash
|
|
|
|
// Check if this ticket already appears in the database.
|
|
ticket, ticketFound, err := db.GetTicketByHash(hash)
|
|
if err != nil {
|
|
log.Errorf("GetTicketByHash error: %v", err)
|
|
sendErrorResponse("database error", http.StatusInternalServerError, c)
|
|
return
|
|
}
|
|
|
|
// 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
|
|
if ticketFound {
|
|
commitmentAddress = ticket.CommitmentAddress
|
|
} else {
|
|
dcrdClient := c.MustGet("DcrdClient").(*rpc.DcrdRPC)
|
|
commitmentAddress, err = dcrdClient.GetTicketCommitmentAddress(hash, cfg.NetParams)
|
|
if err != nil {
|
|
log.Errorf("GetTicketCommitmentAddress error: %v", err)
|
|
sendErrorResponse(err.Error(), http.StatusInternalServerError, c)
|
|
return
|
|
}
|
|
}
|
|
|
|
// Validate request signature to ensure ticket ownership.
|
|
err = validateSignature(reqBytes, commitmentAddress, c)
|
|
if err != nil {
|
|
log.Warnf("Bad signature from %s: %v", c.ClientIP(), err)
|
|
sendErrorResponse("bad signature", http.StatusBadRequest, c)
|
|
return
|
|
}
|
|
|
|
// Add ticket information to context so downstream handlers don't need
|
|
// to access the db for it.
|
|
c.Set("Ticket", ticket)
|
|
c.Set("KnownTicket", ticketFound)
|
|
c.Set("CommitmentAddress", commitmentAddress)
|
|
}
|
|
|
|
}
|