* Calculate fee from percentage. - Reverted config to accept a fee percentage, not absolute value. - The fee amount to be paid is now included in the `getfeeaddress` response. The current best block is used to calculate the fee percentage, and new blocks may be mined before the fee is paid, so the fee expiry period is shortened from 24 hours to 1 hour to mitigate this. - Rename ticket db field to FeeAmount so it is more representative of the data it holds. - API fields renamed to "FeePercentage" and "FeeAmount" - Relay fee is still hard coded. * Use getbestblockhash
120 lines
3.0 KiB
Go
120 lines
3.0 KiB
Go
package rpc
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/decred/dcrd/blockchain/stake/v3"
|
|
"github.com/decred/dcrd/chaincfg/v3"
|
|
dcrdtypes "github.com/decred/dcrd/rpc/jsonrpc/types/v2"
|
|
"github.com/decred/dcrd/wire"
|
|
)
|
|
|
|
const (
|
|
requiredDcrdVersion = "6.1.1"
|
|
)
|
|
|
|
// DcrdRPC provides methods for calling dcrd JSON-RPCs without exposing the details
|
|
// of JSON encoding.
|
|
type DcrdRPC struct {
|
|
Caller
|
|
ctx context.Context
|
|
}
|
|
|
|
// DcrdClient creates a new DcrdRPC client instance from a caller.
|
|
func DcrdClient(ctx context.Context, c Caller) (*DcrdRPC, error) {
|
|
|
|
// Verify dcrd is at the required api version.
|
|
var verMap map[string]dcrdtypes.VersionResult
|
|
err := c.Call(ctx, "version", &verMap)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("version check failed: %v", err)
|
|
}
|
|
dcrdVersion, exists := verMap["dcrdjsonrpcapi"]
|
|
if !exists {
|
|
return nil, fmt.Errorf("version response missing 'dcrdjsonrpcapi'")
|
|
}
|
|
if dcrdVersion.VersionString != requiredDcrdVersion {
|
|
return nil, fmt.Errorf("wrong dcrd RPC version: got %s, expected %s",
|
|
dcrdVersion.VersionString, requiredDcrdVersion)
|
|
}
|
|
|
|
// TODO: Ensure correct network.
|
|
|
|
return &DcrdRPC{c, ctx}, nil
|
|
}
|
|
|
|
func (c *DcrdRPC) GetRawTransaction(txHash string) (*dcrdtypes.TxRawResult, error) {
|
|
verbose := 1
|
|
var resp dcrdtypes.TxRawResult
|
|
err := c.Call(c.ctx, "getrawtransaction", &resp, txHash, verbose)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &resp, nil
|
|
}
|
|
|
|
func (c *DcrdRPC) SendRawTransaction(txHex string) (string, error) {
|
|
allowHighFees := false
|
|
var txHash string
|
|
err := c.Call(c.ctx, "sendrawtransaction", &txHash, txHex, allowHighFees)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return txHash, nil
|
|
}
|
|
|
|
func (c *DcrdRPC) GetTicketCommitmentAddress(ticketHash string, netParams *chaincfg.Params) (string, error) {
|
|
// Retrieve and parse the transaction.
|
|
resp, err := c.GetRawTransaction(ticketHash)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
msgHex, err := hex.DecodeString(resp.Hex)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
msgTx := wire.NewMsgTx()
|
|
if err = msgTx.FromBytes(msgHex); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Ensure transaction is a valid ticket.
|
|
if !stake.IsSStx(msgTx) {
|
|
return "", errors.New("invalid transcation - not sstx")
|
|
}
|
|
if len(msgTx.TxOut) != 3 {
|
|
return "", fmt.Errorf("invalid transcation - expected 3 outputs, got %d", len(msgTx.TxOut))
|
|
}
|
|
|
|
// Get ticket commitment address.
|
|
addr, err := stake.AddrFromSStxPkScrCommitment(msgTx.TxOut[1].PkScript, netParams)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return addr.Address(), nil
|
|
}
|
|
|
|
func (c *DcrdRPC) NotifyBlocks() error {
|
|
return c.Call(c.ctx, "notifyblocks", nil)
|
|
}
|
|
|
|
func (c *DcrdRPC) GetBestBlockHeader() (*dcrdtypes.GetBlockHeaderVerboseResult, error) {
|
|
var bestBlockHash string
|
|
err := c.Call(c.ctx, "getbestblockhash", &bestBlockHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
verbose := true
|
|
var blockHeader dcrdtypes.GetBlockHeaderVerboseResult
|
|
err = c.Call(c.ctx, "getblockheader", &blockHeader, bestBlockHash, verbose)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &blockHeader, nil
|
|
}
|