From be28c86a2c44f9ede46a20266b5a660bfc01a8f1 Mon Sep 17 00:00:00 2001 From: Anirudha Bose Date: Sun, 13 Sep 2020 09:47:39 +0200 Subject: [PATCH] rpcclient: add deriveaddresses RPC command --- btcjson/chainsvrcmds.go | 98 +++++++++++++++++++++--------------- btcjson/chainsvrcmds_test.go | 45 +++++++++++++++++ btcjson/chainsvrresults.go | 3 ++ rpcclient/chain.go | 41 +++++++++++++++ rpcclient/example_test.go | 18 +++++++ 5 files changed, 164 insertions(+), 41 deletions(-) diff --git a/btcjson/chainsvrcmds.go b/btcjson/chainsvrcmds.go index a499169de3..b15bce898a 100644 --- a/btcjson/chainsvrcmds.go +++ b/btcjson/chainsvrcmds.go @@ -80,6 +80,47 @@ func NewCreateRawTransactionCmd(inputs []TransactionInput, amounts map[string]fl } } +// DecodeRawTransactionCmd defines the decoderawtransaction JSON-RPC command. +type DecodeRawTransactionCmd struct { + HexTx string +} + +// NewDecodeRawTransactionCmd returns a new instance which can be used to issue +// a decoderawtransaction JSON-RPC command. +func NewDecodeRawTransactionCmd(hexTx string) *DecodeRawTransactionCmd { + return &DecodeRawTransactionCmd{ + HexTx: hexTx, + } +} + +// DecodeScriptCmd defines the decodescript JSON-RPC command. +type DecodeScriptCmd struct { + HexScript string +} + +// NewDecodeScriptCmd returns a new instance which can be used to issue a +// decodescript JSON-RPC command. +func NewDecodeScriptCmd(hexScript string) *DecodeScriptCmd { + return &DecodeScriptCmd{ + HexScript: hexScript, + } +} + +// DeriveAddressesCmd defines the deriveaddresses JSON-RPC command. +type DeriveAddressesCmd struct { + Descriptor string + Range *DescriptorRange +} + +// NewDeriveAddressesCmd returns a new instance which can be used to issue a +// deriveaddresses JSON-RPC command. +func NewDeriveAddressesCmd(descriptor string, descriptorRange *DescriptorRange) *DeriveAddressesCmd { + return &DeriveAddressesCmd{ + Descriptor: descriptor, + Range: descriptorRange, + } +} + // ChangeType defines the different output types to use for the change address // of a transaction built by the node. type ChangeType string @@ -124,32 +165,6 @@ func NewFundRawTransactionCmd(serializedTx []byte, opts FundRawTransactionOpts, } } -// DecodeRawTransactionCmd defines the decoderawtransaction JSON-RPC command. -type DecodeRawTransactionCmd struct { - HexTx string -} - -// NewDecodeRawTransactionCmd returns a new instance which can be used to issue -// a decoderawtransaction JSON-RPC command. -func NewDecodeRawTransactionCmd(hexTx string) *DecodeRawTransactionCmd { - return &DecodeRawTransactionCmd{ - HexTx: hexTx, - } -} - -// DecodeScriptCmd defines the decodescript JSON-RPC command. -type DecodeScriptCmd struct { - HexScript string -} - -// NewDecodeScriptCmd returns a new instance which can be used to issue a -// decodescript JSON-RPC command. -func NewDecodeScriptCmd(hexScript string) *DecodeScriptCmd { - return &DecodeScriptCmd{ - HexScript: hexScript, - } -} - // GetAddedNodeInfoCmd defines the getaddednodeinfo JSON-RPC command. type GetAddedNodeInfoCmd struct { DNS bool @@ -467,6 +482,19 @@ func NewGetConnectionCountCmd() *GetConnectionCountCmd { return &GetConnectionCountCmd{} } +// GetDescriptorInfoCmd defines the getdescriptorinfo JSON-RPC command. +type GetDescriptorInfoCmd struct { + Descriptor string +} + +// NewGetDescriptorInfoCmd returns a new instance which can be used to issue a +// getdescriptorinfo JSON-RPC command. +func NewGetDescriptorInfoCmd(descriptor string) *GetDescriptorInfoCmd { + return &GetDescriptorInfoCmd{ + Descriptor: descriptor, + } +} + // GetDifficultyCmd defines the getdifficulty JSON-RPC command. type GetDifficultyCmd struct{} @@ -956,28 +984,16 @@ func NewVerifyTxOutProofCmd(proof string) *VerifyTxOutProofCmd { } } -// GetDescriptorInfoCmd defines the getdescriptorinfo JSON-RPC command. -type GetDescriptorInfoCmd struct { - Descriptor string -} - -// NewGetDescriptorInfoCmd returns a new instance which can be used to issue a -// getdescriptorinfo JSON-RPC command. -func NewGetDescriptorInfoCmd(descriptor string) *GetDescriptorInfoCmd { - return &GetDescriptorInfoCmd{ - Descriptor: descriptor, - } -} - func init() { // No special flags for commands in this file. flags := UsageFlag(0) MustRegisterCmd("addnode", (*AddNodeCmd)(nil), flags) MustRegisterCmd("createrawtransaction", (*CreateRawTransactionCmd)(nil), flags) - MustRegisterCmd("fundrawtransaction", (*FundRawTransactionCmd)(nil), flags) MustRegisterCmd("decoderawtransaction", (*DecodeRawTransactionCmd)(nil), flags) MustRegisterCmd("decodescript", (*DecodeScriptCmd)(nil), flags) + MustRegisterCmd("deriveaddresses", (*DeriveAddressesCmd)(nil), flags) + MustRegisterCmd("fundrawtransaction", (*FundRawTransactionCmd)(nil), flags) MustRegisterCmd("getaddednodeinfo", (*GetAddedNodeInfoCmd)(nil), flags) MustRegisterCmd("getbestblockhash", (*GetBestBlockHashCmd)(nil), flags) MustRegisterCmd("getblock", (*GetBlockCmd)(nil), flags) @@ -993,6 +1009,7 @@ func init() { MustRegisterCmd("getchaintips", (*GetChainTipsCmd)(nil), flags) MustRegisterCmd("getchaintxstats", (*GetChainTxStatsCmd)(nil), flags) MustRegisterCmd("getconnectioncount", (*GetConnectionCountCmd)(nil), flags) + MustRegisterCmd("getdescriptorinfo", (*GetDescriptorInfoCmd)(nil), flags) MustRegisterCmd("getdifficulty", (*GetDifficultyCmd)(nil), flags) MustRegisterCmd("getgenerate", (*GetGenerateCmd)(nil), flags) MustRegisterCmd("gethashespersec", (*GetHashesPerSecCmd)(nil), flags) @@ -1027,5 +1044,4 @@ func init() { MustRegisterCmd("verifychain", (*VerifyChainCmd)(nil), flags) MustRegisterCmd("verifymessage", (*VerifyMessageCmd)(nil), flags) MustRegisterCmd("verifytxoutproof", (*VerifyTxOutProofCmd)(nil), flags) - MustRegisterCmd("getdescriptorinfo", (*GetDescriptorInfoCmd)(nil), flags) } diff --git a/btcjson/chainsvrcmds_test.go b/btcjson/chainsvrcmds_test.go index bb589fdf6c..bdf16bbee8 100644 --- a/btcjson/chainsvrcmds_test.go +++ b/btcjson/chainsvrcmds_test.go @@ -220,6 +220,51 @@ func TestChainSvrCmds(t *testing.T) { marshalled: `{"jsonrpc":"1.0","method":"decodescript","params":["00"],"id":1}`, unmarshalled: &btcjson.DecodeScriptCmd{HexScript: "00"}, }, + { + name: "deriveaddresses no range", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("deriveaddresses", "00") + }, + staticCmd: func() interface{} { + return btcjson.NewDeriveAddressesCmd("00", nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"deriveaddresses","params":["00"],"id":1}`, + unmarshalled: &btcjson.DeriveAddressesCmd{Descriptor: "00"}, + }, + { + name: "deriveaddresses int range", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd( + "deriveaddresses", "00", btcjson.DescriptorRange{Value: 2}) + }, + staticCmd: func() interface{} { + return btcjson.NewDeriveAddressesCmd( + "00", &btcjson.DescriptorRange{Value: 2}) + }, + marshalled: `{"jsonrpc":"1.0","method":"deriveaddresses","params":["00",2],"id":1}`, + unmarshalled: &btcjson.DeriveAddressesCmd{ + Descriptor: "00", + Range: &btcjson.DescriptorRange{Value: 2}, + }, + }, + { + name: "deriveaddresses slice range", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd( + "deriveaddresses", "00", + btcjson.DescriptorRange{Value: []int{0, 2}}, + ) + }, + staticCmd: func() interface{} { + return btcjson.NewDeriveAddressesCmd( + "00", &btcjson.DescriptorRange{Value: []int{0, 2}}) + }, + marshalled: `{"jsonrpc":"1.0","method":"deriveaddresses","params":["00",[0,2]],"id":1}`, + unmarshalled: &btcjson.DeriveAddressesCmd{ + Descriptor: "00", + Range: &btcjson.DescriptorRange{Value: []int{0, 2}}, + }, + }, { name: "getaddednodeinfo", newCmd: func() (interface{}, error) { diff --git a/btcjson/chainsvrresults.go b/btcjson/chainsvrresults.go index eb9a006077..bdd135b559 100644 --- a/btcjson/chainsvrresults.go +++ b/btcjson/chainsvrresults.go @@ -764,3 +764,6 @@ type GetDescriptorInfoResult struct { IsSolvable bool `json:"issolvable"` // whether the descriptor is solvable HasPrivateKeys bool `json:"hasprivatekeys"` // whether the descriptor has at least one private key } + +// DeriveAddressesResult models the data from the deriveaddresses command. +type DeriveAddressesResult []string diff --git a/rpcclient/chain.go b/rpcclient/chain.go index 4d7c455dd0..35adbc1000 100644 --- a/rpcclient/chain.go +++ b/rpcclient/chain.go @@ -1263,6 +1263,45 @@ func (c *Client) GetBlockStats(hashOrHeight interface{}, stats *[]string) (*btcj return c.GetBlockStatsAsync(hashOrHeight, stats).Receive() } +// FutureDeriveAddressesResult is a future promise to deliver the result of an +// DeriveAddressesAsync RPC invocation (or an applicable error). +type FutureDeriveAddressesResult chan *response + +// Receive waits for the response promised by the future and derives one or more addresses +// corresponding to the given output descriptor. +func (r FutureDeriveAddressesResult) Receive() (*btcjson.DeriveAddressesResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + var deriveAddressesResult btcjson.DeriveAddressesResult + + err = json.Unmarshal(res, &deriveAddressesResult) + if err != nil { + return nil, err + } + + return &deriveAddressesResult, nil +} + +// DeriveAddressesAsync 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 DeriveAddresses for the blocking version and more details. +func (c *Client) DeriveAddressesAsync(descriptor string, descriptorRange *btcjson.DescriptorRange) FutureDeriveAddressesResult { + cmd := btcjson.NewDeriveAddressesCmd(descriptor, descriptorRange) + return c.sendCmd(cmd) +} + +// DeriveAddresses derives one or more addresses corresponding to an output +// descriptor. If a ranged descriptor is used, the end or the range +// (in [begin,end] notation) to derive must be specified. +func (c *Client) DeriveAddresses(descriptor string, descriptorRange *btcjson.DescriptorRange) (*btcjson.DeriveAddressesResult, error) { + return c.DeriveAddressesAsync(descriptor, descriptorRange).Receive() +} + // FutureGetDescriptorInfoResult is a future promise to deliver the result of a // GetDescriptorInfoAsync RPC invocation (or an applicable error). type FutureGetDescriptorInfoResult chan *response @@ -1276,10 +1315,12 @@ func (r FutureGetDescriptorInfoResult) Receive() (*btcjson.GetDescriptorInfoResu } var descriptorInfo btcjson.GetDescriptorInfoResult + err = json.Unmarshal(res, &descriptorInfo) if err != nil { return nil, err } + return &descriptorInfo, nil } diff --git a/rpcclient/example_test.go b/rpcclient/example_test.go index 6083e115ec..c9474512fa 100644 --- a/rpcclient/example_test.go +++ b/rpcclient/example_test.go @@ -59,3 +59,21 @@ func ExampleClient_ImportMulti() { fmt.Println(resp[0].Success) // true } + +func ExampleClient_DeriveAddresses() { + client, err := New(connCfg, nil) + if err != nil { + panic(err) + } + defer client.Shutdown() + + addrs, err := client.DeriveAddresses( + "pkh([f34db33f/44'/0'/0']xpub6Cc939fyHvfB9pPLWd3bSyyQFvgKbwhidca49jGCM5Hz5ypEPGf9JVXB4NBuUfPgoHnMjN6oNgdC9KRqM11RZtL8QLW6rFKziNwHDYhZ6Kx/0/*)#ed7px9nu", + &btcjson.DescriptorRange{Value: []int{0, 2}}) + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", addrs) + // &[14NjenDKkGGq1McUgoSkeUHJpW3rrKLbPW 1Pn6i3cvdGhqbdgNjXHfbaYfiuviPiymXj 181x1NbgGYKLeMXkDdXEAqepG75EgU8XtG] +}