Compare scripts rather than addresses. (#267)
* Decode fee address with stdaddr. Compare script and script versions rather than just comparing address strings. Also move the fee amount check higher, so it is with the rest of the fee validating code. * Compare voting addr scripts rather than addrs. Rather than comparing the address strings, ensure both voting script and script version match.
This commit is contained in:
parent
dad662b0f0
commit
978b78e745
@ -5,12 +5,13 @@
|
|||||||
package webapi
|
package webapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/decred/dcrd/blockchain/v4"
|
"github.com/decred/dcrd/blockchain/v4"
|
||||||
"github.com/decred/dcrd/dcrutil/v4"
|
"github.com/decred/dcrd/dcrutil/v4"
|
||||||
"github.com/decred/dcrd/txscript/v4"
|
|
||||||
"github.com/decred/dcrd/txscript/v4/stdaddr"
|
"github.com/decred/dcrd/txscript/v4/stdaddr"
|
||||||
"github.com/decred/vspd/database"
|
"github.com/decred/vspd/database"
|
||||||
"github.com/decred/vspd/rpc"
|
"github.com/decred/vspd/rpc"
|
||||||
@ -18,11 +19,6 @@ import (
|
|||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// Assume the treasury is enabled
|
|
||||||
isTreasuryEnabled = true
|
|
||||||
)
|
|
||||||
|
|
||||||
// payFee is the handler for "POST /api/v3/payfee".
|
// payFee is the handler for "POST /api/v3/payfee".
|
||||||
func payFee(c *gin.Context) {
|
func payFee(c *gin.Context) {
|
||||||
const funcName = "payFee"
|
const funcName = "payFee"
|
||||||
@ -128,42 +124,47 @@ func payFee(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop through transaction outputs until we find one which pays to the
|
// Decode fee address to get its payment script details.
|
||||||
// expected fee address. Record how much is being paid to the fee address.
|
feeAddr, err := stdaddr.DecodeAddress(ticket.FeeAddress, cfg.NetParams)
|
||||||
var feePaid dcrutil.Amount
|
|
||||||
const scriptVersion = 0
|
|
||||||
|
|
||||||
findAddress:
|
|
||||||
for _, txOut := range feeTx.TxOut {
|
|
||||||
if txOut.Version != scriptVersion {
|
|
||||||
log.Errorf("%s: Fee tx with invalid script version (clientIP=%s, ticketHash=%s): was %d, expected %d",
|
|
||||||
funcName, c.ClientIP(), ticket.Hash, txOut.Version, scriptVersion)
|
|
||||||
sendErrorWithMsg("invalid script version", errInvalidFeeTx, c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, addresses, _, err := txscript.ExtractPkScriptAddrs(scriptVersion,
|
|
||||||
txOut.PkScript, cfg.NetParams, isTreasuryEnabled)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("%s: Extract PK error (clientIP=%s, ticketHash=%s): %v",
|
log.Errorf("%s: Failed to decode fee address (ticketHash=%s): %v",
|
||||||
funcName, c.ClientIP(), ticket.Hash, err)
|
funcName, ticket.Hash, err)
|
||||||
sendError(errInternalError, c)
|
sendError(errInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, addr := range addresses {
|
|
||||||
if addr.String() == ticket.FeeAddress {
|
wantScriptVer, wantScript := feeAddr.PaymentScript()
|
||||||
|
|
||||||
|
// Confirm the provided fee transaction contains an output which pays to the
|
||||||
|
// expected payment script. Both script and script version should match.
|
||||||
|
var feePaid dcrutil.Amount
|
||||||
|
for _, txOut := range feeTx.TxOut {
|
||||||
|
if txOut.Version == wantScriptVer && bytes.Equal(txOut.PkScript, wantScript) {
|
||||||
feePaid = dcrutil.Amount(txOut.Value)
|
feePaid = dcrutil.Amount(txOut.Value)
|
||||||
break findAddress
|
break
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Confirm a fee payment was found.
|
||||||
if feePaid == 0 {
|
if feePaid == 0 {
|
||||||
log.Warnf("%s: Fee tx did not include expected payment (ticketHash=%s, feeAddress=%s, clientIP=%s)",
|
log.Warnf("%s: Fee tx did not include expected payment (ticketHash=%s, feeAddress=%s, clientIP=%s)",
|
||||||
funcName, ticket.Hash, ticket.FeeAddress, c.ClientIP())
|
funcName, ticket.Hash, ticket.FeeAddress, c.ClientIP())
|
||||||
sendErrorWithMsg("feetx did not include any payments for fee address", errInvalidFeeTx, c)
|
sendErrorWithMsg(
|
||||||
|
fmt.Sprintf("feetx did not include any payments for fee address %s", ticket.FeeAddress),
|
||||||
|
errInvalidFeeTx, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Confirm fee payment is equal to or larger than the minimum expected.
|
||||||
|
minFee := dcrutil.Amount(ticket.FeeAmount)
|
||||||
|
if feePaid < minFee {
|
||||||
|
log.Warnf("%s: Fee too small (ticketHash=%s, clientIP=%s): was %s, expected minimum %s",
|
||||||
|
funcName, ticket.Hash, c.ClientIP(), feePaid, minFee)
|
||||||
|
sendError(errFeeTooSmall, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the provided voting WIF to get its voting rights script.
|
||||||
pkHash := stdaddr.Hash160(votingWIF.PubKey())
|
pkHash := stdaddr.Hash160(votingWIF.PubKey())
|
||||||
wifAddr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(pkHash, cfg.NetParams)
|
wifAddr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(pkHash, cfg.NetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -173,7 +174,9 @@ findAddress:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode ticket transaction to get its voting address.
|
wantScriptVer, wantScript = wifAddr.VotingRightsScript()
|
||||||
|
|
||||||
|
// Decode ticket transaction to get its voting rights script.
|
||||||
ticketTx, err := decodeTransaction(rawTicket.Hex)
|
ticketTx, err := decodeTransaction(rawTicket.Hex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("%s: Failed to decode ticket hex (ticketHash=%s): %v",
|
log.Warnf("%s: Failed to decode ticket hex (ticketHash=%s): %v",
|
||||||
@ -182,36 +185,19 @@ findAddress:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get ticket voting address.
|
actualScriptVer := ticketTx.TxOut[0].Version
|
||||||
_, votingAddr, _, err := txscript.ExtractPkScriptAddrs(scriptVersion, ticketTx.TxOut[0].PkScript, cfg.NetParams, isTreasuryEnabled)
|
actualScript := ticketTx.TxOut[0].PkScript
|
||||||
if err != nil {
|
|
||||||
log.Errorf("%s: ExtractPK error (ticketHash=%s): %v", funcName, ticket.Hash, err)
|
|
||||||
sendError(errInternalError, c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(votingAddr) == 0 {
|
|
||||||
log.Error("%s: No voting address found for ticket (ticketHash=%s)", funcName, ticket.Hash)
|
|
||||||
sendError(errInternalError, c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure provided private key will allow us to vote this ticket.
|
// Ensure provided voting WIF matches the actual voting address of the
|
||||||
if votingAddr[0].String() != wifAddr.String() {
|
// ticket. Both script and script version should match.
|
||||||
log.Warnf("%s: Voting address does not match provided private key: (ticketHash=%s, votingAddr=%+v, wifAddr=%+v)",
|
if actualScriptVer != wantScriptVer || !bytes.Equal(actualScript, wantScript) {
|
||||||
funcName, ticket.Hash, votingAddr[0], wifAddr)
|
log.Warnf("%s: Voting address does not match provided private key: (ticketHash=%s)",
|
||||||
|
funcName, ticket.Hash)
|
||||||
sendErrorWithMsg("voting address does not match provided private key",
|
sendErrorWithMsg("voting address does not match provided private key",
|
||||||
errInvalidPrivKey, c)
|
errInvalidPrivKey, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
minFee := dcrutil.Amount(ticket.FeeAmount)
|
|
||||||
if feePaid < minFee {
|
|
||||||
log.Warnf("%s: Fee too small (ticketHash=%s, clientIP=%s): was %s, expected minimum %s",
|
|
||||||
funcName, ticket.Hash, c.ClientIP(), feePaid, minFee)
|
|
||||||
sendError(errFeeTooSmall, c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point we are satisfied that the request is valid and the fee tx
|
// At this point we are satisfied that the request is valid and the fee tx
|
||||||
// pays sufficient fees to the expected address. Proceed to update the
|
// pays sufficient fees to the expected address. Proceed to update the
|
||||||
// database, and if the ticket is confirmed broadcast the fee transaction.
|
// database, and if the ticket is confirmed broadcast the fee transaction.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user