commit e3f130ade5c3aac465c9408710d22a94456bfdde Author: Dave Collins Date: Wed May 7 11:14:39 2014 -0500 Initial commit. 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)