From e3f130ade5c3aac465c9408710d22a94456bfdde Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 7 May 2014 11:14:39 -0500 Subject: [PATCH] Initial commit. --- .gitignore | 28 + LICENSE | 13 + README.md | 11 + chain.go | 494 ++++++++++++ doc.go | 8 + extensions.go | 291 +++++++ infrastructure.go | 937 ++++++++++++++++++++++ log.go | 74 ++ mining.go | 361 +++++++++ net.go | 327 ++++++++ notify.go | 687 ++++++++++++++++ rawtransactions.go | 516 ++++++++++++ wallet.go | 1862 ++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 5609 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 chain.go create mode 100644 doc.go create mode 100644 extensions.go create mode 100644 infrastructure.go create mode 100644 log.go create mode 100644 mining.go create mode 100644 net.go create mode 100644 notify.go create mode 100644 rawtransactions.go create mode 100644 wallet.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..612e2e96 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# Temp files +*~ + +# Log files +*.log + +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..315cb819 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2014 Conformal Systems LLC. + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..ba5d56f8 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +btcrpcclient +============ + +This package is currently under development. + +You really probably don't want to use this yet! + +## License + +Package btcrpcclient is licensed under the [copyfree](http://copyfree.org) ISC +License. diff --git a/chain.go b/chain.go new file mode 100644 index 00000000..edb2362c --- /dev/null +++ b/chain.go @@ -0,0 +1,494 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "bytes" + "encoding/hex" + "fmt" + "github.com/conformal/btcjson" + "github.com/conformal/btcutil" + "github.com/conformal/btcwire" +) + +// FutureGetBestBlockHashResult is a future promise to deliver the result of a +// GetBestBlockAsync RPC invocation (or an applicable error). +type FutureGetBestBlockHashResult chan *futureResult + +// Receive waits for the response promised by the future and returns the hash of +// the best block in the longest block chain. +func (r FutureGetBestBlockHashResult) Receive() (*btcwire.ShaHash, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + txHashStr, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getbestblockhash: %T\n", reply) + } + + return btcwire.NewShaHashFromStr(txHashStr) +} + +// GetBestBlockHashAsync 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 GetBestBlockHash for the blocking version and more details. +func (c *Client) GetBestBlockHashAsync() FutureGetBestBlockHashResult { + id := c.NextID() + cmd, err := btcjson.NewGetBestBlockHashCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetBestBlockHash returns the hash of the best block in the longest block +// chain. +func (c *Client) GetBestBlockHash() (*btcwire.ShaHash, error) { + return c.GetBestBlockHashAsync().Receive() +} + +// FutureGetBlockResult is a future promise to deliver the result of a +// GetBlockAsync RPC invocation (or an applicable error). +type FutureGetBlockResult chan *futureResult + +// Receive waits for the response promised by the future and returns the raw +// block requested from the server given its hash. +func (r FutureGetBlockResult) Receive() (*btcutil.Block, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + blockHex, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getblock (verbose=0): %T\n", reply) + } + + // Decode the serialized block hex to raw bytes. + serializedBlock, err := hex.DecodeString(blockHex) + if err != nil { + return nil, err + } + + // Deserialize the block and return it. + var msgBlock btcwire.MsgBlock + msgBlock.Deserialize(bytes.NewBuffer(serializedBlock)) + if err != nil { + return nil, err + } + return btcutil.NewBlock(&msgBlock), nil +} + +// GetBlockAsync 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 GetBlock for the blocking version and more details. +func (c *Client) GetBlockAsync(blockHash *btcwire.ShaHash) FutureGetBlockResult { + id := c.NextID() + cmd, err := btcjson.NewGetBlockCmd(id, blockHash.String(), false) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetBlock returns a raw block from the server given its hash. +// +// See GetBlockVerbose to retrieve a data structure with information about the +// block instead. +func (c *Client) GetBlock(blockHash *btcwire.ShaHash) (*btcutil.Block, error) { + return c.GetBlockAsync(blockHash).Receive() +} + +// FutureGetBlockVerboseResult is a future promise to deliver the result of a +// GetBlockVerboseAsync RPC invocation (or an applicable error). +type FutureGetBlockVerboseResult chan *futureResult + +// Receive waits for the response promised by the future and returns the data +// structure from the server with information about the requested block. +func (r FutureGetBlockVerboseResult) Receive() (*btcjson.BlockResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + blockResult, ok := reply.(btcjson.BlockResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getblock (verbose=1): %T\n", reply) + } + + return &blockResult, nil +} + +// GetBlockVerboseAsync 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 GetBlockVerbose for the blocking version and more details. +func (c *Client) GetBlockVerboseAsync(blockHash *btcwire.ShaHash, verboseTx bool) FutureGetBlockVerboseResult { + id := c.NextID() + cmd, err := btcjson.NewGetBlockCmd(id, blockHash.String(), true, + verboseTx) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetBlockVerbose returns a data structure from the server with information +// about a block given its hash. +// +// See GetBlock to retrieve a raw block instead. +func (c *Client) GetBlockVerbose(blockHash *btcwire.ShaHash, verboseTx bool) (*btcjson.BlockResult, error) { + return c.GetBlockVerboseAsync(blockHash, verboseTx).Receive() +} + +// FutureGetBlockCountResult is a future promise to deliver the result of a +// GetBlockCountAsync RPC invocation (or an applicable error). +type FutureGetBlockCountResult chan *futureResult + +// Receive waits for the response promised by the future and returns the number +// of blocks in the longest block chain. +func (r FutureGetBlockCountResult) Receive() (int64, error) { + reply, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Ensure the returned data is the expected type. + count, ok := reply.(float64) + if !ok { + return 0, fmt.Errorf("unexpected response type for "+ + "getblockcount: %T\n", reply) + } + + return int64(count), nil +} + +// GetBlockCountAsync 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 GetBlockCount for the blocking version and more details. +func (c *Client) GetBlockCountAsync() FutureGetBlockCountResult { + id := c.NextID() + cmd, err := btcjson.NewGetBlockCountCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetBlockCount returns the number of blocks in the longest block chain. +func (c *Client) GetBlockCount() (int64, error) { + return c.GetBlockCountAsync().Receive() +} + +// FutureGetDifficultyResult is a future promise to deliver the result of a +// GetDifficultyAsync RPC invocation (or an applicable error). +type FutureGetDifficultyResult chan *futureResult + +// Receive waits for the response promised by the future and returns the +// proof-of-work difficulty as a multiple of the minimum difficulty. +func (r FutureGetDifficultyResult) Receive() (float64, error) { + reply, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Ensure the returned data is the expected type. + difficulty, ok := reply.(float64) + if !ok { + return 0, fmt.Errorf("unexpected response type for "+ + "getdifficulty: %T\n", reply) + } + + return difficulty, nil +} + +// GetDifficultyAsync 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 GetDifficulty for the blocking version and more details. +func (c *Client) GetDifficultyAsync() FutureGetDifficultyResult { + id := c.NextID() + cmd, err := btcjson.NewGetDifficultyCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetDifficulty returns the proof-of-work difficulty as a multiple of the +// minimum difficulty. +func (c *Client) GetDifficulty() (float64, error) { + return c.GetDifficultyAsync().Receive() +} + +// FutureGetBlockHashResult is a future promise to deliver the result of a +// GetBlockHashAsync RPC invocation (or an applicable error). +type FutureGetBlockHashResult chan *futureResult + +// Receive waits for the response promised by the future and returns the hash of +// the block in the best block chain at the given height. +func (r FutureGetBlockHashResult) Receive() (*btcwire.ShaHash, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + txHashStr, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getblockhash: %T\n", reply) + } + + return btcwire.NewShaHashFromStr(txHashStr) +} + +// GetBlockHashAsync 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 GetBlockHash for the blocking version and more details. +func (c *Client) GetBlockHashAsync(blockHeight int64) FutureGetBlockHashResult { + id := c.NextID() + cmd, err := btcjson.NewGetBlockHashCmd(id, blockHeight) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetBlockHash returns the hash of the block in the best block chain at the +// given height. +func (c *Client) GetBlockHash(blockHeight int64) (*btcwire.ShaHash, error) { + return c.GetBlockHashAsync(blockHeight).Receive() +} + +// FutureGetRawMempoolResult is a future promise to deliver the result of a +// GetRawMempoolAsync RPC invocation (or an applicable error). +type FutureGetRawMempoolResult chan *futureResult + +// Receive waits for the response promised by the future and returns the hashes +// of all transactions in the memory pool. +func (r FutureGetRawMempoolResult) Receive() ([]*btcwire.ShaHash, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + txHashStrs, ok := reply.([]string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getrawmempool (verbose=false): %T\n", reply) + } + + txHashes := make([]*btcwire.ShaHash, 0, len(txHashStrs)) + for _, hashStr := range txHashStrs { + txHash, err := btcwire.NewShaHashFromStr(hashStr) + if err != nil { + return nil, err + } + txHashes = append(txHashes, txHash) + } + + return txHashes, nil +} + +// GetRawMempoolAsync 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 GetRawMempool for the blocking version and more details. +func (c *Client) GetRawMempoolAsync() FutureGetRawMempoolResult { + id := c.NextID() + cmd, err := btcjson.NewGetRawMempoolCmd(id, false) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetRawMempool returns the hashes of all transactions in the memory pool. +// +// See GetRawMempoolVerbose to retrieve data structures with information about +// the transactions instead. +func (c *Client) GetRawMempool() ([]*btcwire.ShaHash, error) { + return c.GetRawMempoolAsync().Receive() +} + +// FutureGetRawMempoolVerboseResult is a future promise to deliver the result of +// a GetRawMempoolVerboseAsync RPC invocation (or an applicable error). +type FutureGetRawMempoolVerboseResult chan *futureResult + +// Receive waits for the response promised by the future and returns a map of +// transaction hashes to an associated data structure with information about the +// transaction for all transactions in the memory pool. +func (r FutureGetRawMempoolVerboseResult) Receive() (map[string]btcjson.GetRawMempoolResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + mempoolItems, ok := reply.(map[string]btcjson.GetRawMempoolResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getrawmempool (verbose=true): %T\n", reply) + } + + return mempoolItems, nil +} + +// GetRawMempoolVerboseAsync 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 GetRawMempoolVerbose for the blocking version and more details. +func (c *Client) GetRawMempoolVerboseAsync() FutureGetRawMempoolVerboseResult { + id := c.NextID() + cmd, err := btcjson.NewGetRawMempoolCmd(id, true) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetRawMempoolVerbose returns a map of transaction hashes to an associated +// data structure with information about the transaction for all transactions in +// the memory pool. +// +// See GetRawMempool to retrieve only the transaction hashes instead. +func (c *Client) GetRawMempoolVerbose() (map[string]btcjson.GetRawMempoolResult, error) { + return c.GetRawMempoolVerboseAsync().Receive() +} + +// FutureVerifyChainResult is a future promise to deliver the result of a +// VerifyChainAsync, VerifyChainLevelAsyncRPC, or VerifyChainBlocksAsync +// invocation (or an applicable error). +type FutureVerifyChainResult chan *futureResult + +// Receive waits for the response promised by the future and returns whether +// or not the chain verified based on the check level and number of blocks +// to verify specified in the original call. +func (r FutureVerifyChainResult) Receive() (bool, error) { + reply, err := receiveFuture(r) + if err != nil { + return false, err + } + + // Ensure the returned data is the expected type. + verified, ok := reply.(bool) + if !ok { + return false, fmt.Errorf("unexpected response type for "+ + "verifychain: %T\n", reply) + } + + return verified, nil +} + +// VerifyChainAsync 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 VerifyChain for the blocking version and more details. +func (c *Client) VerifyChainAsync() FutureVerifyChainResult { + id := c.NextID() + cmd, err := btcjson.NewVerifyChainCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// VerifyChain requests the server to verify the block chain database using +// the default check level and number of blocks to verify. +// +// See VerifyChainLevel and VerifyChainBlocks to override the defaults. +func (c *Client) VerifyChain() (bool, error) { + return c.VerifyChainAsync().Receive() +} + +// VerifyChainLevelAsync 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 VerifyChainLevel for the blocking version and more details. +func (c *Client) VerifyChainLevelAsync(checkLevel int32) FutureVerifyChainResult { + id := c.NextID() + cmd, err := btcjson.NewVerifyChainCmd(id, checkLevel) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// VerifyChainLevel requests the server to verify the block chain database using +// the passed check level and default number of blocks to verify. +// +// The check level controls how thorough the verification is with higher numbers +// increasing the amount of checks done as consequently how long the +// verification takes. +// +// See VerifyChain to use the default check level and VerifyChainBlocks to +// override the number of blocks to verify. +func (c *Client) VerifyChainLevel(checkLevel int32) (bool, error) { + return c.VerifyChainLevelAsync(checkLevel).Receive() +} + +// VerifyChainBlocksAsync 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 VerifyChainBlocks for the blocking version and more details. +func (c *Client) VerifyChainBlocksAsync(checkLevel, numBlocks int32) FutureVerifyChainResult { + id := c.NextID() + cmd, err := btcjson.NewVerifyChainCmd(id, checkLevel, numBlocks) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// VerifyChainBlocks requests the server to verify the block chain database +// using the passed check level and number of blocks to verify. +// +// The check level controls how thorough the verification is with higher numbers +// increasing the amount of checks done as consequently how long the +// verification takes. +// +// The number of blocks refers to the number of blocks from the end of the +// current longest chain. +// +// See VerifyChain and VerifyChainLevel to use defaults. +func (c *Client) VerifyChainBlocks(checkLevel, numBlocks int32) (bool, error) { + return c.VerifyChainBlocksAsync(checkLevel, numBlocks).Receive() +} diff --git a/doc.go b/doc.go new file mode 100644 index 00000000..85c32a0c --- /dev/null +++ b/doc.go @@ -0,0 +1,8 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// Package btcrpcclient implements a websocket-enabled Bitcoin JSON-RPC client. +// +// TODO(davec): Doco +package btcrpcclient diff --git a/extensions.go b/extensions.go new file mode 100644 index 00000000..a5c11745 --- /dev/null +++ b/extensions.go @@ -0,0 +1,291 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "encoding/base64" + "fmt" + "github.com/conformal/btcjson" + "github.com/conformal/btcutil" + "github.com/conformal/btcwire" + "github.com/conformal/btcws" +) + +// FutureDebugLevelResult is a future promise to deliver the result of a +// DebugLevelAsync RPC invocation (or an applicable error). +type FutureDebugLevelResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of setting the debug logging level to the passed level specification or the +// list of of the available subsystems for the special keyword 'show'. +func (r FutureDebugLevelResult) Receive() (string, error) { + reply, err := receiveFuture(r) + if err != nil { + return "", err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(string) + if !ok { + return "", fmt.Errorf("unexpected response type for "+ + "debuglevel: %T\n", reply) + } + + return result, nil +} + +// DebugLevelAsync 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 DebugLevel for the blocking version and more details. +// +// NOTE: This is a btcd extension. +func (c *Client) DebugLevelAsync(levelSpec string) FutureDebugLevelResult { + id := c.NextID() + cmd, err := btcjson.NewDebugLevelCmd(id, levelSpec) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// DebugLevel dynamically sets the debug logging level to the passed level +// specification. +// +// The levelspec can either a debug level or of the form: +// =,=,... +// +// Additionally, the special keyword 'show' can be used to get a list of the +// available subsystems. +// +// NOTE: This is a btcd extension. +func (c *Client) DebugLevel(levelSpec string) (string, error) { + return c.DebugLevelAsync(levelSpec).Receive() +} + +// FutureListAddressTransactionsResult is a future promise to deliver the result +// of a ListAddressTransactionsAsync RPC invocation (or an applicable error). +type FutureListAddressTransactionsResult chan *futureResult + +// Receive waits for the response promised by the future and returns information +// about all transactions associated with the provided addresses. +func (r FutureListAddressTransactionsResult) Receive() ([]btcjson.ListTransactionsResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // No transactions. + if reply == nil { + return nil, nil + } + + // Ensure the returned data is the expected type. + transactions, ok := reply.([]btcjson.ListTransactionsResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "listaddresstransactions: %T\n", reply) + } + + return transactions, nil +} + +// ListAddressTransactionsAsync 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 ListAddressTransactions for the blocking version and more details. +// +// NOTE: This is a btcd extension. +func (c *Client) ListAddressTransactionsAsync(addresses []btcutil.Address, account string) FutureListAddressTransactionsResult { + // Convert addresses to strings. + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.EncodeAddress()) + } + id := c.NextID() + cmd, err := btcws.NewListAddressTransactionsCmd(id, addrs, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListAddressTransactions returns information about all transactions associated +// with the provided addresses. +// +// NOTE: This is a btcwallet extension. +func (c *Client) ListAddressTransactions(addresses []btcutil.Address, account string) ([]btcjson.ListTransactionsResult, error) { + return c.ListAddressTransactionsAsync(addresses, account).Receive() +} + +// FutureGetBestBlockResult is a future promise to deliver the result of a +// GetBestBlockAsync RPC invocation (or an applicable error). +type FutureGetBestBlockResult chan *futureResult + +// Receive waits for the response promised by the future and returns the hash +// and height of the block in the longest (best) chain. +func (r FutureGetBestBlockResult) Receive() (*btcwire.ShaHash, int32, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, 0, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(btcws.GetBestBlockResult) + if !ok { + return nil, 0, fmt.Errorf("unexpected response type for "+ + "listaddresstransactions: %T\n", reply) + } + + // Convert hash string. + hash, err := btcwire.NewShaHashFromStr(result.Hash) + if err != nil { + return nil, 0, err + } + + return hash, result.Height, nil +} + +// GetBestBlockAsync 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 GetBestBlock for the blocking version and more details. +// +// NOTE: This is a btcd extension. +func (c *Client) GetBestBlockAsync() FutureGetBestBlockResult { + id := c.NextID() + cmd := btcws.NewGetBestBlockCmd(id) + + return c.sendCmd(cmd) +} + +// GetBestBlock returns the hash and height of the block in the longest (best) +// chain. +// +// NOTE: This is a btcd extension. +func (c *Client) GetBestBlock() (*btcwire.ShaHash, int32, error) { + return c.GetBestBlockAsync().Receive() +} + +// FutureGetCurrentNetResult is a future promise to deliver the result of a +// GetCurrentNetAsync RPC invocation (or an applicable error). +type FutureGetCurrentNetResult chan *futureResult + +// Receive waits for the response promised by the future and returns the network +// the server is running on. +func (r FutureGetCurrentNetResult) Receive() (btcwire.BitcoinNet, error) { + reply, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Ensure the returned data is the expected type. + fnet, ok := reply.(float64) + if !ok { + return 0, fmt.Errorf("unexpected response type for "+ + "getcurrentnet: %T\n", reply) + } + + return btcwire.BitcoinNet(fnet), nil +} + +// GetCurrentNetAsync 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 GetCurrentNet for the blocking version and more details. +// +// NOTE: This is a btcd extension. +func (c *Client) GetCurrentNetAsync() FutureGetCurrentNetResult { + id := c.NextID() + cmd := btcws.NewGetCurrentNetCmd(id) + + return c.sendCmd(cmd) +} + +// GetCurrentNet returns the network the server is running on. +// +// NOTE: This is a btcd extension. +func (c *Client) GetCurrentNet() (btcwire.BitcoinNet, error) { + return c.GetCurrentNetAsync().Receive() +} + +// FutureExportWatchingWalletResult is a future promise to deliver the result of +// an ExportWatchingWalletAsync RPC invocation (or an applicable error). +type FutureExportWatchingWalletResult chan *futureResult + +// Receive waits for the response promised by the future and returns the +// exported wallet. +func (r FutureExportWatchingWalletResult) Receive() ([]byte, []byte, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, nil, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(map[string]interface{}) + if !ok { + return nil, nil, fmt.Errorf("unexpected response type for "+ + "exportwatchingwallet: %T\n", reply) + } + + base64Wallet, ok := result["wallet"].(string) + if !ok { + return nil, nil, fmt.Errorf("unexpected response type for "+ + "exportwatchingwallet 'wallet' field: %T\n", + result["wallet"]) + } + base64TxStore, ok := result["tx"].(string) + if !ok { + return nil, nil, fmt.Errorf("unexpected response type for "+ + "exportwatchingwallet 'tx' field: %T\n", + result["tx"]) + } + + walletBytes, err := base64.StdEncoding.DecodeString(base64Wallet) + if err != nil { + return nil, nil, err + } + + txStoreBytes, err := base64.StdEncoding.DecodeString(base64TxStore) + if err != nil { + return nil, nil, err + } + + return walletBytes, txStoreBytes, nil + +} + +// ExportWatchingWalletAsync 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 ExportWatchingWallet for the blocking version and more details. +// +// NOTE: This is a btcwallet extension. +func (c *Client) ExportWatchingWalletAsync(account string) FutureExportWatchingWalletResult { + id := c.NextID() + cmd, err := btcws.NewExportWatchingWalletCmd(id, account, true) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ExportWatchingWallet returns the raw bytes for a watching-only version of +// wallet.bin and tx.bin, respectively, for the specified account that can be +// used by btcwallet to enable a wallet which does not have the private keys +// necessary to spend funds. +// +// NOTE: This is a btcwallet extension. +func (c *Client) ExportWatchingWallet(account string) ([]byte, []byte, error) { + return c.ExportWatchingWalletAsync(account).Receive() +} diff --git a/infrastructure.go b/infrastructure.go new file mode 100644 index 00000000..fa51e7bc --- /dev/null +++ b/infrastructure.go @@ -0,0 +1,937 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "bytes" + "code.google.com/p/go.net/websocket" + "container/list" + "crypto/tls" + "crypto/x509" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "github.com/conformal/btcjson" + "github.com/conformal/go-socks" + "net" + "net/http" + "net/url" + "sync" + "sync/atomic" + "time" +) + +var ( + // ErrInvalidAuth is an error to describe the condition where the client + // is either unable to authenticate or the specified endpoint is + // incorrect. + ErrInvalidAuth = errors.New("authentication failure") + + // ErrClientDisconnect is an error to describe the condition where the + // client has been disconnected from the RPC server. When the + // DisableAutoReconnect option is not set, any outstanding futures + // when a client disconnect occurs will return this error as will + // any new requests. + ErrClientDisconnect = errors.New("the client has been disconnected") + + // ErrClientShutdown is an error to describe the condition where the + // client is either already shutdown, or in the process of shutting + // down. Any outstanding futures when a client shutdown occurs will + // return this error as will any new requests. + ErrClientShutdown = errors.New("the client has been shutdown") +) + +const ( + // sendBufferSize is the number of elements the websocket send channel + // can queue before blocking. + sendBufferSize = 50 + + // sendPostBufferSize is the number of elements the HTTP POST send + // channel can queue before blocking. + sendPostBufferSize = 100 + + // connectionRetryInterval is the amount of time to wait in between + // retries when automatically reconnecting to an RPC server. + connectionRetryInterval = time.Second * 5 +) + +// futureResult holds information about a future promise to deliver the result +// of an asynchronous request. +type futureResult struct { + reply *btcjson.Reply + err error +} + +// sendPostDetails houses an HTTP POST request to send to an RPC server as well +// as the original JSON-RPC command and a channel to reply on when the server +// responds with the result. +type sendPostDetails struct { + command btcjson.Cmd + request *http.Request + responseChan chan *futureResult +} + +// jsonRequest holds information about a json request that is used to properly +// detect, interpret, and deliver a reply to it. +type jsonRequest struct { + cmd btcjson.Cmd + responseChan chan *futureResult +} + +// Client represents a Bitcoin RPC client which allows easy access to the +// various RPC methods available on a Bitcoin RPC server. Each of the wrapper +// functions handle the details of converting the passed and return types to and +// from the underlying JSON types which are required for the JSON-RPC +// invocations +// +// The client provides each RPC in both synchronous (blocking) and asynchronous +// (non-blocking) forms. The asynchronous forms are based on the concept of +// futures where they return an instance of a type that promises to deliver the +// result of the invocation at some future time. Invoking the Receive method on +// the returned future will block until the result is available if it's not +// already. +type Client struct { + id int64 // atomic, so must stay 64-bit aligned + + // config holds the connection configuration assoiated with this client. + config *ConnConfig + + // wsConn is the underlying websocket connection when not in HTTP POST + // mode. + wsConn *websocket.Conn + + // httpClient is the underlying HTTP client to use when running in HTTP + // POST mode. + httpClient *http.Client + + // mtx is a mutex to protect access to connection related fields. + mtx sync.Mutex + + // disconnected indicated whether or not the server is disconnected. + disconnected bool + + // retryCount holds the number of times the client has tried to + // reconnect to the RPC server. + retryCount int64 + + // Track command and their response channels by ID. + requestLock sync.Mutex + requestMap map[int64]*list.Element + requestList *list.List + + // Notification handlers. + ntfnHandlers *NotificationHandlers + + // Networking infrastructure. + sendChan chan []byte + sendPostChan chan *sendPostDetails + disconnect chan struct{} + shutdown chan struct{} + wg sync.WaitGroup +} + +// NextID returns the next id to be used when sending a JSON-RPC message. This +// ID allows responses to be associated with particular requests per the +// JSON-RPC specification. Typically the consumer of the client does not need +// to call this function, however, if a custom request is being created and used +// this function should be used to ensure the ID is unique amongst all requests +// being made. +func (c *Client) NextID() int64 { + return atomic.AddInt64(&c.id, 1) +} + +// addRequest associates the passed jsonRequest with the passed id. This allows +// the response from the remote server to be unmarshalled to the appropriate +// type and sent to the specified channel when it is received. +// +// This function is safe for concurrent access. +func (c *Client) addRequest(id int64, request *jsonRequest) { + c.requestLock.Lock() + defer c.requestLock.Unlock() + + // TODO(davec): Already there? + element := c.requestList.PushBack(request) + c.requestMap[id] = element +} + +// removeRequest returns and removes the jsonRequest which contains the response +// channel and original method associated with the passed id or nil if there is +// no association. +// +// This function is safe for concurrent access. +func (c *Client) removeRequest(id int64) *jsonRequest { + c.requestLock.Lock() + defer c.requestLock.Unlock() + + element := c.requestMap[id] + if element != nil { + delete(c.requestMap, id) + request := c.requestList.Remove(element).(*jsonRequest) + return request + } + + return nil +} + +// removeAllRequests removes all the jsonRequests which contain the response +// channels for outstanding requests. +// +// This function is safe for concurrent access. +func (c *Client) removeAllRequests() { + c.requestLock.Lock() + defer c.requestLock.Unlock() + + c.requestMap = make(map[int64]*list.Element) + c.requestList.Init() +} + +// handleMessage is the main handler for incoming requests. It enforces +// authentication, parses the incoming json, looks up and executes handlers +// (including pass through for standard RPC commands), sends the appropriate +// response. It also detects commands which are marked as long-running and +// sends them off to the asyncHander for processing. +func (c *Client) handleMessage(msg string) { + // Attempt to unmarshal the message as a known JSON-RPC command. + if cmd, err := btcjson.ParseMarshaledCmd([]byte(msg)); err == nil { + // Commands that have an ID associated with them are not + // notifications. Since this is a client, it should not + // be receiving non-notifications. + if cmd.Id() != nil { + // Invalid response + log.Warnf("Remote server sent a non-notification "+ + "JSON-RPC Request (Id: %v)", cmd.Id()) + return + } + + // Deliver the notification. + log.Tracef("Received notification [%s]", cmd.Method()) + c.handleNotification(cmd) + return + } + + // The message was not a command/notification, so it should be a reply + // to a previous request. + + var r btcjson.Reply + if err := json.Unmarshal([]byte(msg), &r); err != nil { + log.Warnf("Unable to unmarshal inbound message as " + + "notification or response") + return + } + + // Ensure the reply has an id. + if r.Id == nil { + log.Warnf("Received response with no id") + return + } + + // Ensure the id is the expected type. + fid, ok := (*r.Id).(float64) + if !ok { + log.Warnf("Received unexpected id type: %T (value %v)", + *r.Id, *r.Id) + return + } + id := int64(fid) + log.Tracef("Received response for id %d (result %v)", id, r.Result) + request := c.removeRequest(id) + + // Nothing more to do if there is no request associated with this reply. + if request == nil || request.responseChan == nil { + log.Warnf("Received unexpected reply: %s (id %d)", r.Result, id) + return + } + + // Unmarshal the reply into a concrete result if possible and deliver + // it to the associated channel. + reply, err := btcjson.ReadResultCmd(request.cmd.Method(), []byte(msg)) + if err != nil { + log.Warnf("Failed to unmarshal reply to command [%s] "+ + "(id %d): %v", request.cmd.Method(), id, err) + request.responseChan <- &futureResult{reply: nil, err: err} + return + } + request.responseChan <- &futureResult{reply: &reply, err: nil} +} + +// wsInHandler handles all incoming messages for the websocket connection +// associated with the client. It must be run as a goroutine. +func (c *Client) wsInHandler() { +out: + for { + // Break out of the loop once the shutdown channel has been + // closed. Use a non-blocking select here so we fall through + // otherwise. + select { + case <-c.shutdown: + break out + default: + } + + var msg string + if err := websocket.Message.Receive(c.wsConn, &msg); err != nil { + // Log the error if it's not due to disconnecting. + if _, ok := err.(*net.OpError); !ok { + log.Errorf("Websocket receive error from "+ + "%s: %v", c.config.Host, err) + } + break out + } + c.handleMessage(msg) + } + + // Ensure the connection is closed. + c.Disconnect() + c.wg.Done() + log.Tracef("RPC client input handler done for %s", c.config.Host) +} + +// wsOutHandler handles all outgoing messages for the websocket connection. It +// uses a buffered channel to serialize output messages while allowing the +// sender to continue running asynchronously. It must be run as a goroutine. +func (c *Client) wsOutHandler() { +out: + for { + // Send any messages ready for send until the client is + // disconnected closed. + select { + case msg := <-c.sendChan: + err := websocket.Message.Send(c.wsConn, string(msg)) + if err != nil { + c.Disconnect() + break out + } + + case <-c.disconnect: + break out + } + } + + // Drain any channels before exiting so nothing is left waiting around + // to send. +cleanup: + for { + select { + case <-c.sendChan: + default: + break cleanup + } + } + c.wg.Done() + log.Tracef("RPC client output handler done for %s", c.config.Host) +} + +// sendMessage sends the passed JSON to the connected server using the +// websocket connection. It is backed by a buffered channel, so it will not +// block until the send channel is full. +func (c *Client) sendMessage(marshalledJSON []byte) { + // Don't send the message if disconnected. + if c.Disconnected() { + return + } + + c.sendChan <- marshalledJSON +} + +// resendCmds resends any commands that had not completed when the client +// disconnected. It is intended to be called once the client has reconnected. +func (c *Client) resendCmds() { + // Since it's possible to block on send and more commands might be + // added by the caller while resending, make a copy of all of the + // commands that need to be resent now and work from the copy. This + // also allows the lock to be released quickly. + c.requestLock.Lock() + resendCmds := make([]*jsonRequest, 0, c.requestList.Len()) + for e := c.requestList.Front(); e != nil; e = e.Next() { + req := e.Value.(*jsonRequest) + resendCmds = append(resendCmds, req) + } + c.requestLock.Unlock() + + for _, req := range resendCmds { + // Stop resending commands if the client disconnected again + // since the next reconnect will handle them. + if c.Disconnected() { + return + } + + c.marshalAndSend(req.cmd, req.responseChan) + time.Sleep(time.Second * 2) + } +} + +// wsReconnectHandler listens for client disconnects and automatically tries +// to reconnect with retry interval that scales based on the number of retries. +// It also resends any commands that had not completed when the client +// disconnected so the disconnect/reconnect process is largely transparent to +// the caller. This function is not run when the DisableAutoReconnect config +// options is set. +// +// This function must be run as a goroutine. +func (c *Client) wsReconnectHandler() { +out: + for { + select { + case <-c.disconnect: + // On disconnect, fallthrough to reestablish the + // connection. + + case <-c.shutdown: + break out + } + + reconnect: + for { + select { + case <-c.shutdown: + break out + default: + } + + wsConn, err := dial(c.config) + if err != nil { + c.retryCount++ + log.Infof("Failed to connect to %s: %v", + c.config.Host, err) + + // Scale the retry interval by the number of + // retries so there is a backoff up to a max + // of 1 minute. + scaledInterval := connectionRetryInterval.Nanoseconds() * c.retryCount + scaledDuration := time.Duration(scaledInterval) + if scaledDuration > time.Minute { + scaledDuration = time.Minute + } + log.Infof("Retrying connection to %s in "+ + "%s", c.config.Host, scaledDuration) + time.Sleep(scaledDuration) + continue reconnect + } + + log.Infof("Reestablished connection to RPC server %s", + c.config.Host) + + // Reset the connection state and signal the reconnect + // has happened. + c.wsConn = wsConn + c.retryCount = 0 + c.disconnect = make(chan struct{}) + + c.mtx.Lock() + c.disconnected = false + c.mtx.Unlock() + + // Start processing input and output for the + // new connection. + c.start() + + // Reissue pending commands in another goroutine since + // the send can block. + go c.resendCmds() + + // Break out of the reconnect loop back to wait for + // disconnect again. + break reconnect + } + } + c.wg.Done() + log.Tracef("RPC client reconnect handler done for %s", c.config.Host) +} + +// handleSendPostMessage handles performing the passed HTTP request, reading the +// result, unmarshalling it, and delivering the unmarhsalled result to the +// provided response channel. +func (c *Client) handleSendPostMessage(details *sendPostDetails) { + // Post the request. + cmd := details.command + log.Tracef("Sending command [%s] with id %d", cmd.Method(), cmd.Id()) + httpResponse, err := c.httpClient.Do(details.request) + if err != nil { + details.responseChan <- &futureResult{reply: nil, err: err} + return + } + + // Read the raw bytes and close the response. + respBytes, err := btcjson.GetRaw(httpResponse.Body) + if err != nil { + details.responseChan <- &futureResult{reply: nil, err: err} + return + } + + // Unmarshal the reply into a concrete result if possible. + reply, err := btcjson.ReadResultCmd(cmd.Method(), respBytes) + if err != nil { + details.responseChan <- &futureResult{reply: nil, err: err} + return + } + details.responseChan <- &futureResult{reply: &reply, err: nil} +} + +// sendPostHandler handles all outgoing messages when the client is running +// in HTTP POST mode. It uses a buffered channel to serialize output messages +// while allowing the sender to continue running asynchronously. It must be run +// as a goroutine. +func (c *Client) sendPostHandler() { +out: + for { + // Send any messages ready for send until the shutdown channel + // is closed. + select { + case details := <-c.sendPostChan: + c.handleSendPostMessage(details) + + case <-c.shutdown: + break out + } + } + + // Drain any wait channels before exiting so nothing is left waiting + // around to send. +cleanup: + for { + select { + case details := <-c.sendPostChan: + details.responseChan <- &futureResult{ + reply: nil, + err: ErrClientShutdown, + } + + default: + break cleanup + } + } + c.wg.Done() + log.Tracef("RPC client send handler done for %s", c.config.Host) + +} + +// sendPostRequest sends the passed HTTP request to the RPC server using the +// HTTP client associated with the client. It is backed by a buffered channel, +// so it will not block until the send channel is full. +func (c *Client) sendPostRequest(req *http.Request, command btcjson.Cmd, responseChan chan *futureResult) { + // Don't send the message if shutting down. + select { + case <-c.shutdown: + responseChan <- &futureResult{reply: nil, err: ErrClientShutdown} + default: + } + + c.sendPostChan <- &sendPostDetails{ + request: req, + command: command, + responseChan: responseChan, + } +} + +// Disconnected returns whether or not the server is disconnected. +func (c *Client) Disconnected() bool { + c.mtx.Lock() + defer c.mtx.Unlock() + + return c.disconnected +} + +// newFutureError returns a new future result channel that already has the +// passed error waitin on the channel with the reply set to nil. This is useful +// to easily return errors from the various Async functions. +func newFutureError(err error) chan *futureResult { + responseChan := make(chan *futureResult, 1) + responseChan <- &futureResult{err: err} + return responseChan +} + +// receiveFuture receives from the passed futureResult channel to extract a +// reply or any errors. The examined errors include an error in the +// futureResult and the error in the reply from the server. This will block +// until the result is available on the passed channel. +func receiveFuture(responseChan chan *futureResult) (interface{}, error) { + // Wait for a response on the returned channel. + response := <-responseChan + if response.err != nil { + return nil, response.err + } + + // At this point, the command was either sent to the server and + // there is a response from it, or it is intentionally a nil result + // used to bybass sends for cases such a requesting notifications when + // there are no handlers. + reply := response.reply + if reply == nil { + return nil, nil + } + + if reply.Error != nil { + return nil, reply.Error + } + + return reply.Result, nil +} + +// marshalAndSendPost marshals the passed command to JSON-RPC and sends it to +// the server by issuing an HTTP POST request and returns a response channel +// on which the reply will be delivered. Typically a new connection is opened +// and closed for each command when using this method, however, the underlying +// HTTP client might coalesce multiple commands depending on several factors +// including the remote server configuration. +func (c *Client) marshalAndSendPost(cmd btcjson.Cmd, responseChan chan *futureResult) { + marshalledJSON, err := json.Marshal(cmd) + if err != nil { + responseChan <- &futureResult{reply: nil, err: err} + return + } + + // Generate a request to the configured RPC server. + protocol := "http" + if !c.config.DisableTLS { + protocol = "https" + } + url := protocol + "://" + c.config.Host + req, err := http.NewRequest("POST", url, bytes.NewReader(marshalledJSON)) + if err != nil { + responseChan <- &futureResult{reply: nil, err: err} + return + } + req.Close = true + req.Header.Set("Content-Type", "application/json") + + // Configure basic access authorization. + req.SetBasicAuth(c.config.User, c.config.Pass) + + log.Tracef("Sending command [%s] with id %d", cmd.Method(), cmd.Id()) + c.sendPostRequest(req, cmd, responseChan) +} + +// marshalAndSend marshals the passed command to JSON-RPC and sends it to the +// server. It returns a response channel on which the reply will be delivered. +func (c *Client) marshalAndSend(cmd btcjson.Cmd, responseChan chan *futureResult) { + marshalledJSON, err := json.Marshal(cmd) + if err != nil { + responseChan <- &futureResult{reply: nil, err: err} + return + } + + log.Tracef("Sending command [%s] with id %d", cmd.Method(), cmd.Id()) + c.sendMessage(marshalledJSON) +} + +// sendCmd sends the passed command to the associated server and returns a +// response channel on which the reply will be deliver at some point in the +// future. It handles both websocket and HTTP POST mode depending on the +// configuration of the client. +func (c *Client) sendCmd(cmd btcjson.Cmd) chan *futureResult { + // Choose which marshal and send function to use depending on whether + // the client running in HTTP POST mode or not. When running in HTTP + // POST mode, the command is issued via an HTTP client. Otherwise, + // the command is issued via the asynchronous websocket channels. + responseChan := make(chan *futureResult, 1) + if c.config.HttpPostMode { + c.marshalAndSendPost(cmd, responseChan) + return responseChan + } + + c.addRequest(cmd.Id().(int64), &jsonRequest{ + cmd: cmd, + responseChan: responseChan, + }) + c.marshalAndSend(cmd, responseChan) + return responseChan +} + +// sendCmdAndWait sends the passed command to the associated server, waits +// for the reply, and returns the result from it. It will return the error +// field in the reply if there is one. +func (c *Client) sendCmdAndWait(cmd btcjson.Cmd) (interface{}, error) { + // Marshal the command to JSON-RPC, send it to the connected server, and + // wait for a response on the returned channel. + return receiveFuture(c.sendCmd(cmd)) +} + +// Disconnect disconnects the current websocket associated with the client. The +// connection will automatically be re-established +// +// This function has no effect when the client is running in HTTP POST mode. +func (c *Client) Disconnect() { + if c.config.HttpPostMode { + return + } + + c.mtx.Lock() + defer c.mtx.Unlock() + + // Nothing to do if already disconnected. + if c.disconnected { + return + } + + log.Tracef("Disconnecting RPC client %s", c.config.Host) + close(c.disconnect) + c.wsConn.Close() + c.disconnected = true + + // When operating without auto reconnect, send errors to any pending + // requests and shutdown the client. + if c.config.DisableAutoReconnect { + c.requestLock.Lock() + for e := c.requestList.Front(); e != nil; e = e.Next() { + req := e.Value.(*jsonRequest) + req.responseChan <- &futureResult{ + reply: nil, + err: ErrClientDisconnect, + } + } + c.requestLock.Unlock() + c.removeAllRequests() + c.Shutdown() + } +} + +// Shutdown shuts down the client by disconnecting any connections associated +// with the client and, when automatic reconnect is enabled, preventing future +// attempts to reconnect. It also stops all goroutines. +func (c *Client) Shutdown() { + // Ignore the shutdown request if the client is already in the process + // of shutting down or already shutdown. + select { + case <-c.shutdown: + return + default: + } + + log.Tracef("Shutting down RPC client %s", c.config.Host) + close(c.shutdown) + + // Send the ErrClientShutdown error to any pending requests. + c.requestLock.Lock() + for e := c.requestList.Front(); e != nil; e = e.Next() { + req := e.Value.(*jsonRequest) + req.responseChan <- &futureResult{ + reply: nil, + err: ErrClientShutdown, + } + } + c.requestLock.Unlock() + c.removeAllRequests() +} + +// Start begins processing input and output messages. +func (c *Client) start() { + log.Tracef("Starting RPC client %s", c.config.Host) + + // Start the I/O processing handlers depending on whether the client is + // in HTTP POST mode or the default websocket mode. + if c.config.HttpPostMode { + c.wg.Add(1) + go c.sendPostHandler() + } else { + c.wg.Add(2) + go c.wsInHandler() + go c.wsOutHandler() + } +} + +// WaitForShutdown blocks until the client goroutines are stopped and the +// connection is closed. +func (c *Client) WaitForShutdown() { + c.wg.Wait() +} + +// ConnConfig describes the connection configuration parameters for the client. +// This +type ConnConfig struct { + // Host is the IP address and port of the RPC server you want to connect + // to. + Host string + + // Endpoint is the websocket endpoint on the RPC server. This is + // typically "ws" or "frontend". + Endpoint string + + // User is the username to use to authenticate to the RPC server. + User string + + // Pass is the passphrase to use to authenticate to the RPC server. + Pass string + + // DisableTLS specifies whether transport layer security should be + // disabled. It is recommended to always use TLS if the RPC server + // supports it as otherwise your username and password is sent across + // the wire in cleartext. + DisableTLS bool + + // Certificates are the bytes for a PEM-encoded certificate chain used + // for the TLS connection. It has no effect if the DisableTLS parameter + // is true. + Certificates []byte + + // Proxy specifies to connect through a SOCKS 5 proxy server. It may + // be an empty string if a proxy is not required. + Proxy string + + // ProxyUser is an optional username to use for the proxy server if it + // requires authentication. It has no effect if the Proxy parameter + // is not set. + ProxyUser string + + // ProxyPass is an optional password to use for the proxy server if it + // requires authentication. It has no effect if the Proxy parameter + // is not set. + ProxyPass string + + // DisableAutoReconnect specifies the client should not automatically + // try to reconnect to the server when it has been disconnected. + DisableAutoReconnect bool + + // HttpPostMode instructs the client to run using multiple independent + // connections issuing HTTP POST requests instead of using the default + // of websockets. Websockets are generally preferred as some of the + // features of the client such notifications only work with websockets, + // however, not all servers support the websocket extensions, so this + // flag can be set to true to use basic HTTP POST requests instead. + HttpPostMode bool +} + +// newHttpClient returns a new http client that is configured according to the +// proxy and TLS settings in the associated connection configuration. +func newHttpClient(config *ConnConfig) (*http.Client, error) { + // Set proxy function if there is a proxy configured. + var proxyFunc func(*http.Request) (*url.URL, error) + if config.Proxy != "" { + proxyURL, err := url.Parse(config.Proxy) + if err != nil { + return nil, err + } + proxyFunc = http.ProxyURL(proxyURL) + } + + // Configure TLS if needed. + var tlsConfig *tls.Config + if !config.DisableTLS { + pool := x509.NewCertPool() + pool.AppendCertsFromPEM(config.Certificates) + tlsConfig = &tls.Config{ + RootCAs: pool, + } + } + + client := http.Client{ + Transport: &http.Transport{ + Proxy: proxyFunc, + TLSClientConfig: tlsConfig, + }, + } + + return &client, nil +} + +// dial opens a websocket connection using the passed connection configuration +// details. +func dial(config *ConnConfig) (*websocket.Conn, error) { + // Connect to websocket. + url := fmt.Sprintf("wss://%s/%s", config.Host, config.Endpoint) + wsConfig, err := websocket.NewConfig(url, "https://localhost/") + if err != nil { + return nil, err + } + + pool := x509.NewCertPool() + pool.AppendCertsFromPEM(config.Certificates) + wsConfig.TlsConfig = &tls.Config{ + RootCAs: pool, + MinVersion: tls.VersionTLS12, + } + + // The wallet requires basic authorization, so use a custom config with + // with the Authorization header set. + login := config.User + ":" + config.Pass + auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) + wsConfig.Header.Add("Authorization", auth) + + // Attempt to connect to running wallet instance using a proxy if one + // is configured. + if config.Proxy != "" { + proxy := &socks.Proxy{ + Addr: config.Proxy, + Username: config.ProxyUser, + Password: config.ProxyPass, + } + conn, err := proxy.Dial("tcp", config.Host) + if err != nil { + return nil, err + } + + tlsConn := tls.Client(conn, wsConfig.TlsConfig) + ws, err := websocket.NewClient(wsConfig, tlsConn) + if err != nil { + return nil, err + } + + return ws, nil + } + + // No proxy was specified, so attempt to connect to running wallet + // instance directly. + ws, err := websocket.DialConfig(wsConfig) + if err != nil { + // XXX(davec): This is not really accurate, but unfortunately + // the current websocket package does not expose the status + // code, so it's impossible to tell for sure. + if dialError, ok := err.(*websocket.DialError); ok { + if dialError.Err == websocket.ErrBadStatus { + return nil, ErrInvalidAuth + } + } + return nil, err + } + return ws, nil +} + +// New create a new RPC client based on the provided connection configuration +// details. The notification handlers parameter may be nil if you are not +// interested in receiving notifications and will be ignored when if the +// configuration is set to run in HTTP POST mode. +func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error) { + // Either open a websocket connection or create an HTTP client depending + // on the HTTP POST mode. Also, set the notification handlers to nil + // when running in HTTP POST mode. + var wsConn *websocket.Conn + var httpClient *http.Client + if config.HttpPostMode { + ntfnHandlers = nil + + var err error + httpClient, err = newHttpClient(config) + if err != nil { + return nil, err + } + } else { + var err error + wsConn, err = dial(config) + if err != nil { + return nil, err + } + } + + client := &Client{ + config: config, + wsConn: wsConn, + httpClient: httpClient, + requestMap: make(map[int64]*list.Element), + requestList: list.New(), + ntfnHandlers: ntfnHandlers, + sendChan: make(chan []byte, sendBufferSize), + sendPostChan: make(chan *sendPostDetails, sendPostBufferSize), + disconnect: make(chan struct{}), + shutdown: make(chan struct{}), + } + client.start() + + if !client.config.HttpPostMode && !client.config.DisableAutoReconnect { + client.wg.Add(1) + go client.wsReconnectHandler() + } + + return client, nil +} diff --git a/log.go b/log.go new file mode 100644 index 00000000..204ee66a --- /dev/null +++ b/log.go @@ -0,0 +1,74 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "errors" + "github.com/conformal/btclog" + "io" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log btclog.Logger + +// The default amount of logging is none. +func init() { + DisableLog() +} + +// DisableLog disables all library log output. Logging output is disabled +// by default until either UseLogger or SetLogWriter are called. +func DisableLog() { + log = btclog.Disabled +} + +// UseLogger uses a specified Logger to output package logging info. +// This should be used in preference to SetLogWriter if the caller is also +// using btclog. +func UseLogger(logger btclog.Logger) { + log = logger +} + +// SetLogWriter uses a specified io.Writer to output package logging info. +// This allows a caller to direct package logging output without needing a +// dependency on seelog. If the caller is also using btclog, UseLogger should +// be used instead. +func SetLogWriter(w io.Writer, level string) error { + if w == nil { + return errors.New("nil writer") + } + + lvl, ok := btclog.LogLevelFromString(level) + if !ok { + return errors.New("invalid log level") + } + + l, err := btclog.NewLoggerFromWriter(w, lvl) + if err != nil { + return err + } + + UseLogger(l) + return nil +} + +// LogClosure is a closure that can be printed with %v to be used to +// generate expensive-to-create data for a detailed log level and avoid doing +// the work if the data isn't printed. +type logClosure func() string + +// String invokes the log closure and returns the results string. +func (c logClosure) String() string { + return c() +} + +// newLogClosure returns a new closure over the passed function which allows +// it to be used as a parameter in a logging function that is only invoked when +// the logging level is such that the message will actually be logged. +func newLogClosure(c func() string) logClosure { + return logClosure(c) +} diff --git a/mining.go b/mining.go new file mode 100644 index 00000000..c138f31a --- /dev/null +++ b/mining.go @@ -0,0 +1,361 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "encoding/hex" + "fmt" + "github.com/conformal/btcjson" + "github.com/conformal/btcutil" +) + +// FutureGetGenerateResult is a future promise to deliver the result of a +// GetGenerateAsync RPC invocation (or an applicable error). +type FutureGetGenerateResult chan *futureResult + +// Receive waits for the response promised by the future and returns true if the +// server is set to mine, otherwise false. +func (r FutureGetGenerateResult) Receive() (bool, error) { + reply, err := receiveFuture(r) + if err != nil { + return false, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(bool) + if !ok { + return false, fmt.Errorf("unexpected response type for "+ + "getgenerate: %T\n", reply) + } + + return result, nil +} + +// GetGenerateAsync 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 GetGenerate for the blocking version and more details. +func (c *Client) GetGenerateAsync() FutureGetGenerateResult { + id := c.NextID() + cmd, err := btcjson.NewGetGenerateCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetGenerate returns true if the server is set to mine, otherwise false. +func (c *Client) GetGenerate() (bool, error) { + return c.GetGenerateAsync().Receive() +} + +// FutureSetGenerateResult is a future promise to deliver the result of a +// SetGenerateAsync RPC invocation (or an applicable error). +type FutureSetGenerateResult chan *futureResult + +// Receive waits for the response promised by the future and returns an error if +// any occurred when setting the server to generate coins (mine) or not. +func (r FutureSetGenerateResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// SetGenerateAsync 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 SetGenerate for the blocking version and more details. +func (c *Client) SetGenerateAsync(enable bool, numCPUs int) FutureSetGenerateResult { + id := c.NextID() + cmd, err := btcjson.NewSetGenerateCmd(id, enable, numCPUs) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SetGenerate sets the server to generate coins (mine) or not. +func (c *Client) SetGenerate(enable bool, numCPUs int) error { + return c.SetGenerateAsync(enable, numCPUs).Receive() +} + +// FutureGetHashesPerSecResult is a future promise to deliver the result of a +// GetHashesPerSecAsync RPC invocation (or an applicable error). +type FutureGetHashesPerSecResult chan *futureResult + +// Receive waits for the response promised by the future and returns a recent +// hashes per second performance measurement while generating coins (mining). +// Zero is returned if the server is not mining. +func (r FutureGetHashesPerSecResult) Receive() (int64, error) { + reply, err := receiveFuture(r) + if err != nil { + return -1, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(int64) + if !ok { + return -1, fmt.Errorf("unexpected response type for "+ + "getnetworkhashps: %T\n", reply) + } + + return result, nil +} + +// GetHashesPerSecAsync 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 GetHashesPerSec for the blocking version and more details. +func (c *Client) GetHashesPerSecAsync() FutureGetHashesPerSecResult { + id := c.NextID() + cmd, err := btcjson.NewGetHashesPerSecCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetHashesPerSec returns a recent hashes per second performance measurement +// while generating coins (mining). Zero is returned if the server is not +// mining. +func (c *Client) GetHashesPerSec() (int64, error) { + return c.GetHashesPerSecAsync().Receive() +} + +// FutureGetMiningInfoResult is a future promise to deliver the result of a +// GetMiningInfoAsync RPC invocation (or an applicable error). +type FutureGetMiningInfoResult chan *futureResult + +// Receive waits for the response promised by the future and returns the mining +// information. +func (r FutureGetMiningInfoResult) Receive() (*btcjson.GetMiningInfoResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(btcjson.GetMiningInfoResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getmininginfo: %T\n", reply) + } + + return &result, nil +} + +// GetMiningInfoAsync 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 GetMiningInfo for the blocking version and more details. +func (c *Client) GetMiningInfoAsync() FutureGetMiningInfoResult { + id := c.NextID() + cmd, err := btcjson.NewGetMiningInfoCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetMiningInfo returns mining information. +func (c *Client) GetMiningInfo() (*btcjson.GetMiningInfoResult, error) { + return c.GetMiningInfoAsync().Receive() +} + +// FutureGetNetworkHashPS is a future promise to deliver the result of a +// GetNetworkHashPSAsync RPC invocation (or an applicable error). +type FutureGetNetworkHashPS chan *futureResult + +// Receive waits for the response promised by the future and returns the +// estimated network hashes per second for the block heights provided by the +// parameters. +func (r FutureGetNetworkHashPS) Receive() (int64, error) { + reply, err := receiveFuture(r) + if err != nil { + return -1, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(int64) + if !ok { + return -1, fmt.Errorf("unexpected response type for "+ + "getnetworkhashps: %T\n", reply) + } + + return result, nil +} + +// GetNetworkHashPSAsync 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 GetNetworkHashPS for the blocking version and more details. +func (c *Client) GetNetworkHashPSAsync() FutureGetNetworkHashPS { + id := c.NextID() + cmd, err := btcjson.NewGetNetworkHashPSCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetNetworkHashPS returns the estimated network hashes per second using the +// default number of blocks and the most recent block height. +// +// See GetNetworkHashPS2 to override the number of blocks to use and +// GetNetworkHashPS3 to override the height at which to calculate the estimate. +func (c *Client) GetNetworkHashPS() (int64, error) { + return c.GetNetworkHashPSAsync().Receive() +} + +// GetNetworkHashPS2Async 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 GetNetworkHashPS2 for the blocking version and more details. +func (c *Client) GetNetworkHashPS2Async(blocks int) FutureGetNetworkHashPS { + id := c.NextID() + cmd, err := btcjson.NewGetNetworkHashPSCmd(id, blocks) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetNetworkHashPS2 returns the estimated network hashes per second for the +// specified previous number of blocks working backwards from the most recent +// block height. The blocks parameter can also be -1 in which case the number +// of blocks since the last difficulty change will be used. +// +// See GetNetworkHashPS to use defaults and GetNetworkHashPS3 to override the +// height at which to calculate the estimate. +func (c *Client) GetNetworkHashPS2(blocks int) (int64, error) { + return c.GetNetworkHashPS2Async(blocks).Receive() +} + +// GetNetworkHashPS3Async 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 GetNetworkHashPS3 for the blocking version and more details. +func (c *Client) GetNetworkHashPS3Async(blocks, height int) FutureGetNetworkHashPS { + id := c.NextID() + cmd, err := btcjson.NewGetNetworkHashPSCmd(id, blocks, height) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetNetworkHashPS3 returns the estimated network hashes per second for the +// specified previous number of blocks working backwards from the specified +// block height. The blocks parameter can also be -1 in which case the number +// of blocks since the last difficulty change will be used. +// +// See GetNetworkHashPS and GetNetworkHashPS2 to use defaults. +func (c *Client) GetNetworkHashPS3(blocks, height int) (int64, error) { + return c.GetNetworkHashPS3Async(blocks, height).Receive() +} + +// FutureGetWork is a future promise to deliver the result of a +// GetWorkAsync RPC invocation (or an applicable error). +type FutureGetWork chan *futureResult + +// Receive waits for the response promised by the future and returns the hash +// data to work on. +func (r FutureGetWork) Receive() (*btcjson.GetWorkResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(btcjson.GetWorkResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getwork: %T\n", reply) + } + + return &result, nil +} + +// GetWorkAsync 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 GetWork for the blocking version and more details. +func (c *Client) GetWorkAsync() FutureGetWork { + id := c.NextID() + cmd, err := btcjson.NewGetWorkCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// TODO(davec): Correct this +// GetWork returns hash data to work on. +func (c *Client) GetWork() (*btcjson.GetWorkResult, error) { + return c.GetWorkAsync().Receive() +} + +// FutureSubmitBlockResult is a future promise to deliver the result of a +// SubmitBlockAsync RPC invocation (or an applicable error). +type FutureSubmitBlockResult chan *futureResult + +// Receive waits for the response promised by the future and returns an error if +// any occurred when submitting the block. +func (r FutureSubmitBlockResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil + +} + +// SubmitBlockAsync 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 SubmitBlock for the blocking version and more details. +func (c *Client) SubmitBlockAsync(block *btcutil.Block, options *btcjson.SubmitBlockOptions) FutureSubmitBlockResult { + id := c.NextID() + blockBytes, err := block.Bytes() + if err != nil { + return newFutureError(err) + } + + blockHex := hex.EncodeToString(blockBytes) + cmd, err := btcjson.NewSubmitBlockCmd(id, blockHex, options) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SubmitBlock attempts to submit a new block into the bitcoin network. +func (c *Client) SubmitBlock(block *btcutil.Block, options *btcjson.SubmitBlockOptions) error { + return c.SubmitBlockAsync(block, options).Receive() +} + +// TODO(davec): Implement GetBlockTemplate diff --git a/net.go b/net.go new file mode 100644 index 00000000..072fa094 --- /dev/null +++ b/net.go @@ -0,0 +1,327 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "fmt" + "github.com/conformal/btcjson" +) + +// AddNodeCommand enumerates the available commands that the AddNode function +// accepts. +type AddNodeCommand string + +// Constants used to indicate the command for the AddNode function. +const ( + // ANAdd indicates the specified host should be added as a persistent + // peer. + ANAdd AddNodeCommand = "add" + + // ANRemove indicates the specified peer should be removed. + ANRemove AddNodeCommand = "remove" + + // ANOneTry indicates the specified host should try to connect once, + // but it should not be made persistent. + ANOneTry AddNodeCommand = "onetry" +) + +// String returns the AddNodeCommand in human-readable form. +func (cmd AddNodeCommand) String() string { + return string(cmd) +} + +// FutureAddNodeResult is a future promise to deliver the result of an +// AddNodeAsync RPC invocation (or an applicable error). +type FutureAddNodeResult chan *futureResult + +// Receive waits for the response promised by the future and returns an error if +// any occurred when performing the specified command. +func (r FutureAddNodeResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// AddNodeAsync 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 AddNode for the blocking version and more details. +func (c *Client) AddNodeAsync(host string, command AddNodeCommand) FutureAddNodeResult { + id := c.NextID() + cmd, err := btcjson.NewAddNodeCmd(id, host, string(command)) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// AddNode attempts to perform the passed command on the passed persistent peer. +// For example, it can be used to add or a remove a persistent peer, or to do +// a one time connection to a peer. +// +// It may not be used to remove non-persistent peers. +func (c *Client) AddNode(host string, command AddNodeCommand) error { + return c.AddNodeAsync(host, command).Receive() +} + +// FutureGetAddedNodeInfoResult is a future promise to deliver the result of a +// GetAddedNodeInfoAsync RPC invocation (or an applicable error). +type FutureGetAddedNodeInfoResult chan *futureResult + +// Receive waits for the response promised by the future and returns information +// about manually added (persistent) peers. +func (r FutureGetAddedNodeInfoResult) Receive() ([]btcjson.GetAddedNodeInfoResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + nodeInfo, ok := reply.([]btcjson.GetAddedNodeInfoResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getaddednodeinfo (dns=true): %T\n", reply) + } + + return nodeInfo, nil +} + +// GetAddedNodeInfoAsync 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 GetAddedNodeInfo for the blocking version and more details. +func (c *Client) GetAddedNodeInfoAsync(peer string) FutureGetAddedNodeInfoResult { + id := c.NextID() + cmd, err := btcjson.NewGetAddedNodeInfoCmd(id, true, peer) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetAddedNodeInfo returns information about manually added (persistent) peers. +// +// See GetAddedNodeInfoNoDNS to retrieve only a list of the added (persistent) +// peers. +func (c *Client) GetAddedNodeInfo(peer string) ([]btcjson.GetAddedNodeInfoResult, error) { + return c.GetAddedNodeInfoAsync(peer).Receive() +} + +// FutureGetAddedNodeInfoNoDNSResult is a future promise to deliver the result +// of a GetAddedNodeInfoNoDNSAsync RPC invocation (or an applicable error). +type FutureGetAddedNodeInfoNoDNSResult chan *futureResult + +// Receive waits for the response promised by the future and returns a list of +// manually added (persistent) peers. +func (r FutureGetAddedNodeInfoNoDNSResult) Receive() ([]string, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + nodes, ok := reply.([]string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getaddednodeinfo (dns=false): %T\n", reply) + } + + return nodes, nil +} + +// GetAddedNodeInfoNoDNSAsync 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 GetAddedNodeInfoNoDNS for the blocking version and more details. +func (c *Client) GetAddedNodeInfoNoDNSAsync(peer string) FutureGetAddedNodeInfoNoDNSResult { + id := c.NextID() + cmd, err := btcjson.NewGetAddedNodeInfoCmd(id, false, peer) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetAddedNodeInfoNoDNS returns a list of manually added (persistent) peers. +// This works by setting the dns flag to false in the underlying RPC. +// +// See GetAddedNodeInfo to obtain more information about each added (persistent) +// peer. +func (c *Client) GetAddedNodeInfoNoDNS(peer string) ([]string, error) { + return c.GetAddedNodeInfoNoDNSAsync(peer).Receive() +} + +// FutureGetConnectionCountResult is a future promise to deliver the result +// of a GetConnectionCountAsync RPC invocation (or an applicable error). +type FutureGetConnectionCountResult chan *futureResult + +// Receive waits for the response promised by the future and returns the number +// of active connections to other peers. +func (r FutureGetConnectionCountResult) Receive() (int64, error) { + reply, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Ensure the returned data is the expected type. + fcount, ok := reply.(float64) + if !ok { + return 0, fmt.Errorf("unexpected response type for "+ + "getconnectioncount: %T\n", reply) + } + + return int64(fcount), nil +} + +// GetConnectionCountAsync 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 GetConnectionCount for the blocking version and more details. +func (c *Client) GetConnectionCountAsync() FutureGetConnectionCountResult { + id := c.NextID() + cmd, err := btcjson.NewGetConnectionCountCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetConnectionCount returns the number of active connections to other peers. +func (c *Client) GetConnectionCount() (int64, error) { + return c.GetConnectionCountAsync().Receive() +} + +// FuturePingResult is a future promise to deliver the result of a PingAsync RPC +// invocation (or an applicable error). +type FuturePingResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of queueing a ping to be sent to each connected peer. +func (r FuturePingResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// PingAsync 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 Ping for the blocking version and more details. +func (c *Client) PingAsync() FuturePingResult { + id := c.NextID() + cmd, err := btcjson.NewPingCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// Ping queues a ping to be sent to each connected peer. +// +// Use the GetPeerInfo function and examine the PingTime and PingWait fields to +// access the ping times. +func (c *Client) Ping() error { + return c.PingAsync().Receive() +} + +// FutureGetPeerInfoResult is a future promise to deliver the result of a +// GetPeerInfoAsync RPC invocation (or an applicable error). +type FutureGetPeerInfoResult chan *futureResult + +// Receive waits for the response promised by the future and returns data about +// each connected network peer. +func (r FutureGetPeerInfoResult) Receive() ([]btcjson.GetPeerInfoResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + peerInfo, ok := reply.([]btcjson.GetPeerInfoResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getpeerinfo: %T\n", reply) + } + + return peerInfo, nil +} + +// GetPeerInfoAsync 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 GetPeerInfo for the blocking version and more details. +func (c *Client) GetPeerInfoAsync() FutureGetPeerInfoResult { + id := c.NextID() + cmd, err := btcjson.NewGetPeerInfoCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetPeerInfo returns data about each connected network peer. +func (c *Client) GetPeerInfo() ([]btcjson.GetPeerInfoResult, error) { + return c.GetPeerInfoAsync().Receive() +} + +// FutureGetNetTotalsResult is a future promise to deliver the result of a +// GetNetTotalsAsync RPC invocation (or an applicable error). +type FutureGetNetTotalsResult chan *futureResult + +// Receive waits for the response promised by the future and returns network +// traffic statistics. +func (r FutureGetNetTotalsResult) Receive() (*btcjson.GetNetTotalsResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + totals, ok := reply.(btcjson.GetNetTotalsResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getnettotals: %T\n", reply) + } + + return &totals, nil +} + +// GetNetTotalsAsync 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 GetNetTotals for the blocking version and more details. +func (c *Client) GetNetTotalsAsync() FutureGetNetTotalsResult { + id := c.NextID() + cmd, err := btcjson.NewGetNetTotalsCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetNetTotals returns network traffic statistics. +func (c *Client) GetNetTotals() (*btcjson.GetNetTotalsResult, error) { + return c.GetNetTotalsAsync().Receive() +} diff --git a/notify.go b/notify.go new file mode 100644 index 00000000..0da70e93 --- /dev/null +++ b/notify.go @@ -0,0 +1,687 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "bytes" + "encoding/hex" + "errors" + "github.com/conformal/btcjson" + "github.com/conformal/btcutil" + "github.com/conformal/btcwire" + "github.com/conformal/btcws" +) + +var ( + // ErrNotificationsNotSupported is an error to describe the condition + // where the caller is trying to request notifications when they are + // not supported due to the client being configured to run in HTTP POST + // mode. + ErrNotificationsNotSupported = errors.New("notifications are not " + + "supported when running in HTTP POST mode") +) + +// newNilFutureResult returns a new future result channel that already has the +// result waiting on the channel with the reply set to nil. This is useful +// to ignore things such as notifications when the caller didn't specify any +// notification handlers. +func newNilFutureResult() chan *futureResult { + responseChan := make(chan *futureResult, 1) + responseChan <- &futureResult{reply: nil} + return responseChan +} + +// NotificationHandlers defines callback function pointers to invoke with +// notifications. Since all of the functions are nil by default, all +// notifications are effectively ignored until their handlers are set to a +// concrete callback. +// +// NOTE: These handlers must NOT directly call any blocking calls on the client +// instance since the input reader goroutine blocks until the callback has +// completed. Doing so will result in a deadlock situation. +type NotificationHandlers struct { + // OnBlockConnected is invoked when a block is connected to the longest + // (best) chain. It will only be invoked if a preceding call to + // NotifyBlocks has been made to register for the notification and the + // function is non-nil. + OnBlockConnected func(hash *btcwire.ShaHash, height int32) + + // OnBlockDisconnected is invoked when a block is disconnected from the + // longest (best) chain. It will only be invoked if a preceding call to + // NotifyBlocks has been made to register for the notification and the + // function is non-nil. + OnBlockDisconnected func(hash *btcwire.ShaHash, height int32) + + // OnRecvTx is invoked when a transaction that receives funds to a + // registered address is received into the memory pool and also + // connected to the longest (best) chain. It will only be invoked if a + // preceding call to NotifyReceived, Rescan, or RescanEndHeight has been + // made to register for the notification and the function is non-nil. + OnRecvTx func(transaction *btcutil.Tx, details *btcws.BlockDetails) + + // OnRedeemingTx is invoked when a transaction that spends a registered + // outpoint is received into the memory pool and also connected to the + // longest (best) chain. It will only be invoked if a preceding call to + // NotifySpent, Rescan, or RescanEndHeight has been made to register for + // the notification and the function is non-nil. + // + // NOTE: The NotifyReceived will automatically register notifications + // for the outpoints that are now "owned" as a result of receiving + // funds to the registered addresses. This means it is possible for + // this to invoked indirectly as the result of a NotifyReceived call. + OnRedeemingTx func(transaction *btcutil.Tx, details *btcws.BlockDetails) + + // OnRescanProgress is invoked periodically when a rescan is underway. + // It will only be invoked if a preceding call to Rescan or + // RescanEndHeight has been made and the function is non-nil. + OnRescanProgress func(lastProcessedHeight int32) + + // OnTxAccepted is invoked when a transaction is accepted into the + // memory pool. It will only be invoked if a preceding call to + // NotifyNewTransactions with the verbose flag set to false has been + // made to register for the notification and the function is non-nil. + OnTxAccepted func(hash *btcwire.ShaHash, amount btcutil.Amount) + + // OnTxAccepted is invoked when a transaction is accepted into the + // memory pool. It will only be invoked if a preceding call to + // NotifyNewTransactions with the verbose flag set to true has been + // made to register for the notification and the function is non-nil. + OnTxAcceptedVerbose func(txDetails *btcjson.TxRawResult) + + // OnBtcdConnected is invoked when a wallet connects or disconnects from + // btcd. + // + // This will only be available when client is connected to a wallet + // server such as btcwallet. + OnBtcdConnected func(connected bool) + + // OnAccountBalance is invoked with account balance updates. + // + // This will only be available when speaking to a wallet server + // such as btcwallet. + OnAccountBalance func(account string, balance btcutil.Amount, confirmed bool) + + // OnWalletLockState is invoked when a wallet is locked or unlocked. + // + // This will only be available when client is connected to a wallet + // server such as btcwallet. + OnWalletLockState func(locked bool) + + // OnUnknownNotification is invoked when an unrecognized notification + // is received. This typically means the notification handling code + // for this package needs to be updated for a new notification type or + // the caller is using a custom notification this package does not know + // about. + OnUnknownNotification func(ntfn interface{}) +} + +// handleNotification examines the passed notification type, performs +// conversions to get the raw notification types into higher level types and +// delivers the notification to the appropriate On handler registered with +// the client. +func (c *Client) handleNotification(cmd btcjson.Cmd) { + // Ignore the notification if the client is not interested in any + // notifications. + if c.ntfnHandlers == nil { + return + } + + switch ntfn := cmd.(type) { + // OnBlockConnected + case *btcws.BlockConnectedNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnBlockConnected == nil { + return + } + + hash, err := btcwire.NewShaHashFromStr(ntfn.Hash) + if err != nil { + log.Warnf("Received block connected notification with "+ + "invalid hash string: %q", ntfn.Hash) + return + } + + c.ntfnHandlers.OnBlockConnected(hash, ntfn.Height) + + // OnBlockDisconnected + case *btcws.BlockDisconnectedNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnBlockDisconnected == nil { + return + } + + hash, err := btcwire.NewShaHashFromStr(ntfn.Hash) + if err != nil { + log.Warnf("Received block disconnected notification "+ + "with invalid hash string: %q", ntfn.Hash) + return + } + + c.ntfnHandlers.OnBlockDisconnected(hash, ntfn.Height) + + // OnRecvTx + case *btcws.RecvTxNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnRecvTx == nil { + return + } + + // Decode the serialized transaction hex to raw bytes. + serializedTx, err := hex.DecodeString(ntfn.HexTx) + if err != nil { + log.Warnf("Received recvtx notification with invalid "+ + "transaction hex '%q': %v", ntfn.HexTx, err) + } + + // Deserialize the transaction. + var msgTx btcwire.MsgTx + err = msgTx.Deserialize(bytes.NewReader(serializedTx)) + if err != nil { + log.Warnf("Received recvtx notification with "+ + "transaction that failed to deserialize: %v", + err) + } + + c.ntfnHandlers.OnRecvTx(btcutil.NewTx(&msgTx), ntfn.Block) + + // OnRedeemingTx + case *btcws.RedeemingTxNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnRedeemingTx == nil { + return + } + + // Decode the serialized transaction hex to raw bytes. + serializedTx, err := hex.DecodeString(ntfn.HexTx) + if err != nil { + log.Warnf("Received redeemingtx notification with "+ + "invalid transaction hex '%q': %v", ntfn.HexTx, + err) + } + + // Deserialize the transaction. + var msgTx btcwire.MsgTx + err = msgTx.Deserialize(bytes.NewReader(serializedTx)) + if err != nil { + log.Warnf("Received redeemingtx notification with "+ + "transaction that failed to deserialize: %v", + err) + } + + c.ntfnHandlers.OnRedeemingTx(btcutil.NewTx(&msgTx), ntfn.Block) + + // OnRescanProgress + case *btcws.RescanProgressNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnRescanProgress == nil { + return + } + + c.ntfnHandlers.OnRescanProgress(ntfn.LastProcessed) + + // OnTxAccepted + case *btcws.TxAcceptedNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnTxAccepted == nil { + return + } + + hash, err := btcwire.NewShaHashFromStr(ntfn.TxID) + if err != nil { + log.Warnf("Received tx accepted notification with "+ + "invalid hash string: %q", ntfn.TxID) + return + } + + c.ntfnHandlers.OnTxAccepted(hash, btcutil.Amount(ntfn.Amount)) + + // OnTxAcceptedVerbose + case *btcws.TxAcceptedVerboseNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnTxAcceptedVerbose == nil { + return + } + + c.ntfnHandlers.OnTxAcceptedVerbose(ntfn.RawTx) + + // OnBtcdConnected + case *btcws.BtcdConnectedNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnBtcdConnected == nil { + return + } + + c.ntfnHandlers.OnBtcdConnected(ntfn.Connected) + + // OnAccountBalance + case *btcws.AccountBalanceNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnAccountBalance == nil { + return + } + + balance, err := btcjson.JSONToAmount(ntfn.Balance) + if err != nil { + log.Warnf("Received account balance notification with "+ + "an amount that does not parse: %v", + ntfn.Balance) + return + } + + c.ntfnHandlers.OnAccountBalance(ntfn.Account, + btcutil.Amount(balance), ntfn.Confirmed) + + // OnWalletLockState + case *btcws.WalletLockStateNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnWalletLockState == nil { + return + } + + c.ntfnHandlers.OnWalletLockState(ntfn.Locked) + + // OnUnknownNotification + default: + if c.ntfnHandlers.OnUnknownNotification == nil { + return + } + + c.ntfnHandlers.OnUnknownNotification(ntfn) + } +} + +// FutureNotifyBlocksResult is a future promise to deliver the result of a +// NotifyBlocksAsync RPC invocation (or an applicable error). +type FutureNotifyBlocksResult chan *futureResult + +// Receive waits for the response promised by the future and returns an error +// if the registration was not successful. +func (r FutureNotifyBlocksResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// NotifyBlocksAsync 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 NotifyBlocks for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifyBlocksAsync() FutureNotifyBlocksResult { + // Not supported in HTTP POST mode. + if c.config.HttpPostMode { + return newFutureError(ErrNotificationsNotSupported) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + id := c.NextID() + cmd := btcws.NewNotifyBlocksCmd(id) + + return c.sendCmd(cmd) +} + +// NotifyBlocks registers the client to receive notifications when blocks are +// connected and disconnected from the main chain. The notifications are +// delivered to the notification handlers associated with the client. Calling +// this function has no effect if there are no notification handlers and will +// result in an error if the client is configured to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via one of +// OnBlockConnected or OnBlockDisconnected. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifyBlocks() error { + return c.NotifyBlocksAsync().Receive() +} + +// FutureNotifySpentResult is a future promise to deliver the result of a +// NotifySpentAsync RPC invocation (or an applicable error). +type FutureNotifySpentResult chan *futureResult + +// Receive waits for the response promised by the future and returns an error +// if the registration was not successful. +func (r FutureNotifySpentResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// NotifySpentAsync 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 NotifySpent for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifySpentAsync(outpoint *btcwire.OutPoint) FutureNotifySpentResult { + // Not supported in HTTP POST mode. + if c.config.HttpPostMode { + return newFutureError(ErrNotificationsNotSupported) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + id := c.NextID() + cmd := btcws.NewNotifySpentCmd(id, btcws.NewOutPointFromWire(outpoint)) + + return c.sendCmd(cmd) +} + +// NotifySpent registers the client to receive notifications when the passed +// transaction output is spent. The notifications are delivered to the +// notification handlers associated with the client. Calling this function has +// no effect if there are no notification handlers and will result in an error +// if the client is configured to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via +// OnRedeemingTx. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifySpent(outpoint *btcwire.OutPoint) error { + return c.NotifySpentAsync(outpoint).Receive() +} + +// FutureNotifyNewTransactionsResult is a future promise to deliver the result +// of a NotifyNewTransactionsAsync RPC invocation (or an applicable error). +type FutureNotifyNewTransactionsResult chan *futureResult + +// Receive waits for the response promised by the future and returns an error +// if the registration was not successful. +func (r FutureNotifyNewTransactionsResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// NotifyNewTransactionsAsync 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 NotifyNewTransactionsAsync for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifyNewTransactionsAsync(verbose bool) FutureNotifyNewTransactionsResult { + // Not supported in HTTP POST mode. + if c.config.HttpPostMode { + return newFutureError(ErrNotificationsNotSupported) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + id := c.NextID() + cmd, err := btcws.NewNotifyNewTransactionsCmd(id, verbose) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// NotifyNewTransactions registers the client to receive notifications every +// time a new transaction is accepted to the memory pool. The notifications are +// delivered to the notification handlers associated with the client. Calling +// this function has no effect if there are no notification handlers and will +// result in an error if the client is configured to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via one of +// OnTxAccepted (when verbose is false) or OnTxAcceptedVerbose (when verbose is +// true). +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifyNewTransactions(verbose bool) error { + return c.NotifyNewTransactionsAsync(verbose).Receive() +} + +// FutureNotifyReceivedResult is a future promise to deliver the result of a +// NotifyReceivedAsync RPC invocation (or an applicable error). +type FutureNotifyReceivedResult chan *futureResult + +// Receive waits for the response promised by the future and returns an error +// if the registration was not successful. +func (r FutureNotifyReceivedResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// NotifyReceivedAsync 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 NotifyReceived for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifyReceivedAsync(addresses []btcutil.Address) FutureNotifyReceivedResult { + // Not supported in HTTP POST mode. + if c.config.HttpPostMode { + return newFutureError(ErrNotificationsNotSupported) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + // Convert addresses to strings. + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.EncodeAddress()) + } + id := c.NextID() + cmd := btcws.NewNotifyReceivedCmd(id, addrs) + + return c.sendCmd(cmd) +} + +// NotifyReceived registers the client to receive notifications every time a +// new transaction which pays to one of the passed addresses is accepted to +// memory pool or in a block connected to the block chain. In addition, when +// one of these transactions is detected, the client is also automatically +// registered for notifications when the new transaction outpoints the address +// now has available are spent (See NotifySpent). The notifications are +// delivered to the notification handlers associated with the client. Calling +// this function has no effect if there are no notification handlers and will +// result in an error if the client is configured to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via one of +// *OnRecvTx (for transactions that receive funds to one of the passed +// addresses) or OnRedeemingTx (for transactions which spend from one +// of the outpoints which are automatically registered upon receipt of funds to +// the address). +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifyReceived(addresses []btcutil.Address) error { + return c.NotifyReceivedAsync(addresses).Receive() +} + +// FutureRescanResult is a future promise to deliver the result of a RescanAsync +// or RescanEndHeightAsync RPC invocation (or an applicable error). +type FutureRescanResult chan *futureResult + +// Receive waits for the response promised by the future and returns an error +// if the rescan was not successful. +func (r FutureRescanResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// RescanAsync 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 Rescan for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) RescanAsync(startHeight int32, addresses []btcutil.Address, + outpoints []*btcwire.OutPoint) FutureRescanResult { + + // Not supported in HTTP POST mode. + if c.config.HttpPostMode { + return newFutureError(ErrNotificationsNotSupported) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + // Convert addresses to strings. + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.EncodeAddress()) + } + + // Convert outpoints. + ops := make([]btcws.OutPoint, 0, len(outpoints)) + for _, op := range outpoints { + ops = append(ops, *btcws.NewOutPointFromWire(op)) + } + + id := c.NextID() + cmd, err := btcws.NewRescanCmd(id, startHeight, addrs, ops) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// Rescan rescans the block chain starting from the provided start height to the +// end of the longest chain for transactions that pay to the passed addresses +// and transactions which spend the passed outpoints. +// +// The notifications of found transactions are delivered to the notification +// handlers associated with client and this call will not return until the +// rescan has completed. Calling this function has no effect if there are no +// notification handlers and will result in an error if the client is configured +// to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via one of +// OnRedeemingTx (for transactions which spend from the one of the +// passed outpoints), OnRecvTx (for transactions that receive funds +// to one of the passed addresses), and OnRescanProgress (for rescan progress +// updates). +// +// See RescanEndHeight to also specify a block height at which to stop the +// rescan if a bounded rescan is desired instead. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) Rescan(startHeight int32, addresses []btcutil.Address, + outpoints []*btcwire.OutPoint) error { + + return c.RescanAsync(startHeight, addresses, outpoints).Receive() +} + +// RescanEndHeightAsync 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 RescanEndHeight for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) RescanEndHeightAsync(startHeight int32, + addresses []btcutil.Address, outpoints []*btcwire.OutPoint, + endHeight int64) FutureRescanResult { + + // Not supported in HTTP POST mode. + if c.config.HttpPostMode { + return newFutureError(ErrNotificationsNotSupported) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + // Convert addresses to strings. + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.EncodeAddress()) + } + + // Convert outpoints. + ops := make([]btcws.OutPoint, 0, len(outpoints)) + for _, op := range outpoints { + ops = append(ops, *btcws.NewOutPointFromWire(op)) + } + + id := c.NextID() + cmd, err := btcws.NewRescanCmd(id, startHeight, addrs, ops, endHeight) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// RescanEndHeight rescans the block chain starting from the provided start +// height up to the provided end height for transactions that pay to the passed +// addresses and transactions which spend the passed outpoints. +// +// The notifications of found transactions are delivered to the notification +// handlers associated with client and this call will not return until the +// rescan has completed. Calling this function has no effect if there are no +// notification handlers and will result in an error if the client is configured +// to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via one of +// OnRedeemingTx (for transactions which spend from the one of the +// passed outpoints), OnRecvTx (for transactions that receive funds +// to one of the passed addresses), and OnRescanProgress (for rescan progress +// updates). +// +// See Rescan to also perform a rescan through current end of the longest chain. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) RescanEndHeight(startHeight int32, addresses []btcutil.Address, + outpoints []*btcwire.OutPoint, endHeight int64) error { + + return c.RescanEndHeightAsync(startHeight, addresses, outpoints, + endHeight).Receive() +} diff --git a/rawtransactions.go b/rawtransactions.go new file mode 100644 index 00000000..c12783e6 --- /dev/null +++ b/rawtransactions.go @@ -0,0 +1,516 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "bytes" + "encoding/hex" + "fmt" + "github.com/conformal/btcjson" + "github.com/conformal/btcutil" + "github.com/conformal/btcwire" +) + +// SigHashType enumerates the available signature hashing types that the +// SignRawTransaction function accepts. +type SigHashType string + +// Constants used to indicate the signature hash type for SignRawTransaction. +const ( + // SigHashAll indicates ALL of the outputs should be signed. + SigHashAll SigHashType = "ALL" + + // SigHashNone indicates NONE of the outputs should be signed. This + // can be thought of as specifying the signer does not care where the + // bitcoins go. + SigHashNone SigHashType = "NONE" + + // SigHashSingle indicates that a SINGLE output should be signed. This + // can be thought of specifying the signer only cares about where ONE of + // the outputs goes, but not any of the others. + SigHashSingle SigHashType = "SINGLE" + + // SigHashAllAnyoneCanPay indicates that signer does not care where the + // other inputs to the transaction come from, so it allows other people + // to add inputs. In addition, it uses the SigHashAll signing method + // for outputs. + SigHashAllAnyoneCanPay SigHashType = "ALL|ANYONECANPAY" + + // SigHashNoneAnyoneCanPay indicates that signer does not care where the + // other inputs to the transaction come from, so it allows other people + // to add inputs. In addition, it uses the SigHashNone signing method + // for outputs. + SigHashNoneAnyoneCanPay SigHashType = "NONE|ANYONECANPAY" + + // SigHashAllAnyoneCanPay indicates that signer does not care where the + // other inputs to the transaction come from, so it allows other people + // to add inputs. In addition, it uses the SigHashSingle signing method + // for outputs. + SigHashSingleAnyoneCanPay SigHashType = "SINGLE|ANYONECANPAY" +) + +// String returns the SighHashType in human-readable form. +func (s SigHashType) String() string { + return string(s) +} + +// FutureGetRawTransactionResult is a future promise to deliver the result of a +// GetRawTransactionAsync RPC invocation (or an applicable error). +type FutureGetRawTransactionResult chan *futureResult + +// Receive waits for the response promised by the future and returns a +// transaction given its hash. +func (r FutureGetRawTransactionResult) Receive() (*btcutil.Tx, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + txHex, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getrawtransaction (verbose=0): %T\n", reply) + } + + // Decode the serialized transaction hex to raw bytes. + serializedTx, err := hex.DecodeString(txHex) + if err != nil { + return nil, err + } + + // Deserialize the transaction and return it. + var msgTx btcwire.MsgTx + if err := msgTx.Deserialize(bytes.NewBuffer(serializedTx)); err != nil { + return nil, err + } + return btcutil.NewTx(&msgTx), nil +} + +// GetRawTransactionAsync 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 GetRawTransaction for the blocking version and more details. +func (c *Client) GetRawTransactionAsync(txHash *btcwire.ShaHash) FutureGetRawTransactionResult { + id := c.NextID() + cmd, err := btcjson.NewGetRawTransactionCmd(id, txHash.String(), 0) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetRawTransaction returns a transaction given its hash. +// +// See GetRawTransactionVerbose to obtain additional information about the +// transaction. +func (c *Client) GetRawTransaction(txHash *btcwire.ShaHash) (*btcutil.Tx, error) { + return c.GetRawTransactionAsync(txHash).Receive() +} + +// FutureGetRawTransactionVerboseResult is a future promise to deliver the +// result of a GetRawTransactionVerboseAsync RPC invocation (or an applicable +// error). +type FutureGetRawTransactionVerboseResult chan *futureResult + +// Receive waits for the response promised by the future and returns information +// about a transaction given its hash. +func (r FutureGetRawTransactionVerboseResult) Receive() (*btcjson.TxRawResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(*btcjson.TxRawResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getrawtransaction (verbose=1): %T\n", reply) + } + + return result, nil +} + +// GetRawTransactionVerboseAsync 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 GetRawTransactionVerbose for the blocking version and more details. +func (c *Client) GetRawTransactionVerboseAsync(txHash *btcwire.ShaHash) FutureGetRawTransactionVerboseResult { + id := c.NextID() + cmd, err := btcjson.NewGetRawTransactionCmd(id, txHash.String(), 1) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetRawTransactionVerbose returns information about a transaction given +// its hash. +// +// See GetRawTransaction to obtain only the transaction already deserialized. +func (c *Client) GetRawTransactionVerbose(txHash *btcwire.ShaHash) (*btcjson.TxRawResult, error) { + return c.GetRawTransactionVerboseAsync(txHash).Receive() +} + +// FutureDecodeRawTransactionResult is a future promise to deliver the result +// of a DecodeRawTransactionAsync RPC invocation (or an applicable error). +type FutureDecodeRawTransactionResult chan *futureResult + +// Receive waits for the response promised by the future and returns information +// about a transaction given its serialized bytes. +func (r FutureDecodeRawTransactionResult) Receive() (*btcjson.TxRawResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(*btcjson.TxRawResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "decoderawtransaction: %T\n", reply) + } + + return result, nil +} + +// DecodeRawTransactionAsync 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 DecodeRawTransaction for the blocking version and more details. +func (c *Client) DecodeRawTransactionAsync(serializedTx []byte) FutureDecodeRawTransactionResult { + id := c.NextID() + txHex := hex.EncodeToString(serializedTx) + cmd, err := btcjson.NewDecodeRawTransactionCmd(id, txHex) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// DecodeRawTransaction returns information about a transaction given its +// serialized bytes. +func (c *Client) DecodeRawTransaction(serializedTx []byte) (*btcjson.TxRawResult, error) { + return c.DecodeRawTransactionAsync(serializedTx).Receive() +} + +// FutureCreateRawTransactionResult is a future promise to deliver the result +// of a CreateRawTransactionAsync RPC invocation (or an applicable error). +type FutureCreateRawTransactionResult chan *futureResult + +// Receive waits for the response promised by the future and returns a new +// transaction spending the provided inputs and sending to the provided +// addresses. +func (r FutureCreateRawTransactionResult) Receive() (*btcwire.MsgTx, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + txHex, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "createrawtransaction: %T\n", reply) + } + + // Decode the serialized transaction hex to raw bytes. + serializedTx, err := hex.DecodeString(txHex) + if err != nil { + return nil, err + } + + // Deserialize the transaction and return it. + var msgTx btcwire.MsgTx + if err := msgTx.Deserialize(bytes.NewBuffer(serializedTx)); err != nil { + return nil, err + } + return &msgTx, nil +} + +// CreateRawTransactionAsync 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 CreateRawTransaction for the blocking version and more details. +func (c *Client) CreateRawTransactionAsync(inputs []btcjson.TransactionInput, amounts map[string]int64) FutureCreateRawTransactionResult { + id := c.NextID() + cmd, err := btcjson.NewCreateRawTransactionCmd(id, inputs, amounts) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// CreateRawTransaction returns a new transaction spending the provided inputs +// and sending to the provided addresses. +func (c *Client) CreateRawTransaction(inputs []btcjson.TransactionInput, amounts map[string]int64) (*btcwire.MsgTx, error) { + return c.CreateRawTransactionAsync(inputs, amounts).Receive() +} + +// FutureSendRawTransactionResult is a future promise to deliver the result +// of a SendRawTransactionAsync RPC invocation (or an applicable error). +type FutureSendRawTransactionResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of submitting the encoded transaction to the server which then relays it to +// the network. +func (r FutureSendRawTransactionResult) Receive() (*btcwire.ShaHash, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + txHashStr, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "decoderawtransaction: %T\n", reply) + } + + return btcwire.NewShaHashFromStr(txHashStr) +} + +// SendRawTransactionAsync 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 SendRawTransaction for the blocking version and more details. +func (c *Client) SendRawTransactionAsync(tx *btcwire.MsgTx, allowHighFees bool) FutureSendRawTransactionResult { + // Serialize the transaction and convert to hex string. + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(buf); err != nil { + return newFutureError(err) + } + txHex := hex.EncodeToString(buf.Bytes()) + + id := c.NextID() + cmd, err := btcjson.NewSendRawTransactionCmd(id, txHex, allowHighFees) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendRawTransaction submits the encoded transaction to the server which will +// then relay it to the network. +func (c *Client) SendRawTransaction(tx *btcwire.MsgTx, allowHighFees bool) (*btcwire.ShaHash, error) { + return c.SendRawTransactionAsync(tx, allowHighFees).Receive() +} + +// FutureSignRawTransactionResult is a future promise to deliver the result +// of one of the SignRawTransactionAsync family of RPC invocations (or an +// applicable error). +type FutureSignRawTransactionResult chan *futureResult + +// Receive waits for the response promised by the future and returns the +// signed transaction as well as whether or not all inputs are now signed. +func (r FutureSignRawTransactionResult) Receive() (*btcwire.MsgTx, bool, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, false, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(btcjson.SignRawTransactionResult) + if !ok { + return nil, false, fmt.Errorf("unexpected response type for "+ + "signrawtransaction: %T\n", reply) + } + + // Decode the serialized transaction hex to raw bytes. + serializedTx, err := hex.DecodeString(result.Hex) + if err != nil { + return nil, false, err + } + + // Deserialize the transaction and return it. + var msgTx btcwire.MsgTx + if err := msgTx.Deserialize(bytes.NewBuffer(serializedTx)); err != nil { + return nil, false, err + } + + return &msgTx, result.Complete, nil +} + +// SignRawTransactionAsync 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 SignRawTransaction for the blocking version and more details. +func (c *Client) SignRawTransactionAsync(tx *btcwire.MsgTx) FutureSignRawTransactionResult { + // Serialize the transaction and convert to hex string. + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(buf); err != nil { + return newFutureError(err) + } + txHex := hex.EncodeToString(buf.Bytes()) + + id := c.NextID() + cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SignRawTransaction signs inputs for the passed transaction and returns the +// signed transaction as well as whether or not all inputs are now signed. +// +// This function assumes the RPC server already knows the input transactions and +// private keys for the passed transaction which needs to be signed and uses the +// default signature hash type. Use one of the SignRawTransaction# variants to +// specify that information if needed. +func (c *Client) SignRawTransaction(tx *btcwire.MsgTx) (*btcwire.MsgTx, bool, error) { + return c.SignRawTransactionAsync(tx).Receive() +} + +// SignRawTransaction2Async 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 SignRawTransaction2 for the blocking version and more details. +func (c *Client) SignRawTransaction2Async(tx *btcwire.MsgTx, inputs []btcjson.RawTxInput) FutureSignRawTransactionResult { + // Serialize the transaction and convert to hex string. + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(buf); err != nil { + return newFutureError(err) + } + txHex := hex.EncodeToString(buf.Bytes()) + + id := c.NextID() + cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex, inputs) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SignRawTransaction2 signs inputs for the passed transaction given the list +// of information about the input transactions needed to perform the signing +// process. +// +// This only input transactions that need to be specified are ones the +// RPC server does not already know. Already known input transactions will be +// merged with the specified transactions. +// +// See SignRawTransaction if the RPC server already knows the input +// transactions. +func (c *Client) SignRawTransaction2(tx *btcwire.MsgTx, inputs []btcjson.RawTxInput) (*btcwire.MsgTx, bool, error) { + return c.SignRawTransaction2Async(tx, inputs).Receive() +} + +// SignRawTransaction3Async 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 SignRawTransaction3 for the blocking version and more details. +func (c *Client) SignRawTransaction3Async(tx *btcwire.MsgTx, + inputs []btcjson.RawTxInput, + privKeysWIF []string) FutureSignRawTransactionResult { + + // Serialize the transaction and convert to hex string. + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(buf); err != nil { + return newFutureError(err) + } + txHex := hex.EncodeToString(buf.Bytes()) + + id := c.NextID() + cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex, inputs, + privKeysWIF) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SignRawTransaction3 signs inputs for the passed transaction given the list +// of information about extra input transactions and a list of private keys +// needed to perform the signing process. The private keys must be in wallet +// import format (WIF). +// +// This only input transactions that need to be specified are ones the +// RPC server does not already know. Already known input transactions will be +// merged with the specified transactions. This means the list of transaction +// inputs can be nil if the RPC server already knows them all. +// +// NOTE: Unlike the merging functionality of the input transactions, ONLY the +// specified private keys will be used, so even if the server already knows some +// of the private keys, they will NOT be used. +// +// See SignRawTransaction if the RPC server already knows the input +// transactions and private keys or SignRawTransaction2 if it already knows the +// private keys. +func (c *Client) SignRawTransaction3(tx *btcwire.MsgTx, + inputs []btcjson.RawTxInput, + privKeysWIF []string) (*btcwire.MsgTx, bool, error) { + + return c.SignRawTransaction3Async(tx, inputs, privKeysWIF).Receive() +} + +// SignRawTransaction4Async 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 SignRawTransaction4 for the blocking version and more details. +func (c *Client) SignRawTransaction4Async(tx *btcwire.MsgTx, + inputs []btcjson.RawTxInput, privKeysWIF []string, + hashType SigHashType) FutureSignRawTransactionResult { + + // Serialize the transaction and convert to hex string. + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(buf); err != nil { + return newFutureError(err) + } + txHex := hex.EncodeToString(buf.Bytes()) + + id := c.NextID() + cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex, inputs, + privKeysWIF, string(hashType)) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SignRawTransaction4 signs inputs for the passed transaction using the +// the specified signature hash type given the list of information about extra +// input transactions and a potential list of private keys needed to perform +// the signing process. The private keys, if specified, must be in wallet +// import format (WIF). +// +// The only input transactions that need to be specified are ones the RPC server +// does not already know. This means the list of transaction inputs can be nil +// if the RPC server already knows them all. +// +// NOTE: Unlike the merging functionality of the input transactions, ONLY the +// specified private keys will be used, so even if the server already knows some +// of the private keys, they will NOT be used. The list of private keys can be +// nil in which case any private keys the RPC server knows will be used. +// +// This function should only used if a non-default signature hash type is +// desired. Otherwise, see SignRawTransaction if the RPC server already knows +// the input transactions and private keys, SignRawTransaction2 if it already +// knows the private keys, or SignRawTransaction3 if it does not know both. +func (c *Client) SignRawTransaction4(tx *btcwire.MsgTx, + inputs []btcjson.RawTxInput, privKeysWIF []string, + hashType SigHashType) (*btcwire.MsgTx, bool, error) { + + return c.SignRawTransaction4Async(tx, inputs, privKeysWIF, + hashType).Receive() +} diff --git a/wallet.go b/wallet.go new file mode 100644 index 00000000..d599e0e8 --- /dev/null +++ b/wallet.go @@ -0,0 +1,1862 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "fmt" + "github.com/conformal/btcjson" + "github.com/conformal/btcutil" + "github.com/conformal/btcwire" + "github.com/conformal/btcws" +) + +// ***************************** +// Transaction Listing Functions +// ***************************** + +// FutureGetTransactionResult is a future promise to deliver the result +// of a GetTransactionAsync RPC invocation (or an applicable error). +type FutureGetTransactionResult chan *futureResult + +// Receive waits for the response promised by the future and returns detailed +// information about a wallet transaction. +func (r FutureGetTransactionResult) Receive() (*btcjson.GetTransactionResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(btcjson.GetTransactionResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "gettransaction: %T\n", reply) + } + + return &result, nil +} + +// GetTransactionAsync 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 GetTransaction for the blocking version and more details. +func (c *Client) GetTransactionAsync(txHash *btcwire.ShaHash) FutureGetTransactionResult { + id := c.NextID() + cmd, err := btcjson.NewGetTransactionCmd(id, txHash.String()) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetTransaction returns detailed information about a wallet transaction. +// +// See GetRawTransaction to return the raw transaction instead. +func (c *Client) GetTransaction(txHash *btcwire.ShaHash) (*btcjson.GetTransactionResult, error) { + return c.GetTransactionAsync(txHash).Receive() +} + +// FutureListTransactionsResult is a future promise to deliver the result of a +// ListTransactionsAsync, ListTransactionsCountAsync, or +// ListTransactionsCountFromAsync RPC invocation (or an applicable error). +type FutureListTransactionsResult chan *futureResult + +// Receive waits for the response promised by the future and returns a list of +// the most recent transactions. +func (r FutureListTransactionsResult) Receive() ([]btcjson.ListTransactionsResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // No transactions. + if reply == nil { + return nil, nil + } + + // Ensure the returned data is the expected type. + transactions, ok := reply.([]btcjson.ListTransactionsResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "listtransactions: %T\n", reply) + } + + return transactions, nil +} + +// ListTransactionsAsync 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 ListTransactions for the blocking version and more details. +func (c *Client) ListTransactionsAsync(account string) FutureListTransactionsResult { + id := c.NextID() + cmd, err := btcjson.NewListTransactionsCmd(id, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListTransactions returns a list of the most recent transactions. +// +// See the ListTransactionsCount and ListTransactionsCountFrom to control the +// number of transactions returned and starting point, respectively. +func (c *Client) ListTransactions(account string) ([]btcjson.ListTransactionsResult, error) { + return c.ListTransactionsAsync(account).Receive() +} + +// ListTransactionsCountAsync 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 ListTransactionsCount for the blocking version and more details. +func (c *Client) ListTransactionsCountAsync(account string, count int) FutureListTransactionsResult { + id := c.NextID() + cmd, err := btcjson.NewListTransactionsCmd(id, account, count) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListTransactionsCount returns a list of the most recent transactions up +// to the passed count. +// +// See the ListTransactions and ListTransactionsCountFrom functions for +// different options. +func (c *Client) ListTransactionsCount(account string, count int) ([]btcjson.ListTransactionsResult, error) { + return c.ListTransactionsCountAsync(account, count).Receive() +} + +// ListTransactionsCountFromAsync 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 ListTransactionsCountFrom for the blocking version and more details. +func (c *Client) ListTransactionsCountFromAsync(account string, count, from int) FutureListTransactionsResult { + id := c.NextID() + cmd, err := btcjson.NewListTransactionsCmd(id, account, count, from) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListTransactionsCountFrom returns a list of the most recent transactions up +// to the passed count while skipping the first 'from' transactions. +// +// See the ListTransactions and ListTransactionsCount functions to use defaults. +func (c *Client) ListTransactionsCountFrom(account string, count, from int) ([]btcjson.ListTransactionsResult, error) { + return c.ListTransactionsCountFromAsync(account, count, from).Receive() +} + +// FutureListSinceBlockResult is a future promise to deliver the result of a +// ListSinceBlockAsync or ListSinceBlockMinConfAsync RPC invocation (or an +// applicable error). +type FutureListSinceBlockResult chan *futureResult + +// Receive waits for the response promised by the future and returns all +// transactions added in blocks since the specified block hash, or all +// transactions if it is nil. +func (r FutureListSinceBlockResult) Receive() (*btcjson.ListSinceBlockResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + listResult, ok := reply.(btcjson.ListSinceBlockResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "listsinceblock: %T\n", reply) + } + + return &listResult, nil +} + +// ListSinceBlockAsync 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 ListSinceBlock for the blocking version and more details. +func (c *Client) ListSinceBlockAsync(blockHash *btcwire.ShaHash) FutureListSinceBlockResult { + hash := "" + if blockHash != nil { + hash = blockHash.String() + } + id := c.NextID() + cmd, err := btcjson.NewListSinceBlockCmd(id, hash) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListSinceBlock returns all transactions added in blocks since the specified +// block hash, or all transactions if it is nil, using the default number of +// minimum confirmations as a filter. +// +// See ListSinceBlockMinConf to override the minimum number of confirmations. +func (c *Client) ListSinceBlock(blockHash *btcwire.ShaHash) (*btcjson.ListSinceBlockResult, error) { + return c.ListSinceBlockAsync(blockHash).Receive() +} + +// ListSinceBlockMinConfAsync 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 ListSinceBlockMinConf for the blocking version and more details. +func (c *Client) ListSinceBlockMinConfAsync(blockHash *btcwire.ShaHash, minConfirms int) FutureListSinceBlockResult { + hash := "" + if blockHash != nil { + hash = blockHash.String() + } + id := c.NextID() + cmd, err := btcjson.NewListSinceBlockCmd(id, hash, minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListSinceBlockMinConf returns all transactions added in blocks since the +// specified block hash, or all transactions if it is nil, using the specified +// number of minimum confirmations as a filter. +// +// See ListSinceBlock to use the default minimum number of confirmations. +func (c *Client) ListSinceBlockMinConf(blockHash *btcwire.ShaHash, minConfirms int) (*btcjson.ListSinceBlockResult, error) { + return c.ListSinceBlockMinConfAsync(blockHash, minConfirms).Receive() +} + +// ************************** +// Transaction Send Functions +// ************************** + +// FutureSetTxFeeResult is a future promise to deliver the result of a +// SetTxFeeAsync RPC invocation (or an applicable error). +type FutureSetTxFeeResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of setting an optional transaction fee per KB that helps ensure transactions +// are processed quickly. Most transaction are 1KB. +func (r FutureSetTxFeeResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// SetTxFeeAsync 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 SetTxFee for the blocking version and more details. +func (c *Client) SetTxFeeAsync(fee btcutil.Amount) FutureSetTxFeeResult { + id := c.NextID() + cmd, err := btcjson.NewSetTxFeeCmd(id, int64(fee)) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SetTxFee sets an optional transaction fee per KB that helps ensure +// transactions are processed quickly. Most transaction are 1KB. +func (c *Client) SetTxFee(fee btcutil.Amount) error { + return c.SetTxFeeAsync(fee).Receive() +} + +// FutureSendToAddressResult is a future promise to deliver the result of a +// SendToAddressAsync RPC invocation (or an applicable error). +type FutureSendToAddressResult chan *futureResult + +// Receive waits for the response promised by the future and returns the hash +// of the transaction sending the passed amount to the given address. +func (r FutureSendToAddressResult) Receive() (*btcwire.ShaHash, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + txHash, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "sendtoaddress: %T\n", reply) + } + + return btcwire.NewShaHashFromStr(txHash) +} + +// SendToAddressAsync 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 SendToAddress for the blocking version and more details. +func (c *Client) SendToAddressAsync(address btcutil.Address, amount btcutil.Amount) FutureSendToAddressResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewSendToAddressCmd(id, addr, int64(amount)) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendToAddress sends the passed amount to the given address. +// +// See SendToAddressComment to associate comments with the transaction in the +// wallet. The comments are not part of the transaction and are only internal +// to the wallet. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendToAddress(address btcutil.Address, amount btcutil.Amount) (*btcwire.ShaHash, error) { + return c.SendToAddressAsync(address, amount).Receive() +} + +// SendToAddressCommentAsync 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 SendToAddressComment for the blocking version and more details. +func (c *Client) SendToAddressCommentAsync(address btcutil.Address, + amount btcutil.Amount, comment, + commentTo string) FutureSendToAddressResult { + + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewSendToAddressCmd(id, addr, int64(amount), + comment, commentTo) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendToAddressComment sends the passed amount to the given address and stores +// the provided comment and comment to in the wallet. The comment parameter is +// intended to be used for the purpose of the transaction while the commentTo +// parameter is indended to be used for who the transaction is being sent to. +// +// The comments are not part of the transaction and are only internal +// to the wallet. +// +// See SendToAddress to avoid using comments. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendToAddressComment(address btcutil.Address, amount btcutil.Amount, comment, commentTo string) (*btcwire.ShaHash, error) { + return c.SendToAddressCommentAsync(address, amount, comment, + commentTo).Receive() +} + +// FutureSendFromResult is a future promise to deliver the result of a +// SendFromAsync, SendFromMinConfAsync, or SendFromCommentAsync RPC invocation +// (or an applicable error). +type FutureSendFromResult chan *futureResult + +// Receive waits for the response promised by the future and returns the hash +// of the transaction sending amount to the given address using the provided +// account as a source of funds. +func (r FutureSendFromResult) Receive() (*btcwire.ShaHash, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + txHash, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "sendfrom: %T\n", reply) + } + + return btcwire.NewShaHashFromStr(txHash) +} + +// SendFromAsync 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 SendFrom for the blocking version and more details. +func (c *Client) SendFromAsync(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount) FutureSendFromResult { + id := c.NextID() + addr := toAddress.EncodeAddress() + cmd, err := btcjson.NewSendFromCmd(id, fromAccount, addr, int64(amount)) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendFrom sends the passed amount to the given address using the provided +// account as a source of funds. Only funds with the default number of minimum +// confirmations will be used. +// +// See SendFromMinConf and SendFromComment for different options. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendFrom(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount) (*btcwire.ShaHash, error) { + return c.SendFromAsync(fromAccount, toAddress, amount).Receive() +} + +// SendFromMinConfAsync 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 SendFromMinConf for the blocking version and more details. +func (c *Client) SendFromMinConfAsync(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount, minConfirms int) FutureSendFromResult { + id := c.NextID() + addr := toAddress.EncodeAddress() + cmd, err := btcjson.NewSendFromCmd(id, fromAccount, addr, int64(amount), + minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendFromMinConf sends the passed amount to the given address using the +// provided account as a source of funds. Only funds with the passed number of +// minimum confirmations will be used. +// +// See SendFrom to use the default number of minimum confirmations and +// SendFromComment for additional options. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendFromMinConf(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount, minConfirms int) (*btcwire.ShaHash, error) { + return c.SendFromMinConfAsync(fromAccount, toAddress, amount, + minConfirms).Receive() +} + +// SendFromCommentAsync 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 SendFromComment for the blocking version and more details. +func (c *Client) SendFromCommentAsync(fromAccount string, + toAddress btcutil.Address, amount btcutil.Amount, minConfirms int, + comment, commentTo string) FutureSendFromResult { + + id := c.NextID() + addr := toAddress.EncodeAddress() + cmd, err := btcjson.NewSendFromCmd(id, fromAccount, addr, int64(amount), + minConfirms, comment, commentTo) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendFromMinConf sends the passed amount to the given address using the +// provided account as a source of funds and stores the provided comment and +// comment to in the wallet. The comment parameter is intended to be used for +// the purpose of the transaction while the commentTo parameter is indended to +// be used for who the transaction is being sent to. Only funds with the passed +// number of minimum confirmations will be used. +// +// See SendFrom and SendFromMinConf to use defaults. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendFromComment(fromAccount string, toAddress btcutil.Address, + amount btcutil.Amount, minConfirms int, + comment, commentTo string) (*btcwire.ShaHash, error) { + + return c.SendFromCommentAsync(fromAccount, toAddress, amount, + minConfirms, comment, commentTo).Receive() +} + +// FutureSendManyResult is a future promise to deliver the result of a +// SendManyAsync, SendManyMinConfAsync, or SendManyCommentAsync RPC invocation +// (or an applicable error). +type FutureSendManyResult chan *futureResult + +// Receive waits for the response promised by the future and returns the hash +// of the transaction sending multiple amounts to multiple addresses using the +// provided account as a source of funds. +func (r FutureSendManyResult) Receive() (*btcwire.ShaHash, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + txHash, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "sendmany: %T\n", reply) + } + + return btcwire.NewShaHashFromStr(txHash) +} + +// SendManyAsync 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 SendMany for the blocking version and more details. +func (c *Client) SendManyAsync(fromAccount string, amounts map[btcutil.Address]btcutil.Amount) FutureSendManyResult { + convertedAmounts := make(map[string]int64, len(amounts)) + for addr, amount := range amounts { + convertedAmounts[addr.EncodeAddress()] = int64(amount) + } + id := c.NextID() + cmd, err := btcjson.NewSendManyCmd(id, fromAccount, convertedAmounts) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendMany sends multiple amounts to multiple addresses using the provided +// account as a source of funds in a single transaction. Only funds with the +// default number of minimum confirmations will be used. +// +// See SendManyMinConf and SendManyComment for different options. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendMany(fromAccount string, amounts map[btcutil.Address]btcutil.Amount) (*btcwire.ShaHash, error) { + return c.SendManyAsync(fromAccount, amounts).Receive() +} + +// SendManyMinConfAsync 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 SendManyMinConf for the blocking version and more details. +func (c *Client) SendManyMinConfAsync(fromAccount string, + amounts map[btcutil.Address]btcutil.Amount, + minConfirms int) FutureSendManyResult { + + convertedAmounts := make(map[string]int64, len(amounts)) + for addr, amount := range amounts { + convertedAmounts[addr.EncodeAddress()] = int64(amount) + } + id := c.NextID() + cmd, err := btcjson.NewSendManyCmd(id, fromAccount, convertedAmounts, + minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendManyMinConf sends multiple amounts to multiple addresses using the +// provided account as a source of funds in a single transaction. Only funds +// with the passed number of minimum confirmations will be used. +// +// See SendMany to use the default number of minimum confirmations and +// SendManyComment for additional options. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendManyMinConf(fromAccount string, + amounts map[btcutil.Address]btcutil.Amount, + minConfirms int) (*btcwire.ShaHash, error) { + + return c.SendManyMinConfAsync(fromAccount, amounts, minConfirms).Receive() +} + +// SendManyCommentAsync 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 SendManyComment for the blocking version and more details. +func (c *Client) SendManyCommentAsync(fromAccount string, + amounts map[btcutil.Address]btcutil.Amount, minConfirms int, + comment string) FutureSendManyResult { + + convertedAmounts := make(map[string]int64, len(amounts)) + for addr, amount := range amounts { + convertedAmounts[addr.EncodeAddress()] = int64(amount) + } + id := c.NextID() + cmd, err := btcjson.NewSendManyCmd(id, fromAccount, convertedAmounts, + minConfirms, comment) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendManyComment sends multiple amounts to multiple addresses using the +// provided account as a source of funds in a single transaction and stores the +// provided comment in the wallet. The comment parameter is intended to be used +// for the purpose of the transaction Only funds with the passed number of +// minimum confirmations will be used. +// +// See SendMany and SendManyMinConf to use defaults. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendManyComment(fromAccount string, + amounts map[btcutil.Address]btcutil.Amount, minConfirms int, + comment string) (*btcwire.ShaHash, error) { + + return c.SendManyCommentAsync(fromAccount, amounts, minConfirms, + comment).Receive() +} + +// ************************* +// Address/Account Functions +// ************************* + +// FutureAddMultisigAddressResult is a future promise to deliver the result of a +// AddMultisigAddressAsync RPC invocation (or an applicable error). +type FutureAddMultisigAddressResult chan *futureResult + +// Receive waits for the response promised by the future and returns the +// multisignature address that requires the specified number of signatures for +// the provided addresses. +func (r FutureAddMultisigAddressResult) Receive() (btcutil.Address, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + addr, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "addmultisigaddress: %T\n", reply) + } + + return btcutil.DecodeAddress(addr, btcwire.MainNet) +} + +// AddMultisigAddressAsync 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 AddMultisigAddress for the blocking version and more details. +func (c *Client) AddMultisigAddressAsync(requiredSigs int, addresses []btcutil.Address, account string) FutureAddMultisigAddressResult { + id := c.NextID() + + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.EncodeAddress()) + } + + cmd, err := btcjson.NewAddMultisigAddressCmd(id, requiredSigs, addrs, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// AddMultisigAddress adds a multisignature address that requires the specified +// number of signatures for the provided addresses to the wallet. +func (c *Client) AddMultisigAddress(requiredSigs int, addresses []btcutil.Address, account string) (btcutil.Address, error) { + return c.AddMultisigAddressAsync(requiredSigs, addresses, + account).Receive() +} + +// FutureCreateMultisigResult is a future promise to deliver the result of a +// CreateMultisigAsync RPC invocation (or an applicable error). +type FutureCreateMultisigResult chan *futureResult + +// Receive waits for the response promised by the future and returns the +// multisignature address and script needed to redeem it. +func (r FutureCreateMultisigResult) Receive() (*btcjson.CreateMultiSigResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(btcjson.CreateMultiSigResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "createmultisig: %T\n", reply) + } + + return &result, nil +} + +// CreateMultisigAsync 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 CreateMultisig for the blocking version and more details. +func (c *Client) CreateMultisigAsync(requiredSigs int, addresses []btcutil.Address) FutureCreateMultisigResult { + id := c.NextID() + + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.EncodeAddress()) + } + + cmd, err := btcjson.NewCreateMultisigCmd(id, requiredSigs, addrs) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// CreateMultisig creates a multisignature address that requires the specified +// number of signatures for the provided addresses and returns the +// multisignature address and script needed to redeem it. +func (c *Client) CreateMultisig(requiredSigs int, addresses []btcutil.Address) (*btcjson.CreateMultiSigResult, error) { + return c.CreateMultisigAsync(requiredSigs, addresses).Receive() +} + +// FutureGetNewAddressResult is a future promise to deliver the result of a +// GetNewAddressAsync RPC invocation (or an applicable error). +type FutureGetNewAddressResult chan *futureResult + +// Receive waits for the response promised by the future and returns a new +// address. +func (r FutureGetNewAddressResult) Receive() (btcutil.Address, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + addr, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getnewaddress: %T\n", reply) + } + + return btcutil.DecodeAddress(addr, btcwire.MainNet) +} + +// GetNewAddressAsync 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 GetNewAddress for the blocking version and more details. +func (c *Client) GetNewAddressAsync() FutureGetNewAddressResult { + id := c.NextID() + cmd, err := btcjson.NewGetNewAddressCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetNewAddress returns a new address. +func (c *Client) GetNewAddress() (btcutil.Address, error) { + return c.GetNewAddressAsync().Receive() +} + +// FutureGetRawChangeAddressResult is a future promise to deliver the result of +// a GetRawChangeAddressAsync RPC invocation (or an applicable error). +type FutureGetRawChangeAddressResult chan *futureResult + +// Receive waits for the response promised by the future and returns a new +// address for receiving change that will be associated with the provided +// account. Note that this is only for raw transactions and NOT for normal use. +func (r FutureGetRawChangeAddressResult) Receive() (btcutil.Address, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + addr, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getrawchangeaddress: %T\n", reply) + } + + return btcutil.DecodeAddress(addr, btcwire.MainNet) +} + +// GetRawChangeAddressAsync 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 GetRawChangeAddress for the blocking version and more details. +func (c *Client) GetRawChangeAddressAsync(account string) FutureGetRawChangeAddressResult { + id := c.NextID() + cmd, err := btcjson.NewGetRawChangeAddressCmd(id, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetRawChangeAddress returns a new address for receiving change that will be +// associated with the provided account. Note that this is only for raw +// transactions and NOT for normal use. +func (c *Client) GetRawChangeAddress(account string) (btcutil.Address, error) { + return c.GetRawChangeAddressAsync(account).Receive() +} + +// FutureGetAccountAddressResult is a future promise to deliver the result of a +// GetAccountAddressAsync RPC invocation (or an applicable error). +type FutureGetAccountAddressResult chan *futureResult + +// Receive waits for the response promised by the future and returns the current +// Bitcoin address for receiving payments to the specified account. +func (r FutureGetAccountAddressResult) Receive() (btcutil.Address, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + addr, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getaccountaddress: %T\n", reply) + } + + return btcutil.DecodeAddress(addr, btcwire.MainNet) +} + +// GetAccountAddressAsync 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 GetAccountAddress for the blocking version and more details. +func (c *Client) GetAccountAddressAsync(account string) FutureGetAccountAddressResult { + id := c.NextID() + cmd, err := btcjson.NewGetAccountAddressCmd(id, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetAccountAddress returns the current Bitcoin address for receiving payments +// to the specified account. +func (c *Client) GetAccountAddress(account string) (btcutil.Address, error) { + return c.GetAccountAddressAsync(account).Receive() +} + +// FutureGetAccountResult is a future promise to deliver the result of a +// GetAccountAsync RPC invocation (or an applicable error). +type FutureGetAccountResult chan *futureResult + +// Receive waits for the response promised by the future and returns the account +// associated with the passed address. +func (r FutureGetAccountResult) Receive() (string, error) { + reply, err := receiveFuture(r) + if err != nil { + return "", err + } + + // Ensure the returned data is the expected type. + account, ok := reply.(string) + if !ok { + return "", fmt.Errorf("unexpected response type for "+ + "getaccount: %T\n", reply) + } + + return account, nil +} + +// GetAccountAsync 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 GetAccount for the blocking version and more details. +func (c *Client) GetAccountAsync(address btcutil.Address) FutureGetAccountResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewGetAccountCmd(id, addr) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetAccount returns the account associated with the passed address. +func (c *Client) GetAccount(address btcutil.Address) (string, error) { + return c.GetAccountAsync(address).Receive() +} + +// FutureSetAccountResult is a future promise to deliver the result of a +// SetAccountAsync RPC invocation (or an applicable error). +type FutureSetAccountResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of setting the account to be associated with the passed address. +func (r FutureSetAccountResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// SetAccountAsync 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 SetAccount for the blocking version and more details. +func (c *Client) SetAccountAsync(address btcutil.Address, account string) FutureSetAccountResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewSetAccountCmd(id, addr, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SetAccount sets the account associated with the passed address. +func (c *Client) SetAccount(address btcutil.Address, account string) error { + return c.SetAccountAsync(address, account).Receive() +} + +// FutureGetAddressesByAccountResult is a future promise to deliver the result +// of a GetAddressesByAccountAsync RPC invocation (or an applicable error). +type FutureGetAddressesByAccountResult chan *futureResult + +// Receive waits for the response promised by the future and returns the list of +// addresses associated with the passed account. +func (r FutureGetAddressesByAccountResult) Receive() ([]btcutil.Address, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + addrStrings, ok := reply.([]string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getaddressesbyaccount: %T\n", reply) + } + + addrs := make([]btcutil.Address, 0, len(addrStrings)) + for _, addrStr := range addrStrings { + addr, err := btcutil.DecodeAddress(addrStr, btcwire.MainNet) + if err != nil { + return nil, err + } + addrs = append(addrs, addr) + } + + return addrs, nil +} + +// GetAddressesByAccountAsync 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 GetAddressesByAccount for the blocking version and more details. +func (c *Client) GetAddressesByAccountAsync(account string) FutureGetAddressesByAccountResult { + id := c.NextID() + cmd, err := btcjson.NewGetAddressesByAccountCmd(id, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetAddressesByAccount returns the list of addresses associated with the +// passed account. +func (c *Client) GetAddressesByAccount(account string) ([]btcutil.Address, error) { + return c.GetAddressesByAccountAsync(account).Receive() +} + +// FutureValidateAddressResult is a future promise to deliver the result of a +// ValidateAddressAsync RPC invocation (or an applicable error). +type FutureValidateAddressResult chan *futureResult + +// Receive waits for the response promised by the future and returns information +// about the given bitcoin address. +func (r FutureValidateAddressResult) Receive() (*btcjson.ValidateAddressResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(btcjson.ValidateAddressResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "validateaddress: %T\n", reply) + } + + return &result, nil +} + +// ValidateAddressAsync 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 ValidateAddress for the blocking version and more details. +func (c *Client) ValidateAddressAsync(address btcutil.Address) FutureValidateAddressResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewValidateAddressCmd(id, addr) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ValidateAddress returns information about the given bitcoin address. +func (c *Client) ValidateAddress(address btcutil.Address) (*btcjson.ValidateAddressResult, error) { + return c.ValidateAddressAsync(address).Receive() +} + +// FutureKeyPoolRefillResult is a future promise to deliver the result of a +// KeyPoolRefillAsync RPC invocation (or an applicable error). +type FutureKeyPoolRefillResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of refilling the key pool. +func (r FutureKeyPoolRefillResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// KeyPoolRefillAsync 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 KeyPoolRefill for the blocking version and more details. +func (c *Client) KeyPoolRefillAsync() FutureKeyPoolRefillResult { + id := c.NextID() + cmd, err := btcjson.NewKeyPoolRefillCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// KeyPoolRefill fills the key pool as necessary to reach the default size. +// +// See KeyPoolRefillSize to override the size of the key pool. +func (c *Client) KeyPoolRefill() error { + return c.KeyPoolRefillAsync().Receive() +} + +// KeyPoolRefillSizeAsync 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 KeyPoolRefillSize for the blocking version and more details. +func (c *Client) KeyPoolRefillSizeAsync(newSize uint) FutureKeyPoolRefillResult { + id := c.NextID() + cmd, err := btcjson.NewKeyPoolRefillCmd(id, newSize) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// KeyPoolRefillSize fills the key pool as necessary to reach the specified +// size. +func (c *Client) KeyPoolRefillSize(newSize uint) error { + return c.KeyPoolRefillSizeAsync(newSize).Receive() +} + +// ************************ +// Amount/Balance Functions +// ************************ + +// FutureListAccountsResult is a future promise to deliver the result of a +// ListAccountsAsync or ListAccountsMinConfAsync RPC invocation (or an +// applicable error). +type FutureListAccountsResult chan *futureResult + +// Receive waits for the response promised by the future and returns returns a +// map of account names and their associated balances. +func (r FutureListAccountsResult) Receive() (map[string]btcutil.Amount, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + accounts, ok := reply.(map[string]float64) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "listaccounts: %T\n", reply) + } + + accountsMap := make(map[string]btcutil.Amount) + for k, v := range accounts { + satoshi, err := btcjson.JSONToAmount(v) + if err != nil { + return nil, err + } + + accountsMap[k] = btcutil.Amount(satoshi) + } + + return accountsMap, nil +} + +// ListAccountsAsync 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 ListAccounts for the blocking version and more details. +func (c *Client) ListAccountsAsync() FutureListAccountsResult { + id := c.NextID() + cmd, err := btcjson.NewListAccountsCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListAccounts returns a map of account names and their associated balances +// using the default number of minimum confirmations. +// +// See ListAccountsMinConf to override the minimum number of confirmations. +func (c *Client) ListAccounts() (map[string]btcutil.Amount, error) { + return c.ListAccountsAsync().Receive() +} + +// ListAccountsMinConfAsync 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 ListAccountsMinConf for the blocking version and more details. +func (c *Client) ListAccountsMinConfAsync(minConfirms int) FutureListAccountsResult { + id := c.NextID() + cmd, err := btcjson.NewListAccountsCmd(id, minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListAccountsMinConf returns a map of account names and their associated +// balances using the specified number of minimum confirmations. +// +// See ListAccounts to use the default minimum number of confirmations. +func (c *Client) ListAccountsMinConf(minConfirms int) (map[string]btcutil.Amount, error) { + return c.ListAccountsMinConfAsync(minConfirms).Receive() +} + +// FutureGetBalanceResult is a future promise to deliver the result of a +// GetBalanceAsync or GetBalanceMinConfAsync RPC invocation (or an applicable +// error). +type FutureGetBalanceResult chan *futureResult + +// Receive waits for the response promised by the future and returns the +// available balance from the server for the specified account. +func (r FutureGetBalanceResult) Receive() (btcutil.Amount, error) { + reply, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Ensure the returned data is the expected type. + balance, ok := reply.(float64) + if !ok { + return 0, fmt.Errorf("unexpected response type for "+ + "getbalance: %T\n", reply) + } + + satoshi, err := btcjson.JSONToAmount(balance) + if err != nil { + return 0, err + } + + return btcutil.Amount(satoshi), nil +} + +// GetBalanceAsync 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 GetBalance for the blocking version and more details. +func (c *Client) GetBalanceAsync(account string) FutureGetBalanceResult { + // TODO(davec): Remove this hack once btcwallet is fixed. + if account == "*" { + account = "" + } + id := c.NextID() + cmd, err := btcjson.NewGetBalanceCmd(id, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetBalance returns the available balance from the server for the specified +// account using the default number of minimum confirmations. The account may +// be "*" for all accounts. +// +// See GetBalanceMinConf to override the minimum number of confirmations. +func (c *Client) GetBalance(account string) (btcutil.Amount, error) { + return c.GetBalanceAsync(account).Receive() +} + +// GetBalanceMinConfAsync 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 GetBalanceMinConf for the blocking version and more details. +func (c *Client) GetBalanceMinConfAsync(account string, minConfirms int) FutureGetBalanceResult { + // TODO(davec): Remove this hack once btcwallet is fixed. + if account == "*" { + account = "" + } + id := c.NextID() + cmd, err := btcjson.NewGetBalanceCmd(id, account, minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetBalanceMinConf returns the available balance from the server for the +// specified account using the specified number of minimum confirmations. The +// account may be "*" for all accounts. +// +// See GetBalance to use the default minimum number of confirmations. +func (c *Client) GetBalanceMinConf(account string, minConfirms int) (btcutil.Amount, error) { + return c.GetBalanceMinConfAsync(account, minConfirms).Receive() +} + +// FutureGetReceivedByAccountResult is a future promise to deliver the result of +// a GetReceivedByAccountAsync or GetReceivedByAccountMinConfAsync RPC +// invocation (or an applicable error). +type FutureGetReceivedByAccountResult chan *futureResult + +// Receive waits for the response promised by the future and returns the total +// amount received with the specified account. +func (r FutureGetReceivedByAccountResult) Receive() (btcutil.Amount, error) { + reply, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Ensure the returned data is the expected type. + balance, ok := reply.(float64) + if !ok { + return 0, fmt.Errorf("unexpected response type for "+ + "getreceivedbyaccount: %T\n", reply) + } + + satoshi, err := btcjson.JSONToAmount(balance) + if err != nil { + return 0, err + } + + return btcutil.Amount(satoshi), nil +} + +// GetReceivedByAccountAsync 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 GetReceivedByAccount for the blocking version and more details. +func (c *Client) GetReceivedByAccountAsync(account string) FutureGetReceivedByAccountResult { + id := c.NextID() + cmd, err := btcjson.NewGetReceivedByAccountCmd(id, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetReceivedByAccount returns the total amount received with the specified +// account with at least the default number of minimum confirmations. +// +// See GetReceivedByAccountMinConf to override the minimum number of +// confirmations. +func (c *Client) GetReceivedByAccount(account string) (btcutil.Amount, error) { + return c.GetReceivedByAccountAsync(account).Receive() +} + +// GetReceivedByAccountMinConfAsync 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 GetReceivedByAccountMinConf for the blocking version and more details. +func (c *Client) GetReceivedByAccountMinConfAsync(account string, minConfirms int) FutureGetReceivedByAccountResult { + id := c.NextID() + cmd, err := btcjson.NewGetReceivedByAccountCmd(id, account, minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetReceivedByAccountMinConf returns the total amount received with the +// specified account with at least the specified number of minimum +// confirmations. +// +// See GetReceivedByAccount to use the default minimum number of confirmations. +func (c *Client) GetReceivedByAccountMinConf(account string, minConfirms int) (btcutil.Amount, error) { + return c.GetReceivedByAccountMinConfAsync(account, minConfirms).Receive() +} + +// FutureGetUnconfirmedBalanceResult is a future promise to deliver the result +// of a GetUnconfirmedBalanceAsync RPC invocation (or an applicable error). +type FutureGetUnconfirmedBalanceResult chan *futureResult + +// Receive waits for the response promised by the future and returns returns the +// unconfirmed balance from the server for the specified account. +func (r FutureGetUnconfirmedBalanceResult) Receive() (btcutil.Amount, error) { + reply, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Ensure the returned data is the expected type. + balance, ok := reply.(float64) + if !ok { + return 0, fmt.Errorf("unexpected response type for "+ + "getunconfirmedbalance: %T\n", reply) + } + + satoshi, err := btcjson.JSONToAmount(balance) + if err != nil { + return 0, err + } + + return btcutil.Amount(satoshi), nil +} + +// GetUnconfirmedBalanceAsync 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 GetUnconfirmedBalance for the blocking version and more details. +func (c *Client) GetUnconfirmedBalanceAsync(account string) FutureGetUnconfirmedBalanceResult { + id := c.NextID() + cmd, err := btcws.NewGetUnconfirmedBalanceCmd(id, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetUnconfirmedBalance returns the unconfirmed balance from the server for +// the specified account. +func (c *Client) GetUnconfirmedBalance(account string) (btcutil.Amount, error) { + return c.GetUnconfirmedBalanceAsync(account).Receive() +} + +// FutureGetReceivedByAddressResult is a future promise to deliver the result of +// a GetReceivedByAddressAsync or GetReceivedByAddressMinConfAsync RPC +// invocation (or an applicable error). +type FutureGetReceivedByAddressResult chan *futureResult + +// Receive waits for the response promised by the future and returns the total +// amount received by the specified address. +func (r FutureGetReceivedByAddressResult) Receive() (btcutil.Amount, error) { + reply, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Ensure the returned data is the expected type. + balance, ok := reply.(float64) + if !ok { + return 0, fmt.Errorf("unexpected response type for "+ + "getreceivedbyaddress: %T\n", reply) + } + + satoshi, err := btcjson.JSONToAmount(balance) + if err != nil { + return 0, err + } + + return btcutil.Amount(satoshi), nil +} + +// GetReceivedByAddressAsync 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 GetReceivedByAddress for the blocking version and more details. +func (c *Client) GetReceivedByAddressAsync(address btcutil.Address) FutureGetReceivedByAddressResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewGetReceivedByAddressCmd(id, addr) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) + +} + +// GetReceivedByAddress returns the total amount received by the specified +// address with at least the default number of minimum confirmations. +// +// See GetReceivedByAddressMinConf to override the minimum number of +// confirmations. +func (c *Client) GetReceivedByAddress(address btcutil.Address) (btcutil.Amount, error) { + return c.GetReceivedByAddressAsync(address).Receive() +} + +// GetReceivedByAddressMinConfAsync 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 GetReceivedByAddressMinConf for the blocking version and more details. +func (c *Client) GetReceivedByAddressMinConfAsync(address btcutil.Address, minConfirms int) FutureGetReceivedByAddressResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewGetReceivedByAddressCmd(id, addr, minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetReceivedByAddressMinConf returns the total amount received by the specified +// address with at least the specified number of minimum confirmations. +// +// See GetReceivedByAddress to use the default minimum number of confirmations. +func (c *Client) GetReceivedByAddressMinConf(address btcutil.Address, minConfirms int) (btcutil.Amount, error) { + return c.GetReceivedByAddressMinConfAsync(address, minConfirms).Receive() +} + +// FutureListReceivedByAccountResult is a future promise to deliver the result +// of a ListReceivedByAccountAsync, ListReceivedByAccountMinConfAsync, or +// ListReceivedByAccountIncludeEmptyAsync RPC invocation (or an applicable +// error). +type FutureListReceivedByAccountResult chan *futureResult + +// Receive waits for the response promised by the future and returns a list of +// balances by account. +func (r FutureListReceivedByAccountResult) Receive() ([]btcjson.ListReceivedByAccountResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // No results. + if reply == nil { + return nil, nil + } + + // Ensure the returned data is the expected type. + result, ok := reply.([]btcjson.ListReceivedByAccountResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "listreceivedbyaccount: %T\n", reply) + } + + return result, nil +} + +// ListReceivedByAccountAsync 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 ListReceivedByAccount for the blocking version and more details. +func (c *Client) ListReceivedByAccountAsync() FutureListReceivedByAccountResult { + id := c.NextID() + cmd, err := btcjson.NewListReceivedByAccountCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListReceivedByAccount lists balances by account using the default number +// of minimum confirmations and including accounts that haven't received any +// payments. +// +// See ListReceivedByAccountMinConf to override the minimum number of +// confirmations and ListReceivedByAccountIncludeEmpty to filter accounts that +// haven't received any payments from the results. +func (c *Client) ListReceivedByAccount() ([]btcjson.ListReceivedByAccountResult, error) { + return c.ListReceivedByAccountAsync().Receive() +} + +// ListReceivedByAccountMinConfAsync 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 ListReceivedByAccountMinConf for the blocking version and more details. +func (c *Client) ListReceivedByAccountMinConfAsync(minConfirms int) FutureListReceivedByAccountResult { + id := c.NextID() + cmd, err := btcjson.NewListReceivedByAccountCmd(id, minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListReceivedByAccountMinConf lists balances by account using the specified +// number of minimum confirmations and including accounts that haven't received +// any payments. +// +// See ListReceivedByAccount to use the default minimum number of confirmations +// and ListReceivedByAccountIncludeEmpty to also filter accounts that haven't +// received any payments from the results. +func (c *Client) ListReceivedByAccountMinConf(minConfirms int) ([]btcjson.ListReceivedByAccountResult, error) { + return c.ListReceivedByAccountMinConfAsync(minConfirms).Receive() +} + +// ListReceivedByAccountIncludeEmptyAsync 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 ListReceivedByAccountIncludeEmpt for the blocking version and more details. +func (c *Client) ListReceivedByAccountIncludeEmptyAsync(minConfirms int, includeEmpty bool) FutureListReceivedByAccountResult { + id := c.NextID() + cmd, err := btcjson.NewListReceivedByAccountCmd(id, minConfirms, includeEmpty) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListReceivedByAccountIncludeEmpty lists balances by account using the +// specified number of minimum confirmations and including accounts that +// haven't received any payments depending on specified flag. +// +// See ListReceivedByAccount and ListReceivedByAccountMinConf to use defaults. +func (c *Client) ListReceivedByAccountIncludeEmpty(minConfirms int, includeEmpty bool) ([]btcjson.ListReceivedByAccountResult, error) { + return c.ListReceivedByAccountIncludeEmptyAsync(minConfirms, + includeEmpty).Receive() +} + +// ************************ +// Wallet Locking Functions +// ************************ + +// FutureWalletLockResult is a future promise to deliver the result of a +// WalletLockAsync RPC invocation (or an applicable error). +type FutureWalletLockResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of locking the wallet. +func (r FutureWalletLockResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// WalletLockAsync 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 WalletLock for the blocking version and more details. +func (c *Client) WalletLockAsync() FutureWalletLockResult { + id := c.NextID() + cmd, err := btcjson.NewWalletLockCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// WalletLock locks the wallet by removing the encryption key from memory. +// +// After calling this function, the WalletPassphrase function must be used to +// unlock the wallet prior to calling any other function which requires the +// wallet to be unlocked. +func (c *Client) WalletLock() error { + return c.WalletLockAsync().Receive() +} + +// WalletPassphrase unlocks the wallet by using the passphrase to derive the +// decryption key which is then stored in memory for the specified timeout +// (in seconds). +func (c *Client) WalletPassphrase(passphrase string, timeoutSecs int64) error { + id := c.NextID() + cmd, err := btcjson.NewWalletPassphraseCmd(id, passphrase, timeoutSecs) + if err != nil { + return err + } + + _, err = c.sendCmdAndWait(cmd) + if err != nil { + return err + } + + return nil +} + +// FutureWalletPassphraseChangeResult is a future promise to deliver the result +// of a WalletPassphraseChangeAsync RPC invocation (or an applicable error). +type FutureWalletPassphraseChangeResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of unlocking the wallet. +func (r FutureWalletPassphraseChangeResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// WalletPassphraseChangeAsync 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 WalletPassphraseChange for the blocking version and more details. +func (c *Client) WalletPassphraseChangeAsync(old, new string) FutureWalletPassphraseChangeResult { + id := c.NextID() + cmd, err := btcjson.NewWalletPassphraseChangeCmd(id, old, new) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// WalletPassphraseChange changes the wallet passphrase from the specified old +// to new passphrase. +func (c *Client) WalletPassphraseChange(old, new string) error { + return c.WalletPassphraseChangeAsync(old, new).Receive() +} + +// ************************* +// Message Signing Functions +// ************************* + +// FutureSignMessageResult is a future promise to deliver the result of a +// SignMessageAsync RPC invocation (or an applicable error). +type FutureSignMessageResult chan *futureResult + +// Receive waits for the response promised by the future and returns the message +// signed with the private key of the specified address. +func (r FutureSignMessageResult) Receive() (string, error) { + reply, err := receiveFuture(r) + if err != nil { + return "", err + } + + // Ensure the returned data is the expected type. + b64, ok := reply.(string) + if !ok { + return "", fmt.Errorf("unexpected response type for "+ + "signmessage: %T\n", reply) + } + + return b64, nil +} + +// SignMessageAsync 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 SignMessage for the blocking version and more details. +func (c *Client) SignMessageAsync(address btcutil.Address, message string) FutureSignMessageResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewSignMessageCmd(id, addr, message) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SignMessage signs a message with the private key of the specified address. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SignMessage(address btcutil.Address, message string) (string, error) { + return c.SignMessageAsync(address, message).Receive() +} + +// FutureVerifyMessageResult is a future promise to deliver the result of a +// VerifyMessageAsync RPC invocation (or an applicable error). +type FutureVerifyMessageResult chan *futureResult + +// Receive waits for the response promised by the future and returns whether or +// not the message was successfully verified. +func (r FutureVerifyMessageResult) Receive() (bool, error) { + reply, err := receiveFuture(r) + if err != nil { + return false, err + } + + // Ensure the returned data is the expected type. + verified, ok := reply.(bool) + if !ok { + return false, fmt.Errorf("unexpected response type for "+ + "verifymessage: %T\n", reply) + } + + return verified, nil +} + +// VerifyMessageAsync 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 VerifyMessage for the blocking version and more details. +func (c *Client) VerifyMessageAsync(address btcutil.Address, signature, message string) FutureVerifyMessageResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewVerifyMessageCmd(id, addr, signature, message) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// VerifyMessage verifies a signed message. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) VerifyMessage(address btcutil.Address, signature, message string) (bool, error) { + return c.VerifyMessageAsync(address, signature, message).Receive() +} + +// ********************* +// Dump/Import Functions +// ********************* + +// FutureDumpPrivKeyResult is a future promise to deliver the result of a +// DumpPrivKeyAsync RPC invocation (or an applicable error). +type FutureDumpPrivKeyResult chan *futureResult + +// Receive waits for the response promised by the future and returns the private +// key corresponding to the passed address encoded in the wallet import format +// (WIF) +func (r FutureDumpPrivKeyResult) Receive() (string, error) { + reply, err := receiveFuture(r) + if err != nil { + return "", err + } + + // Ensure the returned data is the expected type. + privKeyWIF, ok := reply.(string) + if !ok { + return "", fmt.Errorf("unexpected response type for "+ + "dumpprivkey: %T\n", reply) + } + + return privKeyWIF, nil +} + +// DumpPrivKeyAsync 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 DumpPrivKey for the blocking version and more details. +func (c *Client) DumpPrivKeyAsync(address btcutil.Address) FutureDumpPrivKeyResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewDumpPrivKeyCmd(id, addr) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// DumpPrivKey gets the private key corresponding to the passed address encoded +// in the wallet import format (WIF). +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) DumpPrivKey(address btcutil.Address) (string, error) { + return c.DumpPrivKeyAsync(address).Receive() +} + +// FutureImportPrivKeyResult is a future promise to deliver the result of an +// ImportPrivKeyAsync RPC invocation (or an applicable error). +type FutureImportPrivKeyResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of importing the passed private key which must be the wallet import format +// (WIF). +func (r FutureImportPrivKeyResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// ImportPrivKeyAsync 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 ImportPrivKey for the blocking version and more details. +func (c *Client) ImportPrivKeyAsync(privKeyWIF string) FutureImportPrivKeyResult { + id := c.NextID() + cmd, err := btcjson.NewImportPrivKeyCmd(id, privKeyWIF) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ImportPrivKey imports the passed private key which must be the wallet import +// format (WIF). +func (c *Client) ImportPrivKey(privKeyWIF string) error { + return c.ImportPrivKeyAsync(privKeyWIF).Receive() +} + +// TODO(davec): Implement +// backupwallet (NYI in btcwallet) +// encryptwallet (Won't be supported by btcwallet since it's always encrypted) +// getwalletinfo (NYI in btcwallet or btcjson) +// listaddressgroupings (NYI in btcwallet) +// listlockunspent (NYI in btcwallet) +// listreceivedbyaddress (NYI in btcwallet) +// listreceivedbyaccount (NYI in btcwallet) +// lockunspent (NYI in btcwallet) +// move (NYI in btcwallet) + +// DUMP +// importwallet (NYI in btcwallet) +// dumpwallet (NYI in btcwallet)