* Delay fee broadcast and adding tickets to wallets. - Adds a `background` package which implements a dcrd notification handler. On each blockconnected notification, tickets with 6+ confirmations are marked confirmed, relevant fee transactions are broadcast, and any fees with 6+ confirmations have their tickets added to voting wallets. - VSP fee is now an absolute value measured in DCR rather than a percentage. This simplifies the code and is more appropriate for an MVP. We can re-add percentage based fees later. - Database code for tickets is now simplified to just "Insert/Update", rather than having functions for updating particular fields. - Pay fee response no longer includes the fee tx hash, because we dont necessarily broadcast the fee tx straight away. * Const for required confs
104 lines
2.6 KiB
Go
104 lines
2.6 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)
|
|
}
|