add /feeaddress (#3)
This commit is contained in:
parent
b45bbf8896
commit
1a5bd03e32
1
go.mod
1
go.mod
@ -4,6 +4,7 @@ go 1.13
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
decred.org/dcrwallet v1.2.3-0.20200507155221-397dd551e317
|
decred.org/dcrwallet v1.2.3-0.20200507155221-397dd551e317
|
||||||
|
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200311044114-143c1884e4c8
|
||||||
github.com/decred/dcrd/chaincfg/chainhash v1.0.2
|
github.com/decred/dcrd/chaincfg/chainhash v1.0.2
|
||||||
github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200511175520-d08cb3f72b3b
|
github.com/decred/dcrd/chaincfg/v3 v3.0.0-20200511175520-d08cb3f72b3b
|
||||||
github.com/decred/dcrd/dcrec v1.0.0
|
github.com/decred/dcrd/dcrec v1.0.0
|
||||||
|
|||||||
2
go.sum
2
go.sum
@ -19,6 +19,7 @@ github.com/decred/base58 v1.0.2/go.mod h1:pXP9cXCfM2sFLb2viz2FNIdeMWmZDBKG3ZBYbi
|
|||||||
github.com/decred/dcrd/addrmgr v1.1.0/go.mod h1:exghL+0+QeVvO4MXezWJ1C2tcpBn3ngfuP6S1R+adB8=
|
github.com/decred/dcrd/addrmgr v1.1.0/go.mod h1:exghL+0+QeVvO4MXezWJ1C2tcpBn3ngfuP6S1R+adB8=
|
||||||
github.com/decred/dcrd/blockchain/stake/v2 v2.0.2/go.mod h1:o2TT/l/YFdrt15waUdlZ3g90zfSwlA0WgQqHV9UGJF4=
|
github.com/decred/dcrd/blockchain/stake/v2 v2.0.2/go.mod h1:o2TT/l/YFdrt15waUdlZ3g90zfSwlA0WgQqHV9UGJF4=
|
||||||
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:aDL94kcVJfaaJP+acWUJrlK7g7xEOqTSiFe6bSN3yRQ=
|
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200215031403-6b2ce76f0986/go.mod h1:aDL94kcVJfaaJP+acWUJrlK7g7xEOqTSiFe6bSN3yRQ=
|
||||||
|
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200311044114-143c1884e4c8 h1:6oEo1yQYyfnT9qCERrLWMi9BlDzVBeyl011ssIAVQ3w=
|
||||||
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:4zE60yDWlfCDtmqnyP5o1k1K0oyhNn3Tvqo6F93/+RU=
|
github.com/decred/dcrd/blockchain/stake/v3 v3.0.0-20200311044114-143c1884e4c8/go.mod h1:4zE60yDWlfCDtmqnyP5o1k1K0oyhNn3Tvqo6F93/+RU=
|
||||||
github.com/decred/dcrd/blockchain/standalone v1.1.0 h1:yclvVGEY09Gf8A4GSAo+NCtL1dW2TYJ4OKp4+g0ICI0=
|
github.com/decred/dcrd/blockchain/standalone v1.1.0 h1:yclvVGEY09Gf8A4GSAo+NCtL1dW2TYJ4OKp4+g0ICI0=
|
||||||
github.com/decred/dcrd/blockchain/standalone v1.1.0/go.mod h1:6K8ZgzlWM1Kz2TwXbrtiAvfvIwfAmlzrtpA7CVPCUPE=
|
github.com/decred/dcrd/blockchain/standalone v1.1.0/go.mod h1:6K8ZgzlWM1Kz2TwXbrtiAvfvIwfAmlzrtpA7CVPCUPE=
|
||||||
@ -39,6 +40,7 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK
|
|||||||
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
|
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
|
||||||
github.com/decred/dcrd/crypto/ripemd160 v1.0.0 h1:MciTnR4NfBqDFRFjFkrn8WPLP4Vo7t6ww6ghfn6wcXQ=
|
github.com/decred/dcrd/crypto/ripemd160 v1.0.0 h1:MciTnR4NfBqDFRFjFkrn8WPLP4Vo7t6ww6ghfn6wcXQ=
|
||||||
github.com/decred/dcrd/crypto/ripemd160 v1.0.0/go.mod h1:F0H8cjIuWTRoixr/LM3REB8obcWkmYx0gbxpQWR8RPg=
|
github.com/decred/dcrd/crypto/ripemd160 v1.0.0/go.mod h1:F0H8cjIuWTRoixr/LM3REB8obcWkmYx0gbxpQWR8RPg=
|
||||||
|
github.com/decred/dcrd/database/v2 v2.0.1 h1:ghLzkKpVpwvjrdRv3njrEfkvygQpYQX66sGVs8ha+E8=
|
||||||
github.com/decred/dcrd/database/v2 v2.0.1/go.mod h1:ZOaWTv3IlNqCA+y7q3q5EozgmiDOmNwCSq3ntZn2CDo=
|
github.com/decred/dcrd/database/v2 v2.0.1/go.mod h1:ZOaWTv3IlNqCA+y7q3q5EozgmiDOmNwCSq3ntZn2CDo=
|
||||||
github.com/decred/dcrd/dcrec v1.0.0 h1:W+z6Es+Rai3MXYVoPAxYr5U1DGis0Co33scJ6uH2J6o=
|
github.com/decred/dcrd/dcrec v1.0.0 h1:W+z6Es+Rai3MXYVoPAxYr5U1DGis0Co33scJ6uH2J6o=
|
||||||
github.com/decred/dcrd/dcrec v1.0.0/go.mod h1:HIaqbEJQ+PDzQcORxnqen5/V1FR3B4VpIfmePklt8Q8=
|
github.com/decred/dcrd/dcrec v1.0.0/go.mod h1:HIaqbEJQ+PDzQcORxnqen5/V1FR3B4VpIfmePklt8Q8=
|
||||||
|
|||||||
118
methods.go
118
methods.go
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/ed25519"
|
"crypto/ed25519"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -14,6 +15,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"decred.org/dcrwallet/wallet/txrules"
|
"decred.org/dcrwallet/wallet/txrules"
|
||||||
|
"github.com/decred/dcrd/blockchain/stake/v3"
|
||||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||||
"github.com/decred/dcrd/dcrec"
|
"github.com/decred/dcrd/dcrec"
|
||||||
"github.com/decred/dcrd/dcrutil/v3"
|
"github.com/decred/dcrd/dcrutil/v3"
|
||||||
@ -52,6 +54,120 @@ func fee(c *gin.Context) {
|
|||||||
}, http.StatusOK, c)
|
}, http.StatusOK, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func feeAddress(c *gin.Context) {
|
||||||
|
// HTTP GET Params required
|
||||||
|
// ticketHash - hash of ticket
|
||||||
|
// signature - signmessage signature using the ticket commitment address
|
||||||
|
// - message = "vsp v3 getfeeaddress ticketHash"
|
||||||
|
|
||||||
|
// ticketHash
|
||||||
|
ticketHashStr := c.Param("ticketHash")
|
||||||
|
if len(ticketHashStr) != chainhash.MaxHashStringSize {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, errors.New("invalid ticket hash"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
txHash, err := chainhash.NewHashFromStr(ticketHashStr)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, errors.New("invalid ticket hash"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// signature - sanity check signature is in base64 encoding
|
||||||
|
signature := c.Param("signature")
|
||||||
|
if _, err = base64.StdEncoding.DecodeString(signature); err != nil {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, errors.New("invalid signature"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check DB for cached response
|
||||||
|
// TODO: check db
|
||||||
|
|
||||||
|
ctx := c.Request.Context()
|
||||||
|
|
||||||
|
var resp dcrdtypes.TxRawResult
|
||||||
|
err = nodeConnection.Call(ctx, "getrawtransaction", &resp, txHash.String(), true)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, errors.New("unknown transaction"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resp.Confirmations < 2 || resp.BlockHeight < 0 {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, errors.New("transaction does not have minimum confirmations"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resp.Confirmations > int64(uint32(cfg.netParams.TicketMaturity)+cfg.netParams.TicketExpiry) {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, errors.New("ticket has expired"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msgHex, err := hex.DecodeString(resp.Hex)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, errors.New("unable to decode transaction"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msgTx := wire.NewMsgTx()
|
||||||
|
if err = msgTx.FromBytes(msgHex); err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, errors.New("failed to deserialize transaction"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !stake.IsSStx(msgTx) {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, errors.New("transaction is not a ticket"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(msgTx.TxOut) != 3 {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, errors.New("invalid ticket"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get commitment address
|
||||||
|
addr, err := stake.AddrFromSStxPkScrCommitment(msgTx.TxOut[1].PkScript, cfg.netParams)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, errors.New("failed to get commitment address"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify message
|
||||||
|
message := fmt.Sprintf("vsp v3 getfeeaddress %s", msgTx.TxHash())
|
||||||
|
var valid bool
|
||||||
|
err = nodeConnection.Call(ctx, "verifymessage", &valid, addr.Address(), signature, message)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, errors.New("RPC server error"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !valid {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, errors.New("invalid signature"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get blockheight and sdiff which is required by
|
||||||
|
// txrules.StakePoolTicketFee, and store them in the database
|
||||||
|
// for processing by payfee
|
||||||
|
var blockHeader dcrdtypes.GetBlockHeaderVerboseResult
|
||||||
|
err = nodeConnection.Call(ctx, "getblockheader", &blockHeader, resp.BlockHash, true)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, errors.New("RPC server error"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sDiff := blockHeader.SBits
|
||||||
|
|
||||||
|
var newAddress string
|
||||||
|
err = nodeConnection.Call(ctx, "getnewaddress", &newAddress, "fees")
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, errors.New("unable to generate fee address"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Insert into DB
|
||||||
|
_ = sDiff
|
||||||
|
|
||||||
|
sendJSONResponse(feeAddressResponse{
|
||||||
|
Timestamp: time.Now().Unix(),
|
||||||
|
TicketHash: txHash.String(),
|
||||||
|
CommitmentSignature: signature,
|
||||||
|
FeeAddress: newAddress,
|
||||||
|
}, http.StatusOK, c)
|
||||||
|
}
|
||||||
|
|
||||||
func payFee(c *gin.Context) {
|
func payFee(c *gin.Context) {
|
||||||
// HTTP GET Params required
|
// HTTP GET Params required
|
||||||
// feeTx - serialized wire.MsgTx
|
// feeTx - serialized wire.MsgTx
|
||||||
@ -194,7 +310,7 @@ findAddress:
|
|||||||
// PayFee2 is copied from the stakepoold implementation in #625
|
// PayFee2 is copied from the stakepoold implementation in #625
|
||||||
func PayFee2(ctx context.Context, ticketHash *chainhash.Hash, votingWIF *dcrutil.WIF, feeTx *wire.MsgTx) (string, error) {
|
func PayFee2(ctx context.Context, ticketHash *chainhash.Hash, votingWIF *dcrutil.WIF, feeTx *wire.MsgTx) (string, error) {
|
||||||
var resp dcrdtypes.TxRawResult
|
var resp dcrdtypes.TxRawResult
|
||||||
err := nodeConnection.Call(ctx, "getrawtransaction", &resp, ticketHash.String())
|
err := nodeConnection.Call(ctx, "getrawtransaction", &resp, ticketHash.String(), true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("PayFee: getrawtransaction: %v", err)
|
fmt.Printf("PayFee: getrawtransaction: %v", err)
|
||||||
return "", errors.New("RPC server error")
|
return "", errors.New("RPC server error")
|
||||||
|
|||||||
@ -10,7 +10,7 @@ type feeResponse struct {
|
|||||||
Fee float64 `json:"fee"`
|
Fee float64 `json:"fee"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type getFeeAddressResponse struct {
|
type feeAddressResponse struct {
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
TicketHash string `json:"ticketHash"`
|
TicketHash string `json:"ticketHash"`
|
||||||
CommitmentSignature string `json:"commitmentSignature"`
|
CommitmentSignature string `json:"commitmentSignature"`
|
||||||
|
|||||||
@ -12,6 +12,7 @@ func newRouter() *gin.Engine {
|
|||||||
api.Use()
|
api.Use()
|
||||||
{
|
{
|
||||||
router.GET("/fee", fee)
|
router.GET("/fee", fee)
|
||||||
|
router.GET("/feeaddress", feeAddress)
|
||||||
router.GET("/pubkey", pubKey)
|
router.GET("/pubkey", pubKey)
|
||||||
router.GET("/payfee", payFee)
|
router.GET("/payfee", payFee)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user