From 30622e2e151c0e109132686a3c9cefb6f3242fdb Mon Sep 17 00:00:00 2001 From: Anirudha Bose Date: Fri, 9 Oct 2020 22:20:49 +0200 Subject: [PATCH] rpcclient: implement createwallet with functional options --- btcjson/walletsvrcmds.go | 23 ++++++++++ btcjson/walletsvrcmds_test.go | 55 +++++++++++++++++++++++ btcjson/walletsvrresults.go | 6 +++ rpcclient/example_test.go | 23 +++++++++- rpcclient/wallet.go | 85 +++++++++++++++++++++++++++++++++++ 5 files changed, 190 insertions(+), 2 deletions(-) diff --git a/btcjson/walletsvrcmds.go b/btcjson/walletsvrcmds.go index f63a59445b..2ff9ae1832 100644 --- a/btcjson/walletsvrcmds.go +++ b/btcjson/walletsvrcmds.go @@ -63,6 +63,28 @@ func NewCreateMultisigCmd(nRequired int, keys []string) *CreateMultisigCmd { } } +// CreateWalletCmd defines the createwallet JSON-RPC command. +type CreateWalletCmd struct { + WalletName string + DisablePrivateKeys *bool `jsonrpcdefault:"false"` + Blank *bool `jsonrpcdefault:"false"` + Passphrase *string `jsonrpcdefault:"\"\""` + AvoidReuse *bool `jsonrpcdefault:"false"` +} + +// NewCreateWalletCmd returns a new instance which can be used to issue a +// createwallet JSON-RPC command. +func NewCreateWalletCmd(walletName string, disablePrivateKeys *bool, + blank *bool, passphrase *string, avoidReuse *bool) *CreateWalletCmd { + return &CreateWalletCmd{ + WalletName: walletName, + DisablePrivateKeys: disablePrivateKeys, + Blank: blank, + Passphrase: passphrase, + AvoidReuse: avoidReuse, + } +} + // DumpPrivKeyCmd defines the dumpprivkey JSON-RPC command. type DumpPrivKeyCmd struct { Address string @@ -1065,6 +1087,7 @@ func init() { MustRegisterCmd("addwitnessaddress", (*AddWitnessAddressCmd)(nil), flags) MustRegisterCmd("backupwallet", (*BackupWalletCmd)(nil), flags) MustRegisterCmd("createmultisig", (*CreateMultisigCmd)(nil), flags) + MustRegisterCmd("createwallet", (*CreateWalletCmd)(nil), flags) MustRegisterCmd("dumpprivkey", (*DumpPrivKeyCmd)(nil), flags) MustRegisterCmd("encryptwallet", (*EncryptWalletCmd)(nil), flags) MustRegisterCmd("estimatesmartfee", (*EstimateSmartFeeCmd)(nil), flags) diff --git a/btcjson/walletsvrcmds_test.go b/btcjson/walletsvrcmds_test.go index 5d36a12ab4..9ee6f0c749 100644 --- a/btcjson/walletsvrcmds_test.go +++ b/btcjson/walletsvrcmds_test.go @@ -62,6 +62,61 @@ func TestWalletSvrCmds(t *testing.T) { Account: btcjson.String("test"), }, }, + { + name: "createwallet", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("createwallet", "mywallet", true, true, "secret", true) + }, + staticCmd: func() interface{} { + return btcjson.NewCreateWalletCmd("mywallet", + btcjson.Bool(true), btcjson.Bool(true), + btcjson.String("secret"), btcjson.Bool(true)) + }, + marshalled: `{"jsonrpc":"1.0","method":"createwallet","params":["mywallet",true,true,"secret",true],"id":1}`, + unmarshalled: &btcjson.CreateWalletCmd{ + WalletName: "mywallet", + DisablePrivateKeys: btcjson.Bool(true), + Blank: btcjson.Bool(true), + Passphrase: btcjson.String("secret"), + AvoidReuse: btcjson.Bool(true), + }, + }, + { + name: "createwallet - optional1", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("createwallet", "mywallet") + }, + staticCmd: func() interface{} { + return btcjson.NewCreateWalletCmd("mywallet", + nil, nil, nil, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"createwallet","params":["mywallet"],"id":1}`, + unmarshalled: &btcjson.CreateWalletCmd{ + WalletName: "mywallet", + DisablePrivateKeys: btcjson.Bool(false), + Blank: btcjson.Bool(false), + Passphrase: btcjson.String(""), + AvoidReuse: btcjson.Bool(false), + }, + }, + { + name: "createwallet - optional2", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("createwallet", "mywallet", "null", "null", "secret") + }, + staticCmd: func() interface{} { + return btcjson.NewCreateWalletCmd("mywallet", + nil, nil, btcjson.String("secret"), nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"createwallet","params":["mywallet",null,null,"secret"],"id":1}`, + unmarshalled: &btcjson.CreateWalletCmd{ + WalletName: "mywallet", + DisablePrivateKeys: nil, + Blank: nil, + Passphrase: btcjson.String("secret"), + AvoidReuse: btcjson.Bool(false), + }, + }, { name: "addwitnessaddress", newCmd: func() (interface{}, error) { diff --git a/btcjson/walletsvrresults.go b/btcjson/walletsvrresults.go index e2ba201daf..78a6e647f5 100644 --- a/btcjson/walletsvrresults.go +++ b/btcjson/walletsvrresults.go @@ -11,6 +11,12 @@ import ( "github.com/btcsuite/btcd/txscript" ) +// CreateWalletResult models the result of the createwallet command. +type CreateWalletResult struct { + Name string `json:"name"` + Warning string `json:"warning"` +} + // embeddedAddressInfo includes all getaddressinfo output fields, excluding // metadata and relation to the wallet. // diff --git a/rpcclient/example_test.go b/rpcclient/example_test.go index 35159317b5..9ba9adadef 100644 --- a/rpcclient/example_test.go +++ b/rpcclient/example_test.go @@ -11,8 +11,8 @@ import ( var connCfg = &ConnConfig{ Host: "localhost:8332", - User: "yourrpcuser", - Pass: "yourrpcpass", + User: "user", + Pass: "pass", HTTPPostMode: true, DisableTLS: true, } @@ -135,3 +135,22 @@ func ExampleClient_GetTxOutSetInfo() { fmt.Println(r.Transactions) // 9285603 fmt.Println(r.DiskSize) // 1320871611 } + +func ExampleClient_CreateWallet() { + client, err := New(connCfg, nil) + if err != nil { + panic(err) + } + defer client.Shutdown() + + r, err := client.CreateWallet( + "mywallet", + WithCreateWalletBlank(), + WithCreateWalletPassphrase("secret"), + ) + if err != nil { + panic(err) + } + + fmt.Println(r.Name) // mywallet +} diff --git a/rpcclient/wallet.go b/rpcclient/wallet.go index c7064bfe5f..4bff252304 100644 --- a/rpcclient/wallet.go +++ b/rpcclient/wallet.go @@ -939,6 +939,91 @@ func (c *Client) CreateNewAccount(account string) error { return c.CreateNewAccountAsync(account).Receive() } +// FutureCreateWalletResult is a future promise to deliver the result of a +// CreateWalletAsync RPC invocation (or an applicable error). +type FutureCreateWalletResult chan *response + +// Receive waits for the response promised by the future and returns the +// result of creating a new wallet. +func (r FutureCreateWalletResult) Receive() (*btcjson.CreateWalletResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + var createWalletResult btcjson.CreateWalletResult + err = json.Unmarshal(res, &createWalletResult) + if err != nil { + return nil, err + } + return &createWalletResult, nil +} + +// CreateWalletOpt defines a functional-option to be used with CreateWallet +// method. +type CreateWalletOpt func(*btcjson.CreateWalletCmd) + +// WithCreateWalletDisablePrivateKeys disables the possibility of private keys +// to be used with a wallet created using the CreateWallet method. Using this +// option will make the wallet watch-only. +func WithCreateWalletDisablePrivateKeys() CreateWalletOpt { + return func(c *btcjson.CreateWalletCmd) { + c.DisablePrivateKeys = btcjson.Bool(true) + } +} + +// WithCreateWalletBlank specifies creation of a blank wallet. +func WithCreateWalletBlank() CreateWalletOpt { + return func(c *btcjson.CreateWalletCmd) { + c.Blank = btcjson.Bool(true) + } +} + +// WithCreateWalletPassphrase specifies a passphrase to encrypt the wallet +// with. +func WithCreateWalletPassphrase(value string) CreateWalletOpt { + return func(c *btcjson.CreateWalletCmd) { + c.Passphrase = btcjson.String(value) + } +} + +// WithCreateWalletAvoidReuse specifies keeping track of coin reuse, and +// treat dirty and clean coins differently with privacy considerations in mind. +func WithCreateWalletAvoidReuse() CreateWalletOpt { + return func(c *btcjson.CreateWalletCmd) { + c.AvoidReuse = btcjson.Bool(true) + } +} + +// CreateWalletAsync 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 CreateWallet for the blocking version and more details. +func (c *Client) CreateWalletAsync(name string, opts ...CreateWalletOpt) FutureCreateWalletResult { + cmd := btcjson.NewCreateWalletCmd(name, nil, nil, nil, nil) + + // Apply each specified option to mutate the default command. + for _, opt := range opts { + opt(cmd) + } + + return c.sendCmd(cmd) +} + +// CreateWallet creates a new wallet account, with the possibility to use +// private keys. +// +// Optional parameters can be specified using functional-options pattern. The +// following functions are available: +// * WithCreateWalletDisablePrivateKeys +// * WithCreateWalletBlank +// * WithCreateWalletPassphrase +// * WithCreateWalletAvoidReuse +func (c *Client) CreateWallet(name string, opts ...CreateWalletOpt) (*btcjson.CreateWalletResult, error) { + return c.CreateWalletAsync(name, opts...).Receive() +} + // FutureGetAddressInfoResult is a future promise to deliver the result of an // GetAddressInfoAsync RPC invocation (or an applicable error). type FutureGetAddressInfoResult chan *response