From 1a5bd03e3283edb139946d2651035b757cde3bc8 Mon Sep 17 00:00:00 2001 From: David Hill Date: Thu, 14 May 2020 07:54:42 -0500 Subject: [PATCH] add /feeaddress (#3) --- go.mod | 1 + go.sum | 2 + methods.go | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++- responses.go | 2 +- router.go | 1 + 5 files changed, 122 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 9419d0d..f11b016 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.13 require ( 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/v3 v3.0.0-20200511175520-d08cb3f72b3b github.com/decred/dcrd/dcrec v1.0.0 diff --git a/go.sum b/go.sum index 502ad30..58fdfe5 100644 --- a/go.sum +++ b/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/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-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/standalone v1.1.0 h1:yclvVGEY09Gf8A4GSAo+NCtL1dW2TYJ4OKp4+g0ICI0= 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/ripemd160 v1.0.0 h1:MciTnR4NfBqDFRFjFkrn8WPLP4Vo7t6ww6ghfn6wcXQ= 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/dcrec v1.0.0 h1:W+z6Es+Rai3MXYVoPAxYr5U1DGis0Co33scJ6uH2J6o= github.com/decred/dcrd/dcrec v1.0.0/go.mod h1:HIaqbEJQ+PDzQcORxnqen5/V1FR3B4VpIfmePklt8Q8= diff --git a/methods.go b/methods.go index db48abd..15dec60 100644 --- a/methods.go +++ b/methods.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "crypto/ed25519" + "encoding/base64" "encoding/binary" "encoding/hex" "encoding/json" @@ -14,6 +15,7 @@ import ( "time" "decred.org/dcrwallet/wallet/txrules" + "github.com/decred/dcrd/blockchain/stake/v3" "github.com/decred/dcrd/chaincfg/chainhash" "github.com/decred/dcrd/dcrec" "github.com/decred/dcrd/dcrutil/v3" @@ -52,6 +54,120 @@ func fee(c *gin.Context) { }, 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) { // HTTP GET Params required // feeTx - serialized wire.MsgTx @@ -194,7 +310,7 @@ findAddress: // 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) { var resp dcrdtypes.TxRawResult - err := nodeConnection.Call(ctx, "getrawtransaction", &resp, ticketHash.String()) + err := nodeConnection.Call(ctx, "getrawtransaction", &resp, ticketHash.String(), true) if err != nil { fmt.Printf("PayFee: getrawtransaction: %v", err) return "", errors.New("RPC server error") diff --git a/responses.go b/responses.go index 4d9c185..2357fb2 100644 --- a/responses.go +++ b/responses.go @@ -10,7 +10,7 @@ type feeResponse struct { Fee float64 `json:"fee"` } -type getFeeAddressResponse struct { +type feeAddressResponse struct { Timestamp int64 `json:"timestamp"` TicketHash string `json:"ticketHash"` CommitmentSignature string `json:"commitmentSignature"` diff --git a/router.go b/router.go index b542353..b1b6236 100644 --- a/router.go +++ b/router.go @@ -12,6 +12,7 @@ func newRouter() *gin.Engine { api.Use() { router.GET("/fee", fee) + router.GET("/feeaddress", feeAddress) router.GET("/pubkey", pubKey) router.GET("/payfee", payFee) }