Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rpcclient: add deriveaddresses RPC command #1631

Merged
merged 1 commit into from
Sep 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 57 additions & 41 deletions btcjson/chainsvrcmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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{}

Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}
45 changes: 45 additions & 0 deletions btcjson/chainsvrcmds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions btcjson/chainsvrresults.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
41 changes: 41 additions & 0 deletions rpcclient/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}

Expand Down
18 changes: 18 additions & 0 deletions rpcclient/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}