From 8b1be464630c54988cabe965c739a8b2575701ed Mon Sep 17 00:00:00 2001 From: Torkel Rogstad Date: Tue, 26 Nov 2019 14:33:48 +0100 Subject: [PATCH] Add generatetoaddress and estimatesmartfee RPCs --- btcjson/btcdextcmds.go | 18 ++++++++++++++ btcjson/btcdextcmds_test.go | 18 ++++++++++++++ btcjson/chainsvrresults.go | 8 ++++++ btcjson/walletsvrcmds.go | 25 +++++++++++++++++++ btcjson/walletsvrcmds_test.go | 28 +++++++++++++++++++++ rpcclient/chain.go | 35 ++++++++++++++++++++++++++ rpcclient/mining.go | 47 +++++++++++++++++++++++++++++++++++ 7 files changed, 179 insertions(+) diff --git a/btcjson/btcdextcmds.go b/btcjson/btcdextcmds.go index 963ccb3a..a3ca46ba 100644 --- a/btcjson/btcdextcmds.go +++ b/btcjson/btcdextcmds.go @@ -59,6 +59,23 @@ func NewDebugLevelCmd(levelSpec string) *DebugLevelCmd { } } +// GenerateToAddressCmd defines the generatetoaddress JSON-RPC command. +type GenerateToAddressCmd struct { + NumBlocks int64 + Address string + MaxTries *int64 `jsonrpcdefault:"1000000"` +} + +// NewGenerateToAddressCmd returns a new instance which can be used to issue a +// generatetoaddress JSON-RPC command. +func NewGenerateToAddressCmd(numBlocks int64, address string, maxTries *int64) *GenerateToAddressCmd { + return &GenerateToAddressCmd{ + NumBlocks: numBlocks, + Address: address, + MaxTries: maxTries, + } +} + // GenerateCmd defines the generate JSON-RPC command. type GenerateCmd struct { NumBlocks uint32 @@ -131,6 +148,7 @@ func init() { MustRegisterCmd("debuglevel", (*DebugLevelCmd)(nil), flags) MustRegisterCmd("node", (*NodeCmd)(nil), flags) MustRegisterCmd("generate", (*GenerateCmd)(nil), flags) + MustRegisterCmd("generatetoaddress", (*GenerateToAddressCmd)(nil), flags) MustRegisterCmd("getbestblock", (*GetBestBlockCmd)(nil), flags) MustRegisterCmd("getcurrentnet", (*GetCurrentNetCmd)(nil), flags) MustRegisterCmd("getheaders", (*GetHeadersCmd)(nil), flags) diff --git a/btcjson/btcdextcmds_test.go b/btcjson/btcdextcmds_test.go index 10e6da38..143ec522 100644 --- a/btcjson/btcdextcmds_test.go +++ b/btcjson/btcdextcmds_test.go @@ -114,6 +114,24 @@ func TestBtcdExtCmds(t *testing.T) { NumBlocks: 1, }, }, + { + name: "generatetoaddress", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("generatetoaddress", 1, "1Address") + }, + staticCmd: func() interface{} { + return btcjson.NewGenerateToAddressCmd(1, "1Address", nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"generatetoaddress","params":[1,"1Address"],"id":1}`, + unmarshalled: &btcjson.GenerateToAddressCmd{ + NumBlocks: 1, + Address: "1Address", + MaxTries: func() *int64 { + var i int64 = 1000000 + return &i + }(), + }, + }, { name: "getbestblock", newCmd: func() (interface{}, error) { diff --git a/btcjson/chainsvrresults.go b/btcjson/chainsvrresults.go index 7ce32f45..a3a26c89 100644 --- a/btcjson/chainsvrresults.go +++ b/btcjson/chainsvrresults.go @@ -624,3 +624,11 @@ type ValidateAddressChainResult struct { IsValid bool `json:"isvalid"` Address string `json:"address,omitempty"` } + +// EstimateSmartFeeResult models the data returned buy the chain server +// estimatesmartfee command +type EstimateSmartFeeResult struct { + FeeRate *float64 `json:"feerate,omitempty"` + Errors []string `json:"errors,omitempty"` + Blocks int64 `json:"blocks"` +} diff --git a/btcjson/walletsvrcmds.go b/btcjson/walletsvrcmds.go index 66975551..53bb93da 100644 --- a/btcjson/walletsvrcmds.go +++ b/btcjson/walletsvrcmds.go @@ -81,6 +81,30 @@ func NewEncryptWalletCmd(passphrase string) *EncryptWalletCmd { } } +// EstimateSmartFeeMode defines the different fee estimation modes available +// for the estimatesmartfee JSON-RPC command. +type EstimateSmartFeeMode string + +var ( + EstimateModeUnset EstimateSmartFeeMode = "UNSET" + EstimateModeEconomical EstimateSmartFeeMode = "ECONOMICAL" + EstimateModeConservative EstimateSmartFeeMode = "CONSERVATIVE" +) + +// EstimateSmartFeeCmd defines the estimatesmartfee JSON-RPC command. +type EstimateSmartFeeCmd struct { + ConfTarget int64 + EstimateMode *EstimateSmartFeeMode `jsonrpcdefault:"\"CONSERVATIVE\""` +} + +// NewEstimateSmartFeeCmd returns a new instance which can be used to issue a +// estimatesmartfee JSON-RPC command. +func NewEstimateSmartFeeCmd(confTarget int64, mode *EstimateSmartFeeMode) *EstimateSmartFeeCmd { + return &EstimateSmartFeeCmd{ + ConfTarget: confTarget, EstimateMode: mode, + } +} + // EstimateFeeCmd defines the estimatefee JSON-RPC command. type EstimateFeeCmd struct { NumBlocks int64 @@ -662,6 +686,7 @@ func init() { MustRegisterCmd("createmultisig", (*CreateMultisigCmd)(nil), flags) MustRegisterCmd("dumpprivkey", (*DumpPrivKeyCmd)(nil), flags) MustRegisterCmd("encryptwallet", (*EncryptWalletCmd)(nil), flags) + MustRegisterCmd("estimatesmartfee", (*EstimateSmartFeeCmd)(nil), flags) MustRegisterCmd("estimatefee", (*EstimateFeeCmd)(nil), flags) MustRegisterCmd("estimatepriority", (*EstimatePriorityCmd)(nil), flags) MustRegisterCmd("getaccount", (*GetAccountCmd)(nil), flags) diff --git a/btcjson/walletsvrcmds_test.go b/btcjson/walletsvrcmds_test.go index efc08cc9..2e0f1d0f 100644 --- a/btcjson/walletsvrcmds_test.go +++ b/btcjson/walletsvrcmds_test.go @@ -128,6 +128,34 @@ func TestWalletSvrCmds(t *testing.T) { NumBlocks: 6, }, }, + { + name: "estimatesmartfee - no mode", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("estimatesmartfee", 6) + }, + staticCmd: func() interface{} { + return btcjson.NewEstimateSmartFeeCmd(6, nil) + }, + marshalled: `{"jsonrpc":"1.0","method":"estimatesmartfee","params":[6],"id":1}`, + unmarshalled: &btcjson.EstimateSmartFeeCmd{ + ConfTarget: 6, + EstimateMode: &btcjson.EstimateModeConservative, + }, + }, + { + name: "estimatesmartfee - economical mode", + newCmd: func() (interface{}, error) { + return btcjson.NewCmd("estimatesmartfee", 6, btcjson.EstimateModeEconomical) + }, + staticCmd: func() interface{} { + return btcjson.NewEstimateSmartFeeCmd(6, &btcjson.EstimateModeEconomical) + }, + marshalled: `{"jsonrpc":"1.0","method":"estimatesmartfee","params":[6,"ECONOMICAL"],"id":1}`, + unmarshalled: &btcjson.EstimateSmartFeeCmd{ + ConfTarget: 6, + EstimateMode: &btcjson.EstimateModeEconomical, + }, + }, { name: "estimatepriority", newCmd: func() (interface{}, error) { diff --git a/rpcclient/chain.go b/rpcclient/chain.go index 6815e1fa..c656a19a 100644 --- a/rpcclient/chain.go +++ b/rpcclient/chain.go @@ -669,6 +669,41 @@ func (c *Client) EstimateFee(numBlocks int64) (float64, error) { return c.EstimateFeeAsync(numBlocks).Receive() } +// FutureEstimateFeeResult is a future promise to deliver the result of a +// EstimateSmartFeeAsync RPC invocation (or an applicable error). +type FutureEstimateSmartFeeResult chan *response + +// Receive waits for the response promised by the future and returns the +// estimated fee. +func (r FutureEstimateSmartFeeResult) Receive() (*btcjson.EstimateSmartFeeResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + var verified btcjson.EstimateSmartFeeResult + err = json.Unmarshal(res, &verified) + if err != nil { + return nil, err + } + return &verified, nil +} + +// EstimateSmartFeeAsync 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 EstimateSmartFee for the blocking version and more details. +func (c *Client) EstimateSmartFeeAsync(confTarget int64, mode *btcjson.EstimateSmartFeeMode) FutureEstimateSmartFeeResult { + cmd := btcjson.NewEstimateSmartFeeCmd(confTarget, mode) + return c.sendCmd(cmd) +} + +// EstimateSmartFee requests the server to estimate a fee level based on the given parameters. +func (c *Client) EstimateSmartFee(confTarget int64, mode *btcjson.EstimateSmartFeeMode) (*btcjson.EstimateSmartFeeResult, error) { + return c.EstimateSmartFeeAsync(confTarget, mode).Receive() +} + // FutureVerifyChainResult is a future promise to deliver the result of a // VerifyChainAsync, VerifyChainLevelAsyncRPC, or VerifyChainBlocksAsync // invocation (or an applicable error). diff --git a/rpcclient/mining.go b/rpcclient/mining.go index 76a9d305..8717d327 100644 --- a/rpcclient/mining.go +++ b/rpcclient/mining.go @@ -61,6 +61,53 @@ func (c *Client) Generate(numBlocks uint32) ([]*chainhash.Hash, error) { return c.GenerateAsync(numBlocks).Receive() } +// FutureGenerateToAddressResult is a future promise to deliver the result of a +// GenerateToAddressResult RPC invocation (or an applicable error). +type FutureGenerateToAddressResult chan *response + +// Receive waits for the response promised by the future and returns the hashes of +// of the generated blocks. +func (f FutureGenerateToAddressResult) Receive() ([]*chainhash.Hash, error) { + res, err := receiveFuture(f) + if err != nil { + return nil, err + } + + // Unmarshal result as a list of strings. + var result []string + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + // Convert each block hash to a chainhash.Hash and store a pointer to + // each. + convertedResult := make([]*chainhash.Hash, len(result)) + for i, hashString := range result { + convertedResult[i], err = chainhash.NewHashFromStr(hashString) + if err != nil { + return nil, err + } + } + + return convertedResult, nil +} + +// GenerateToAddressAsync 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 GenerateToAddress for the blocking version and more details. +func (c *Client) GenerateToAddressAsync(numBlocks int64, address btcutil.Address, maxTries *int64) FutureGenerateToAddressResult { + cmd := btcjson.NewGenerateToAddressCmd(numBlocks, address.EncodeAddress(), maxTries) + return c.sendCmd(cmd) +} + +// GenerateToAddress generates numBlocks blocks to the given address and returns their hashes. +func (c *Client) GenerateToAddress(numBlocks int64, address btcutil.Address, maxTries *int64) ([]*chainhash.Hash, error) { + return c.GenerateToAddressAsync(numBlocks, address, maxTries).Receive() +} + // FutureGetGenerateResult is a future promise to deliver the result of a // GetGenerateAsync RPC invocation (or an applicable error). type FutureGetGenerateResult chan *response