mirror of
https://github.com/LBRYFoundation/lbcd.git
synced 2025-08-23 17:47:24 +00:00
btcjson,rpcclient: add support for PSBT commands to rpcclient
This commit is contained in:
parent
fff96610aa
commit
6f49f1f194
6 changed files with 283 additions and 3 deletions
|
@ -80,11 +80,24 @@ func NewCreateRawTransactionCmd(inputs []TransactionInput, amounts map[string]fl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChangeType defines the different output types to use for the change address
|
||||||
|
// of a transaction built by the node.
|
||||||
|
type ChangeType string
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ChangeTypeLegacy indicates a P2PKH change address type.
|
||||||
|
ChangeTypeLegacy ChangeType = "legacy"
|
||||||
|
// ChangeTypeP2SHSegWit indicates a P2WPKH-in-P2SH change address type.
|
||||||
|
ChangeTypeP2SHSegWit ChangeType = "p2sh-segwit"
|
||||||
|
// ChangeTypeBech32 indicates a P2WPKH change address type.
|
||||||
|
ChangeTypeBech32 ChangeType = "bech32"
|
||||||
|
)
|
||||||
|
|
||||||
// FundRawTransactionOpts are the different options that can be passed to rawtransaction
|
// FundRawTransactionOpts are the different options that can be passed to rawtransaction
|
||||||
type FundRawTransactionOpts struct {
|
type FundRawTransactionOpts struct {
|
||||||
ChangeAddress *string `json:"changeAddress,omitempty"`
|
ChangeAddress *string `json:"changeAddress,omitempty"`
|
||||||
ChangePosition *int `json:"changePosition,omitempty"`
|
ChangePosition *int `json:"changePosition,omitempty"`
|
||||||
ChangeType *string `json:"change_type,omitempty"`
|
ChangeType *ChangeType `json:"change_type,omitempty"`
|
||||||
IncludeWatching *bool `json:"includeWatching,omitempty"`
|
IncludeWatching *bool `json:"includeWatching,omitempty"`
|
||||||
LockUnspents *bool `json:"lockUnspents,omitempty"`
|
LockUnspents *bool `json:"lockUnspents,omitempty"`
|
||||||
FeeRate *float64 `json:"feeRate,omitempty"` // BTC/kB
|
FeeRate *float64 `json:"feeRate,omitempty"` // BTC/kB
|
||||||
|
|
|
@ -127,7 +127,7 @@ func TestChainSvrCmds(t *testing.T) {
|
||||||
}
|
}
|
||||||
changeAddress := "bcrt1qeeuctq9wutlcl5zatge7rjgx0k45228cxez655"
|
changeAddress := "bcrt1qeeuctq9wutlcl5zatge7rjgx0k45228cxez655"
|
||||||
change := 1
|
change := 1
|
||||||
changeType := "legacy"
|
changeType := btcjson.ChangeTypeLegacy
|
||||||
watching := true
|
watching := true
|
||||||
lockUnspents := true
|
lockUnspents := true
|
||||||
feeRate := 0.7
|
feeRate := 0.7
|
||||||
|
@ -151,7 +151,7 @@ func TestChainSvrCmds(t *testing.T) {
|
||||||
unmarshalled: func() interface{} {
|
unmarshalled: func() interface{} {
|
||||||
changeAddress := "bcrt1qeeuctq9wutlcl5zatge7rjgx0k45228cxez655"
|
changeAddress := "bcrt1qeeuctq9wutlcl5zatge7rjgx0k45228cxez655"
|
||||||
change := 1
|
change := 1
|
||||||
changeType := "legacy"
|
changeType := btcjson.ChangeTypeLegacy
|
||||||
watching := true
|
watching := true
|
||||||
lockUnspents := true
|
lockUnspents := true
|
||||||
feeRate := 0.7
|
feeRate := 0.7
|
||||||
|
|
|
@ -8,8 +8,11 @@
|
||||||
package btcjson
|
package btcjson
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddMultisigAddressCmd defines the addmutisigaddress JSON-RPC command.
|
// AddMultisigAddressCmd defines the addmutisigaddress JSON-RPC command.
|
||||||
|
@ -893,6 +896,88 @@ func NewImportMultiCmd(requests []ImportMultiRequest, options *ImportMultiOption
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PsbtInput represents an input to include in the PSBT created by the
|
||||||
|
// WalletCreateFundedPsbtCmd command.
|
||||||
|
type PsbtInput struct {
|
||||||
|
Txid string `json:"txid"`
|
||||||
|
Vout uint32 `json:"vout"`
|
||||||
|
Sequence uint32 `json:"sequence"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PsbtOutput represents an output to include in the PSBT created by the
|
||||||
|
// WalletCreateFundedPsbtCmd command.
|
||||||
|
type PsbtOutput map[string]interface{}
|
||||||
|
|
||||||
|
// NewPsbtOutput returns a new instance of a PSBT output to use with the
|
||||||
|
// WalletCreateFundedPsbtCmd command.
|
||||||
|
func NewPsbtOutput(address string, amount btcutil.Amount) PsbtOutput {
|
||||||
|
return PsbtOutput{address: amount.ToBTC()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPsbtDataOutput returns a new instance of a PSBT data output to use with
|
||||||
|
// the WalletCreateFundedPsbtCmd command.
|
||||||
|
func NewPsbtDataOutput(data []byte) PsbtOutput {
|
||||||
|
return PsbtOutput{"data": hex.EncodeToString(data)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalletCreateFundedPsbtOpts represents the optional options struct provided
|
||||||
|
// with a WalletCreateFundedPsbtCmd command.
|
||||||
|
type WalletCreateFundedPsbtOpts struct {
|
||||||
|
ChangeAddress *string `json:"changeAddress,omitempty"`
|
||||||
|
ChangePosition *int64 `json:"changePosition,omitempty"`
|
||||||
|
ChangeType *ChangeType `json:"change_type,omitempty"`
|
||||||
|
IncludeWatching *bool `json:"includeWatching,omitempty"`
|
||||||
|
LockUnspents *bool `json:"lockUnspents,omitempty"`
|
||||||
|
FeeRate *int64 `json:"feeRate,omitempty"`
|
||||||
|
SubtractFeeFromOutputs *[]int64 `json:"subtractFeeFromOutputs,omitempty"`
|
||||||
|
Replaceable *bool `json:"replaceable,omitempty"`
|
||||||
|
ConfTarget *int64 `json:"conf_target,omitempty"`
|
||||||
|
EstimateMode *string `json:"estimate_mode,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalletCreateFundedPsbtCmd defines the walletcreatefundedpsbt JSON-RPC command.
|
||||||
|
type WalletCreateFundedPsbtCmd struct {
|
||||||
|
Inputs []PsbtInput
|
||||||
|
Outputs []PsbtOutput
|
||||||
|
Locktime *uint32
|
||||||
|
Options *WalletCreateFundedPsbtOpts
|
||||||
|
Bip32Derivs *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWalletCreateFundedPsbtCmd returns a new instance which can be used to issue a
|
||||||
|
// walletcreatefundedpsbt JSON-RPC command.
|
||||||
|
func NewWalletCreateFundedPsbtCmd(
|
||||||
|
inputs []PsbtInput, outputs []PsbtOutput, locktime *uint32,
|
||||||
|
options *WalletCreateFundedPsbtOpts, bip32Derivs *bool,
|
||||||
|
) *WalletCreateFundedPsbtCmd {
|
||||||
|
return &WalletCreateFundedPsbtCmd{
|
||||||
|
Inputs: inputs,
|
||||||
|
Outputs: outputs,
|
||||||
|
Locktime: locktime,
|
||||||
|
Options: options,
|
||||||
|
Bip32Derivs: bip32Derivs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalletProcessPsbtCmd defines the walletprocesspsbt JSON-RPC command.
|
||||||
|
type WalletProcessPsbtCmd struct {
|
||||||
|
Psbt string
|
||||||
|
Sign *bool `jsonrpcdefault:"true"`
|
||||||
|
SighashType *string `jsonrpcdefault:"\"ALL\""`
|
||||||
|
Bip32Derivs *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWalletProcessPsbtCmd returns a new instance which can be used to issue a
|
||||||
|
// walletprocesspsbt JSON-RPC command.
|
||||||
|
func NewWalletProcessPsbtCmd(psbt string, sign *bool, sighashType *string, bip32Derivs *bool) *WalletProcessPsbtCmd {
|
||||||
|
return &WalletProcessPsbtCmd{
|
||||||
|
Psbt: psbt,
|
||||||
|
Sign: sign,
|
||||||
|
SighashType: sighashType,
|
||||||
|
Bip32Derivs: bip32Derivs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// The commands in this file are only usable with a wallet server.
|
// The commands in this file are only usable with a wallet server.
|
||||||
flags := UFWalletOnly
|
flags := UFWalletOnly
|
||||||
|
@ -939,4 +1024,6 @@ func init() {
|
||||||
MustRegisterCmd("walletlock", (*WalletLockCmd)(nil), flags)
|
MustRegisterCmd("walletlock", (*WalletLockCmd)(nil), flags)
|
||||||
MustRegisterCmd("walletpassphrase", (*WalletPassphraseCmd)(nil), flags)
|
MustRegisterCmd("walletpassphrase", (*WalletPassphraseCmd)(nil), flags)
|
||||||
MustRegisterCmd("walletpassphrasechange", (*WalletPassphraseChangeCmd)(nil), flags)
|
MustRegisterCmd("walletpassphrasechange", (*WalletPassphraseChangeCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("walletcreatefundedpsbt", (*WalletCreateFundedPsbtCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("walletprocesspsbt", (*WalletProcessPsbtCmd)(nil), flags)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
|
"github.com/btcsuite/btcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestWalletSvrCmds tests all of the wallet server commands marshal and
|
// TestWalletSvrCmds tests all of the wallet server commands marshal and
|
||||||
|
@ -1495,6 +1496,81 @@ func TestWalletSvrCmds(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "walletcreatefundedpsbt",
|
||||||
|
newCmd: func() (interface{}, error) {
|
||||||
|
return btcjson.NewCmd(
|
||||||
|
"walletcreatefundedpsbt",
|
||||||
|
[]btcjson.PsbtInput{
|
||||||
|
{
|
||||||
|
Txid: "1234",
|
||||||
|
Vout: 0,
|
||||||
|
Sequence: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]btcjson.PsbtOutput{
|
||||||
|
btcjson.NewPsbtOutput("1234", btcutil.Amount(1234)),
|
||||||
|
btcjson.NewPsbtDataOutput([]byte{1, 2, 3, 4}),
|
||||||
|
},
|
||||||
|
btcjson.Uint32(1),
|
||||||
|
btcjson.WalletCreateFundedPsbtOpts{},
|
||||||
|
btcjson.Bool(true),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
staticCmd: func() interface{} {
|
||||||
|
return btcjson.NewWalletCreateFundedPsbtCmd(
|
||||||
|
[]btcjson.PsbtInput{
|
||||||
|
{
|
||||||
|
Txid: "1234",
|
||||||
|
Vout: 0,
|
||||||
|
Sequence: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]btcjson.PsbtOutput{
|
||||||
|
btcjson.NewPsbtOutput("1234", btcutil.Amount(1234)),
|
||||||
|
btcjson.NewPsbtDataOutput([]byte{1, 2, 3, 4}),
|
||||||
|
},
|
||||||
|
btcjson.Uint32(1),
|
||||||
|
&btcjson.WalletCreateFundedPsbtOpts{},
|
||||||
|
btcjson.Bool(true),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
marshalled: `{"jsonrpc":"1.0","method":"walletcreatefundedpsbt","params":[[{"txid":"1234","vout":0,"sequence":0}],[{"1234":0.00001234},{"data":"01020304"}],1,{},true],"id":1}`,
|
||||||
|
unmarshalled: &btcjson.WalletCreateFundedPsbtCmd{
|
||||||
|
Inputs: []btcjson.PsbtInput{
|
||||||
|
{
|
||||||
|
Txid: "1234",
|
||||||
|
Vout: 0,
|
||||||
|
Sequence: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outputs: []btcjson.PsbtOutput{
|
||||||
|
btcjson.NewPsbtOutput("1234", btcutil.Amount(1234)),
|
||||||
|
btcjson.NewPsbtDataOutput([]byte{1, 2, 3, 4}),
|
||||||
|
},
|
||||||
|
Locktime: btcjson.Uint32(1),
|
||||||
|
Options: &btcjson.WalletCreateFundedPsbtOpts{},
|
||||||
|
Bip32Derivs: btcjson.Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "walletprocesspsbt",
|
||||||
|
newCmd: func() (interface{}, error) {
|
||||||
|
return btcjson.NewCmd(
|
||||||
|
"walletprocesspsbt", "1234", btcjson.Bool(true), btcjson.String("ALL"), btcjson.Bool(true))
|
||||||
|
},
|
||||||
|
staticCmd: func() interface{} {
|
||||||
|
return btcjson.NewWalletProcessPsbtCmd(
|
||||||
|
"1234", btcjson.Bool(true), btcjson.String("ALL"), btcjson.Bool(true))
|
||||||
|
},
|
||||||
|
marshalled: `{"jsonrpc":"1.0","method":"walletprocesspsbt","params":["1234",true,"ALL",true],"id":1}`,
|
||||||
|
unmarshalled: &btcjson.WalletProcessPsbtCmd{
|
||||||
|
Psbt: "1234",
|
||||||
|
Sign: btcjson.Bool(true),
|
||||||
|
SighashType: btcjson.String("ALL"),
|
||||||
|
Bip32Derivs: btcjson.Bool(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
|
|
@ -186,3 +186,18 @@ type ImportMultiResults []struct {
|
||||||
Error *RPCError `json:"error,omitempty"`
|
Error *RPCError `json:"error,omitempty"`
|
||||||
Warnings *[]string `json:"warnings,omitempty"`
|
Warnings *[]string `json:"warnings,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WalletCreateFundedPsbtResult models the data returned from the
|
||||||
|
// walletcreatefundedpsbtresult command.
|
||||||
|
type WalletCreateFundedPsbtResult struct {
|
||||||
|
Psbt string `json:"psbt"`
|
||||||
|
Fee float64 `json:"fee"`
|
||||||
|
ChangePos int64 `json:"changepos"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalletProcessPsbtResult models the data returned from the
|
||||||
|
// walletprocesspsbtresult command.
|
||||||
|
type WalletProcessPsbtResult struct {
|
||||||
|
Psbt string `json:"psbt"`
|
||||||
|
Complete bool `json:"complete"`
|
||||||
|
}
|
||||||
|
|
|
@ -2424,6 +2424,95 @@ func (c *Client) GetInfo() (*btcjson.InfoWalletResult, error) {
|
||||||
return c.GetInfoAsync().Receive()
|
return c.GetInfoAsync().Receive()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FutureImportPubKeyResult is a future promise to deliver the result of an
|
||||||
|
// WalletCreateFundedPsbt RPC invocation (or an applicable error).
|
||||||
|
type FutureWalletCreateFundedPsbtResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the
|
||||||
|
// partially signed transaction in PSBT format along with the resulting fee
|
||||||
|
// and change output index.
|
||||||
|
func (r FutureWalletCreateFundedPsbtResult) Receive() (*btcjson.WalletCreateFundedPsbtResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a getinfo result object.
|
||||||
|
var psbtRes btcjson.WalletCreateFundedPsbtResult
|
||||||
|
err = json.Unmarshal(res, &psbtRes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &psbtRes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalletCreateFundedPsbtAsync returns an instance of a type that can be used
|
||||||
|
// to get the result of the RPC at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See WalletCreateFundedPsbt for the blocking version and more details.
|
||||||
|
func (c *Client) WalletCreateFundedPsbtAsync(
|
||||||
|
inputs []btcjson.PsbtInput, outputs []btcjson.PsbtOutput, locktime *uint32,
|
||||||
|
options *btcjson.WalletCreateFundedPsbtOpts, bip32Derivs *bool,
|
||||||
|
) FutureWalletCreateFundedPsbtResult {
|
||||||
|
cmd := btcjson.NewWalletCreateFundedPsbtCmd(inputs, outputs, locktime, options, bip32Derivs)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalletCreateFundedPsbt creates and funds a transaction in the Partially
|
||||||
|
// Signed Transaction format. Inputs will be added if supplied inputs are not
|
||||||
|
// enough.
|
||||||
|
func (c *Client) WalletCreateFundedPsbt(
|
||||||
|
inputs []btcjson.PsbtInput, outputs []btcjson.PsbtOutput, locktime *uint32,
|
||||||
|
options *btcjson.WalletCreateFundedPsbtOpts, bip32Derivs *bool,
|
||||||
|
) (*btcjson.WalletCreateFundedPsbtResult, error) {
|
||||||
|
return c.WalletCreateFundedPsbtAsync(inputs, outputs, locktime, options, bip32Derivs).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureWalletProcessPsbtResult is a future promise to deliver the result of a
|
||||||
|
// WalletCreateFundedPsb RPC invocation (or an applicable error).
|
||||||
|
type FutureWalletProcessPsbtResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns an updated
|
||||||
|
// PSBT with signed inputs from the wallet and a boolen indicating if the the
|
||||||
|
// transaction has a complete set of signatures.
|
||||||
|
func (r FutureWalletProcessPsbtResult) Receive() (*btcjson.WalletProcessPsbtResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a getinfo result object.
|
||||||
|
var psbtRes btcjson.WalletProcessPsbtResult
|
||||||
|
err = json.Unmarshal(res, &psbtRes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &psbtRes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalletProcessPsbtAsync returns an instance of a type that can be used
|
||||||
|
// to get the result of the RPC at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See WalletProcessPsbt for the blocking version and more details.
|
||||||
|
func (c *Client) WalletProcessPsbtAsync(
|
||||||
|
psbt string, sign *bool, sighashType SigHashType, bip32Derivs *bool,
|
||||||
|
) FutureWalletProcessPsbtResult {
|
||||||
|
cmd := btcjson.NewWalletProcessPsbtCmd(psbt, sign, btcjson.String(sighashType.String()), bip32Derivs)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalletProcessPsbt updates a PSBT with input information from our wallet and
|
||||||
|
// then signs inputs.
|
||||||
|
func (c *Client) WalletProcessPsbt(
|
||||||
|
psbt string, sign *bool, sighashType SigHashType, bip32Derivs *bool,
|
||||||
|
) (*btcjson.WalletProcessPsbtResult, error) {
|
||||||
|
return c.WalletProcessPsbtAsync(psbt, sign, sighashType, bip32Derivs).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(davec): Implement
|
// TODO(davec): Implement
|
||||||
// backupwallet (NYI in btcwallet)
|
// backupwallet (NYI in btcwallet)
|
||||||
// encryptwallet (Won't be supported by btcwallet since it's always encrypted)
|
// encryptwallet (Won't be supported by btcwallet since it's always encrypted)
|
||||||
|
|
Loading…
Add table
Reference in a new issue