Add fundrawtransaction RPC call
This commit is contained in:
parent
73d69f09d0
commit
e4f59022a3
4 changed files with 231 additions and 1 deletions
|
@ -8,6 +8,7 @@
|
||||||
package btcjson
|
package btcjson
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
@ -79,6 +80,37 @@ func NewCreateRawTransactionCmd(inputs []TransactionInput, amounts map[string]fl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FundRawTransactionOpts are the different options that can be passed to rawtransaction
|
||||||
|
type FundRawTransactionOpts struct {
|
||||||
|
ChangeAddress *string `json:"changeAddress,omitempty"`
|
||||||
|
ChangePosition *int `json:"changePosition,omitempty"`
|
||||||
|
ChangeType *string `json:"change_type,omitempty"`
|
||||||
|
IncludeWatching *bool `json:"includeWatching,omitempty"`
|
||||||
|
LockUnspents *bool `json:"lockUnspents,omitempty"`
|
||||||
|
FeeRate *float64 `json:"feeRate,omitempty"` // BTC/kB
|
||||||
|
SubtractFeeFromOutputs []int `json:"subtractFeeFromOutputs,omitempty"`
|
||||||
|
Replaceable *bool `json:"replaceable,omitempty"`
|
||||||
|
ConfTarget *int `json:"conf_target,omitempty"`
|
||||||
|
EstimateMode *EstimateSmartFeeMode `json:"estimate_mode,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FundRawTransactionCmd defines the fundrawtransaction JSON-RPC command
|
||||||
|
type FundRawTransactionCmd struct {
|
||||||
|
HexTx string
|
||||||
|
Options FundRawTransactionOpts
|
||||||
|
IsWitness *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFundRawTransactionCmd returns a new instance which can be used to issue
|
||||||
|
// a fundrawtransaction JSON-RPC command
|
||||||
|
func NewFundRawTransactionCmd(serializedTx []byte, opts FundRawTransactionOpts, isWitness *bool) *FundRawTransactionCmd {
|
||||||
|
return &FundRawTransactionCmd{
|
||||||
|
HexTx: hex.EncodeToString(serializedTx),
|
||||||
|
Options: opts,
|
||||||
|
IsWitness: isWitness,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DecodeRawTransactionCmd defines the decoderawtransaction JSON-RPC command.
|
// DecodeRawTransactionCmd defines the decoderawtransaction JSON-RPC command.
|
||||||
type DecodeRawTransactionCmd struct {
|
type DecodeRawTransactionCmd struct {
|
||||||
HexTx string
|
HexTx string
|
||||||
|
@ -856,6 +888,7 @@ func init() {
|
||||||
|
|
||||||
MustRegisterCmd("addnode", (*AddNodeCmd)(nil), flags)
|
MustRegisterCmd("addnode", (*AddNodeCmd)(nil), flags)
|
||||||
MustRegisterCmd("createrawtransaction", (*CreateRawTransactionCmd)(nil), flags)
|
MustRegisterCmd("createrawtransaction", (*CreateRawTransactionCmd)(nil), flags)
|
||||||
|
MustRegisterCmd("fundrawtransaction", (*FundRawTransactionCmd)(nil), flags)
|
||||||
MustRegisterCmd("decoderawtransaction", (*DecodeRawTransactionCmd)(nil), flags)
|
MustRegisterCmd("decoderawtransaction", (*DecodeRawTransactionCmd)(nil), flags)
|
||||||
MustRegisterCmd("decodescript", (*DecodeScriptCmd)(nil), flags)
|
MustRegisterCmd("decodescript", (*DecodeScriptCmd)(nil), flags)
|
||||||
MustRegisterCmd("getaddednodeinfo", (*GetAddedNodeInfoCmd)(nil), flags)
|
MustRegisterCmd("getaddednodeinfo", (*GetAddedNodeInfoCmd)(nil), flags)
|
||||||
|
|
|
@ -6,6 +6,7 @@ package btcjson_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -95,7 +96,108 @@ func TestChainSvrCmds(t *testing.T) {
|
||||||
LockTime: btcjson.Int64(12312333333),
|
LockTime: btcjson.Int64(12312333333),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "fundrawtransaction - empty opts",
|
||||||
|
newCmd: func() (i interface{}, e error) {
|
||||||
|
return btcjson.NewCmd("fundrawtransaction", "deadbeef", "{}")
|
||||||
|
},
|
||||||
|
staticCmd: func() interface{} {
|
||||||
|
deadbeef, err := hex.DecodeString("deadbeef")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return btcjson.NewFundRawTransactionCmd(deadbeef, btcjson.FundRawTransactionOpts{}, nil)
|
||||||
|
},
|
||||||
|
marshalled: `{"jsonrpc":"1.0","method":"fundrawtransaction","params":["deadbeef",{}],"id":1}`,
|
||||||
|
unmarshalled: &btcjson.FundRawTransactionCmd{
|
||||||
|
HexTx: "deadbeef",
|
||||||
|
Options: btcjson.FundRawTransactionOpts{},
|
||||||
|
IsWitness: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fundrawtransaction - full opts",
|
||||||
|
newCmd: func() (i interface{}, e error) {
|
||||||
|
return btcjson.NewCmd("fundrawtransaction", "deadbeef", `{"changeAddress":"bcrt1qeeuctq9wutlcl5zatge7rjgx0k45228cxez655","changePosition":1,"change_type":"legacy","includeWatching":true,"lockUnspents":true,"feeRate":0.7,"subtractFeeFromOutputs":[0],"replaceable":true,"conf_target":8,"estimate_mode":"ECONOMICAL"}`)
|
||||||
|
},
|
||||||
|
staticCmd: func() interface{} {
|
||||||
|
deadbeef, err := hex.DecodeString("deadbeef")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
changeAddress := "bcrt1qeeuctq9wutlcl5zatge7rjgx0k45228cxez655"
|
||||||
|
change := 1
|
||||||
|
changeType := "legacy"
|
||||||
|
watching := true
|
||||||
|
lockUnspents := true
|
||||||
|
feeRate := 0.7
|
||||||
|
replaceable := true
|
||||||
|
confTarget := 8
|
||||||
|
|
||||||
|
return btcjson.NewFundRawTransactionCmd(deadbeef, btcjson.FundRawTransactionOpts{
|
||||||
|
ChangeAddress: &changeAddress,
|
||||||
|
ChangePosition: &change,
|
||||||
|
ChangeType: &changeType,
|
||||||
|
IncludeWatching: &watching,
|
||||||
|
LockUnspents: &lockUnspents,
|
||||||
|
FeeRate: &feeRate,
|
||||||
|
SubtractFeeFromOutputs: []int{0},
|
||||||
|
Replaceable: &replaceable,
|
||||||
|
ConfTarget: &confTarget,
|
||||||
|
EstimateMode: &btcjson.EstimateModeEconomical,
|
||||||
|
}, nil)
|
||||||
|
},
|
||||||
|
marshalled: `{"jsonrpc":"1.0","method":"fundrawtransaction","params":["deadbeef",{"changeAddress":"bcrt1qeeuctq9wutlcl5zatge7rjgx0k45228cxez655","changePosition":1,"change_type":"legacy","includeWatching":true,"lockUnspents":true,"feeRate":0.7,"subtractFeeFromOutputs":[0],"replaceable":true,"conf_target":8,"estimate_mode":"ECONOMICAL"}],"id":1}`,
|
||||||
|
unmarshalled: func() interface{} {
|
||||||
|
changeAddress := "bcrt1qeeuctq9wutlcl5zatge7rjgx0k45228cxez655"
|
||||||
|
change := 1
|
||||||
|
changeType := "legacy"
|
||||||
|
watching := true
|
||||||
|
lockUnspents := true
|
||||||
|
feeRate := 0.7
|
||||||
|
replaceable := true
|
||||||
|
confTarget := 8
|
||||||
|
return &btcjson.FundRawTransactionCmd{
|
||||||
|
HexTx: "deadbeef",
|
||||||
|
Options: btcjson.FundRawTransactionOpts{
|
||||||
|
ChangeAddress: &changeAddress,
|
||||||
|
ChangePosition: &change,
|
||||||
|
ChangeType: &changeType,
|
||||||
|
IncludeWatching: &watching,
|
||||||
|
LockUnspents: &lockUnspents,
|
||||||
|
FeeRate: &feeRate,
|
||||||
|
SubtractFeeFromOutputs: []int{0},
|
||||||
|
Replaceable: &replaceable,
|
||||||
|
ConfTarget: &confTarget,
|
||||||
|
EstimateMode: &btcjson.EstimateModeEconomical,
|
||||||
|
},
|
||||||
|
IsWitness: nil,
|
||||||
|
}
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fundrawtransaction - iswitness",
|
||||||
|
newCmd: func() (i interface{}, e error) {
|
||||||
|
return btcjson.NewCmd("fundrawtransaction", "deadbeef", "{}", true)
|
||||||
|
},
|
||||||
|
staticCmd: func() interface{} {
|
||||||
|
deadbeef, err := hex.DecodeString("deadbeef")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
t := true
|
||||||
|
return btcjson.NewFundRawTransactionCmd(deadbeef, btcjson.FundRawTransactionOpts{}, &t)
|
||||||
|
},
|
||||||
|
marshalled: `{"jsonrpc":"1.0","method":"fundrawtransaction","params":["deadbeef",{},true],"id":1}`,
|
||||||
|
unmarshalled: &btcjson.FundRawTransactionCmd{
|
||||||
|
HexTx: "deadbeef",
|
||||||
|
Options: btcjson.FundRawTransactionOpts{},
|
||||||
|
IsWitness: func() *bool {
|
||||||
|
t := true
|
||||||
|
return &t
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "decoderawtransaction",
|
name: "decoderawtransaction",
|
||||||
newCmd: func() (interface{}, error) {
|
newCmd: func() (interface{}, error) {
|
||||||
|
|
|
@ -4,7 +4,14 @@
|
||||||
|
|
||||||
package btcjson
|
package btcjson
|
||||||
|
|
||||||
import "encoding/json"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
"github.com/btcsuite/btcutil"
|
||||||
|
)
|
||||||
|
|
||||||
// GetBlockHeaderVerboseResult models the data from the getblockheader command when
|
// GetBlockHeaderVerboseResult models the data from the getblockheader command when
|
||||||
// the verbose flag is set. When the verbose flag is not set, getblockheader
|
// the verbose flag is set. When the verbose flag is not set, getblockheader
|
||||||
|
@ -676,3 +683,50 @@ type EstimateSmartFeeResult struct {
|
||||||
Errors []string `json:"errors,omitempty"`
|
Errors []string `json:"errors,omitempty"`
|
||||||
Blocks int64 `json:"blocks"`
|
Blocks int64 `json:"blocks"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ json.Unmarshaler = &FundRawTransactionResult{}
|
||||||
|
|
||||||
|
type rawFundRawTransactionResult struct {
|
||||||
|
Transaction string `json:"hex"`
|
||||||
|
Fee float64 `json:"fee"`
|
||||||
|
ChangePosition int `json:"changepos"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FundRawTransactionResult is the result of the fundrawtransaction JSON-RPC call
|
||||||
|
type FundRawTransactionResult struct {
|
||||||
|
Transaction *wire.MsgTx
|
||||||
|
Fee btcutil.Amount
|
||||||
|
ChangePosition int // the position of the added change output, or -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals the result of the fundrawtransaction JSON-RPC call
|
||||||
|
func (f *FundRawTransactionResult) UnmarshalJSON(data []byte) error {
|
||||||
|
var rawRes rawFundRawTransactionResult
|
||||||
|
if err := json.Unmarshal(data, &rawRes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
txBytes, err := hex.DecodeString(rawRes.Transaction)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var msgTx wire.MsgTx
|
||||||
|
witnessErr := msgTx.Deserialize(bytes.NewReader(txBytes))
|
||||||
|
if witnessErr != nil {
|
||||||
|
legacyErr := msgTx.DeserializeNoWitness(bytes.NewReader(txBytes))
|
||||||
|
if legacyErr != nil {
|
||||||
|
return legacyErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fee, err := btcutil.NewAmount(rawRes.Fee)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Transaction = &msgTx
|
||||||
|
f.Fee = fee
|
||||||
|
f.ChangePosition = rawRes.ChangePosition
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -205,6 +205,47 @@ func (c *Client) DecodeRawTransaction(serializedTx []byte) (*btcjson.TxRawResult
|
||||||
return c.DecodeRawTransactionAsync(serializedTx).Receive()
|
return c.DecodeRawTransactionAsync(serializedTx).Receive()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FutureFundRawTransactionResult is a future promise to deliver the result
|
||||||
|
// of a FutureFundRawTransactionAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureFundRawTransactionResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns information
|
||||||
|
// about a funding attempt
|
||||||
|
func (r FutureFundRawTransactionResult) Receive() (*btcjson.FundRawTransactionResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var marshalled btcjson.FundRawTransactionResult
|
||||||
|
if err := json.Unmarshal(res, &marshalled); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &marshalled, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FundRawTransactionAsync 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 FundRawTransaction for the blocking version and more details.
|
||||||
|
func (c *Client) FundRawTransactionAsync(tx *wire.MsgTx, opts btcjson.FundRawTransactionOpts, isWitness *bool) FutureFundRawTransactionResult {
|
||||||
|
var txBuf bytes.Buffer
|
||||||
|
if err := tx.Serialize(&txBuf); err != nil {
|
||||||
|
return newFutureError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewFundRawTransactionCmd(txBuf.Bytes(), opts, isWitness)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FundRawTransaction returns the result of trying to fund the given transaction with
|
||||||
|
// funds from the node wallet
|
||||||
|
func (c *Client) FundRawTransaction(tx *wire.MsgTx, opts btcjson.FundRawTransactionOpts, isWitness *bool) (*btcjson.FundRawTransactionResult, error) {
|
||||||
|
return c.FundRawTransactionAsync(tx, opts, isWitness).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
// FutureCreateRawTransactionResult is a future promise to deliver the result
|
// FutureCreateRawTransactionResult is a future promise to deliver the result
|
||||||
// of a CreateRawTransactionAsync RPC invocation (or an applicable error).
|
// of a CreateRawTransactionAsync RPC invocation (or an applicable error).
|
||||||
type FutureCreateRawTransactionResult chan *response
|
type FutureCreateRawTransactionResult chan *response
|
||||||
|
|
Loading…
Reference in a new issue