From e3f130ade5c3aac465c9408710d22a94456bfdde Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 7 May 2014 11:14:39 -0500 Subject: [PATCH 001/153] Initial commit. --- .gitignore | 28 + LICENSE | 13 + README.md | 11 + chain.go | 494 ++++++++++++ doc.go | 8 + extensions.go | 291 +++++++ infrastructure.go | 937 ++++++++++++++++++++++ log.go | 74 ++ mining.go | 361 +++++++++ net.go | 327 ++++++++ notify.go | 687 ++++++++++++++++ rawtransactions.go | 516 ++++++++++++ wallet.go | 1862 ++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 5609 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 chain.go create mode 100644 doc.go create mode 100644 extensions.go create mode 100644 infrastructure.go create mode 100644 log.go create mode 100644 mining.go create mode 100644 net.go create mode 100644 notify.go create mode 100644 rawtransactions.go create mode 100644 wallet.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..612e2e96 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# Temp files +*~ + +# Log files +*.log + +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..315cb819 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2014 Conformal Systems LLC. + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..ba5d56f8 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +btcrpcclient +============ + +This package is currently under development. + +You really probably don't want to use this yet! + +## License + +Package btcrpcclient is licensed under the [copyfree](http://copyfree.org) ISC +License. diff --git a/chain.go b/chain.go new file mode 100644 index 00000000..edb2362c --- /dev/null +++ b/chain.go @@ -0,0 +1,494 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "bytes" + "encoding/hex" + "fmt" + "github.com/conformal/btcjson" + "github.com/conformal/btcutil" + "github.com/conformal/btcwire" +) + +// FutureGetBestBlockHashResult is a future promise to deliver the result of a +// GetBestBlockAsync RPC invocation (or an applicable error). +type FutureGetBestBlockHashResult chan *futureResult + +// Receive waits for the response promised by the future and returns the hash of +// the best block in the longest block chain. +func (r FutureGetBestBlockHashResult) Receive() (*btcwire.ShaHash, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + txHashStr, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getbestblockhash: %T\n", reply) + } + + return btcwire.NewShaHashFromStr(txHashStr) +} + +// GetBestBlockHashAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetBestBlockHash for the blocking version and more details. +func (c *Client) GetBestBlockHashAsync() FutureGetBestBlockHashResult { + id := c.NextID() + cmd, err := btcjson.NewGetBestBlockHashCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetBestBlockHash returns the hash of the best block in the longest block +// chain. +func (c *Client) GetBestBlockHash() (*btcwire.ShaHash, error) { + return c.GetBestBlockHashAsync().Receive() +} + +// FutureGetBlockResult is a future promise to deliver the result of a +// GetBlockAsync RPC invocation (or an applicable error). +type FutureGetBlockResult chan *futureResult + +// Receive waits for the response promised by the future and returns the raw +// block requested from the server given its hash. +func (r FutureGetBlockResult) Receive() (*btcutil.Block, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + blockHex, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getblock (verbose=0): %T\n", reply) + } + + // Decode the serialized block hex to raw bytes. + serializedBlock, err := hex.DecodeString(blockHex) + if err != nil { + return nil, err + } + + // Deserialize the block and return it. + var msgBlock btcwire.MsgBlock + msgBlock.Deserialize(bytes.NewBuffer(serializedBlock)) + if err != nil { + return nil, err + } + return btcutil.NewBlock(&msgBlock), nil +} + +// GetBlockAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetBlock for the blocking version and more details. +func (c *Client) GetBlockAsync(blockHash *btcwire.ShaHash) FutureGetBlockResult { + id := c.NextID() + cmd, err := btcjson.NewGetBlockCmd(id, blockHash.String(), false) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetBlock returns a raw block from the server given its hash. +// +// See GetBlockVerbose to retrieve a data structure with information about the +// block instead. +func (c *Client) GetBlock(blockHash *btcwire.ShaHash) (*btcutil.Block, error) { + return c.GetBlockAsync(blockHash).Receive() +} + +// FutureGetBlockVerboseResult is a future promise to deliver the result of a +// GetBlockVerboseAsync RPC invocation (or an applicable error). +type FutureGetBlockVerboseResult chan *futureResult + +// Receive waits for the response promised by the future and returns the data +// structure from the server with information about the requested block. +func (r FutureGetBlockVerboseResult) Receive() (*btcjson.BlockResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + blockResult, ok := reply.(btcjson.BlockResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getblock (verbose=1): %T\n", reply) + } + + return &blockResult, nil +} + +// GetBlockVerboseAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetBlockVerbose for the blocking version and more details. +func (c *Client) GetBlockVerboseAsync(blockHash *btcwire.ShaHash, verboseTx bool) FutureGetBlockVerboseResult { + id := c.NextID() + cmd, err := btcjson.NewGetBlockCmd(id, blockHash.String(), true, + verboseTx) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetBlockVerbose returns a data structure from the server with information +// about a block given its hash. +// +// See GetBlock to retrieve a raw block instead. +func (c *Client) GetBlockVerbose(blockHash *btcwire.ShaHash, verboseTx bool) (*btcjson.BlockResult, error) { + return c.GetBlockVerboseAsync(blockHash, verboseTx).Receive() +} + +// FutureGetBlockCountResult is a future promise to deliver the result of a +// GetBlockCountAsync RPC invocation (or an applicable error). +type FutureGetBlockCountResult chan *futureResult + +// Receive waits for the response promised by the future and returns the number +// of blocks in the longest block chain. +func (r FutureGetBlockCountResult) Receive() (int64, error) { + reply, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Ensure the returned data is the expected type. + count, ok := reply.(float64) + if !ok { + return 0, fmt.Errorf("unexpected response type for "+ + "getblockcount: %T\n", reply) + } + + return int64(count), nil +} + +// GetBlockCountAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetBlockCount for the blocking version and more details. +func (c *Client) GetBlockCountAsync() FutureGetBlockCountResult { + id := c.NextID() + cmd, err := btcjson.NewGetBlockCountCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetBlockCount returns the number of blocks in the longest block chain. +func (c *Client) GetBlockCount() (int64, error) { + return c.GetBlockCountAsync().Receive() +} + +// FutureGetDifficultyResult is a future promise to deliver the result of a +// GetDifficultyAsync RPC invocation (or an applicable error). +type FutureGetDifficultyResult chan *futureResult + +// Receive waits for the response promised by the future and returns the +// proof-of-work difficulty as a multiple of the minimum difficulty. +func (r FutureGetDifficultyResult) Receive() (float64, error) { + reply, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Ensure the returned data is the expected type. + difficulty, ok := reply.(float64) + if !ok { + return 0, fmt.Errorf("unexpected response type for "+ + "getdifficulty: %T\n", reply) + } + + return difficulty, nil +} + +// GetDifficultyAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetDifficulty for the blocking version and more details. +func (c *Client) GetDifficultyAsync() FutureGetDifficultyResult { + id := c.NextID() + cmd, err := btcjson.NewGetDifficultyCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetDifficulty returns the proof-of-work difficulty as a multiple of the +// minimum difficulty. +func (c *Client) GetDifficulty() (float64, error) { + return c.GetDifficultyAsync().Receive() +} + +// FutureGetBlockHashResult is a future promise to deliver the result of a +// GetBlockHashAsync RPC invocation (or an applicable error). +type FutureGetBlockHashResult chan *futureResult + +// Receive waits for the response promised by the future and returns the hash of +// the block in the best block chain at the given height. +func (r FutureGetBlockHashResult) Receive() (*btcwire.ShaHash, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + txHashStr, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getblockhash: %T\n", reply) + } + + return btcwire.NewShaHashFromStr(txHashStr) +} + +// GetBlockHashAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetBlockHash for the blocking version and more details. +func (c *Client) GetBlockHashAsync(blockHeight int64) FutureGetBlockHashResult { + id := c.NextID() + cmd, err := btcjson.NewGetBlockHashCmd(id, blockHeight) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetBlockHash returns the hash of the block in the best block chain at the +// given height. +func (c *Client) GetBlockHash(blockHeight int64) (*btcwire.ShaHash, error) { + return c.GetBlockHashAsync(blockHeight).Receive() +} + +// FutureGetRawMempoolResult is a future promise to deliver the result of a +// GetRawMempoolAsync RPC invocation (or an applicable error). +type FutureGetRawMempoolResult chan *futureResult + +// Receive waits for the response promised by the future and returns the hashes +// of all transactions in the memory pool. +func (r FutureGetRawMempoolResult) Receive() ([]*btcwire.ShaHash, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + txHashStrs, ok := reply.([]string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getrawmempool (verbose=false): %T\n", reply) + } + + txHashes := make([]*btcwire.ShaHash, 0, len(txHashStrs)) + for _, hashStr := range txHashStrs { + txHash, err := btcwire.NewShaHashFromStr(hashStr) + if err != nil { + return nil, err + } + txHashes = append(txHashes, txHash) + } + + return txHashes, nil +} + +// GetRawMempoolAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetRawMempool for the blocking version and more details. +func (c *Client) GetRawMempoolAsync() FutureGetRawMempoolResult { + id := c.NextID() + cmd, err := btcjson.NewGetRawMempoolCmd(id, false) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetRawMempool returns the hashes of all transactions in the memory pool. +// +// See GetRawMempoolVerbose to retrieve data structures with information about +// the transactions instead. +func (c *Client) GetRawMempool() ([]*btcwire.ShaHash, error) { + return c.GetRawMempoolAsync().Receive() +} + +// FutureGetRawMempoolVerboseResult is a future promise to deliver the result of +// a GetRawMempoolVerboseAsync RPC invocation (or an applicable error). +type FutureGetRawMempoolVerboseResult chan *futureResult + +// Receive waits for the response promised by the future and returns a map of +// transaction hashes to an associated data structure with information about the +// transaction for all transactions in the memory pool. +func (r FutureGetRawMempoolVerboseResult) Receive() (map[string]btcjson.GetRawMempoolResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + mempoolItems, ok := reply.(map[string]btcjson.GetRawMempoolResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getrawmempool (verbose=true): %T\n", reply) + } + + return mempoolItems, nil +} + +// GetRawMempoolVerboseAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetRawMempoolVerbose for the blocking version and more details. +func (c *Client) GetRawMempoolVerboseAsync() FutureGetRawMempoolVerboseResult { + id := c.NextID() + cmd, err := btcjson.NewGetRawMempoolCmd(id, true) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetRawMempoolVerbose returns a map of transaction hashes to an associated +// data structure with information about the transaction for all transactions in +// the memory pool. +// +// See GetRawMempool to retrieve only the transaction hashes instead. +func (c *Client) GetRawMempoolVerbose() (map[string]btcjson.GetRawMempoolResult, error) { + return c.GetRawMempoolVerboseAsync().Receive() +} + +// FutureVerifyChainResult is a future promise to deliver the result of a +// VerifyChainAsync, VerifyChainLevelAsyncRPC, or VerifyChainBlocksAsync +// invocation (or an applicable error). +type FutureVerifyChainResult chan *futureResult + +// Receive waits for the response promised by the future and returns whether +// or not the chain verified based on the check level and number of blocks +// to verify specified in the original call. +func (r FutureVerifyChainResult) Receive() (bool, error) { + reply, err := receiveFuture(r) + if err != nil { + return false, err + } + + // Ensure the returned data is the expected type. + verified, ok := reply.(bool) + if !ok { + return false, fmt.Errorf("unexpected response type for "+ + "verifychain: %T\n", reply) + } + + return verified, nil +} + +// VerifyChainAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See VerifyChain for the blocking version and more details. +func (c *Client) VerifyChainAsync() FutureVerifyChainResult { + id := c.NextID() + cmd, err := btcjson.NewVerifyChainCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// VerifyChain requests the server to verify the block chain database using +// the default check level and number of blocks to verify. +// +// See VerifyChainLevel and VerifyChainBlocks to override the defaults. +func (c *Client) VerifyChain() (bool, error) { + return c.VerifyChainAsync().Receive() +} + +// VerifyChainLevelAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See VerifyChainLevel for the blocking version and more details. +func (c *Client) VerifyChainLevelAsync(checkLevel int32) FutureVerifyChainResult { + id := c.NextID() + cmd, err := btcjson.NewVerifyChainCmd(id, checkLevel) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// VerifyChainLevel requests the server to verify the block chain database using +// the passed check level and default number of blocks to verify. +// +// The check level controls how thorough the verification is with higher numbers +// increasing the amount of checks done as consequently how long the +// verification takes. +// +// See VerifyChain to use the default check level and VerifyChainBlocks to +// override the number of blocks to verify. +func (c *Client) VerifyChainLevel(checkLevel int32) (bool, error) { + return c.VerifyChainLevelAsync(checkLevel).Receive() +} + +// VerifyChainBlocksAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See VerifyChainBlocks for the blocking version and more details. +func (c *Client) VerifyChainBlocksAsync(checkLevel, numBlocks int32) FutureVerifyChainResult { + id := c.NextID() + cmd, err := btcjson.NewVerifyChainCmd(id, checkLevel, numBlocks) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// VerifyChainBlocks requests the server to verify the block chain database +// using the passed check level and number of blocks to verify. +// +// The check level controls how thorough the verification is with higher numbers +// increasing the amount of checks done as consequently how long the +// verification takes. +// +// The number of blocks refers to the number of blocks from the end of the +// current longest chain. +// +// See VerifyChain and VerifyChainLevel to use defaults. +func (c *Client) VerifyChainBlocks(checkLevel, numBlocks int32) (bool, error) { + return c.VerifyChainBlocksAsync(checkLevel, numBlocks).Receive() +} diff --git a/doc.go b/doc.go new file mode 100644 index 00000000..85c32a0c --- /dev/null +++ b/doc.go @@ -0,0 +1,8 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +// Package btcrpcclient implements a websocket-enabled Bitcoin JSON-RPC client. +// +// TODO(davec): Doco +package btcrpcclient diff --git a/extensions.go b/extensions.go new file mode 100644 index 00000000..a5c11745 --- /dev/null +++ b/extensions.go @@ -0,0 +1,291 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "encoding/base64" + "fmt" + "github.com/conformal/btcjson" + "github.com/conformal/btcutil" + "github.com/conformal/btcwire" + "github.com/conformal/btcws" +) + +// FutureDebugLevelResult is a future promise to deliver the result of a +// DebugLevelAsync RPC invocation (or an applicable error). +type FutureDebugLevelResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of setting the debug logging level to the passed level specification or the +// list of of the available subsystems for the special keyword 'show'. +func (r FutureDebugLevelResult) Receive() (string, error) { + reply, err := receiveFuture(r) + if err != nil { + return "", err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(string) + if !ok { + return "", fmt.Errorf("unexpected response type for "+ + "debuglevel: %T\n", reply) + } + + return result, nil +} + +// DebugLevelAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See DebugLevel for the blocking version and more details. +// +// NOTE: This is a btcd extension. +func (c *Client) DebugLevelAsync(levelSpec string) FutureDebugLevelResult { + id := c.NextID() + cmd, err := btcjson.NewDebugLevelCmd(id, levelSpec) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// DebugLevel dynamically sets the debug logging level to the passed level +// specification. +// +// The levelspec can either a debug level or of the form: +// =,=,... +// +// Additionally, the special keyword 'show' can be used to get a list of the +// available subsystems. +// +// NOTE: This is a btcd extension. +func (c *Client) DebugLevel(levelSpec string) (string, error) { + return c.DebugLevelAsync(levelSpec).Receive() +} + +// FutureListAddressTransactionsResult is a future promise to deliver the result +// of a ListAddressTransactionsAsync RPC invocation (or an applicable error). +type FutureListAddressTransactionsResult chan *futureResult + +// Receive waits for the response promised by the future and returns information +// about all transactions associated with the provided addresses. +func (r FutureListAddressTransactionsResult) Receive() ([]btcjson.ListTransactionsResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // No transactions. + if reply == nil { + return nil, nil + } + + // Ensure the returned data is the expected type. + transactions, ok := reply.([]btcjson.ListTransactionsResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "listaddresstransactions: %T\n", reply) + } + + return transactions, nil +} + +// ListAddressTransactionsAsync returns an instance of a type that can be used +// to get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListAddressTransactions for the blocking version and more details. +// +// NOTE: This is a btcd extension. +func (c *Client) ListAddressTransactionsAsync(addresses []btcutil.Address, account string) FutureListAddressTransactionsResult { + // Convert addresses to strings. + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.EncodeAddress()) + } + id := c.NextID() + cmd, err := btcws.NewListAddressTransactionsCmd(id, addrs, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListAddressTransactions returns information about all transactions associated +// with the provided addresses. +// +// NOTE: This is a btcwallet extension. +func (c *Client) ListAddressTransactions(addresses []btcutil.Address, account string) ([]btcjson.ListTransactionsResult, error) { + return c.ListAddressTransactionsAsync(addresses, account).Receive() +} + +// FutureGetBestBlockResult is a future promise to deliver the result of a +// GetBestBlockAsync RPC invocation (or an applicable error). +type FutureGetBestBlockResult chan *futureResult + +// Receive waits for the response promised by the future and returns the hash +// and height of the block in the longest (best) chain. +func (r FutureGetBestBlockResult) Receive() (*btcwire.ShaHash, int32, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, 0, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(btcws.GetBestBlockResult) + if !ok { + return nil, 0, fmt.Errorf("unexpected response type for "+ + "listaddresstransactions: %T\n", reply) + } + + // Convert hash string. + hash, err := btcwire.NewShaHashFromStr(result.Hash) + if err != nil { + return nil, 0, err + } + + return hash, result.Height, nil +} + +// GetBestBlockAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetBestBlock for the blocking version and more details. +// +// NOTE: This is a btcd extension. +func (c *Client) GetBestBlockAsync() FutureGetBestBlockResult { + id := c.NextID() + cmd := btcws.NewGetBestBlockCmd(id) + + return c.sendCmd(cmd) +} + +// GetBestBlock returns the hash and height of the block in the longest (best) +// chain. +// +// NOTE: This is a btcd extension. +func (c *Client) GetBestBlock() (*btcwire.ShaHash, int32, error) { + return c.GetBestBlockAsync().Receive() +} + +// FutureGetCurrentNetResult is a future promise to deliver the result of a +// GetCurrentNetAsync RPC invocation (or an applicable error). +type FutureGetCurrentNetResult chan *futureResult + +// Receive waits for the response promised by the future and returns the network +// the server is running on. +func (r FutureGetCurrentNetResult) Receive() (btcwire.BitcoinNet, error) { + reply, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Ensure the returned data is the expected type. + fnet, ok := reply.(float64) + if !ok { + return 0, fmt.Errorf("unexpected response type for "+ + "getcurrentnet: %T\n", reply) + } + + return btcwire.BitcoinNet(fnet), nil +} + +// GetCurrentNetAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetCurrentNet for the blocking version and more details. +// +// NOTE: This is a btcd extension. +func (c *Client) GetCurrentNetAsync() FutureGetCurrentNetResult { + id := c.NextID() + cmd := btcws.NewGetCurrentNetCmd(id) + + return c.sendCmd(cmd) +} + +// GetCurrentNet returns the network the server is running on. +// +// NOTE: This is a btcd extension. +func (c *Client) GetCurrentNet() (btcwire.BitcoinNet, error) { + return c.GetCurrentNetAsync().Receive() +} + +// FutureExportWatchingWalletResult is a future promise to deliver the result of +// an ExportWatchingWalletAsync RPC invocation (or an applicable error). +type FutureExportWatchingWalletResult chan *futureResult + +// Receive waits for the response promised by the future and returns the +// exported wallet. +func (r FutureExportWatchingWalletResult) Receive() ([]byte, []byte, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, nil, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(map[string]interface{}) + if !ok { + return nil, nil, fmt.Errorf("unexpected response type for "+ + "exportwatchingwallet: %T\n", reply) + } + + base64Wallet, ok := result["wallet"].(string) + if !ok { + return nil, nil, fmt.Errorf("unexpected response type for "+ + "exportwatchingwallet 'wallet' field: %T\n", + result["wallet"]) + } + base64TxStore, ok := result["tx"].(string) + if !ok { + return nil, nil, fmt.Errorf("unexpected response type for "+ + "exportwatchingwallet 'tx' field: %T\n", + result["tx"]) + } + + walletBytes, err := base64.StdEncoding.DecodeString(base64Wallet) + if err != nil { + return nil, nil, err + } + + txStoreBytes, err := base64.StdEncoding.DecodeString(base64TxStore) + if err != nil { + return nil, nil, err + } + + return walletBytes, txStoreBytes, nil + +} + +// ExportWatchingWalletAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ExportWatchingWallet for the blocking version and more details. +// +// NOTE: This is a btcwallet extension. +func (c *Client) ExportWatchingWalletAsync(account string) FutureExportWatchingWalletResult { + id := c.NextID() + cmd, err := btcws.NewExportWatchingWalletCmd(id, account, true) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ExportWatchingWallet returns the raw bytes for a watching-only version of +// wallet.bin and tx.bin, respectively, for the specified account that can be +// used by btcwallet to enable a wallet which does not have the private keys +// necessary to spend funds. +// +// NOTE: This is a btcwallet extension. +func (c *Client) ExportWatchingWallet(account string) ([]byte, []byte, error) { + return c.ExportWatchingWalletAsync(account).Receive() +} diff --git a/infrastructure.go b/infrastructure.go new file mode 100644 index 00000000..fa51e7bc --- /dev/null +++ b/infrastructure.go @@ -0,0 +1,937 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "bytes" + "code.google.com/p/go.net/websocket" + "container/list" + "crypto/tls" + "crypto/x509" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "github.com/conformal/btcjson" + "github.com/conformal/go-socks" + "net" + "net/http" + "net/url" + "sync" + "sync/atomic" + "time" +) + +var ( + // ErrInvalidAuth is an error to describe the condition where the client + // is either unable to authenticate or the specified endpoint is + // incorrect. + ErrInvalidAuth = errors.New("authentication failure") + + // ErrClientDisconnect is an error to describe the condition where the + // client has been disconnected from the RPC server. When the + // DisableAutoReconnect option is not set, any outstanding futures + // when a client disconnect occurs will return this error as will + // any new requests. + ErrClientDisconnect = errors.New("the client has been disconnected") + + // ErrClientShutdown is an error to describe the condition where the + // client is either already shutdown, or in the process of shutting + // down. Any outstanding futures when a client shutdown occurs will + // return this error as will any new requests. + ErrClientShutdown = errors.New("the client has been shutdown") +) + +const ( + // sendBufferSize is the number of elements the websocket send channel + // can queue before blocking. + sendBufferSize = 50 + + // sendPostBufferSize is the number of elements the HTTP POST send + // channel can queue before blocking. + sendPostBufferSize = 100 + + // connectionRetryInterval is the amount of time to wait in between + // retries when automatically reconnecting to an RPC server. + connectionRetryInterval = time.Second * 5 +) + +// futureResult holds information about a future promise to deliver the result +// of an asynchronous request. +type futureResult struct { + reply *btcjson.Reply + err error +} + +// sendPostDetails houses an HTTP POST request to send to an RPC server as well +// as the original JSON-RPC command and a channel to reply on when the server +// responds with the result. +type sendPostDetails struct { + command btcjson.Cmd + request *http.Request + responseChan chan *futureResult +} + +// jsonRequest holds information about a json request that is used to properly +// detect, interpret, and deliver a reply to it. +type jsonRequest struct { + cmd btcjson.Cmd + responseChan chan *futureResult +} + +// Client represents a Bitcoin RPC client which allows easy access to the +// various RPC methods available on a Bitcoin RPC server. Each of the wrapper +// functions handle the details of converting the passed and return types to and +// from the underlying JSON types which are required for the JSON-RPC +// invocations +// +// The client provides each RPC in both synchronous (blocking) and asynchronous +// (non-blocking) forms. The asynchronous forms are based on the concept of +// futures where they return an instance of a type that promises to deliver the +// result of the invocation at some future time. Invoking the Receive method on +// the returned future will block until the result is available if it's not +// already. +type Client struct { + id int64 // atomic, so must stay 64-bit aligned + + // config holds the connection configuration assoiated with this client. + config *ConnConfig + + // wsConn is the underlying websocket connection when not in HTTP POST + // mode. + wsConn *websocket.Conn + + // httpClient is the underlying HTTP client to use when running in HTTP + // POST mode. + httpClient *http.Client + + // mtx is a mutex to protect access to connection related fields. + mtx sync.Mutex + + // disconnected indicated whether or not the server is disconnected. + disconnected bool + + // retryCount holds the number of times the client has tried to + // reconnect to the RPC server. + retryCount int64 + + // Track command and their response channels by ID. + requestLock sync.Mutex + requestMap map[int64]*list.Element + requestList *list.List + + // Notification handlers. + ntfnHandlers *NotificationHandlers + + // Networking infrastructure. + sendChan chan []byte + sendPostChan chan *sendPostDetails + disconnect chan struct{} + shutdown chan struct{} + wg sync.WaitGroup +} + +// NextID returns the next id to be used when sending a JSON-RPC message. This +// ID allows responses to be associated with particular requests per the +// JSON-RPC specification. Typically the consumer of the client does not need +// to call this function, however, if a custom request is being created and used +// this function should be used to ensure the ID is unique amongst all requests +// being made. +func (c *Client) NextID() int64 { + return atomic.AddInt64(&c.id, 1) +} + +// addRequest associates the passed jsonRequest with the passed id. This allows +// the response from the remote server to be unmarshalled to the appropriate +// type and sent to the specified channel when it is received. +// +// This function is safe for concurrent access. +func (c *Client) addRequest(id int64, request *jsonRequest) { + c.requestLock.Lock() + defer c.requestLock.Unlock() + + // TODO(davec): Already there? + element := c.requestList.PushBack(request) + c.requestMap[id] = element +} + +// removeRequest returns and removes the jsonRequest which contains the response +// channel and original method associated with the passed id or nil if there is +// no association. +// +// This function is safe for concurrent access. +func (c *Client) removeRequest(id int64) *jsonRequest { + c.requestLock.Lock() + defer c.requestLock.Unlock() + + element := c.requestMap[id] + if element != nil { + delete(c.requestMap, id) + request := c.requestList.Remove(element).(*jsonRequest) + return request + } + + return nil +} + +// removeAllRequests removes all the jsonRequests which contain the response +// channels for outstanding requests. +// +// This function is safe for concurrent access. +func (c *Client) removeAllRequests() { + c.requestLock.Lock() + defer c.requestLock.Unlock() + + c.requestMap = make(map[int64]*list.Element) + c.requestList.Init() +} + +// handleMessage is the main handler for incoming requests. It enforces +// authentication, parses the incoming json, looks up and executes handlers +// (including pass through for standard RPC commands), sends the appropriate +// response. It also detects commands which are marked as long-running and +// sends them off to the asyncHander for processing. +func (c *Client) handleMessage(msg string) { + // Attempt to unmarshal the message as a known JSON-RPC command. + if cmd, err := btcjson.ParseMarshaledCmd([]byte(msg)); err == nil { + // Commands that have an ID associated with them are not + // notifications. Since this is a client, it should not + // be receiving non-notifications. + if cmd.Id() != nil { + // Invalid response + log.Warnf("Remote server sent a non-notification "+ + "JSON-RPC Request (Id: %v)", cmd.Id()) + return + } + + // Deliver the notification. + log.Tracef("Received notification [%s]", cmd.Method()) + c.handleNotification(cmd) + return + } + + // The message was not a command/notification, so it should be a reply + // to a previous request. + + var r btcjson.Reply + if err := json.Unmarshal([]byte(msg), &r); err != nil { + log.Warnf("Unable to unmarshal inbound message as " + + "notification or response") + return + } + + // Ensure the reply has an id. + if r.Id == nil { + log.Warnf("Received response with no id") + return + } + + // Ensure the id is the expected type. + fid, ok := (*r.Id).(float64) + if !ok { + log.Warnf("Received unexpected id type: %T (value %v)", + *r.Id, *r.Id) + return + } + id := int64(fid) + log.Tracef("Received response for id %d (result %v)", id, r.Result) + request := c.removeRequest(id) + + // Nothing more to do if there is no request associated with this reply. + if request == nil || request.responseChan == nil { + log.Warnf("Received unexpected reply: %s (id %d)", r.Result, id) + return + } + + // Unmarshal the reply into a concrete result if possible and deliver + // it to the associated channel. + reply, err := btcjson.ReadResultCmd(request.cmd.Method(), []byte(msg)) + if err != nil { + log.Warnf("Failed to unmarshal reply to command [%s] "+ + "(id %d): %v", request.cmd.Method(), id, err) + request.responseChan <- &futureResult{reply: nil, err: err} + return + } + request.responseChan <- &futureResult{reply: &reply, err: nil} +} + +// wsInHandler handles all incoming messages for the websocket connection +// associated with the client. It must be run as a goroutine. +func (c *Client) wsInHandler() { +out: + for { + // Break out of the loop once the shutdown channel has been + // closed. Use a non-blocking select here so we fall through + // otherwise. + select { + case <-c.shutdown: + break out + default: + } + + var msg string + if err := websocket.Message.Receive(c.wsConn, &msg); err != nil { + // Log the error if it's not due to disconnecting. + if _, ok := err.(*net.OpError); !ok { + log.Errorf("Websocket receive error from "+ + "%s: %v", c.config.Host, err) + } + break out + } + c.handleMessage(msg) + } + + // Ensure the connection is closed. + c.Disconnect() + c.wg.Done() + log.Tracef("RPC client input handler done for %s", c.config.Host) +} + +// wsOutHandler handles all outgoing messages for the websocket connection. It +// uses a buffered channel to serialize output messages while allowing the +// sender to continue running asynchronously. It must be run as a goroutine. +func (c *Client) wsOutHandler() { +out: + for { + // Send any messages ready for send until the client is + // disconnected closed. + select { + case msg := <-c.sendChan: + err := websocket.Message.Send(c.wsConn, string(msg)) + if err != nil { + c.Disconnect() + break out + } + + case <-c.disconnect: + break out + } + } + + // Drain any channels before exiting so nothing is left waiting around + // to send. +cleanup: + for { + select { + case <-c.sendChan: + default: + break cleanup + } + } + c.wg.Done() + log.Tracef("RPC client output handler done for %s", c.config.Host) +} + +// sendMessage sends the passed JSON to the connected server using the +// websocket connection. It is backed by a buffered channel, so it will not +// block until the send channel is full. +func (c *Client) sendMessage(marshalledJSON []byte) { + // Don't send the message if disconnected. + if c.Disconnected() { + return + } + + c.sendChan <- marshalledJSON +} + +// resendCmds resends any commands that had not completed when the client +// disconnected. It is intended to be called once the client has reconnected. +func (c *Client) resendCmds() { + // Since it's possible to block on send and more commands might be + // added by the caller while resending, make a copy of all of the + // commands that need to be resent now and work from the copy. This + // also allows the lock to be released quickly. + c.requestLock.Lock() + resendCmds := make([]*jsonRequest, 0, c.requestList.Len()) + for e := c.requestList.Front(); e != nil; e = e.Next() { + req := e.Value.(*jsonRequest) + resendCmds = append(resendCmds, req) + } + c.requestLock.Unlock() + + for _, req := range resendCmds { + // Stop resending commands if the client disconnected again + // since the next reconnect will handle them. + if c.Disconnected() { + return + } + + c.marshalAndSend(req.cmd, req.responseChan) + time.Sleep(time.Second * 2) + } +} + +// wsReconnectHandler listens for client disconnects and automatically tries +// to reconnect with retry interval that scales based on the number of retries. +// It also resends any commands that had not completed when the client +// disconnected so the disconnect/reconnect process is largely transparent to +// the caller. This function is not run when the DisableAutoReconnect config +// options is set. +// +// This function must be run as a goroutine. +func (c *Client) wsReconnectHandler() { +out: + for { + select { + case <-c.disconnect: + // On disconnect, fallthrough to reestablish the + // connection. + + case <-c.shutdown: + break out + } + + reconnect: + for { + select { + case <-c.shutdown: + break out + default: + } + + wsConn, err := dial(c.config) + if err != nil { + c.retryCount++ + log.Infof("Failed to connect to %s: %v", + c.config.Host, err) + + // Scale the retry interval by the number of + // retries so there is a backoff up to a max + // of 1 minute. + scaledInterval := connectionRetryInterval.Nanoseconds() * c.retryCount + scaledDuration := time.Duration(scaledInterval) + if scaledDuration > time.Minute { + scaledDuration = time.Minute + } + log.Infof("Retrying connection to %s in "+ + "%s", c.config.Host, scaledDuration) + time.Sleep(scaledDuration) + continue reconnect + } + + log.Infof("Reestablished connection to RPC server %s", + c.config.Host) + + // Reset the connection state and signal the reconnect + // has happened. + c.wsConn = wsConn + c.retryCount = 0 + c.disconnect = make(chan struct{}) + + c.mtx.Lock() + c.disconnected = false + c.mtx.Unlock() + + // Start processing input and output for the + // new connection. + c.start() + + // Reissue pending commands in another goroutine since + // the send can block. + go c.resendCmds() + + // Break out of the reconnect loop back to wait for + // disconnect again. + break reconnect + } + } + c.wg.Done() + log.Tracef("RPC client reconnect handler done for %s", c.config.Host) +} + +// handleSendPostMessage handles performing the passed HTTP request, reading the +// result, unmarshalling it, and delivering the unmarhsalled result to the +// provided response channel. +func (c *Client) handleSendPostMessage(details *sendPostDetails) { + // Post the request. + cmd := details.command + log.Tracef("Sending command [%s] with id %d", cmd.Method(), cmd.Id()) + httpResponse, err := c.httpClient.Do(details.request) + if err != nil { + details.responseChan <- &futureResult{reply: nil, err: err} + return + } + + // Read the raw bytes and close the response. + respBytes, err := btcjson.GetRaw(httpResponse.Body) + if err != nil { + details.responseChan <- &futureResult{reply: nil, err: err} + return + } + + // Unmarshal the reply into a concrete result if possible. + reply, err := btcjson.ReadResultCmd(cmd.Method(), respBytes) + if err != nil { + details.responseChan <- &futureResult{reply: nil, err: err} + return + } + details.responseChan <- &futureResult{reply: &reply, err: nil} +} + +// sendPostHandler handles all outgoing messages when the client is running +// in HTTP POST mode. It uses a buffered channel to serialize output messages +// while allowing the sender to continue running asynchronously. It must be run +// as a goroutine. +func (c *Client) sendPostHandler() { +out: + for { + // Send any messages ready for send until the shutdown channel + // is closed. + select { + case details := <-c.sendPostChan: + c.handleSendPostMessage(details) + + case <-c.shutdown: + break out + } + } + + // Drain any wait channels before exiting so nothing is left waiting + // around to send. +cleanup: + for { + select { + case details := <-c.sendPostChan: + details.responseChan <- &futureResult{ + reply: nil, + err: ErrClientShutdown, + } + + default: + break cleanup + } + } + c.wg.Done() + log.Tracef("RPC client send handler done for %s", c.config.Host) + +} + +// sendPostRequest sends the passed HTTP request to the RPC server using the +// HTTP client associated with the client. It is backed by a buffered channel, +// so it will not block until the send channel is full. +func (c *Client) sendPostRequest(req *http.Request, command btcjson.Cmd, responseChan chan *futureResult) { + // Don't send the message if shutting down. + select { + case <-c.shutdown: + responseChan <- &futureResult{reply: nil, err: ErrClientShutdown} + default: + } + + c.sendPostChan <- &sendPostDetails{ + request: req, + command: command, + responseChan: responseChan, + } +} + +// Disconnected returns whether or not the server is disconnected. +func (c *Client) Disconnected() bool { + c.mtx.Lock() + defer c.mtx.Unlock() + + return c.disconnected +} + +// newFutureError returns a new future result channel that already has the +// passed error waitin on the channel with the reply set to nil. This is useful +// to easily return errors from the various Async functions. +func newFutureError(err error) chan *futureResult { + responseChan := make(chan *futureResult, 1) + responseChan <- &futureResult{err: err} + return responseChan +} + +// receiveFuture receives from the passed futureResult channel to extract a +// reply or any errors. The examined errors include an error in the +// futureResult and the error in the reply from the server. This will block +// until the result is available on the passed channel. +func receiveFuture(responseChan chan *futureResult) (interface{}, error) { + // Wait for a response on the returned channel. + response := <-responseChan + if response.err != nil { + return nil, response.err + } + + // At this point, the command was either sent to the server and + // there is a response from it, or it is intentionally a nil result + // used to bybass sends for cases such a requesting notifications when + // there are no handlers. + reply := response.reply + if reply == nil { + return nil, nil + } + + if reply.Error != nil { + return nil, reply.Error + } + + return reply.Result, nil +} + +// marshalAndSendPost marshals the passed command to JSON-RPC and sends it to +// the server by issuing an HTTP POST request and returns a response channel +// on which the reply will be delivered. Typically a new connection is opened +// and closed for each command when using this method, however, the underlying +// HTTP client might coalesce multiple commands depending on several factors +// including the remote server configuration. +func (c *Client) marshalAndSendPost(cmd btcjson.Cmd, responseChan chan *futureResult) { + marshalledJSON, err := json.Marshal(cmd) + if err != nil { + responseChan <- &futureResult{reply: nil, err: err} + return + } + + // Generate a request to the configured RPC server. + protocol := "http" + if !c.config.DisableTLS { + protocol = "https" + } + url := protocol + "://" + c.config.Host + req, err := http.NewRequest("POST", url, bytes.NewReader(marshalledJSON)) + if err != nil { + responseChan <- &futureResult{reply: nil, err: err} + return + } + req.Close = true + req.Header.Set("Content-Type", "application/json") + + // Configure basic access authorization. + req.SetBasicAuth(c.config.User, c.config.Pass) + + log.Tracef("Sending command [%s] with id %d", cmd.Method(), cmd.Id()) + c.sendPostRequest(req, cmd, responseChan) +} + +// marshalAndSend marshals the passed command to JSON-RPC and sends it to the +// server. It returns a response channel on which the reply will be delivered. +func (c *Client) marshalAndSend(cmd btcjson.Cmd, responseChan chan *futureResult) { + marshalledJSON, err := json.Marshal(cmd) + if err != nil { + responseChan <- &futureResult{reply: nil, err: err} + return + } + + log.Tracef("Sending command [%s] with id %d", cmd.Method(), cmd.Id()) + c.sendMessage(marshalledJSON) +} + +// sendCmd sends the passed command to the associated server and returns a +// response channel on which the reply will be deliver at some point in the +// future. It handles both websocket and HTTP POST mode depending on the +// configuration of the client. +func (c *Client) sendCmd(cmd btcjson.Cmd) chan *futureResult { + // Choose which marshal and send function to use depending on whether + // the client running in HTTP POST mode or not. When running in HTTP + // POST mode, the command is issued via an HTTP client. Otherwise, + // the command is issued via the asynchronous websocket channels. + responseChan := make(chan *futureResult, 1) + if c.config.HttpPostMode { + c.marshalAndSendPost(cmd, responseChan) + return responseChan + } + + c.addRequest(cmd.Id().(int64), &jsonRequest{ + cmd: cmd, + responseChan: responseChan, + }) + c.marshalAndSend(cmd, responseChan) + return responseChan +} + +// sendCmdAndWait sends the passed command to the associated server, waits +// for the reply, and returns the result from it. It will return the error +// field in the reply if there is one. +func (c *Client) sendCmdAndWait(cmd btcjson.Cmd) (interface{}, error) { + // Marshal the command to JSON-RPC, send it to the connected server, and + // wait for a response on the returned channel. + return receiveFuture(c.sendCmd(cmd)) +} + +// Disconnect disconnects the current websocket associated with the client. The +// connection will automatically be re-established +// +// This function has no effect when the client is running in HTTP POST mode. +func (c *Client) Disconnect() { + if c.config.HttpPostMode { + return + } + + c.mtx.Lock() + defer c.mtx.Unlock() + + // Nothing to do if already disconnected. + if c.disconnected { + return + } + + log.Tracef("Disconnecting RPC client %s", c.config.Host) + close(c.disconnect) + c.wsConn.Close() + c.disconnected = true + + // When operating without auto reconnect, send errors to any pending + // requests and shutdown the client. + if c.config.DisableAutoReconnect { + c.requestLock.Lock() + for e := c.requestList.Front(); e != nil; e = e.Next() { + req := e.Value.(*jsonRequest) + req.responseChan <- &futureResult{ + reply: nil, + err: ErrClientDisconnect, + } + } + c.requestLock.Unlock() + c.removeAllRequests() + c.Shutdown() + } +} + +// Shutdown shuts down the client by disconnecting any connections associated +// with the client and, when automatic reconnect is enabled, preventing future +// attempts to reconnect. It also stops all goroutines. +func (c *Client) Shutdown() { + // Ignore the shutdown request if the client is already in the process + // of shutting down or already shutdown. + select { + case <-c.shutdown: + return + default: + } + + log.Tracef("Shutting down RPC client %s", c.config.Host) + close(c.shutdown) + + // Send the ErrClientShutdown error to any pending requests. + c.requestLock.Lock() + for e := c.requestList.Front(); e != nil; e = e.Next() { + req := e.Value.(*jsonRequest) + req.responseChan <- &futureResult{ + reply: nil, + err: ErrClientShutdown, + } + } + c.requestLock.Unlock() + c.removeAllRequests() +} + +// Start begins processing input and output messages. +func (c *Client) start() { + log.Tracef("Starting RPC client %s", c.config.Host) + + // Start the I/O processing handlers depending on whether the client is + // in HTTP POST mode or the default websocket mode. + if c.config.HttpPostMode { + c.wg.Add(1) + go c.sendPostHandler() + } else { + c.wg.Add(2) + go c.wsInHandler() + go c.wsOutHandler() + } +} + +// WaitForShutdown blocks until the client goroutines are stopped and the +// connection is closed. +func (c *Client) WaitForShutdown() { + c.wg.Wait() +} + +// ConnConfig describes the connection configuration parameters for the client. +// This +type ConnConfig struct { + // Host is the IP address and port of the RPC server you want to connect + // to. + Host string + + // Endpoint is the websocket endpoint on the RPC server. This is + // typically "ws" or "frontend". + Endpoint string + + // User is the username to use to authenticate to the RPC server. + User string + + // Pass is the passphrase to use to authenticate to the RPC server. + Pass string + + // DisableTLS specifies whether transport layer security should be + // disabled. It is recommended to always use TLS if the RPC server + // supports it as otherwise your username and password is sent across + // the wire in cleartext. + DisableTLS bool + + // Certificates are the bytes for a PEM-encoded certificate chain used + // for the TLS connection. It has no effect if the DisableTLS parameter + // is true. + Certificates []byte + + // Proxy specifies to connect through a SOCKS 5 proxy server. It may + // be an empty string if a proxy is not required. + Proxy string + + // ProxyUser is an optional username to use for the proxy server if it + // requires authentication. It has no effect if the Proxy parameter + // is not set. + ProxyUser string + + // ProxyPass is an optional password to use for the proxy server if it + // requires authentication. It has no effect if the Proxy parameter + // is not set. + ProxyPass string + + // DisableAutoReconnect specifies the client should not automatically + // try to reconnect to the server when it has been disconnected. + DisableAutoReconnect bool + + // HttpPostMode instructs the client to run using multiple independent + // connections issuing HTTP POST requests instead of using the default + // of websockets. Websockets are generally preferred as some of the + // features of the client such notifications only work with websockets, + // however, not all servers support the websocket extensions, so this + // flag can be set to true to use basic HTTP POST requests instead. + HttpPostMode bool +} + +// newHttpClient returns a new http client that is configured according to the +// proxy and TLS settings in the associated connection configuration. +func newHttpClient(config *ConnConfig) (*http.Client, error) { + // Set proxy function if there is a proxy configured. + var proxyFunc func(*http.Request) (*url.URL, error) + if config.Proxy != "" { + proxyURL, err := url.Parse(config.Proxy) + if err != nil { + return nil, err + } + proxyFunc = http.ProxyURL(proxyURL) + } + + // Configure TLS if needed. + var tlsConfig *tls.Config + if !config.DisableTLS { + pool := x509.NewCertPool() + pool.AppendCertsFromPEM(config.Certificates) + tlsConfig = &tls.Config{ + RootCAs: pool, + } + } + + client := http.Client{ + Transport: &http.Transport{ + Proxy: proxyFunc, + TLSClientConfig: tlsConfig, + }, + } + + return &client, nil +} + +// dial opens a websocket connection using the passed connection configuration +// details. +func dial(config *ConnConfig) (*websocket.Conn, error) { + // Connect to websocket. + url := fmt.Sprintf("wss://%s/%s", config.Host, config.Endpoint) + wsConfig, err := websocket.NewConfig(url, "https://localhost/") + if err != nil { + return nil, err + } + + pool := x509.NewCertPool() + pool.AppendCertsFromPEM(config.Certificates) + wsConfig.TlsConfig = &tls.Config{ + RootCAs: pool, + MinVersion: tls.VersionTLS12, + } + + // The wallet requires basic authorization, so use a custom config with + // with the Authorization header set. + login := config.User + ":" + config.Pass + auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) + wsConfig.Header.Add("Authorization", auth) + + // Attempt to connect to running wallet instance using a proxy if one + // is configured. + if config.Proxy != "" { + proxy := &socks.Proxy{ + Addr: config.Proxy, + Username: config.ProxyUser, + Password: config.ProxyPass, + } + conn, err := proxy.Dial("tcp", config.Host) + if err != nil { + return nil, err + } + + tlsConn := tls.Client(conn, wsConfig.TlsConfig) + ws, err := websocket.NewClient(wsConfig, tlsConn) + if err != nil { + return nil, err + } + + return ws, nil + } + + // No proxy was specified, so attempt to connect to running wallet + // instance directly. + ws, err := websocket.DialConfig(wsConfig) + if err != nil { + // XXX(davec): This is not really accurate, but unfortunately + // the current websocket package does not expose the status + // code, so it's impossible to tell for sure. + if dialError, ok := err.(*websocket.DialError); ok { + if dialError.Err == websocket.ErrBadStatus { + return nil, ErrInvalidAuth + } + } + return nil, err + } + return ws, nil +} + +// New create a new RPC client based on the provided connection configuration +// details. The notification handlers parameter may be nil if you are not +// interested in receiving notifications and will be ignored when if the +// configuration is set to run in HTTP POST mode. +func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error) { + // Either open a websocket connection or create an HTTP client depending + // on the HTTP POST mode. Also, set the notification handlers to nil + // when running in HTTP POST mode. + var wsConn *websocket.Conn + var httpClient *http.Client + if config.HttpPostMode { + ntfnHandlers = nil + + var err error + httpClient, err = newHttpClient(config) + if err != nil { + return nil, err + } + } else { + var err error + wsConn, err = dial(config) + if err != nil { + return nil, err + } + } + + client := &Client{ + config: config, + wsConn: wsConn, + httpClient: httpClient, + requestMap: make(map[int64]*list.Element), + requestList: list.New(), + ntfnHandlers: ntfnHandlers, + sendChan: make(chan []byte, sendBufferSize), + sendPostChan: make(chan *sendPostDetails, sendPostBufferSize), + disconnect: make(chan struct{}), + shutdown: make(chan struct{}), + } + client.start() + + if !client.config.HttpPostMode && !client.config.DisableAutoReconnect { + client.wg.Add(1) + go client.wsReconnectHandler() + } + + return client, nil +} diff --git a/log.go b/log.go new file mode 100644 index 00000000..204ee66a --- /dev/null +++ b/log.go @@ -0,0 +1,74 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "errors" + "github.com/conformal/btclog" + "io" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log btclog.Logger + +// The default amount of logging is none. +func init() { + DisableLog() +} + +// DisableLog disables all library log output. Logging output is disabled +// by default until either UseLogger or SetLogWriter are called. +func DisableLog() { + log = btclog.Disabled +} + +// UseLogger uses a specified Logger to output package logging info. +// This should be used in preference to SetLogWriter if the caller is also +// using btclog. +func UseLogger(logger btclog.Logger) { + log = logger +} + +// SetLogWriter uses a specified io.Writer to output package logging info. +// This allows a caller to direct package logging output without needing a +// dependency on seelog. If the caller is also using btclog, UseLogger should +// be used instead. +func SetLogWriter(w io.Writer, level string) error { + if w == nil { + return errors.New("nil writer") + } + + lvl, ok := btclog.LogLevelFromString(level) + if !ok { + return errors.New("invalid log level") + } + + l, err := btclog.NewLoggerFromWriter(w, lvl) + if err != nil { + return err + } + + UseLogger(l) + return nil +} + +// LogClosure is a closure that can be printed with %v to be used to +// generate expensive-to-create data for a detailed log level and avoid doing +// the work if the data isn't printed. +type logClosure func() string + +// String invokes the log closure and returns the results string. +func (c logClosure) String() string { + return c() +} + +// newLogClosure returns a new closure over the passed function which allows +// it to be used as a parameter in a logging function that is only invoked when +// the logging level is such that the message will actually be logged. +func newLogClosure(c func() string) logClosure { + return logClosure(c) +} diff --git a/mining.go b/mining.go new file mode 100644 index 00000000..c138f31a --- /dev/null +++ b/mining.go @@ -0,0 +1,361 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "encoding/hex" + "fmt" + "github.com/conformal/btcjson" + "github.com/conformal/btcutil" +) + +// FutureGetGenerateResult is a future promise to deliver the result of a +// GetGenerateAsync RPC invocation (or an applicable error). +type FutureGetGenerateResult chan *futureResult + +// Receive waits for the response promised by the future and returns true if the +// server is set to mine, otherwise false. +func (r FutureGetGenerateResult) Receive() (bool, error) { + reply, err := receiveFuture(r) + if err != nil { + return false, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(bool) + if !ok { + return false, fmt.Errorf("unexpected response type for "+ + "getgenerate: %T\n", reply) + } + + return result, nil +} + +// GetGenerateAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetGenerate for the blocking version and more details. +func (c *Client) GetGenerateAsync() FutureGetGenerateResult { + id := c.NextID() + cmd, err := btcjson.NewGetGenerateCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetGenerate returns true if the server is set to mine, otherwise false. +func (c *Client) GetGenerate() (bool, error) { + return c.GetGenerateAsync().Receive() +} + +// FutureSetGenerateResult is a future promise to deliver the result of a +// SetGenerateAsync RPC invocation (or an applicable error). +type FutureSetGenerateResult chan *futureResult + +// Receive waits for the response promised by the future and returns an error if +// any occurred when setting the server to generate coins (mine) or not. +func (r FutureSetGenerateResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// SetGenerateAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See SetGenerate for the blocking version and more details. +func (c *Client) SetGenerateAsync(enable bool, numCPUs int) FutureSetGenerateResult { + id := c.NextID() + cmd, err := btcjson.NewSetGenerateCmd(id, enable, numCPUs) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SetGenerate sets the server to generate coins (mine) or not. +func (c *Client) SetGenerate(enable bool, numCPUs int) error { + return c.SetGenerateAsync(enable, numCPUs).Receive() +} + +// FutureGetHashesPerSecResult is a future promise to deliver the result of a +// GetHashesPerSecAsync RPC invocation (or an applicable error). +type FutureGetHashesPerSecResult chan *futureResult + +// Receive waits for the response promised by the future and returns a recent +// hashes per second performance measurement while generating coins (mining). +// Zero is returned if the server is not mining. +func (r FutureGetHashesPerSecResult) Receive() (int64, error) { + reply, err := receiveFuture(r) + if err != nil { + return -1, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(int64) + if !ok { + return -1, fmt.Errorf("unexpected response type for "+ + "getnetworkhashps: %T\n", reply) + } + + return result, nil +} + +// GetHashesPerSecAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetHashesPerSec for the blocking version and more details. +func (c *Client) GetHashesPerSecAsync() FutureGetHashesPerSecResult { + id := c.NextID() + cmd, err := btcjson.NewGetHashesPerSecCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetHashesPerSec returns a recent hashes per second performance measurement +// while generating coins (mining). Zero is returned if the server is not +// mining. +func (c *Client) GetHashesPerSec() (int64, error) { + return c.GetHashesPerSecAsync().Receive() +} + +// FutureGetMiningInfoResult is a future promise to deliver the result of a +// GetMiningInfoAsync RPC invocation (or an applicable error). +type FutureGetMiningInfoResult chan *futureResult + +// Receive waits for the response promised by the future and returns the mining +// information. +func (r FutureGetMiningInfoResult) Receive() (*btcjson.GetMiningInfoResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(btcjson.GetMiningInfoResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getmininginfo: %T\n", reply) + } + + return &result, nil +} + +// GetMiningInfoAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetMiningInfo for the blocking version and more details. +func (c *Client) GetMiningInfoAsync() FutureGetMiningInfoResult { + id := c.NextID() + cmd, err := btcjson.NewGetMiningInfoCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetMiningInfo returns mining information. +func (c *Client) GetMiningInfo() (*btcjson.GetMiningInfoResult, error) { + return c.GetMiningInfoAsync().Receive() +} + +// FutureGetNetworkHashPS is a future promise to deliver the result of a +// GetNetworkHashPSAsync RPC invocation (or an applicable error). +type FutureGetNetworkHashPS chan *futureResult + +// Receive waits for the response promised by the future and returns the +// estimated network hashes per second for the block heights provided by the +// parameters. +func (r FutureGetNetworkHashPS) Receive() (int64, error) { + reply, err := receiveFuture(r) + if err != nil { + return -1, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(int64) + if !ok { + return -1, fmt.Errorf("unexpected response type for "+ + "getnetworkhashps: %T\n", reply) + } + + return result, nil +} + +// GetNetworkHashPSAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetNetworkHashPS for the blocking version and more details. +func (c *Client) GetNetworkHashPSAsync() FutureGetNetworkHashPS { + id := c.NextID() + cmd, err := btcjson.NewGetNetworkHashPSCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetNetworkHashPS returns the estimated network hashes per second using the +// default number of blocks and the most recent block height. +// +// See GetNetworkHashPS2 to override the number of blocks to use and +// GetNetworkHashPS3 to override the height at which to calculate the estimate. +func (c *Client) GetNetworkHashPS() (int64, error) { + return c.GetNetworkHashPSAsync().Receive() +} + +// GetNetworkHashPS2Async returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetNetworkHashPS2 for the blocking version and more details. +func (c *Client) GetNetworkHashPS2Async(blocks int) FutureGetNetworkHashPS { + id := c.NextID() + cmd, err := btcjson.NewGetNetworkHashPSCmd(id, blocks) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetNetworkHashPS2 returns the estimated network hashes per second for the +// specified previous number of blocks working backwards from the most recent +// block height. The blocks parameter can also be -1 in which case the number +// of blocks since the last difficulty change will be used. +// +// See GetNetworkHashPS to use defaults and GetNetworkHashPS3 to override the +// height at which to calculate the estimate. +func (c *Client) GetNetworkHashPS2(blocks int) (int64, error) { + return c.GetNetworkHashPS2Async(blocks).Receive() +} + +// GetNetworkHashPS3Async returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetNetworkHashPS3 for the blocking version and more details. +func (c *Client) GetNetworkHashPS3Async(blocks, height int) FutureGetNetworkHashPS { + id := c.NextID() + cmd, err := btcjson.NewGetNetworkHashPSCmd(id, blocks, height) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetNetworkHashPS3 returns the estimated network hashes per second for the +// specified previous number of blocks working backwards from the specified +// block height. The blocks parameter can also be -1 in which case the number +// of blocks since the last difficulty change will be used. +// +// See GetNetworkHashPS and GetNetworkHashPS2 to use defaults. +func (c *Client) GetNetworkHashPS3(blocks, height int) (int64, error) { + return c.GetNetworkHashPS3Async(blocks, height).Receive() +} + +// FutureGetWork is a future promise to deliver the result of a +// GetWorkAsync RPC invocation (or an applicable error). +type FutureGetWork chan *futureResult + +// Receive waits for the response promised by the future and returns the hash +// data to work on. +func (r FutureGetWork) Receive() (*btcjson.GetWorkResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(btcjson.GetWorkResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getwork: %T\n", reply) + } + + return &result, nil +} + +// GetWorkAsync returns an instance of a type that can be used to get the result +// of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetWork for the blocking version and more details. +func (c *Client) GetWorkAsync() FutureGetWork { + id := c.NextID() + cmd, err := btcjson.NewGetWorkCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// TODO(davec): Correct this +// GetWork returns hash data to work on. +func (c *Client) GetWork() (*btcjson.GetWorkResult, error) { + return c.GetWorkAsync().Receive() +} + +// FutureSubmitBlockResult is a future promise to deliver the result of a +// SubmitBlockAsync RPC invocation (or an applicable error). +type FutureSubmitBlockResult chan *futureResult + +// Receive waits for the response promised by the future and returns an error if +// any occurred when submitting the block. +func (r FutureSubmitBlockResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil + +} + +// SubmitBlockAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See SubmitBlock for the blocking version and more details. +func (c *Client) SubmitBlockAsync(block *btcutil.Block, options *btcjson.SubmitBlockOptions) FutureSubmitBlockResult { + id := c.NextID() + blockBytes, err := block.Bytes() + if err != nil { + return newFutureError(err) + } + + blockHex := hex.EncodeToString(blockBytes) + cmd, err := btcjson.NewSubmitBlockCmd(id, blockHex, options) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SubmitBlock attempts to submit a new block into the bitcoin network. +func (c *Client) SubmitBlock(block *btcutil.Block, options *btcjson.SubmitBlockOptions) error { + return c.SubmitBlockAsync(block, options).Receive() +} + +// TODO(davec): Implement GetBlockTemplate diff --git a/net.go b/net.go new file mode 100644 index 00000000..072fa094 --- /dev/null +++ b/net.go @@ -0,0 +1,327 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "fmt" + "github.com/conformal/btcjson" +) + +// AddNodeCommand enumerates the available commands that the AddNode function +// accepts. +type AddNodeCommand string + +// Constants used to indicate the command for the AddNode function. +const ( + // ANAdd indicates the specified host should be added as a persistent + // peer. + ANAdd AddNodeCommand = "add" + + // ANRemove indicates the specified peer should be removed. + ANRemove AddNodeCommand = "remove" + + // ANOneTry indicates the specified host should try to connect once, + // but it should not be made persistent. + ANOneTry AddNodeCommand = "onetry" +) + +// String returns the AddNodeCommand in human-readable form. +func (cmd AddNodeCommand) String() string { + return string(cmd) +} + +// FutureAddNodeResult is a future promise to deliver the result of an +// AddNodeAsync RPC invocation (or an applicable error). +type FutureAddNodeResult chan *futureResult + +// Receive waits for the response promised by the future and returns an error if +// any occurred when performing the specified command. +func (r FutureAddNodeResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// AddNodeAsync returns an instance of a type that can be used to get the result +// of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See AddNode for the blocking version and more details. +func (c *Client) AddNodeAsync(host string, command AddNodeCommand) FutureAddNodeResult { + id := c.NextID() + cmd, err := btcjson.NewAddNodeCmd(id, host, string(command)) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// AddNode attempts to perform the passed command on the passed persistent peer. +// For example, it can be used to add or a remove a persistent peer, or to do +// a one time connection to a peer. +// +// It may not be used to remove non-persistent peers. +func (c *Client) AddNode(host string, command AddNodeCommand) error { + return c.AddNodeAsync(host, command).Receive() +} + +// FutureGetAddedNodeInfoResult is a future promise to deliver the result of a +// GetAddedNodeInfoAsync RPC invocation (or an applicable error). +type FutureGetAddedNodeInfoResult chan *futureResult + +// Receive waits for the response promised by the future and returns information +// about manually added (persistent) peers. +func (r FutureGetAddedNodeInfoResult) Receive() ([]btcjson.GetAddedNodeInfoResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + nodeInfo, ok := reply.([]btcjson.GetAddedNodeInfoResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getaddednodeinfo (dns=true): %T\n", reply) + } + + return nodeInfo, nil +} + +// GetAddedNodeInfoAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetAddedNodeInfo for the blocking version and more details. +func (c *Client) GetAddedNodeInfoAsync(peer string) FutureGetAddedNodeInfoResult { + id := c.NextID() + cmd, err := btcjson.NewGetAddedNodeInfoCmd(id, true, peer) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetAddedNodeInfo returns information about manually added (persistent) peers. +// +// See GetAddedNodeInfoNoDNS to retrieve only a list of the added (persistent) +// peers. +func (c *Client) GetAddedNodeInfo(peer string) ([]btcjson.GetAddedNodeInfoResult, error) { + return c.GetAddedNodeInfoAsync(peer).Receive() +} + +// FutureGetAddedNodeInfoNoDNSResult is a future promise to deliver the result +// of a GetAddedNodeInfoNoDNSAsync RPC invocation (or an applicable error). +type FutureGetAddedNodeInfoNoDNSResult chan *futureResult + +// Receive waits for the response promised by the future and returns a list of +// manually added (persistent) peers. +func (r FutureGetAddedNodeInfoNoDNSResult) Receive() ([]string, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + nodes, ok := reply.([]string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getaddednodeinfo (dns=false): %T\n", reply) + } + + return nodes, nil +} + +// GetAddedNodeInfoNoDNSAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetAddedNodeInfoNoDNS for the blocking version and more details. +func (c *Client) GetAddedNodeInfoNoDNSAsync(peer string) FutureGetAddedNodeInfoNoDNSResult { + id := c.NextID() + cmd, err := btcjson.NewGetAddedNodeInfoCmd(id, false, peer) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetAddedNodeInfoNoDNS returns a list of manually added (persistent) peers. +// This works by setting the dns flag to false in the underlying RPC. +// +// See GetAddedNodeInfo to obtain more information about each added (persistent) +// peer. +func (c *Client) GetAddedNodeInfoNoDNS(peer string) ([]string, error) { + return c.GetAddedNodeInfoNoDNSAsync(peer).Receive() +} + +// FutureGetConnectionCountResult is a future promise to deliver the result +// of a GetConnectionCountAsync RPC invocation (or an applicable error). +type FutureGetConnectionCountResult chan *futureResult + +// Receive waits for the response promised by the future and returns the number +// of active connections to other peers. +func (r FutureGetConnectionCountResult) Receive() (int64, error) { + reply, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Ensure the returned data is the expected type. + fcount, ok := reply.(float64) + if !ok { + return 0, fmt.Errorf("unexpected response type for "+ + "getconnectioncount: %T\n", reply) + } + + return int64(fcount), nil +} + +// GetConnectionCountAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetConnectionCount for the blocking version and more details. +func (c *Client) GetConnectionCountAsync() FutureGetConnectionCountResult { + id := c.NextID() + cmd, err := btcjson.NewGetConnectionCountCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetConnectionCount returns the number of active connections to other peers. +func (c *Client) GetConnectionCount() (int64, error) { + return c.GetConnectionCountAsync().Receive() +} + +// FuturePingResult is a future promise to deliver the result of a PingAsync RPC +// invocation (or an applicable error). +type FuturePingResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of queueing a ping to be sent to each connected peer. +func (r FuturePingResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// PingAsync returns an instance of a type that can be used to get the result of +// the RPC at some future time by invoking the Receive function on the returned +// instance. +// +// See Ping for the blocking version and more details. +func (c *Client) PingAsync() FuturePingResult { + id := c.NextID() + cmd, err := btcjson.NewPingCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// Ping queues a ping to be sent to each connected peer. +// +// Use the GetPeerInfo function and examine the PingTime and PingWait fields to +// access the ping times. +func (c *Client) Ping() error { + return c.PingAsync().Receive() +} + +// FutureGetPeerInfoResult is a future promise to deliver the result of a +// GetPeerInfoAsync RPC invocation (or an applicable error). +type FutureGetPeerInfoResult chan *futureResult + +// Receive waits for the response promised by the future and returns data about +// each connected network peer. +func (r FutureGetPeerInfoResult) Receive() ([]btcjson.GetPeerInfoResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + peerInfo, ok := reply.([]btcjson.GetPeerInfoResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getpeerinfo: %T\n", reply) + } + + return peerInfo, nil +} + +// GetPeerInfoAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetPeerInfo for the blocking version and more details. +func (c *Client) GetPeerInfoAsync() FutureGetPeerInfoResult { + id := c.NextID() + cmd, err := btcjson.NewGetPeerInfoCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetPeerInfo returns data about each connected network peer. +func (c *Client) GetPeerInfo() ([]btcjson.GetPeerInfoResult, error) { + return c.GetPeerInfoAsync().Receive() +} + +// FutureGetNetTotalsResult is a future promise to deliver the result of a +// GetNetTotalsAsync RPC invocation (or an applicable error). +type FutureGetNetTotalsResult chan *futureResult + +// Receive waits for the response promised by the future and returns network +// traffic statistics. +func (r FutureGetNetTotalsResult) Receive() (*btcjson.GetNetTotalsResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + totals, ok := reply.(btcjson.GetNetTotalsResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getnettotals: %T\n", reply) + } + + return &totals, nil +} + +// GetNetTotalsAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetNetTotals for the blocking version and more details. +func (c *Client) GetNetTotalsAsync() FutureGetNetTotalsResult { + id := c.NextID() + cmd, err := btcjson.NewGetNetTotalsCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetNetTotals returns network traffic statistics. +func (c *Client) GetNetTotals() (*btcjson.GetNetTotalsResult, error) { + return c.GetNetTotalsAsync().Receive() +} diff --git a/notify.go b/notify.go new file mode 100644 index 00000000..0da70e93 --- /dev/null +++ b/notify.go @@ -0,0 +1,687 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "bytes" + "encoding/hex" + "errors" + "github.com/conformal/btcjson" + "github.com/conformal/btcutil" + "github.com/conformal/btcwire" + "github.com/conformal/btcws" +) + +var ( + // ErrNotificationsNotSupported is an error to describe the condition + // where the caller is trying to request notifications when they are + // not supported due to the client being configured to run in HTTP POST + // mode. + ErrNotificationsNotSupported = errors.New("notifications are not " + + "supported when running in HTTP POST mode") +) + +// newNilFutureResult returns a new future result channel that already has the +// result waiting on the channel with the reply set to nil. This is useful +// to ignore things such as notifications when the caller didn't specify any +// notification handlers. +func newNilFutureResult() chan *futureResult { + responseChan := make(chan *futureResult, 1) + responseChan <- &futureResult{reply: nil} + return responseChan +} + +// NotificationHandlers defines callback function pointers to invoke with +// notifications. Since all of the functions are nil by default, all +// notifications are effectively ignored until their handlers are set to a +// concrete callback. +// +// NOTE: These handlers must NOT directly call any blocking calls on the client +// instance since the input reader goroutine blocks until the callback has +// completed. Doing so will result in a deadlock situation. +type NotificationHandlers struct { + // OnBlockConnected is invoked when a block is connected to the longest + // (best) chain. It will only be invoked if a preceding call to + // NotifyBlocks has been made to register for the notification and the + // function is non-nil. + OnBlockConnected func(hash *btcwire.ShaHash, height int32) + + // OnBlockDisconnected is invoked when a block is disconnected from the + // longest (best) chain. It will only be invoked if a preceding call to + // NotifyBlocks has been made to register for the notification and the + // function is non-nil. + OnBlockDisconnected func(hash *btcwire.ShaHash, height int32) + + // OnRecvTx is invoked when a transaction that receives funds to a + // registered address is received into the memory pool and also + // connected to the longest (best) chain. It will only be invoked if a + // preceding call to NotifyReceived, Rescan, or RescanEndHeight has been + // made to register for the notification and the function is non-nil. + OnRecvTx func(transaction *btcutil.Tx, details *btcws.BlockDetails) + + // OnRedeemingTx is invoked when a transaction that spends a registered + // outpoint is received into the memory pool and also connected to the + // longest (best) chain. It will only be invoked if a preceding call to + // NotifySpent, Rescan, or RescanEndHeight has been made to register for + // the notification and the function is non-nil. + // + // NOTE: The NotifyReceived will automatically register notifications + // for the outpoints that are now "owned" as a result of receiving + // funds to the registered addresses. This means it is possible for + // this to invoked indirectly as the result of a NotifyReceived call. + OnRedeemingTx func(transaction *btcutil.Tx, details *btcws.BlockDetails) + + // OnRescanProgress is invoked periodically when a rescan is underway. + // It will only be invoked if a preceding call to Rescan or + // RescanEndHeight has been made and the function is non-nil. + OnRescanProgress func(lastProcessedHeight int32) + + // OnTxAccepted is invoked when a transaction is accepted into the + // memory pool. It will only be invoked if a preceding call to + // NotifyNewTransactions with the verbose flag set to false has been + // made to register for the notification and the function is non-nil. + OnTxAccepted func(hash *btcwire.ShaHash, amount btcutil.Amount) + + // OnTxAccepted is invoked when a transaction is accepted into the + // memory pool. It will only be invoked if a preceding call to + // NotifyNewTransactions with the verbose flag set to true has been + // made to register for the notification and the function is non-nil. + OnTxAcceptedVerbose func(txDetails *btcjson.TxRawResult) + + // OnBtcdConnected is invoked when a wallet connects or disconnects from + // btcd. + // + // This will only be available when client is connected to a wallet + // server such as btcwallet. + OnBtcdConnected func(connected bool) + + // OnAccountBalance is invoked with account balance updates. + // + // This will only be available when speaking to a wallet server + // such as btcwallet. + OnAccountBalance func(account string, balance btcutil.Amount, confirmed bool) + + // OnWalletLockState is invoked when a wallet is locked or unlocked. + // + // This will only be available when client is connected to a wallet + // server such as btcwallet. + OnWalletLockState func(locked bool) + + // OnUnknownNotification is invoked when an unrecognized notification + // is received. This typically means the notification handling code + // for this package needs to be updated for a new notification type or + // the caller is using a custom notification this package does not know + // about. + OnUnknownNotification func(ntfn interface{}) +} + +// handleNotification examines the passed notification type, performs +// conversions to get the raw notification types into higher level types and +// delivers the notification to the appropriate On handler registered with +// the client. +func (c *Client) handleNotification(cmd btcjson.Cmd) { + // Ignore the notification if the client is not interested in any + // notifications. + if c.ntfnHandlers == nil { + return + } + + switch ntfn := cmd.(type) { + // OnBlockConnected + case *btcws.BlockConnectedNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnBlockConnected == nil { + return + } + + hash, err := btcwire.NewShaHashFromStr(ntfn.Hash) + if err != nil { + log.Warnf("Received block connected notification with "+ + "invalid hash string: %q", ntfn.Hash) + return + } + + c.ntfnHandlers.OnBlockConnected(hash, ntfn.Height) + + // OnBlockDisconnected + case *btcws.BlockDisconnectedNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnBlockDisconnected == nil { + return + } + + hash, err := btcwire.NewShaHashFromStr(ntfn.Hash) + if err != nil { + log.Warnf("Received block disconnected notification "+ + "with invalid hash string: %q", ntfn.Hash) + return + } + + c.ntfnHandlers.OnBlockDisconnected(hash, ntfn.Height) + + // OnRecvTx + case *btcws.RecvTxNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnRecvTx == nil { + return + } + + // Decode the serialized transaction hex to raw bytes. + serializedTx, err := hex.DecodeString(ntfn.HexTx) + if err != nil { + log.Warnf("Received recvtx notification with invalid "+ + "transaction hex '%q': %v", ntfn.HexTx, err) + } + + // Deserialize the transaction. + var msgTx btcwire.MsgTx + err = msgTx.Deserialize(bytes.NewReader(serializedTx)) + if err != nil { + log.Warnf("Received recvtx notification with "+ + "transaction that failed to deserialize: %v", + err) + } + + c.ntfnHandlers.OnRecvTx(btcutil.NewTx(&msgTx), ntfn.Block) + + // OnRedeemingTx + case *btcws.RedeemingTxNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnRedeemingTx == nil { + return + } + + // Decode the serialized transaction hex to raw bytes. + serializedTx, err := hex.DecodeString(ntfn.HexTx) + if err != nil { + log.Warnf("Received redeemingtx notification with "+ + "invalid transaction hex '%q': %v", ntfn.HexTx, + err) + } + + // Deserialize the transaction. + var msgTx btcwire.MsgTx + err = msgTx.Deserialize(bytes.NewReader(serializedTx)) + if err != nil { + log.Warnf("Received redeemingtx notification with "+ + "transaction that failed to deserialize: %v", + err) + } + + c.ntfnHandlers.OnRedeemingTx(btcutil.NewTx(&msgTx), ntfn.Block) + + // OnRescanProgress + case *btcws.RescanProgressNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnRescanProgress == nil { + return + } + + c.ntfnHandlers.OnRescanProgress(ntfn.LastProcessed) + + // OnTxAccepted + case *btcws.TxAcceptedNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnTxAccepted == nil { + return + } + + hash, err := btcwire.NewShaHashFromStr(ntfn.TxID) + if err != nil { + log.Warnf("Received tx accepted notification with "+ + "invalid hash string: %q", ntfn.TxID) + return + } + + c.ntfnHandlers.OnTxAccepted(hash, btcutil.Amount(ntfn.Amount)) + + // OnTxAcceptedVerbose + case *btcws.TxAcceptedVerboseNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnTxAcceptedVerbose == nil { + return + } + + c.ntfnHandlers.OnTxAcceptedVerbose(ntfn.RawTx) + + // OnBtcdConnected + case *btcws.BtcdConnectedNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnBtcdConnected == nil { + return + } + + c.ntfnHandlers.OnBtcdConnected(ntfn.Connected) + + // OnAccountBalance + case *btcws.AccountBalanceNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnAccountBalance == nil { + return + } + + balance, err := btcjson.JSONToAmount(ntfn.Balance) + if err != nil { + log.Warnf("Received account balance notification with "+ + "an amount that does not parse: %v", + ntfn.Balance) + return + } + + c.ntfnHandlers.OnAccountBalance(ntfn.Account, + btcutil.Amount(balance), ntfn.Confirmed) + + // OnWalletLockState + case *btcws.WalletLockStateNtfn: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnWalletLockState == nil { + return + } + + c.ntfnHandlers.OnWalletLockState(ntfn.Locked) + + // OnUnknownNotification + default: + if c.ntfnHandlers.OnUnknownNotification == nil { + return + } + + c.ntfnHandlers.OnUnknownNotification(ntfn) + } +} + +// FutureNotifyBlocksResult is a future promise to deliver the result of a +// NotifyBlocksAsync RPC invocation (or an applicable error). +type FutureNotifyBlocksResult chan *futureResult + +// Receive waits for the response promised by the future and returns an error +// if the registration was not successful. +func (r FutureNotifyBlocksResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// NotifyBlocksAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See NotifyBlocks for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifyBlocksAsync() FutureNotifyBlocksResult { + // Not supported in HTTP POST mode. + if c.config.HttpPostMode { + return newFutureError(ErrNotificationsNotSupported) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + id := c.NextID() + cmd := btcws.NewNotifyBlocksCmd(id) + + return c.sendCmd(cmd) +} + +// NotifyBlocks registers the client to receive notifications when blocks are +// connected and disconnected from the main chain. The notifications are +// delivered to the notification handlers associated with the client. Calling +// this function has no effect if there are no notification handlers and will +// result in an error if the client is configured to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via one of +// OnBlockConnected or OnBlockDisconnected. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifyBlocks() error { + return c.NotifyBlocksAsync().Receive() +} + +// FutureNotifySpentResult is a future promise to deliver the result of a +// NotifySpentAsync RPC invocation (or an applicable error). +type FutureNotifySpentResult chan *futureResult + +// Receive waits for the response promised by the future and returns an error +// if the registration was not successful. +func (r FutureNotifySpentResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// NotifySpentAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See NotifySpent for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifySpentAsync(outpoint *btcwire.OutPoint) FutureNotifySpentResult { + // Not supported in HTTP POST mode. + if c.config.HttpPostMode { + return newFutureError(ErrNotificationsNotSupported) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + id := c.NextID() + cmd := btcws.NewNotifySpentCmd(id, btcws.NewOutPointFromWire(outpoint)) + + return c.sendCmd(cmd) +} + +// NotifySpent registers the client to receive notifications when the passed +// transaction output is spent. The notifications are delivered to the +// notification handlers associated with the client. Calling this function has +// no effect if there are no notification handlers and will result in an error +// if the client is configured to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via +// OnRedeemingTx. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifySpent(outpoint *btcwire.OutPoint) error { + return c.NotifySpentAsync(outpoint).Receive() +} + +// FutureNotifyNewTransactionsResult is a future promise to deliver the result +// of a NotifyNewTransactionsAsync RPC invocation (or an applicable error). +type FutureNotifyNewTransactionsResult chan *futureResult + +// Receive waits for the response promised by the future and returns an error +// if the registration was not successful. +func (r FutureNotifyNewTransactionsResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// NotifyNewTransactionsAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See NotifyNewTransactionsAsync for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifyNewTransactionsAsync(verbose bool) FutureNotifyNewTransactionsResult { + // Not supported in HTTP POST mode. + if c.config.HttpPostMode { + return newFutureError(ErrNotificationsNotSupported) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + id := c.NextID() + cmd, err := btcws.NewNotifyNewTransactionsCmd(id, verbose) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// NotifyNewTransactions registers the client to receive notifications every +// time a new transaction is accepted to the memory pool. The notifications are +// delivered to the notification handlers associated with the client. Calling +// this function has no effect if there are no notification handlers and will +// result in an error if the client is configured to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via one of +// OnTxAccepted (when verbose is false) or OnTxAcceptedVerbose (when verbose is +// true). +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifyNewTransactions(verbose bool) error { + return c.NotifyNewTransactionsAsync(verbose).Receive() +} + +// FutureNotifyReceivedResult is a future promise to deliver the result of a +// NotifyReceivedAsync RPC invocation (or an applicable error). +type FutureNotifyReceivedResult chan *futureResult + +// Receive waits for the response promised by the future and returns an error +// if the registration was not successful. +func (r FutureNotifyReceivedResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// NotifyReceivedAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See NotifyReceived for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifyReceivedAsync(addresses []btcutil.Address) FutureNotifyReceivedResult { + // Not supported in HTTP POST mode. + if c.config.HttpPostMode { + return newFutureError(ErrNotificationsNotSupported) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + // Convert addresses to strings. + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.EncodeAddress()) + } + id := c.NextID() + cmd := btcws.NewNotifyReceivedCmd(id, addrs) + + return c.sendCmd(cmd) +} + +// NotifyReceived registers the client to receive notifications every time a +// new transaction which pays to one of the passed addresses is accepted to +// memory pool or in a block connected to the block chain. In addition, when +// one of these transactions is detected, the client is also automatically +// registered for notifications when the new transaction outpoints the address +// now has available are spent (See NotifySpent). The notifications are +// delivered to the notification handlers associated with the client. Calling +// this function has no effect if there are no notification handlers and will +// result in an error if the client is configured to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via one of +// *OnRecvTx (for transactions that receive funds to one of the passed +// addresses) or OnRedeemingTx (for transactions which spend from one +// of the outpoints which are automatically registered upon receipt of funds to +// the address). +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) NotifyReceived(addresses []btcutil.Address) error { + return c.NotifyReceivedAsync(addresses).Receive() +} + +// FutureRescanResult is a future promise to deliver the result of a RescanAsync +// or RescanEndHeightAsync RPC invocation (or an applicable error). +type FutureRescanResult chan *futureResult + +// Receive waits for the response promised by the future and returns an error +// if the rescan was not successful. +func (r FutureRescanResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// RescanAsync returns an instance of a type that can be used to get the result +// of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See Rescan for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) RescanAsync(startHeight int32, addresses []btcutil.Address, + outpoints []*btcwire.OutPoint) FutureRescanResult { + + // Not supported in HTTP POST mode. + if c.config.HttpPostMode { + return newFutureError(ErrNotificationsNotSupported) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + // Convert addresses to strings. + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.EncodeAddress()) + } + + // Convert outpoints. + ops := make([]btcws.OutPoint, 0, len(outpoints)) + for _, op := range outpoints { + ops = append(ops, *btcws.NewOutPointFromWire(op)) + } + + id := c.NextID() + cmd, err := btcws.NewRescanCmd(id, startHeight, addrs, ops) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// Rescan rescans the block chain starting from the provided start height to the +// end of the longest chain for transactions that pay to the passed addresses +// and transactions which spend the passed outpoints. +// +// The notifications of found transactions are delivered to the notification +// handlers associated with client and this call will not return until the +// rescan has completed. Calling this function has no effect if there are no +// notification handlers and will result in an error if the client is configured +// to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via one of +// OnRedeemingTx (for transactions which spend from the one of the +// passed outpoints), OnRecvTx (for transactions that receive funds +// to one of the passed addresses), and OnRescanProgress (for rescan progress +// updates). +// +// See RescanEndHeight to also specify a block height at which to stop the +// rescan if a bounded rescan is desired instead. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) Rescan(startHeight int32, addresses []btcutil.Address, + outpoints []*btcwire.OutPoint) error { + + return c.RescanAsync(startHeight, addresses, outpoints).Receive() +} + +// RescanEndHeightAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See RescanEndHeight for the blocking version and more details. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) RescanEndHeightAsync(startHeight int32, + addresses []btcutil.Address, outpoints []*btcwire.OutPoint, + endHeight int64) FutureRescanResult { + + // Not supported in HTTP POST mode. + if c.config.HttpPostMode { + return newFutureError(ErrNotificationsNotSupported) + } + + // Ignore the notification if the client is not interested in + // notifications. + if c.ntfnHandlers == nil { + return newNilFutureResult() + } + + // Convert addresses to strings. + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.EncodeAddress()) + } + + // Convert outpoints. + ops := make([]btcws.OutPoint, 0, len(outpoints)) + for _, op := range outpoints { + ops = append(ops, *btcws.NewOutPointFromWire(op)) + } + + id := c.NextID() + cmd, err := btcws.NewRescanCmd(id, startHeight, addrs, ops, endHeight) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// RescanEndHeight rescans the block chain starting from the provided start +// height up to the provided end height for transactions that pay to the passed +// addresses and transactions which spend the passed outpoints. +// +// The notifications of found transactions are delivered to the notification +// handlers associated with client and this call will not return until the +// rescan has completed. Calling this function has no effect if there are no +// notification handlers and will result in an error if the client is configured +// to run in HTTP POST mode. +// +// The notifications delivered as a result of this call will be via one of +// OnRedeemingTx (for transactions which spend from the one of the +// passed outpoints), OnRecvTx (for transactions that receive funds +// to one of the passed addresses), and OnRescanProgress (for rescan progress +// updates). +// +// See Rescan to also perform a rescan through current end of the longest chain. +// +// NOTE: This is a btcd extension and requires a websocket connection. +func (c *Client) RescanEndHeight(startHeight int32, addresses []btcutil.Address, + outpoints []*btcwire.OutPoint, endHeight int64) error { + + return c.RescanEndHeightAsync(startHeight, addresses, outpoints, + endHeight).Receive() +} diff --git a/rawtransactions.go b/rawtransactions.go new file mode 100644 index 00000000..c12783e6 --- /dev/null +++ b/rawtransactions.go @@ -0,0 +1,516 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "bytes" + "encoding/hex" + "fmt" + "github.com/conformal/btcjson" + "github.com/conformal/btcutil" + "github.com/conformal/btcwire" +) + +// SigHashType enumerates the available signature hashing types that the +// SignRawTransaction function accepts. +type SigHashType string + +// Constants used to indicate the signature hash type for SignRawTransaction. +const ( + // SigHashAll indicates ALL of the outputs should be signed. + SigHashAll SigHashType = "ALL" + + // SigHashNone indicates NONE of the outputs should be signed. This + // can be thought of as specifying the signer does not care where the + // bitcoins go. + SigHashNone SigHashType = "NONE" + + // SigHashSingle indicates that a SINGLE output should be signed. This + // can be thought of specifying the signer only cares about where ONE of + // the outputs goes, but not any of the others. + SigHashSingle SigHashType = "SINGLE" + + // SigHashAllAnyoneCanPay indicates that signer does not care where the + // other inputs to the transaction come from, so it allows other people + // to add inputs. In addition, it uses the SigHashAll signing method + // for outputs. + SigHashAllAnyoneCanPay SigHashType = "ALL|ANYONECANPAY" + + // SigHashNoneAnyoneCanPay indicates that signer does not care where the + // other inputs to the transaction come from, so it allows other people + // to add inputs. In addition, it uses the SigHashNone signing method + // for outputs. + SigHashNoneAnyoneCanPay SigHashType = "NONE|ANYONECANPAY" + + // SigHashAllAnyoneCanPay indicates that signer does not care where the + // other inputs to the transaction come from, so it allows other people + // to add inputs. In addition, it uses the SigHashSingle signing method + // for outputs. + SigHashSingleAnyoneCanPay SigHashType = "SINGLE|ANYONECANPAY" +) + +// String returns the SighHashType in human-readable form. +func (s SigHashType) String() string { + return string(s) +} + +// FutureGetRawTransactionResult is a future promise to deliver the result of a +// GetRawTransactionAsync RPC invocation (or an applicable error). +type FutureGetRawTransactionResult chan *futureResult + +// Receive waits for the response promised by the future and returns a +// transaction given its hash. +func (r FutureGetRawTransactionResult) Receive() (*btcutil.Tx, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + txHex, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getrawtransaction (verbose=0): %T\n", reply) + } + + // Decode the serialized transaction hex to raw bytes. + serializedTx, err := hex.DecodeString(txHex) + if err != nil { + return nil, err + } + + // Deserialize the transaction and return it. + var msgTx btcwire.MsgTx + if err := msgTx.Deserialize(bytes.NewBuffer(serializedTx)); err != nil { + return nil, err + } + return btcutil.NewTx(&msgTx), nil +} + +// GetRawTransactionAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetRawTransaction for the blocking version and more details. +func (c *Client) GetRawTransactionAsync(txHash *btcwire.ShaHash) FutureGetRawTransactionResult { + id := c.NextID() + cmd, err := btcjson.NewGetRawTransactionCmd(id, txHash.String(), 0) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetRawTransaction returns a transaction given its hash. +// +// See GetRawTransactionVerbose to obtain additional information about the +// transaction. +func (c *Client) GetRawTransaction(txHash *btcwire.ShaHash) (*btcutil.Tx, error) { + return c.GetRawTransactionAsync(txHash).Receive() +} + +// FutureGetRawTransactionVerboseResult is a future promise to deliver the +// result of a GetRawTransactionVerboseAsync RPC invocation (or an applicable +// error). +type FutureGetRawTransactionVerboseResult chan *futureResult + +// Receive waits for the response promised by the future and returns information +// about a transaction given its hash. +func (r FutureGetRawTransactionVerboseResult) Receive() (*btcjson.TxRawResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(*btcjson.TxRawResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getrawtransaction (verbose=1): %T\n", reply) + } + + return result, nil +} + +// GetRawTransactionVerboseAsync returns an instance of a type that can be used +// to get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetRawTransactionVerbose for the blocking version and more details. +func (c *Client) GetRawTransactionVerboseAsync(txHash *btcwire.ShaHash) FutureGetRawTransactionVerboseResult { + id := c.NextID() + cmd, err := btcjson.NewGetRawTransactionCmd(id, txHash.String(), 1) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetRawTransactionVerbose returns information about a transaction given +// its hash. +// +// See GetRawTransaction to obtain only the transaction already deserialized. +func (c *Client) GetRawTransactionVerbose(txHash *btcwire.ShaHash) (*btcjson.TxRawResult, error) { + return c.GetRawTransactionVerboseAsync(txHash).Receive() +} + +// FutureDecodeRawTransactionResult is a future promise to deliver the result +// of a DecodeRawTransactionAsync RPC invocation (or an applicable error). +type FutureDecodeRawTransactionResult chan *futureResult + +// Receive waits for the response promised by the future and returns information +// about a transaction given its serialized bytes. +func (r FutureDecodeRawTransactionResult) Receive() (*btcjson.TxRawResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(*btcjson.TxRawResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "decoderawtransaction: %T\n", reply) + } + + return result, nil +} + +// DecodeRawTransactionAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See DecodeRawTransaction for the blocking version and more details. +func (c *Client) DecodeRawTransactionAsync(serializedTx []byte) FutureDecodeRawTransactionResult { + id := c.NextID() + txHex := hex.EncodeToString(serializedTx) + cmd, err := btcjson.NewDecodeRawTransactionCmd(id, txHex) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// DecodeRawTransaction returns information about a transaction given its +// serialized bytes. +func (c *Client) DecodeRawTransaction(serializedTx []byte) (*btcjson.TxRawResult, error) { + return c.DecodeRawTransactionAsync(serializedTx).Receive() +} + +// FutureCreateRawTransactionResult is a future promise to deliver the result +// of a CreateRawTransactionAsync RPC invocation (or an applicable error). +type FutureCreateRawTransactionResult chan *futureResult + +// Receive waits for the response promised by the future and returns a new +// transaction spending the provided inputs and sending to the provided +// addresses. +func (r FutureCreateRawTransactionResult) Receive() (*btcwire.MsgTx, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + txHex, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "createrawtransaction: %T\n", reply) + } + + // Decode the serialized transaction hex to raw bytes. + serializedTx, err := hex.DecodeString(txHex) + if err != nil { + return nil, err + } + + // Deserialize the transaction and return it. + var msgTx btcwire.MsgTx + if err := msgTx.Deserialize(bytes.NewBuffer(serializedTx)); err != nil { + return nil, err + } + return &msgTx, nil +} + +// CreateRawTransactionAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See CreateRawTransaction for the blocking version and more details. +func (c *Client) CreateRawTransactionAsync(inputs []btcjson.TransactionInput, amounts map[string]int64) FutureCreateRawTransactionResult { + id := c.NextID() + cmd, err := btcjson.NewCreateRawTransactionCmd(id, inputs, amounts) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// CreateRawTransaction returns a new transaction spending the provided inputs +// and sending to the provided addresses. +func (c *Client) CreateRawTransaction(inputs []btcjson.TransactionInput, amounts map[string]int64) (*btcwire.MsgTx, error) { + return c.CreateRawTransactionAsync(inputs, amounts).Receive() +} + +// FutureSendRawTransactionResult is a future promise to deliver the result +// of a SendRawTransactionAsync RPC invocation (or an applicable error). +type FutureSendRawTransactionResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of submitting the encoded transaction to the server which then relays it to +// the network. +func (r FutureSendRawTransactionResult) Receive() (*btcwire.ShaHash, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + txHashStr, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "decoderawtransaction: %T\n", reply) + } + + return btcwire.NewShaHashFromStr(txHashStr) +} + +// SendRawTransactionAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See SendRawTransaction for the blocking version and more details. +func (c *Client) SendRawTransactionAsync(tx *btcwire.MsgTx, allowHighFees bool) FutureSendRawTransactionResult { + // Serialize the transaction and convert to hex string. + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(buf); err != nil { + return newFutureError(err) + } + txHex := hex.EncodeToString(buf.Bytes()) + + id := c.NextID() + cmd, err := btcjson.NewSendRawTransactionCmd(id, txHex, allowHighFees) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendRawTransaction submits the encoded transaction to the server which will +// then relay it to the network. +func (c *Client) SendRawTransaction(tx *btcwire.MsgTx, allowHighFees bool) (*btcwire.ShaHash, error) { + return c.SendRawTransactionAsync(tx, allowHighFees).Receive() +} + +// FutureSignRawTransactionResult is a future promise to deliver the result +// of one of the SignRawTransactionAsync family of RPC invocations (or an +// applicable error). +type FutureSignRawTransactionResult chan *futureResult + +// Receive waits for the response promised by the future and returns the +// signed transaction as well as whether or not all inputs are now signed. +func (r FutureSignRawTransactionResult) Receive() (*btcwire.MsgTx, bool, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, false, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(btcjson.SignRawTransactionResult) + if !ok { + return nil, false, fmt.Errorf("unexpected response type for "+ + "signrawtransaction: %T\n", reply) + } + + // Decode the serialized transaction hex to raw bytes. + serializedTx, err := hex.DecodeString(result.Hex) + if err != nil { + return nil, false, err + } + + // Deserialize the transaction and return it. + var msgTx btcwire.MsgTx + if err := msgTx.Deserialize(bytes.NewBuffer(serializedTx)); err != nil { + return nil, false, err + } + + return &msgTx, result.Complete, nil +} + +// SignRawTransactionAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See SignRawTransaction for the blocking version and more details. +func (c *Client) SignRawTransactionAsync(tx *btcwire.MsgTx) FutureSignRawTransactionResult { + // Serialize the transaction and convert to hex string. + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(buf); err != nil { + return newFutureError(err) + } + txHex := hex.EncodeToString(buf.Bytes()) + + id := c.NextID() + cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SignRawTransaction signs inputs for the passed transaction and returns the +// signed transaction as well as whether or not all inputs are now signed. +// +// This function assumes the RPC server already knows the input transactions and +// private keys for the passed transaction which needs to be signed and uses the +// default signature hash type. Use one of the SignRawTransaction# variants to +// specify that information if needed. +func (c *Client) SignRawTransaction(tx *btcwire.MsgTx) (*btcwire.MsgTx, bool, error) { + return c.SignRawTransactionAsync(tx).Receive() +} + +// SignRawTransaction2Async returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See SignRawTransaction2 for the blocking version and more details. +func (c *Client) SignRawTransaction2Async(tx *btcwire.MsgTx, inputs []btcjson.RawTxInput) FutureSignRawTransactionResult { + // Serialize the transaction and convert to hex string. + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(buf); err != nil { + return newFutureError(err) + } + txHex := hex.EncodeToString(buf.Bytes()) + + id := c.NextID() + cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex, inputs) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SignRawTransaction2 signs inputs for the passed transaction given the list +// of information about the input transactions needed to perform the signing +// process. +// +// This only input transactions that need to be specified are ones the +// RPC server does not already know. Already known input transactions will be +// merged with the specified transactions. +// +// See SignRawTransaction if the RPC server already knows the input +// transactions. +func (c *Client) SignRawTransaction2(tx *btcwire.MsgTx, inputs []btcjson.RawTxInput) (*btcwire.MsgTx, bool, error) { + return c.SignRawTransaction2Async(tx, inputs).Receive() +} + +// SignRawTransaction3Async returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See SignRawTransaction3 for the blocking version and more details. +func (c *Client) SignRawTransaction3Async(tx *btcwire.MsgTx, + inputs []btcjson.RawTxInput, + privKeysWIF []string) FutureSignRawTransactionResult { + + // Serialize the transaction and convert to hex string. + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(buf); err != nil { + return newFutureError(err) + } + txHex := hex.EncodeToString(buf.Bytes()) + + id := c.NextID() + cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex, inputs, + privKeysWIF) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SignRawTransaction3 signs inputs for the passed transaction given the list +// of information about extra input transactions and a list of private keys +// needed to perform the signing process. The private keys must be in wallet +// import format (WIF). +// +// This only input transactions that need to be specified are ones the +// RPC server does not already know. Already known input transactions will be +// merged with the specified transactions. This means the list of transaction +// inputs can be nil if the RPC server already knows them all. +// +// NOTE: Unlike the merging functionality of the input transactions, ONLY the +// specified private keys will be used, so even if the server already knows some +// of the private keys, they will NOT be used. +// +// See SignRawTransaction if the RPC server already knows the input +// transactions and private keys or SignRawTransaction2 if it already knows the +// private keys. +func (c *Client) SignRawTransaction3(tx *btcwire.MsgTx, + inputs []btcjson.RawTxInput, + privKeysWIF []string) (*btcwire.MsgTx, bool, error) { + + return c.SignRawTransaction3Async(tx, inputs, privKeysWIF).Receive() +} + +// SignRawTransaction4Async returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See SignRawTransaction4 for the blocking version and more details. +func (c *Client) SignRawTransaction4Async(tx *btcwire.MsgTx, + inputs []btcjson.RawTxInput, privKeysWIF []string, + hashType SigHashType) FutureSignRawTransactionResult { + + // Serialize the transaction and convert to hex string. + buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize())) + if err := tx.Serialize(buf); err != nil { + return newFutureError(err) + } + txHex := hex.EncodeToString(buf.Bytes()) + + id := c.NextID() + cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex, inputs, + privKeysWIF, string(hashType)) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SignRawTransaction4 signs inputs for the passed transaction using the +// the specified signature hash type given the list of information about extra +// input transactions and a potential list of private keys needed to perform +// the signing process. The private keys, if specified, must be in wallet +// import format (WIF). +// +// The only input transactions that need to be specified are ones the RPC server +// does not already know. This means the list of transaction inputs can be nil +// if the RPC server already knows them all. +// +// NOTE: Unlike the merging functionality of the input transactions, ONLY the +// specified private keys will be used, so even if the server already knows some +// of the private keys, they will NOT be used. The list of private keys can be +// nil in which case any private keys the RPC server knows will be used. +// +// This function should only used if a non-default signature hash type is +// desired. Otherwise, see SignRawTransaction if the RPC server already knows +// the input transactions and private keys, SignRawTransaction2 if it already +// knows the private keys, or SignRawTransaction3 if it does not know both. +func (c *Client) SignRawTransaction4(tx *btcwire.MsgTx, + inputs []btcjson.RawTxInput, privKeysWIF []string, + hashType SigHashType) (*btcwire.MsgTx, bool, error) { + + return c.SignRawTransaction4Async(tx, inputs, privKeysWIF, + hashType).Receive() +} diff --git a/wallet.go b/wallet.go new file mode 100644 index 00000000..d599e0e8 --- /dev/null +++ b/wallet.go @@ -0,0 +1,1862 @@ +// Copyright (c) 2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcrpcclient + +import ( + "fmt" + "github.com/conformal/btcjson" + "github.com/conformal/btcutil" + "github.com/conformal/btcwire" + "github.com/conformal/btcws" +) + +// ***************************** +// Transaction Listing Functions +// ***************************** + +// FutureGetTransactionResult is a future promise to deliver the result +// of a GetTransactionAsync RPC invocation (or an applicable error). +type FutureGetTransactionResult chan *futureResult + +// Receive waits for the response promised by the future and returns detailed +// information about a wallet transaction. +func (r FutureGetTransactionResult) Receive() (*btcjson.GetTransactionResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(btcjson.GetTransactionResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "gettransaction: %T\n", reply) + } + + return &result, nil +} + +// GetTransactionAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetTransaction for the blocking version and more details. +func (c *Client) GetTransactionAsync(txHash *btcwire.ShaHash) FutureGetTransactionResult { + id := c.NextID() + cmd, err := btcjson.NewGetTransactionCmd(id, txHash.String()) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetTransaction returns detailed information about a wallet transaction. +// +// See GetRawTransaction to return the raw transaction instead. +func (c *Client) GetTransaction(txHash *btcwire.ShaHash) (*btcjson.GetTransactionResult, error) { + return c.GetTransactionAsync(txHash).Receive() +} + +// FutureListTransactionsResult is a future promise to deliver the result of a +// ListTransactionsAsync, ListTransactionsCountAsync, or +// ListTransactionsCountFromAsync RPC invocation (or an applicable error). +type FutureListTransactionsResult chan *futureResult + +// Receive waits for the response promised by the future and returns a list of +// the most recent transactions. +func (r FutureListTransactionsResult) Receive() ([]btcjson.ListTransactionsResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // No transactions. + if reply == nil { + return nil, nil + } + + // Ensure the returned data is the expected type. + transactions, ok := reply.([]btcjson.ListTransactionsResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "listtransactions: %T\n", reply) + } + + return transactions, nil +} + +// ListTransactionsAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See ListTransactions for the blocking version and more details. +func (c *Client) ListTransactionsAsync(account string) FutureListTransactionsResult { + id := c.NextID() + cmd, err := btcjson.NewListTransactionsCmd(id, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListTransactions returns a list of the most recent transactions. +// +// See the ListTransactionsCount and ListTransactionsCountFrom to control the +// number of transactions returned and starting point, respectively. +func (c *Client) ListTransactions(account string) ([]btcjson.ListTransactionsResult, error) { + return c.ListTransactionsAsync(account).Receive() +} + +// ListTransactionsCountAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListTransactionsCount for the blocking version and more details. +func (c *Client) ListTransactionsCountAsync(account string, count int) FutureListTransactionsResult { + id := c.NextID() + cmd, err := btcjson.NewListTransactionsCmd(id, account, count) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListTransactionsCount returns a list of the most recent transactions up +// to the passed count. +// +// See the ListTransactions and ListTransactionsCountFrom functions for +// different options. +func (c *Client) ListTransactionsCount(account string, count int) ([]btcjson.ListTransactionsResult, error) { + return c.ListTransactionsCountAsync(account, count).Receive() +} + +// ListTransactionsCountFromAsync returns an instance of a type that can be used +// to get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListTransactionsCountFrom for the blocking version and more details. +func (c *Client) ListTransactionsCountFromAsync(account string, count, from int) FutureListTransactionsResult { + id := c.NextID() + cmd, err := btcjson.NewListTransactionsCmd(id, account, count, from) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListTransactionsCountFrom returns a list of the most recent transactions up +// to the passed count while skipping the first 'from' transactions. +// +// See the ListTransactions and ListTransactionsCount functions to use defaults. +func (c *Client) ListTransactionsCountFrom(account string, count, from int) ([]btcjson.ListTransactionsResult, error) { + return c.ListTransactionsCountFromAsync(account, count, from).Receive() +} + +// FutureListSinceBlockResult is a future promise to deliver the result of a +// ListSinceBlockAsync or ListSinceBlockMinConfAsync RPC invocation (or an +// applicable error). +type FutureListSinceBlockResult chan *futureResult + +// Receive waits for the response promised by the future and returns all +// transactions added in blocks since the specified block hash, or all +// transactions if it is nil. +func (r FutureListSinceBlockResult) Receive() (*btcjson.ListSinceBlockResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + listResult, ok := reply.(btcjson.ListSinceBlockResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "listsinceblock: %T\n", reply) + } + + return &listResult, nil +} + +// ListSinceBlockAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See ListSinceBlock for the blocking version and more details. +func (c *Client) ListSinceBlockAsync(blockHash *btcwire.ShaHash) FutureListSinceBlockResult { + hash := "" + if blockHash != nil { + hash = blockHash.String() + } + id := c.NextID() + cmd, err := btcjson.NewListSinceBlockCmd(id, hash) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListSinceBlock returns all transactions added in blocks since the specified +// block hash, or all transactions if it is nil, using the default number of +// minimum confirmations as a filter. +// +// See ListSinceBlockMinConf to override the minimum number of confirmations. +func (c *Client) ListSinceBlock(blockHash *btcwire.ShaHash) (*btcjson.ListSinceBlockResult, error) { + return c.ListSinceBlockAsync(blockHash).Receive() +} + +// ListSinceBlockMinConfAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListSinceBlockMinConf for the blocking version and more details. +func (c *Client) ListSinceBlockMinConfAsync(blockHash *btcwire.ShaHash, minConfirms int) FutureListSinceBlockResult { + hash := "" + if blockHash != nil { + hash = blockHash.String() + } + id := c.NextID() + cmd, err := btcjson.NewListSinceBlockCmd(id, hash, minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListSinceBlockMinConf returns all transactions added in blocks since the +// specified block hash, or all transactions if it is nil, using the specified +// number of minimum confirmations as a filter. +// +// See ListSinceBlock to use the default minimum number of confirmations. +func (c *Client) ListSinceBlockMinConf(blockHash *btcwire.ShaHash, minConfirms int) (*btcjson.ListSinceBlockResult, error) { + return c.ListSinceBlockMinConfAsync(blockHash, minConfirms).Receive() +} + +// ************************** +// Transaction Send Functions +// ************************** + +// FutureSetTxFeeResult is a future promise to deliver the result of a +// SetTxFeeAsync RPC invocation (or an applicable error). +type FutureSetTxFeeResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of setting an optional transaction fee per KB that helps ensure transactions +// are processed quickly. Most transaction are 1KB. +func (r FutureSetTxFeeResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// SetTxFeeAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See SetTxFee for the blocking version and more details. +func (c *Client) SetTxFeeAsync(fee btcutil.Amount) FutureSetTxFeeResult { + id := c.NextID() + cmd, err := btcjson.NewSetTxFeeCmd(id, int64(fee)) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SetTxFee sets an optional transaction fee per KB that helps ensure +// transactions are processed quickly. Most transaction are 1KB. +func (c *Client) SetTxFee(fee btcutil.Amount) error { + return c.SetTxFeeAsync(fee).Receive() +} + +// FutureSendToAddressResult is a future promise to deliver the result of a +// SendToAddressAsync RPC invocation (or an applicable error). +type FutureSendToAddressResult chan *futureResult + +// Receive waits for the response promised by the future and returns the hash +// of the transaction sending the passed amount to the given address. +func (r FutureSendToAddressResult) Receive() (*btcwire.ShaHash, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + txHash, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "sendtoaddress: %T\n", reply) + } + + return btcwire.NewShaHashFromStr(txHash) +} + +// SendToAddressAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See SendToAddress for the blocking version and more details. +func (c *Client) SendToAddressAsync(address btcutil.Address, amount btcutil.Amount) FutureSendToAddressResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewSendToAddressCmd(id, addr, int64(amount)) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendToAddress sends the passed amount to the given address. +// +// See SendToAddressComment to associate comments with the transaction in the +// wallet. The comments are not part of the transaction and are only internal +// to the wallet. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendToAddress(address btcutil.Address, amount btcutil.Amount) (*btcwire.ShaHash, error) { + return c.SendToAddressAsync(address, amount).Receive() +} + +// SendToAddressCommentAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See SendToAddressComment for the blocking version and more details. +func (c *Client) SendToAddressCommentAsync(address btcutil.Address, + amount btcutil.Amount, comment, + commentTo string) FutureSendToAddressResult { + + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewSendToAddressCmd(id, addr, int64(amount), + comment, commentTo) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendToAddressComment sends the passed amount to the given address and stores +// the provided comment and comment to in the wallet. The comment parameter is +// intended to be used for the purpose of the transaction while the commentTo +// parameter is indended to be used for who the transaction is being sent to. +// +// The comments are not part of the transaction and are only internal +// to the wallet. +// +// See SendToAddress to avoid using comments. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendToAddressComment(address btcutil.Address, amount btcutil.Amount, comment, commentTo string) (*btcwire.ShaHash, error) { + return c.SendToAddressCommentAsync(address, amount, comment, + commentTo).Receive() +} + +// FutureSendFromResult is a future promise to deliver the result of a +// SendFromAsync, SendFromMinConfAsync, or SendFromCommentAsync RPC invocation +// (or an applicable error). +type FutureSendFromResult chan *futureResult + +// Receive waits for the response promised by the future and returns the hash +// of the transaction sending amount to the given address using the provided +// account as a source of funds. +func (r FutureSendFromResult) Receive() (*btcwire.ShaHash, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + txHash, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "sendfrom: %T\n", reply) + } + + return btcwire.NewShaHashFromStr(txHash) +} + +// SendFromAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See SendFrom for the blocking version and more details. +func (c *Client) SendFromAsync(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount) FutureSendFromResult { + id := c.NextID() + addr := toAddress.EncodeAddress() + cmd, err := btcjson.NewSendFromCmd(id, fromAccount, addr, int64(amount)) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendFrom sends the passed amount to the given address using the provided +// account as a source of funds. Only funds with the default number of minimum +// confirmations will be used. +// +// See SendFromMinConf and SendFromComment for different options. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendFrom(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount) (*btcwire.ShaHash, error) { + return c.SendFromAsync(fromAccount, toAddress, amount).Receive() +} + +// SendFromMinConfAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See SendFromMinConf for the blocking version and more details. +func (c *Client) SendFromMinConfAsync(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount, minConfirms int) FutureSendFromResult { + id := c.NextID() + addr := toAddress.EncodeAddress() + cmd, err := btcjson.NewSendFromCmd(id, fromAccount, addr, int64(amount), + minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendFromMinConf sends the passed amount to the given address using the +// provided account as a source of funds. Only funds with the passed number of +// minimum confirmations will be used. +// +// See SendFrom to use the default number of minimum confirmations and +// SendFromComment for additional options. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendFromMinConf(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount, minConfirms int) (*btcwire.ShaHash, error) { + return c.SendFromMinConfAsync(fromAccount, toAddress, amount, + minConfirms).Receive() +} + +// SendFromCommentAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See SendFromComment for the blocking version and more details. +func (c *Client) SendFromCommentAsync(fromAccount string, + toAddress btcutil.Address, amount btcutil.Amount, minConfirms int, + comment, commentTo string) FutureSendFromResult { + + id := c.NextID() + addr := toAddress.EncodeAddress() + cmd, err := btcjson.NewSendFromCmd(id, fromAccount, addr, int64(amount), + minConfirms, comment, commentTo) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendFromMinConf sends the passed amount to the given address using the +// provided account as a source of funds and stores the provided comment and +// comment to in the wallet. The comment parameter is intended to be used for +// the purpose of the transaction while the commentTo parameter is indended to +// be used for who the transaction is being sent to. Only funds with the passed +// number of minimum confirmations will be used. +// +// See SendFrom and SendFromMinConf to use defaults. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendFromComment(fromAccount string, toAddress btcutil.Address, + amount btcutil.Amount, minConfirms int, + comment, commentTo string) (*btcwire.ShaHash, error) { + + return c.SendFromCommentAsync(fromAccount, toAddress, amount, + minConfirms, comment, commentTo).Receive() +} + +// FutureSendManyResult is a future promise to deliver the result of a +// SendManyAsync, SendManyMinConfAsync, or SendManyCommentAsync RPC invocation +// (or an applicable error). +type FutureSendManyResult chan *futureResult + +// Receive waits for the response promised by the future and returns the hash +// of the transaction sending multiple amounts to multiple addresses using the +// provided account as a source of funds. +func (r FutureSendManyResult) Receive() (*btcwire.ShaHash, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + txHash, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "sendmany: %T\n", reply) + } + + return btcwire.NewShaHashFromStr(txHash) +} + +// SendManyAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See SendMany for the blocking version and more details. +func (c *Client) SendManyAsync(fromAccount string, amounts map[btcutil.Address]btcutil.Amount) FutureSendManyResult { + convertedAmounts := make(map[string]int64, len(amounts)) + for addr, amount := range amounts { + convertedAmounts[addr.EncodeAddress()] = int64(amount) + } + id := c.NextID() + cmd, err := btcjson.NewSendManyCmd(id, fromAccount, convertedAmounts) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendMany sends multiple amounts to multiple addresses using the provided +// account as a source of funds in a single transaction. Only funds with the +// default number of minimum confirmations will be used. +// +// See SendManyMinConf and SendManyComment for different options. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendMany(fromAccount string, amounts map[btcutil.Address]btcutil.Amount) (*btcwire.ShaHash, error) { + return c.SendManyAsync(fromAccount, amounts).Receive() +} + +// SendManyMinConfAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See SendManyMinConf for the blocking version and more details. +func (c *Client) SendManyMinConfAsync(fromAccount string, + amounts map[btcutil.Address]btcutil.Amount, + minConfirms int) FutureSendManyResult { + + convertedAmounts := make(map[string]int64, len(amounts)) + for addr, amount := range amounts { + convertedAmounts[addr.EncodeAddress()] = int64(amount) + } + id := c.NextID() + cmd, err := btcjson.NewSendManyCmd(id, fromAccount, convertedAmounts, + minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendManyMinConf sends multiple amounts to multiple addresses using the +// provided account as a source of funds in a single transaction. Only funds +// with the passed number of minimum confirmations will be used. +// +// See SendMany to use the default number of minimum confirmations and +// SendManyComment for additional options. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendManyMinConf(fromAccount string, + amounts map[btcutil.Address]btcutil.Amount, + minConfirms int) (*btcwire.ShaHash, error) { + + return c.SendManyMinConfAsync(fromAccount, amounts, minConfirms).Receive() +} + +// SendManyCommentAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See SendManyComment for the blocking version and more details. +func (c *Client) SendManyCommentAsync(fromAccount string, + amounts map[btcutil.Address]btcutil.Amount, minConfirms int, + comment string) FutureSendManyResult { + + convertedAmounts := make(map[string]int64, len(amounts)) + for addr, amount := range amounts { + convertedAmounts[addr.EncodeAddress()] = int64(amount) + } + id := c.NextID() + cmd, err := btcjson.NewSendManyCmd(id, fromAccount, convertedAmounts, + minConfirms, comment) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SendManyComment sends multiple amounts to multiple addresses using the +// provided account as a source of funds in a single transaction and stores the +// provided comment in the wallet. The comment parameter is intended to be used +// for the purpose of the transaction Only funds with the passed number of +// minimum confirmations will be used. +// +// See SendMany and SendManyMinConf to use defaults. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SendManyComment(fromAccount string, + amounts map[btcutil.Address]btcutil.Amount, minConfirms int, + comment string) (*btcwire.ShaHash, error) { + + return c.SendManyCommentAsync(fromAccount, amounts, minConfirms, + comment).Receive() +} + +// ************************* +// Address/Account Functions +// ************************* + +// FutureAddMultisigAddressResult is a future promise to deliver the result of a +// AddMultisigAddressAsync RPC invocation (or an applicable error). +type FutureAddMultisigAddressResult chan *futureResult + +// Receive waits for the response promised by the future and returns the +// multisignature address that requires the specified number of signatures for +// the provided addresses. +func (r FutureAddMultisigAddressResult) Receive() (btcutil.Address, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + addr, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "addmultisigaddress: %T\n", reply) + } + + return btcutil.DecodeAddress(addr, btcwire.MainNet) +} + +// AddMultisigAddressAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See AddMultisigAddress for the blocking version and more details. +func (c *Client) AddMultisigAddressAsync(requiredSigs int, addresses []btcutil.Address, account string) FutureAddMultisigAddressResult { + id := c.NextID() + + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.EncodeAddress()) + } + + cmd, err := btcjson.NewAddMultisigAddressCmd(id, requiredSigs, addrs, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// AddMultisigAddress adds a multisignature address that requires the specified +// number of signatures for the provided addresses to the wallet. +func (c *Client) AddMultisigAddress(requiredSigs int, addresses []btcutil.Address, account string) (btcutil.Address, error) { + return c.AddMultisigAddressAsync(requiredSigs, addresses, + account).Receive() +} + +// FutureCreateMultisigResult is a future promise to deliver the result of a +// CreateMultisigAsync RPC invocation (or an applicable error). +type FutureCreateMultisigResult chan *futureResult + +// Receive waits for the response promised by the future and returns the +// multisignature address and script needed to redeem it. +func (r FutureCreateMultisigResult) Receive() (*btcjson.CreateMultiSigResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(btcjson.CreateMultiSigResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "createmultisig: %T\n", reply) + } + + return &result, nil +} + +// CreateMultisigAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See CreateMultisig for the blocking version and more details. +func (c *Client) CreateMultisigAsync(requiredSigs int, addresses []btcutil.Address) FutureCreateMultisigResult { + id := c.NextID() + + addrs := make([]string, 0, len(addresses)) + for _, addr := range addresses { + addrs = append(addrs, addr.EncodeAddress()) + } + + cmd, err := btcjson.NewCreateMultisigCmd(id, requiredSigs, addrs) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// CreateMultisig creates a multisignature address that requires the specified +// number of signatures for the provided addresses and returns the +// multisignature address and script needed to redeem it. +func (c *Client) CreateMultisig(requiredSigs int, addresses []btcutil.Address) (*btcjson.CreateMultiSigResult, error) { + return c.CreateMultisigAsync(requiredSigs, addresses).Receive() +} + +// FutureGetNewAddressResult is a future promise to deliver the result of a +// GetNewAddressAsync RPC invocation (or an applicable error). +type FutureGetNewAddressResult chan *futureResult + +// Receive waits for the response promised by the future and returns a new +// address. +func (r FutureGetNewAddressResult) Receive() (btcutil.Address, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + addr, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getnewaddress: %T\n", reply) + } + + return btcutil.DecodeAddress(addr, btcwire.MainNet) +} + +// GetNewAddressAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetNewAddress for the blocking version and more details. +func (c *Client) GetNewAddressAsync() FutureGetNewAddressResult { + id := c.NextID() + cmd, err := btcjson.NewGetNewAddressCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetNewAddress returns a new address. +func (c *Client) GetNewAddress() (btcutil.Address, error) { + return c.GetNewAddressAsync().Receive() +} + +// FutureGetRawChangeAddressResult is a future promise to deliver the result of +// a GetRawChangeAddressAsync RPC invocation (or an applicable error). +type FutureGetRawChangeAddressResult chan *futureResult + +// Receive waits for the response promised by the future and returns a new +// address for receiving change that will be associated with the provided +// account. Note that this is only for raw transactions and NOT for normal use. +func (r FutureGetRawChangeAddressResult) Receive() (btcutil.Address, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + addr, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getrawchangeaddress: %T\n", reply) + } + + return btcutil.DecodeAddress(addr, btcwire.MainNet) +} + +// GetRawChangeAddressAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetRawChangeAddress for the blocking version and more details. +func (c *Client) GetRawChangeAddressAsync(account string) FutureGetRawChangeAddressResult { + id := c.NextID() + cmd, err := btcjson.NewGetRawChangeAddressCmd(id, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetRawChangeAddress returns a new address for receiving change that will be +// associated with the provided account. Note that this is only for raw +// transactions and NOT for normal use. +func (c *Client) GetRawChangeAddress(account string) (btcutil.Address, error) { + return c.GetRawChangeAddressAsync(account).Receive() +} + +// FutureGetAccountAddressResult is a future promise to deliver the result of a +// GetAccountAddressAsync RPC invocation (or an applicable error). +type FutureGetAccountAddressResult chan *futureResult + +// Receive waits for the response promised by the future and returns the current +// Bitcoin address for receiving payments to the specified account. +func (r FutureGetAccountAddressResult) Receive() (btcutil.Address, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + addr, ok := reply.(string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getaccountaddress: %T\n", reply) + } + + return btcutil.DecodeAddress(addr, btcwire.MainNet) +} + +// GetAccountAddressAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetAccountAddress for the blocking version and more details. +func (c *Client) GetAccountAddressAsync(account string) FutureGetAccountAddressResult { + id := c.NextID() + cmd, err := btcjson.NewGetAccountAddressCmd(id, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetAccountAddress returns the current Bitcoin address for receiving payments +// to the specified account. +func (c *Client) GetAccountAddress(account string) (btcutil.Address, error) { + return c.GetAccountAddressAsync(account).Receive() +} + +// FutureGetAccountResult is a future promise to deliver the result of a +// GetAccountAsync RPC invocation (or an applicable error). +type FutureGetAccountResult chan *futureResult + +// Receive waits for the response promised by the future and returns the account +// associated with the passed address. +func (r FutureGetAccountResult) Receive() (string, error) { + reply, err := receiveFuture(r) + if err != nil { + return "", err + } + + // Ensure the returned data is the expected type. + account, ok := reply.(string) + if !ok { + return "", fmt.Errorf("unexpected response type for "+ + "getaccount: %T\n", reply) + } + + return account, nil +} + +// GetAccountAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetAccount for the blocking version and more details. +func (c *Client) GetAccountAsync(address btcutil.Address) FutureGetAccountResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewGetAccountCmd(id, addr) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetAccount returns the account associated with the passed address. +func (c *Client) GetAccount(address btcutil.Address) (string, error) { + return c.GetAccountAsync(address).Receive() +} + +// FutureSetAccountResult is a future promise to deliver the result of a +// SetAccountAsync RPC invocation (or an applicable error). +type FutureSetAccountResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of setting the account to be associated with the passed address. +func (r FutureSetAccountResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// SetAccountAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See SetAccount for the blocking version and more details. +func (c *Client) SetAccountAsync(address btcutil.Address, account string) FutureSetAccountResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewSetAccountCmd(id, addr, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SetAccount sets the account associated with the passed address. +func (c *Client) SetAccount(address btcutil.Address, account string) error { + return c.SetAccountAsync(address, account).Receive() +} + +// FutureGetAddressesByAccountResult is a future promise to deliver the result +// of a GetAddressesByAccountAsync RPC invocation (or an applicable error). +type FutureGetAddressesByAccountResult chan *futureResult + +// Receive waits for the response promised by the future and returns the list of +// addresses associated with the passed account. +func (r FutureGetAddressesByAccountResult) Receive() ([]btcutil.Address, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + addrStrings, ok := reply.([]string) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "getaddressesbyaccount: %T\n", reply) + } + + addrs := make([]btcutil.Address, 0, len(addrStrings)) + for _, addrStr := range addrStrings { + addr, err := btcutil.DecodeAddress(addrStr, btcwire.MainNet) + if err != nil { + return nil, err + } + addrs = append(addrs, addr) + } + + return addrs, nil +} + +// GetAddressesByAccountAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetAddressesByAccount for the blocking version and more details. +func (c *Client) GetAddressesByAccountAsync(account string) FutureGetAddressesByAccountResult { + id := c.NextID() + cmd, err := btcjson.NewGetAddressesByAccountCmd(id, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetAddressesByAccount returns the list of addresses associated with the +// passed account. +func (c *Client) GetAddressesByAccount(account string) ([]btcutil.Address, error) { + return c.GetAddressesByAccountAsync(account).Receive() +} + +// FutureValidateAddressResult is a future promise to deliver the result of a +// ValidateAddressAsync RPC invocation (or an applicable error). +type FutureValidateAddressResult chan *futureResult + +// Receive waits for the response promised by the future and returns information +// about the given bitcoin address. +func (r FutureValidateAddressResult) Receive() (*btcjson.ValidateAddressResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + result, ok := reply.(btcjson.ValidateAddressResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "validateaddress: %T\n", reply) + } + + return &result, nil +} + +// ValidateAddressAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See ValidateAddress for the blocking version and more details. +func (c *Client) ValidateAddressAsync(address btcutil.Address) FutureValidateAddressResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewValidateAddressCmd(id, addr) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ValidateAddress returns information about the given bitcoin address. +func (c *Client) ValidateAddress(address btcutil.Address) (*btcjson.ValidateAddressResult, error) { + return c.ValidateAddressAsync(address).Receive() +} + +// FutureKeyPoolRefillResult is a future promise to deliver the result of a +// KeyPoolRefillAsync RPC invocation (or an applicable error). +type FutureKeyPoolRefillResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of refilling the key pool. +func (r FutureKeyPoolRefillResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// KeyPoolRefillAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See KeyPoolRefill for the blocking version and more details. +func (c *Client) KeyPoolRefillAsync() FutureKeyPoolRefillResult { + id := c.NextID() + cmd, err := btcjson.NewKeyPoolRefillCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// KeyPoolRefill fills the key pool as necessary to reach the default size. +// +// See KeyPoolRefillSize to override the size of the key pool. +func (c *Client) KeyPoolRefill() error { + return c.KeyPoolRefillAsync().Receive() +} + +// KeyPoolRefillSizeAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See KeyPoolRefillSize for the blocking version and more details. +func (c *Client) KeyPoolRefillSizeAsync(newSize uint) FutureKeyPoolRefillResult { + id := c.NextID() + cmd, err := btcjson.NewKeyPoolRefillCmd(id, newSize) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// KeyPoolRefillSize fills the key pool as necessary to reach the specified +// size. +func (c *Client) KeyPoolRefillSize(newSize uint) error { + return c.KeyPoolRefillSizeAsync(newSize).Receive() +} + +// ************************ +// Amount/Balance Functions +// ************************ + +// FutureListAccountsResult is a future promise to deliver the result of a +// ListAccountsAsync or ListAccountsMinConfAsync RPC invocation (or an +// applicable error). +type FutureListAccountsResult chan *futureResult + +// Receive waits for the response promised by the future and returns returns a +// map of account names and their associated balances. +func (r FutureListAccountsResult) Receive() (map[string]btcutil.Amount, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Ensure the returned data is the expected type. + accounts, ok := reply.(map[string]float64) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "listaccounts: %T\n", reply) + } + + accountsMap := make(map[string]btcutil.Amount) + for k, v := range accounts { + satoshi, err := btcjson.JSONToAmount(v) + if err != nil { + return nil, err + } + + accountsMap[k] = btcutil.Amount(satoshi) + } + + return accountsMap, nil +} + +// ListAccountsAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See ListAccounts for the blocking version and more details. +func (c *Client) ListAccountsAsync() FutureListAccountsResult { + id := c.NextID() + cmd, err := btcjson.NewListAccountsCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListAccounts returns a map of account names and their associated balances +// using the default number of minimum confirmations. +// +// See ListAccountsMinConf to override the minimum number of confirmations. +func (c *Client) ListAccounts() (map[string]btcutil.Amount, error) { + return c.ListAccountsAsync().Receive() +} + +// ListAccountsMinConfAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListAccountsMinConf for the blocking version and more details. +func (c *Client) ListAccountsMinConfAsync(minConfirms int) FutureListAccountsResult { + id := c.NextID() + cmd, err := btcjson.NewListAccountsCmd(id, minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListAccountsMinConf returns a map of account names and their associated +// balances using the specified number of minimum confirmations. +// +// See ListAccounts to use the default minimum number of confirmations. +func (c *Client) ListAccountsMinConf(minConfirms int) (map[string]btcutil.Amount, error) { + return c.ListAccountsMinConfAsync(minConfirms).Receive() +} + +// FutureGetBalanceResult is a future promise to deliver the result of a +// GetBalanceAsync or GetBalanceMinConfAsync RPC invocation (or an applicable +// error). +type FutureGetBalanceResult chan *futureResult + +// Receive waits for the response promised by the future and returns the +// available balance from the server for the specified account. +func (r FutureGetBalanceResult) Receive() (btcutil.Amount, error) { + reply, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Ensure the returned data is the expected type. + balance, ok := reply.(float64) + if !ok { + return 0, fmt.Errorf("unexpected response type for "+ + "getbalance: %T\n", reply) + } + + satoshi, err := btcjson.JSONToAmount(balance) + if err != nil { + return 0, err + } + + return btcutil.Amount(satoshi), nil +} + +// GetBalanceAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See GetBalance for the blocking version and more details. +func (c *Client) GetBalanceAsync(account string) FutureGetBalanceResult { + // TODO(davec): Remove this hack once btcwallet is fixed. + if account == "*" { + account = "" + } + id := c.NextID() + cmd, err := btcjson.NewGetBalanceCmd(id, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetBalance returns the available balance from the server for the specified +// account using the default number of minimum confirmations. The account may +// be "*" for all accounts. +// +// See GetBalanceMinConf to override the minimum number of confirmations. +func (c *Client) GetBalance(account string) (btcutil.Amount, error) { + return c.GetBalanceAsync(account).Receive() +} + +// GetBalanceMinConfAsync returns an instance of a type that can be used to get +// the result of the RPC at some future time by invoking the Receive function on +// the returned instance. +// +// See GetBalanceMinConf for the blocking version and more details. +func (c *Client) GetBalanceMinConfAsync(account string, minConfirms int) FutureGetBalanceResult { + // TODO(davec): Remove this hack once btcwallet is fixed. + if account == "*" { + account = "" + } + id := c.NextID() + cmd, err := btcjson.NewGetBalanceCmd(id, account, minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetBalanceMinConf returns the available balance from the server for the +// specified account using the specified number of minimum confirmations. The +// account may be "*" for all accounts. +// +// See GetBalance to use the default minimum number of confirmations. +func (c *Client) GetBalanceMinConf(account string, minConfirms int) (btcutil.Amount, error) { + return c.GetBalanceMinConfAsync(account, minConfirms).Receive() +} + +// FutureGetReceivedByAccountResult is a future promise to deliver the result of +// a GetReceivedByAccountAsync or GetReceivedByAccountMinConfAsync RPC +// invocation (or an applicable error). +type FutureGetReceivedByAccountResult chan *futureResult + +// Receive waits for the response promised by the future and returns the total +// amount received with the specified account. +func (r FutureGetReceivedByAccountResult) Receive() (btcutil.Amount, error) { + reply, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Ensure the returned data is the expected type. + balance, ok := reply.(float64) + if !ok { + return 0, fmt.Errorf("unexpected response type for "+ + "getreceivedbyaccount: %T\n", reply) + } + + satoshi, err := btcjson.JSONToAmount(balance) + if err != nil { + return 0, err + } + + return btcutil.Amount(satoshi), nil +} + +// GetReceivedByAccountAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetReceivedByAccount for the blocking version and more details. +func (c *Client) GetReceivedByAccountAsync(account string) FutureGetReceivedByAccountResult { + id := c.NextID() + cmd, err := btcjson.NewGetReceivedByAccountCmd(id, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetReceivedByAccount returns the total amount received with the specified +// account with at least the default number of minimum confirmations. +// +// See GetReceivedByAccountMinConf to override the minimum number of +// confirmations. +func (c *Client) GetReceivedByAccount(account string) (btcutil.Amount, error) { + return c.GetReceivedByAccountAsync(account).Receive() +} + +// GetReceivedByAccountMinConfAsync returns an instance of a type that can be +// used to get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetReceivedByAccountMinConf for the blocking version and more details. +func (c *Client) GetReceivedByAccountMinConfAsync(account string, minConfirms int) FutureGetReceivedByAccountResult { + id := c.NextID() + cmd, err := btcjson.NewGetReceivedByAccountCmd(id, account, minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetReceivedByAccountMinConf returns the total amount received with the +// specified account with at least the specified number of minimum +// confirmations. +// +// See GetReceivedByAccount to use the default minimum number of confirmations. +func (c *Client) GetReceivedByAccountMinConf(account string, minConfirms int) (btcutil.Amount, error) { + return c.GetReceivedByAccountMinConfAsync(account, minConfirms).Receive() +} + +// FutureGetUnconfirmedBalanceResult is a future promise to deliver the result +// of a GetUnconfirmedBalanceAsync RPC invocation (or an applicable error). +type FutureGetUnconfirmedBalanceResult chan *futureResult + +// Receive waits for the response promised by the future and returns returns the +// unconfirmed balance from the server for the specified account. +func (r FutureGetUnconfirmedBalanceResult) Receive() (btcutil.Amount, error) { + reply, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Ensure the returned data is the expected type. + balance, ok := reply.(float64) + if !ok { + return 0, fmt.Errorf("unexpected response type for "+ + "getunconfirmedbalance: %T\n", reply) + } + + satoshi, err := btcjson.JSONToAmount(balance) + if err != nil { + return 0, err + } + + return btcutil.Amount(satoshi), nil +} + +// GetUnconfirmedBalanceAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetUnconfirmedBalance for the blocking version and more details. +func (c *Client) GetUnconfirmedBalanceAsync(account string) FutureGetUnconfirmedBalanceResult { + id := c.NextID() + cmd, err := btcws.NewGetUnconfirmedBalanceCmd(id, account) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetUnconfirmedBalance returns the unconfirmed balance from the server for +// the specified account. +func (c *Client) GetUnconfirmedBalance(account string) (btcutil.Amount, error) { + return c.GetUnconfirmedBalanceAsync(account).Receive() +} + +// FutureGetReceivedByAddressResult is a future promise to deliver the result of +// a GetReceivedByAddressAsync or GetReceivedByAddressMinConfAsync RPC +// invocation (or an applicable error). +type FutureGetReceivedByAddressResult chan *futureResult + +// Receive waits for the response promised by the future and returns the total +// amount received by the specified address. +func (r FutureGetReceivedByAddressResult) Receive() (btcutil.Amount, error) { + reply, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Ensure the returned data is the expected type. + balance, ok := reply.(float64) + if !ok { + return 0, fmt.Errorf("unexpected response type for "+ + "getreceivedbyaddress: %T\n", reply) + } + + satoshi, err := btcjson.JSONToAmount(balance) + if err != nil { + return 0, err + } + + return btcutil.Amount(satoshi), nil +} + +// GetReceivedByAddressAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetReceivedByAddress for the blocking version and more details. +func (c *Client) GetReceivedByAddressAsync(address btcutil.Address) FutureGetReceivedByAddressResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewGetReceivedByAddressCmd(id, addr) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) + +} + +// GetReceivedByAddress returns the total amount received by the specified +// address with at least the default number of minimum confirmations. +// +// See GetReceivedByAddressMinConf to override the minimum number of +// confirmations. +func (c *Client) GetReceivedByAddress(address btcutil.Address) (btcutil.Amount, error) { + return c.GetReceivedByAddressAsync(address).Receive() +} + +// GetReceivedByAddressMinConfAsync returns an instance of a type that can be +// used to get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See GetReceivedByAddressMinConf for the blocking version and more details. +func (c *Client) GetReceivedByAddressMinConfAsync(address btcutil.Address, minConfirms int) FutureGetReceivedByAddressResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewGetReceivedByAddressCmd(id, addr, minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetReceivedByAddressMinConf returns the total amount received by the specified +// address with at least the specified number of minimum confirmations. +// +// See GetReceivedByAddress to use the default minimum number of confirmations. +func (c *Client) GetReceivedByAddressMinConf(address btcutil.Address, minConfirms int) (btcutil.Amount, error) { + return c.GetReceivedByAddressMinConfAsync(address, minConfirms).Receive() +} + +// FutureListReceivedByAccountResult is a future promise to deliver the result +// of a ListReceivedByAccountAsync, ListReceivedByAccountMinConfAsync, or +// ListReceivedByAccountIncludeEmptyAsync RPC invocation (or an applicable +// error). +type FutureListReceivedByAccountResult chan *futureResult + +// Receive waits for the response promised by the future and returns a list of +// balances by account. +func (r FutureListReceivedByAccountResult) Receive() ([]btcjson.ListReceivedByAccountResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // No results. + if reply == nil { + return nil, nil + } + + // Ensure the returned data is the expected type. + result, ok := reply.([]btcjson.ListReceivedByAccountResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "listreceivedbyaccount: %T\n", reply) + } + + return result, nil +} + +// ListReceivedByAccountAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListReceivedByAccount for the blocking version and more details. +func (c *Client) ListReceivedByAccountAsync() FutureListReceivedByAccountResult { + id := c.NextID() + cmd, err := btcjson.NewListReceivedByAccountCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListReceivedByAccount lists balances by account using the default number +// of minimum confirmations and including accounts that haven't received any +// payments. +// +// See ListReceivedByAccountMinConf to override the minimum number of +// confirmations and ListReceivedByAccountIncludeEmpty to filter accounts that +// haven't received any payments from the results. +func (c *Client) ListReceivedByAccount() ([]btcjson.ListReceivedByAccountResult, error) { + return c.ListReceivedByAccountAsync().Receive() +} + +// ListReceivedByAccountMinConfAsync returns an instance of a type that can be +// used to get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See ListReceivedByAccountMinConf for the blocking version and more details. +func (c *Client) ListReceivedByAccountMinConfAsync(minConfirms int) FutureListReceivedByAccountResult { + id := c.NextID() + cmd, err := btcjson.NewListReceivedByAccountCmd(id, minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListReceivedByAccountMinConf lists balances by account using the specified +// number of minimum confirmations and including accounts that haven't received +// any payments. +// +// See ListReceivedByAccount to use the default minimum number of confirmations +// and ListReceivedByAccountIncludeEmpty to also filter accounts that haven't +// received any payments from the results. +func (c *Client) ListReceivedByAccountMinConf(minConfirms int) ([]btcjson.ListReceivedByAccountResult, error) { + return c.ListReceivedByAccountMinConfAsync(minConfirms).Receive() +} + +// ListReceivedByAccountIncludeEmptyAsync returns an instance of a type that can +// be used to get the result of the RPC at some future time by invoking the +// Receive function on the returned instance. +// +// See ListReceivedByAccountIncludeEmpt for the blocking version and more details. +func (c *Client) ListReceivedByAccountIncludeEmptyAsync(minConfirms int, includeEmpty bool) FutureListReceivedByAccountResult { + id := c.NextID() + cmd, err := btcjson.NewListReceivedByAccountCmd(id, minConfirms, includeEmpty) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListReceivedByAccountIncludeEmpty lists balances by account using the +// specified number of minimum confirmations and including accounts that +// haven't received any payments depending on specified flag. +// +// See ListReceivedByAccount and ListReceivedByAccountMinConf to use defaults. +func (c *Client) ListReceivedByAccountIncludeEmpty(minConfirms int, includeEmpty bool) ([]btcjson.ListReceivedByAccountResult, error) { + return c.ListReceivedByAccountIncludeEmptyAsync(minConfirms, + includeEmpty).Receive() +} + +// ************************ +// Wallet Locking Functions +// ************************ + +// FutureWalletLockResult is a future promise to deliver the result of a +// WalletLockAsync RPC invocation (or an applicable error). +type FutureWalletLockResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of locking the wallet. +func (r FutureWalletLockResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// WalletLockAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See WalletLock for the blocking version and more details. +func (c *Client) WalletLockAsync() FutureWalletLockResult { + id := c.NextID() + cmd, err := btcjson.NewWalletLockCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// WalletLock locks the wallet by removing the encryption key from memory. +// +// After calling this function, the WalletPassphrase function must be used to +// unlock the wallet prior to calling any other function which requires the +// wallet to be unlocked. +func (c *Client) WalletLock() error { + return c.WalletLockAsync().Receive() +} + +// WalletPassphrase unlocks the wallet by using the passphrase to derive the +// decryption key which is then stored in memory for the specified timeout +// (in seconds). +func (c *Client) WalletPassphrase(passphrase string, timeoutSecs int64) error { + id := c.NextID() + cmd, err := btcjson.NewWalletPassphraseCmd(id, passphrase, timeoutSecs) + if err != nil { + return err + } + + _, err = c.sendCmdAndWait(cmd) + if err != nil { + return err + } + + return nil +} + +// FutureWalletPassphraseChangeResult is a future promise to deliver the result +// of a WalletPassphraseChangeAsync RPC invocation (or an applicable error). +type FutureWalletPassphraseChangeResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of unlocking the wallet. +func (r FutureWalletPassphraseChangeResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// WalletPassphraseChangeAsync returns an instance of a type that can be used to +// get the result of the RPC at some future time by invoking the Receive +// function on the returned instance. +// +// See WalletPassphraseChange for the blocking version and more details. +func (c *Client) WalletPassphraseChangeAsync(old, new string) FutureWalletPassphraseChangeResult { + id := c.NextID() + cmd, err := btcjson.NewWalletPassphraseChangeCmd(id, old, new) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// WalletPassphraseChange changes the wallet passphrase from the specified old +// to new passphrase. +func (c *Client) WalletPassphraseChange(old, new string) error { + return c.WalletPassphraseChangeAsync(old, new).Receive() +} + +// ************************* +// Message Signing Functions +// ************************* + +// FutureSignMessageResult is a future promise to deliver the result of a +// SignMessageAsync RPC invocation (or an applicable error). +type FutureSignMessageResult chan *futureResult + +// Receive waits for the response promised by the future and returns the message +// signed with the private key of the specified address. +func (r FutureSignMessageResult) Receive() (string, error) { + reply, err := receiveFuture(r) + if err != nil { + return "", err + } + + // Ensure the returned data is the expected type. + b64, ok := reply.(string) + if !ok { + return "", fmt.Errorf("unexpected response type for "+ + "signmessage: %T\n", reply) + } + + return b64, nil +} + +// SignMessageAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See SignMessage for the blocking version and more details. +func (c *Client) SignMessageAsync(address btcutil.Address, message string) FutureSignMessageResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewSignMessageCmd(id, addr, message) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// SignMessage signs a message with the private key of the specified address. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) SignMessage(address btcutil.Address, message string) (string, error) { + return c.SignMessageAsync(address, message).Receive() +} + +// FutureVerifyMessageResult is a future promise to deliver the result of a +// VerifyMessageAsync RPC invocation (or an applicable error). +type FutureVerifyMessageResult chan *futureResult + +// Receive waits for the response promised by the future and returns whether or +// not the message was successfully verified. +func (r FutureVerifyMessageResult) Receive() (bool, error) { + reply, err := receiveFuture(r) + if err != nil { + return false, err + } + + // Ensure the returned data is the expected type. + verified, ok := reply.(bool) + if !ok { + return false, fmt.Errorf("unexpected response type for "+ + "verifymessage: %T\n", reply) + } + + return verified, nil +} + +// VerifyMessageAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See VerifyMessage for the blocking version and more details. +func (c *Client) VerifyMessageAsync(address btcutil.Address, signature, message string) FutureVerifyMessageResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewVerifyMessageCmd(id, addr, signature, message) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// VerifyMessage verifies a signed message. +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) VerifyMessage(address btcutil.Address, signature, message string) (bool, error) { + return c.VerifyMessageAsync(address, signature, message).Receive() +} + +// ********************* +// Dump/Import Functions +// ********************* + +// FutureDumpPrivKeyResult is a future promise to deliver the result of a +// DumpPrivKeyAsync RPC invocation (or an applicable error). +type FutureDumpPrivKeyResult chan *futureResult + +// Receive waits for the response promised by the future and returns the private +// key corresponding to the passed address encoded in the wallet import format +// (WIF) +func (r FutureDumpPrivKeyResult) Receive() (string, error) { + reply, err := receiveFuture(r) + if err != nil { + return "", err + } + + // Ensure the returned data is the expected type. + privKeyWIF, ok := reply.(string) + if !ok { + return "", fmt.Errorf("unexpected response type for "+ + "dumpprivkey: %T\n", reply) + } + + return privKeyWIF, nil +} + +// DumpPrivKeyAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See DumpPrivKey for the blocking version and more details. +func (c *Client) DumpPrivKeyAsync(address btcutil.Address) FutureDumpPrivKeyResult { + id := c.NextID() + addr := address.EncodeAddress() + cmd, err := btcjson.NewDumpPrivKeyCmd(id, addr) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// DumpPrivKey gets the private key corresponding to the passed address encoded +// in the wallet import format (WIF). +// +// NOTE: This function requires to the wallet to be unlocked. See the +// WalletPassphrase function for more details. +func (c *Client) DumpPrivKey(address btcutil.Address) (string, error) { + return c.DumpPrivKeyAsync(address).Receive() +} + +// FutureImportPrivKeyResult is a future promise to deliver the result of an +// ImportPrivKeyAsync RPC invocation (or an applicable error). +type FutureImportPrivKeyResult chan *futureResult + +// Receive waits for the response promised by the future and returns the result +// of importing the passed private key which must be the wallet import format +// (WIF). +func (r FutureImportPrivKeyResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// ImportPrivKeyAsync returns an instance of a type that can be used to get the +// result of the RPC at some future time by invoking the Receive function on the +// returned instance. +// +// See ImportPrivKey for the blocking version and more details. +func (c *Client) ImportPrivKeyAsync(privKeyWIF string) FutureImportPrivKeyResult { + id := c.NextID() + cmd, err := btcjson.NewImportPrivKeyCmd(id, privKeyWIF) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ImportPrivKey imports the passed private key which must be the wallet import +// format (WIF). +func (c *Client) ImportPrivKey(privKeyWIF string) error { + return c.ImportPrivKeyAsync(privKeyWIF).Receive() +} + +// TODO(davec): Implement +// backupwallet (NYI in btcwallet) +// encryptwallet (Won't be supported by btcwallet since it's always encrypted) +// getwalletinfo (NYI in btcwallet or btcjson) +// listaddressgroupings (NYI in btcwallet) +// listlockunspent (NYI in btcwallet) +// listreceivedbyaddress (NYI in btcwallet) +// listreceivedbyaccount (NYI in btcwallet) +// lockunspent (NYI in btcwallet) +// move (NYI in btcwallet) + +// DUMP +// importwallet (NYI in btcwallet) +// dumpwallet (NYI in btcwallet) From cf409a8d794daa490b79f5a63c534e6616c9a412 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 1 May 2014 23:12:02 -0500 Subject: [PATCH 002/153] Disconnect client on shutdown. --- infrastructure.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/infrastructure.go b/infrastructure.go index fa51e7bc..6449a64d 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -714,6 +714,8 @@ func (c *Client) Shutdown() { } c.requestLock.Unlock() c.removeAllRequests() + + c.Disconnect() } // Start begins processing input and output messages. From 43ca7c0f3c0e376642bc93b325db9c1450f0d7c4 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 1 May 2014 23:16:31 -0500 Subject: [PATCH 003/153] Add a couple of examples. --- examples/bitcoincorehttp/main.go | 35 +++++++++++++++++ examples/btcdwebsockets/main.go | 66 ++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 examples/bitcoincorehttp/main.go create mode 100644 examples/btcdwebsockets/main.go diff --git a/examples/bitcoincorehttp/main.go b/examples/bitcoincorehttp/main.go new file mode 100644 index 00000000..605bf844 --- /dev/null +++ b/examples/bitcoincorehttp/main.go @@ -0,0 +1,35 @@ +// 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 main + +import ( + "github.com/conformal/btcrpcclient" + "log" +) + +func main() { + // Connect to local bitcoin core RPC server using HTTP POST mode. + connCfg := &btcrpcclient.ConnConfig{ + Host: "localhost:18332", + User: "yourrpcuser", + Pass: "yourrpcpass", + HttpPostMode: true, // Bitcoin core only supports HTTP POST mode + DisableTLS: true, // Bitcoin core does not provide TLS by default + } + // Notice the notification parameter is nil since notifications are + // not supported in HTTP POST mode. + client, err := btcrpcclient.New(connCfg, nil) + if err != nil { + log.Fatal(err) + } + defer client.Shutdown() + + // Get the current block count. + blockCount, err := client.GetBlockCount() + if err != nil { + log.Fatal(err) + } + log.Printf("Block count: %d", blockCount) +} diff --git a/examples/btcdwebsockets/main.go b/examples/btcdwebsockets/main.go new file mode 100644 index 00000000..69b1092d --- /dev/null +++ b/examples/btcdwebsockets/main.go @@ -0,0 +1,66 @@ +// 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 main + +import ( + "github.com/conformal/btcutil" + "github.com/conformal/btcwire" + "github.com/conformal/btcrpcclient" + "io/ioutil" + "log" + "path/filepath" + "time" +) + +func main() { + // Only override the handlers for notifications you care about. + ntfnHandlers := btcrpcclient.NotificationHandlers{ + OnBlockConnected: func(hash *btcwire.ShaHash, height int32) { + log.Printf("Block connected: %v (%d)", hash, height) + }, + OnBlockDisconnected: func(hash *btcwire.ShaHash, height int32) { + log.Printf("Block disconnected: %v", hash, height) + }, + } + + // Connect to local btcd RPC server using websockets. + btcdHomeDir := btcutil.AppDataDir("btcd", false) + certs, err := ioutil.ReadFile(filepath.Join(btcdHomeDir, "rpc.cert")) + if err != nil { + log.Fatal(err) + } + connCfg := &btcrpcclient.ConnConfig{ + Host: "localhost:8334", + Endpoint: "ws", + User: "yourrpcuser", + Pass: "yourrpcpass", + Certificates: certs, + } + client, err := btcrpcclient.New(connCfg, &ntfnHandlers) + if err != nil { + log.Fatal(err) + } + + // Get the current block count. + blockCount, err := client.GetBlockCount() + if err != nil { + log.Fatal(err) + } + log.Printf("Block count: %d", blockCount) + + // For this example gracefully shutdown the client after 10 seconds. + // Ordinarily when to shutdown the client is highly application + // specific. + log.Println("Client shutdown in 10 seconds...") + time.AfterFunc(time.Second*10, func() { + log.Println("Client shutting down...") + client.Shutdown() + log.Println("Client shutting complete.") + }) + + // Wait until the client either shuts down gracefully (or the user + // terminates the process with Ctrl+C). + client.WaitForShutdown() +} From fa7f670160774ad3528ee2c1baf3b2b886aa7016 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 1 May 2014 23:18:43 -0500 Subject: [PATCH 004/153] Add link to examples in README.md. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index ba5d56f8..93e304cb 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ This package is currently under development. You really probably don't want to use this yet! +## Examples + +See the [examples](https://github.com/conformal/btcrpcclient/blob/master/examples). + ## License Package btcrpcclient is licensed under the [copyfree](http://copyfree.org) ISC From 831d0aeb90f271a315fef5c00f6f514e5a08c475 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sat, 3 May 2014 01:46:20 -0500 Subject: [PATCH 005/153] Add support for getwork submit. --- mining.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/mining.go b/mining.go index c138f31a..efd73424 100644 --- a/mining.go +++ b/mining.go @@ -289,7 +289,7 @@ func (r FutureGetWork) Receive() (*btcjson.GetWorkResult, error) { result, ok := reply.(btcjson.GetWorkResult) if !ok { return nil, fmt.Errorf("unexpected response type for "+ - "getwork: %T\n", reply) + "getwork (request data): %T\n", reply) } return &result, nil @@ -310,12 +310,58 @@ func (c *Client) GetWorkAsync() FutureGetWork { return c.sendCmd(cmd) } -// TODO(davec): Correct this // GetWork returns hash data to work on. +// +// See GetWorkSubmit to submit the found solution. func (c *Client) GetWork() (*btcjson.GetWorkResult, error) { return c.GetWorkAsync().Receive() } +// FutureGetWorkSubmit is a future promise to deliver the result of a +// GetWorkSubmitAsync RPC invocation (or an applicable error). +type FutureGetWorkSubmit chan *futureResult + +// Receive waits for the response promised by the future and returns whether +// or not the submitted block header was accepted. +func (r FutureGetWorkSubmit) Receive() (bool, error) { + reply, err := receiveFuture(r) + if err != nil { + return false, err + } + + // Ensure the returned data is the expected type. + accepted, ok := reply.(bool) + if !ok { + return false, fmt.Errorf("unexpected response type for "+ + "getwork (submit data): %T\n", reply) + } + + return accepted, nil +} + +// GetWorkSubmitAsync 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 GetWorkSubmit for the blocking version and more details. +func (c *Client) GetWorkSubmitAsync(data string) FutureGetWorkSubmit { + id := c.NextID() + cmd, err := btcjson.NewGetWorkCmd(id, data) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetWorkSubmit submits a block header which is a solution to previously +// requested data and returns whether or not the solution was accepted. +// +// See GetWork to request data to work on. +func (c *Client) GetWorkSubmit(data string) (bool, error) { + return c.GetWorkSubmitAsync(data).Receive() +} + // FutureSubmitBlockResult is a future promise to deliver the result of a // SubmitBlockAsync RPC invocation (or an applicable error). type FutureSubmitBlockResult chan *futureResult From 0cc22e11345c8d15deef41921d3cc96792bf4a66 Mon Sep 17 00:00:00 2001 From: GeertJohan Date: Mon, 5 May 2014 21:23:18 +0200 Subject: [PATCH 006/153] Make btcrpcclient use the new and consistent reply types. --- chain.go | 4 ++-- mining.go | 8 ++++---- net.go | 4 ++-- rawtransactions.go | 2 +- wallet.go | 16 ++++++++-------- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/chain.go b/chain.go index edb2362c..9e580824 100644 --- a/chain.go +++ b/chain.go @@ -126,13 +126,13 @@ func (r FutureGetBlockVerboseResult) Receive() (*btcjson.BlockResult, error) { } // Ensure the returned data is the expected type. - blockResult, ok := reply.(btcjson.BlockResult) + blockResult, ok := reply.(*btcjson.BlockResult) if !ok { return nil, fmt.Errorf("unexpected response type for "+ "getblock (verbose=1): %T\n", reply) } - return &blockResult, nil + return blockResult, nil } // GetBlockVerboseAsync returns an instance of a type that can be used to get diff --git a/mining.go b/mining.go index efd73424..924c3caf 100644 --- a/mining.go +++ b/mining.go @@ -146,13 +146,13 @@ func (r FutureGetMiningInfoResult) Receive() (*btcjson.GetMiningInfoResult, erro } // Ensure the returned data is the expected type. - result, ok := reply.(btcjson.GetMiningInfoResult) + result, ok := reply.(*btcjson.GetMiningInfoResult) if !ok { return nil, fmt.Errorf("unexpected response type for "+ "getmininginfo: %T\n", reply) } - return &result, nil + return result, nil } // GetMiningInfoAsync returns an instance of a type that can be used to get @@ -286,13 +286,13 @@ func (r FutureGetWork) Receive() (*btcjson.GetWorkResult, error) { } // Ensure the returned data is the expected type. - result, ok := reply.(btcjson.GetWorkResult) + result, ok := reply.(*btcjson.GetWorkResult) if !ok { return nil, fmt.Errorf("unexpected response type for "+ "getwork (request data): %T\n", reply) } - return &result, nil + return result, nil } // GetWorkAsync returns an instance of a type that can be used to get the result diff --git a/net.go b/net.go index 072fa094..5cf6f4d4 100644 --- a/net.go +++ b/net.go @@ -297,13 +297,13 @@ func (r FutureGetNetTotalsResult) Receive() (*btcjson.GetNetTotalsResult, error) } // Ensure the returned data is the expected type. - totals, ok := reply.(btcjson.GetNetTotalsResult) + totals, ok := reply.(*btcjson.GetNetTotalsResult) if !ok { return nil, fmt.Errorf("unexpected response type for "+ "getnettotals: %T\n", reply) } - return &totals, nil + return totals, nil } // GetNetTotalsAsync returns an instance of a type that can be used to get the diff --git a/rawtransactions.go b/rawtransactions.go index c12783e6..53a29c04 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -322,7 +322,7 @@ func (r FutureSignRawTransactionResult) Receive() (*btcwire.MsgTx, bool, error) } // Ensure the returned data is the expected type. - result, ok := reply.(btcjson.SignRawTransactionResult) + result, ok := reply.(*btcjson.SignRawTransactionResult) if !ok { return nil, false, fmt.Errorf("unexpected response type for "+ "signrawtransaction: %T\n", reply) diff --git a/wallet.go b/wallet.go index d599e0e8..1b35e10f 100644 --- a/wallet.go +++ b/wallet.go @@ -29,13 +29,13 @@ func (r FutureGetTransactionResult) Receive() (*btcjson.GetTransactionResult, er } // Ensure the returned data is the expected type. - result, ok := reply.(btcjson.GetTransactionResult) + result, ok := reply.(*btcjson.GetTransactionResult) if !ok { return nil, fmt.Errorf("unexpected response type for "+ "gettransaction: %T\n", reply) } - return &result, nil + return result, nil } // GetTransactionAsync returns an instance of a type that can be used to get the @@ -173,13 +173,13 @@ func (r FutureListSinceBlockResult) Receive() (*btcjson.ListSinceBlockResult, er } // Ensure the returned data is the expected type. - listResult, ok := reply.(btcjson.ListSinceBlockResult) + listResult, ok := reply.(*btcjson.ListSinceBlockResult) if !ok { return nil, fmt.Errorf("unexpected response type for "+ "listsinceblock: %T\n", reply) } - return &listResult, nil + return listResult, nil } // ListSinceBlockAsync returns an instance of a type that can be used to get @@ -690,13 +690,13 @@ func (r FutureCreateMultisigResult) Receive() (*btcjson.CreateMultiSigResult, er } // Ensure the returned data is the expected type. - result, ok := reply.(btcjson.CreateMultiSigResult) + result, ok := reply.(*btcjson.CreateMultiSigResult) if !ok { return nil, fmt.Errorf("unexpected response type for "+ "createmultisig: %T\n", reply) } - return &result, nil + return result, nil } // CreateMultisigAsync returns an instance of a type that can be used to get @@ -1001,13 +1001,13 @@ func (r FutureValidateAddressResult) Receive() (*btcjson.ValidateAddressResult, } // Ensure the returned data is the expected type. - result, ok := reply.(btcjson.ValidateAddressResult) + result, ok := reply.(*btcjson.ValidateAddressResult) if !ok { return nil, fmt.Errorf("unexpected response type for "+ "validateaddress: %T\n", reply) } - return &result, nil + return result, nil } // ValidateAddressAsync returns an instance of a type that can be used to get From 2f7cb64652dc30995a737fe81328572afedb191d Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Mon, 5 May 2014 18:30:26 -0500 Subject: [PATCH 007/153] Add CONTRIBUTORS file. --- CONTRIBUTORS | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 CONTRIBUTORS diff --git a/CONTRIBUTORS b/CONTRIBUTORS new file mode 100644 index 00000000..59e93efe --- /dev/null +++ b/CONTRIBUTORS @@ -0,0 +1,10 @@ +# This is the list of people who have contributed code to the repository. +# +# Names should be added to this file only after verifying that the individual +# or the individual's organization has agreed to the LICENSE. +# +# Names should be added to this file like so: +# Name + +Dave Collins +Geert-Johan Riemer From 1122a8a9cb65419c59c64728fa56384019616943 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Mon, 5 May 2014 19:45:11 -0500 Subject: [PATCH 008/153] Remove leftover testing sleep. --- infrastructure.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index 6449a64d..558afd55 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -337,7 +337,8 @@ func (c *Client) sendMessage(marshalledJSON []byte) { } // resendCmds resends any commands that had not completed when the client -// disconnected. It is intended to be called once the client has reconnected. +// disconnected. It is intended to be called once the client has reconnected +// as a separate goroutine. 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 @@ -359,7 +360,6 @@ func (c *Client) resendCmds() { } c.marshalAndSend(req.cmd, req.responseChan) - time.Sleep(time.Second * 2) } } From 7c552136bce16b8ba3c5d103b80b8458180542a8 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Mon, 5 May 2014 19:51:33 -0500 Subject: [PATCH 009/153] Move Disconnected next to Disconnect func. --- infrastructure.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index 558afd55..dbce486b 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -526,14 +526,6 @@ func (c *Client) sendPostRequest(req *http.Request, command btcjson.Cmd, respons } } -// 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. @@ -649,6 +641,14 @@ func (c *Client) sendCmdAndWait(cmd btcjson.Cmd) (interface{}, error) { return receiveFuture(c.sendCmd(cmd)) } +// Disconnected returns whether or not the server is disconnected. +func (c *Client) Disconnected() bool { + c.mtx.Lock() + defer c.mtx.Unlock() + + return c.disconnected +} + // Disconnect disconnects the current websocket associated with the client. The // connection will automatically be re-established // From 01183c4eca0e775df83c1ce587d3824ba7a444c9 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Tue, 6 May 2014 12:26:38 -0500 Subject: [PATCH 010/153] Update for recent notifyspent changes. Closes #2. --- notify.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/notify.go b/notify.go index 0da70e93..a3fe3b6f 100644 --- a/notify.go +++ b/notify.go @@ -378,7 +378,7 @@ func (r FutureNotifySpentResult) Receive() error { // 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 { +func (c *Client) NotifySpentAsync(outpoints []*btcwire.OutPoint) FutureNotifySpentResult { // Not supported in HTTP POST mode. if c.config.HttpPostMode { return newFutureError(ErrNotificationsNotSupported) @@ -391,13 +391,17 @@ func (c *Client) NotifySpentAsync(outpoint *btcwire.OutPoint) FutureNotifySpentR } id := c.NextID() - cmd := btcws.NewNotifySpentCmd(id, btcws.NewOutPointFromWire(outpoint)) + ops := make([]btcws.OutPoint, 0, len(outpoints)) + for _, outpoint := range outpoints { + ops = append(ops, *btcws.NewOutPointFromWire(outpoint)) + } + cmd := btcws.NewNotifySpentCmd(id, ops) return c.sendCmd(cmd) } // NotifySpent registers the client to receive notifications when the passed -// transaction output is spent. The notifications are delivered to the +// transaction outputs are 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. @@ -406,8 +410,8 @@ func (c *Client) NotifySpentAsync(outpoint *btcwire.OutPoint) FutureNotifySpentR // 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() +func (c *Client) NotifySpent(outpoints []*btcwire.OutPoint) error { + return c.NotifySpentAsync(outpoints).Receive() } // FutureNotifyNewTransactionsResult is a future promise to deliver the result From 6825e68c59d4e6410f36293bdff993dbebc03322 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 8 May 2014 13:04:05 -0500 Subject: [PATCH 011/153] Use uint64 for the request ID instead of int64. There is no reason for the request ID to be signed. Pointed out by @jrick. --- infrastructure.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index dbce486b..3bf3bfd2 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -94,7 +94,7 @@ type jsonRequest struct { // 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 + id uint64 // atomic, so must stay 64-bit aligned // config holds the connection configuration assoiated with this client. config *ConnConfig @@ -119,7 +119,7 @@ type Client struct { // Track command and their response channels by ID. requestLock sync.Mutex - requestMap map[int64]*list.Element + requestMap map[uint64]*list.Element requestList *list.List // Notification handlers. @@ -139,8 +139,8 @@ type Client struct { // 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) +func (c *Client) NextID() uint64 { + return atomic.AddUint64(&c.id, 1) } // addRequest associates the passed jsonRequest with the passed id. This allows @@ -148,7 +148,7 @@ func (c *Client) NextID() int64 { // 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) { +func (c *Client) addRequest(id uint64, request *jsonRequest) { c.requestLock.Lock() defer c.requestLock.Unlock() @@ -162,7 +162,7 @@ func (c *Client) addRequest(id int64, request *jsonRequest) { // no association. // // This function is safe for concurrent access. -func (c *Client) removeRequest(id int64) *jsonRequest { +func (c *Client) removeRequest(id uint64) *jsonRequest { c.requestLock.Lock() defer c.requestLock.Unlock() @@ -184,7 +184,7 @@ func (c *Client) removeAllRequests() { c.requestLock.Lock() defer c.requestLock.Unlock() - c.requestMap = make(map[int64]*list.Element) + c.requestMap = make(map[uint64]*list.Element) c.requestList.Init() } @@ -235,7 +235,7 @@ func (c *Client) handleMessage(msg string) { *r.Id, *r.Id) return } - id := int64(fid) + id := uint64(fid) log.Tracef("Received response for id %d (result %v)", id, r.Result) request := c.removeRequest(id) @@ -624,7 +624,7 @@ func (c *Client) sendCmd(cmd btcjson.Cmd) chan *futureResult { return responseChan } - c.addRequest(cmd.Id().(int64), &jsonRequest{ + c.addRequest(cmd.Id().(uint64), &jsonRequest{ cmd: cmd, responseChan: responseChan, }) @@ -920,7 +920,7 @@ func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error config: config, wsConn: wsConn, httpClient: httpClient, - requestMap: make(map[int64]*list.Element), + requestMap: make(map[uint64]*list.Element), requestList: list.New(), ntfnHandlers: ntfnHandlers, sendChan: make(chan []byte, sendBufferSize), From 2fc983ece14fb5eb0485ee14c0e85af902a9af58 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 8 May 2014 13:10:09 -0500 Subject: [PATCH 012/153] Finish the comment on the Disconnect function. --- infrastructure.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/infrastructure.go b/infrastructure.go index 3bf3bfd2..db404286 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -650,7 +650,8 @@ func (c *Client) Disconnected() bool { } // Disconnect disconnects the current websocket associated with the client. The -// connection will automatically be re-established +// connection will automatically be re-established unless the client was +// created with the DisableAutoReconnect flag. // // This function has no effect when the client is running in HTTP POST mode. func (c *Client) Disconnect() { From 80f9a8e5e22713845a176838a6b85282d4d8d57d Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 8 May 2014 13:12:56 -0500 Subject: [PATCH 013/153] Fix FutureWalletPassphraseChange.Receive comment. --- wallet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet.go b/wallet.go index 1b35e10f..5d6150ca 100644 --- a/wallet.go +++ b/wallet.go @@ -1630,7 +1630,7 @@ func (c *Client) WalletPassphrase(passphrase string, timeoutSecs int64) error { type FutureWalletPassphraseChangeResult chan *futureResult // Receive waits for the response promised by the future and returns the result -// of unlocking the wallet. +// of changing the wallet passphrase. func (r FutureWalletPassphraseChangeResult) Receive() error { _, err := receiveFuture(r) if err != nil { From eb82f35aacc8708b15844d7f81edc15db0a66f23 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 9 May 2014 00:44:19 -0500 Subject: [PATCH 014/153] Improve btcdwebsockets example. Since the example illustrates callbacks for the OnBlockConnected and OnBlockDisconnected callbacks, also register for the notifications with NotifyBlocks. While here, document the fact that most of the callbacks require registration. --- examples/btcdwebsockets/main.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/btcdwebsockets/main.go b/examples/btcdwebsockets/main.go index 69b1092d..4d3efa08 100644 --- a/examples/btcdwebsockets/main.go +++ b/examples/btcdwebsockets/main.go @@ -5,9 +5,9 @@ package main import ( + "github.com/conformal/btcrpcclient" "github.com/conformal/btcutil" "github.com/conformal/btcwire" - "github.com/conformal/btcrpcclient" "io/ioutil" "log" "path/filepath" @@ -16,6 +16,9 @@ import ( func main() { // Only override the handlers for notifications you care about. + // Also note most of these handlers will only be called if you register + // for notifications. See the documentation of the btcrpcclient + // NotificationHandlers type for more details about each handler. ntfnHandlers := btcrpcclient.NotificationHandlers{ OnBlockConnected: func(hash *btcwire.ShaHash, height int32) { log.Printf("Block connected: %v (%d)", hash, height) @@ -43,6 +46,12 @@ func main() { log.Fatal(err) } + // Register for block connect and disconnect notifications. + if err := client.NotifyBlocks(); err != nil { + log.Fatal(err) + } + log.Println("NotifyBlocks: Registration Complete") + // Get the current block count. blockCount, err := client.GetBlockCount() if err != nil { From 73ecb412e9db87b4678ffa194ca12842ee391232 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 9 May 2014 01:16:47 -0500 Subject: [PATCH 015/153] Add support for TravisCI. Also add TravisCI build status badge to README.md. --- .travis.yml | 3 +++ README.md | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..ae71c02f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: go +go: release +install: go get -d -t -v ./... diff --git a/README.md b/README.md index 93e304cb..12bd020f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ btcrpcclient ============ +[![Build Status](https://travis-ci.org/conformal/btcrpcclient.png?branch=master)] +(https://travis-ci.org/conformal/btcrpcclient) + This package is currently under development. You really probably don't want to use this yet! From 2f8418967634ec324e9307a53e35a2a85a138068 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 9 May 2014 01:26:37 -0500 Subject: [PATCH 016/153] Add godoc reference badge to README.md. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 12bd020f..385c2b30 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ btcrpcclient [![Build Status](https://travis-ci.org/conformal/btcrpcclient.png?branch=master)] (https://travis-ci.org/conformal/btcrpcclient) +[![GoDoc](https://godoc.org/github.com/conformal/btcrpcclient?status.png)] +(http://godoc.org/github.com/conformal/btcrpcclient) This package is currently under development. From 4921282646ea6089917aad0973da11569b7429eb Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 9 May 2014 01:35:27 -0500 Subject: [PATCH 017/153] Add example descriptions to README.md. --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 385c2b30..5544131d 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,16 @@ You really probably don't want to use this yet! ## Examples -See the [examples](https://github.com/conformal/btcrpcclient/blob/master/examples). +The following client examples are available in the [examples](https://github.com/conformal/btcrpcclient/blob/master/examples) +directory: + +- [bitcoincorehttp](https://github.com/conformal/btcrpcclient/blob/master/examples/bitcoincorehttp) + Connects to a bitcoin core RPC server using HTTP POST mode with TLS disabled + and gets the current block count +- [btcdwebsockets](https://github.com/conformal/btcrpcclient/blob/master/examples/btcdwebsockets) + Connects to a btcd RPC server using TLS-secured websockets, + registers for block connected and block disconnected notifications, and gets + the current block count ## License From 8700eeaeb6d95554be8b4edb9d8bb0a514b4ca36 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 9 May 2014 19:26:56 -0500 Subject: [PATCH 018/153] Convert to use gorilla websockets package. Also, since the new package exposes more connection related error information, add a new ErrInvalidEndpoint which is returned if the specified enpoint does not appear to be a valid websocket provider and only return the ErrInvalidAuth error when HTTP authorization failure status codes are detected. Closes #1. --- infrastructure.go | 96 ++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 46 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index db404286..af0af9d8 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -6,7 +6,6 @@ package btcrpcclient import ( "bytes" - "code.google.com/p/go.net/websocket" "container/list" "crypto/tls" "crypto/x509" @@ -16,6 +15,7 @@ import ( "fmt" "github.com/conformal/btcjson" "github.com/conformal/go-socks" + "github.com/gorilla/websocket" "net" "net/http" "net/url" @@ -30,6 +30,11 @@ var ( // incorrect. ErrInvalidAuth = errors.New("authentication failure") + // ErrInvalidEndpoint is an error to describe the condition where the + // websocket handshake failed with the specified endpoint. + ErrInvalidEndpoint = errors.New("the endpoint either does not support " + + "websockets or does not exist") + // 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 @@ -193,9 +198,9 @@ func (c *Client) removeAllRequests() { // (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) { +func (c *Client) handleMessage(msg []byte) { // Attempt to unmarshal the message as a known JSON-RPC command. - if cmd, err := btcjson.ParseMarshaledCmd([]byte(msg)); err == nil { + if cmd, err := btcjson.ParseMarshaledCmd(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. @@ -271,8 +276,8 @@ out: default: } - var msg string - if err := websocket.Message.Receive(c.wsConn, &msg); err != nil { + _, msg, err := c.wsConn.ReadMessage() + if err != nil { // Log the error if it's not due to disconnecting. if _, ok := err.(*net.OpError); !ok { log.Errorf("Websocket receive error from "+ @@ -299,7 +304,7 @@ out: // disconnected closed. select { case msg := <-c.sendChan: - err := websocket.Message.Send(c.wsConn, string(msg)) + err := c.wsConn.WriteMessage(websocket.TextMessage, msg) if err != nil { c.Disconnect() break out @@ -832,63 +837,62 @@ func newHttpClient(config *ConnConfig) (*http.Client, error) { // 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 + // Setup TLS if not disabled. + var tlsConfig *tls.Config + var scheme = "ws" + if !config.DisableTLS { + pool := x509.NewCertPool() + pool.AppendCertsFromPEM(config.Certificates) + tlsConfig = &tls.Config{ + RootCAs: pool, + MinVersion: tls.VersionTLS12, + } + scheme = "wss" } - pool := x509.NewCertPool() - pool.AppendCertsFromPEM(config.Certificates) - wsConfig.TlsConfig = &tls.Config{ - RootCAs: pool, - MinVersion: tls.VersionTLS12, - } + // Create a websocket dialer that will be used to make the connection. + // It is modified by the proxy setting below as needed. + dialer := websocket.Dialer{TLSClientConfig: tlsConfig} - // 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. + // Setup the 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 + dialer.NetDial = proxy.Dial } - // No proxy was specified, so attempt to connect to running wallet - // instance directly. - ws, err := websocket.DialConfig(wsConfig) + // The RPC server requires basic authorization, so create a custom + // request header with the Authorization header set. + login := config.User + ":" + config.Pass + auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login)) + requestHeader := make(http.Header) + requestHeader.Add("Authorization", auth) + + // Dial the connection. + url := fmt.Sprintf("%s://%s/%s", scheme, config.Host, config.Endpoint) + wsConn, resp, err := dialer.Dial(url, requestHeader) 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 { + if err == websocket.ErrBadHandshake { + // Detect HTTP authentication error status codes. + if resp != nil && + (resp.StatusCode == http.StatusUnauthorized || + resp.StatusCode == http.StatusForbidden) { + return nil, ErrInvalidAuth } + + // The connection was authenticated, but the websocket + // handshake still failed, so the endpoint is invalid + // in some way. + return nil, ErrInvalidEndpoint } + return nil, err } - return ws, nil + return wsConn, nil } // New create a new RPC client based on the provided connection configuration From f54e05c8fea3802d51babad534004d526be8fc7c Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 9 May 2014 20:09:46 -0500 Subject: [PATCH 019/153] Initial package overview documentation. --- doc.go | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 3 deletions(-) diff --git a/doc.go b/doc.go index 85c32a0c..9f3821d6 100644 --- a/doc.go +++ b/doc.go @@ -2,7 +2,174 @@ // 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 implements a websocket-enabled Bitcoin JSON-RPC client. + +Overview + +This client is intended to provide a robust and easy to use client for +interfacing with a Bitcoin RPC server that uses a btcd/bitcoin core compatible +Bitcoin JSON-RPC API. This client has been tested with +btcd (https://github.com/conformal/btcd), +btcwallet (https://github.com/conformal/btcwallet), and +bitcoin core (https://github.com/bitcoin). + +In addition to the compatible standard HTTP POST JSON-RPC API, btcd and +btcwallet provide a websocket interface that is more efficient than the standard +HTTP POST method of accessing RPC. The section below discusses the differences +between HTTP POST and websockets. + +By default, this client assumes the RPC server supports websockets and has +TLS enabled. In practice, this currently means it assumes you are talking to +btcd or btcwallet by default. However, configuration options are provided to +fall back to HTTP POST and disable TLS to support talking with inferior bitcoin +core style RPC servers. + +Websockets vs HTTP POST + +In HTTP POST-based JSON-RPC, every request creates a new HTTP connection, +issues the call, waits for the response, and closes the connection. This adds +quite a bit of overhead to every call and lacks flexibility for features such as +notifications. + +In contrast, the websocket-based JSON-RPC interface provided by btcd and +btcwallet only uses a single connection that remains open and allows +asynchronous bi-directional communication. + +The websocket interface supports all of the same commands as HTTP POST, but they +can be invoked without having to go through a connect/disconnect cycle for every +call. In addition, the websocket interface provides other nice features such as +the ability to register for asynchronous notifications of various events. + +Synchronous vs Asynchronous API + +The client provides both a synchronous (blocking) and asynchronous API. + +The synchronous (blocking) API is typically sufficient for most use cases. It +works by issuing the RPC and blocking until the response is received. This +allows straightforward code where you have the response as soon as the function +returns. + +The asynchronous API works on the concept of futures. When you invoke the async +version of a command, it will quickly return an instance of a type that promises +to provide the result of the RPC at some future time. In the background, the +RPC call is issued and the result is stored in the returned instance. Invoking +the Receive method on the returned instance will either return the result +immediately if it has already arrived, or block until it has. This is useful +since it provides the caller with greater control over concurrency. + +Notifications + +The first important part of notifications is to realize that they will only +work when connected via websockets. This should intuitively make sense +because HTTP POST mode does not keep a connection open! + +All notifications provided by btcd require registration to opt-in. For example, +if you want to be notified when funds are received by a set of addresses, you +register the addresses via the NotifyReceived (or NotifyReceivedAsync) function. + +Notification Handlers + +Notifications are exposed by the client through the use of callback handlers +which are setup via a NotificationHandlers instance that is specified by the +caller when creating the client. + +It is important that these notification handlers complete quickly since they +are intentionally in the main read loop and will block further reads until +they complete. This provides the caller with the flexibility to decide what to +do when notifications are coming in faster than they are being handled. + +In particular this means issuing a blocking RPC call from a callback handler +will cause a deadlock as more server responses won't be read until the callback +returns, but the callback would be waiting for a response. Thus, any +additional RPCs must be issued an a completely decoupled manner. + +Automatic Reconnection + +By default, when running in websockets mode, this client will automatically +keep trying to reconnect to the RPC server should the connection be lost. There +is a back-off in between each connection attempt until it reaches one try per +minute. Once a connection is re-established, all previously registered +notifications are automatically re-registered and any in-flight commands are +re-issued. This means from the caller's perspective, the request simply takes +longer to complete. + +The caller may invoke the Shutdown method on the client to force the client +to cease reconnect attempts and return ErrClientShutdown for all outstanding +commands. + +The automatic reconnection can be disabled by setting the DisableAutoReconnect +flag to true in the connection config when creating the client. + +Minor RPC Server Differences and Chain/Wallet Separation + +Some of the commands are extensions specific to a particular RPC server. For +example, the DebugLevel call is an extension only provided by btcd (and +btcwallet passthrough). Therefore if you call one of these commands against +an RPC server that doesn't provide them, you will get an unimplemented error +from the server. An effort has been made to call out which commmands are +extensions in their documentation. + +Also, it is important to realize that btcd intentionally separates the wallet +functionality into a separate process named btcwallet. This means if you are +connected to the btcd RPC server directly, only the RPCs which are related to +chain services will be available. Depending on your application, you might only +need chain-related RPCs. In contrast, btcwallet provides pass through treatment +for chain-related RPCs, so it supports them in addition to wallet-related RPCs. + +Errors + +There are 3 categories of errors that will be returned throughout this package: + + - Errors related to the client connection such as authentication, endpoint, + disconnect, and shutdown + - Errors that occur before communicating with the remote RPC server such as + command creation and marshaling errors or issues talking to the remote + server + - Errors returned from the remote RPC server like unimplemented commands, + nonexistent requested blocks and transactions, malformed data, and incorrect + networks + +The first category of errors are typically one of ErrInvalidAuth, +ErrInvalidEndpoint, ErrClientDisconnect, or ErrClientShutdown. + +NOTE: The ErrClientDisconnect will not be returned unless the +DisableAutoReconnect flag is set since the client automatically handles +reconnect by default as previously described. + +The second category of errors typically indicates a programmer error and as such +the type can vary, but usually will be best handled by simply showing/logging +it. + +The third category of errors, that is errors returned by the server, can be +detected by type asserting the error is a *btcjson.Error. For example, to +detect if a command is unimplemented by the remote RPC server: + + amount, err := client.GetBalance("") + if err != nil { + if jerr, ok := err.(*btcjson.Error); ok { + switch jerr.Code { + case btcjson.ErrUnimplemented.Code: + // Handle not implemented error + + // Handle other specific errors you care about + } + } + + // Log or otherwise handle the error knowing it was not one returned + // from the remote RPC server. + } + +Example Usage + +The following full-blown client examples are in the examples directory: + + - bitcoincorehttp + Connects to a bitcoin core RPC server using HTTP POST mode with TLS disabled + and gets the current block count + - btcdwebsockets + Connects to a btcd RPC server using TLS-secured websockets, registers for + block connected and block disconnected notifications, and gets the current + block count +*/ package btcrpcclient From 9b0d3118263c96a7a56f6c00bc50169e24b8071d Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 9 May 2014 20:48:05 -0500 Subject: [PATCH 020/153] Update README.md. --- README.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5544131d..db7f1644 100644 --- a/README.md +++ b/README.md @@ -10,18 +10,22 @@ This package is currently under development. You really probably don't want to use this yet! -## Examples +## Documentation -The following client examples are available in the [examples](https://github.com/conformal/btcrpcclient/blob/master/examples) -directory: - -- [bitcoincorehttp](https://github.com/conformal/btcrpcclient/blob/master/examples/bitcoincorehttp) - Connects to a bitcoin core RPC server using HTTP POST mode with TLS disabled - and gets the current block count -- [btcdwebsockets](https://github.com/conformal/btcrpcclient/blob/master/examples/btcdwebsockets) +* [API Reference](http://godoc.org/github.com/conformal/btcrpcclient) +* [btcd Websockets Example](https://github.com/conformal/btcrpcclient/blob/master/examples/btcdwebsockets) Connects to a btcd RPC server using TLS-secured websockets, registers for block connected and block disconnected notifications, and gets the current block count +* [Bitcoin Core HTTP POST Example](https://github.com/conformal/btcrpcclient/blob/master/examples/bitcoincorehttp) + Connects to a bitcoin core RPC server using HTTP POST mode with TLS disabled + and gets the current block count + +## Installation + +```bash +$ go get github.com/conformal/btcrpcclient +``` ## License From 315d85fa4e705c6481679d35ea641c3d9645122c Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 9 May 2014 21:05:01 -0500 Subject: [PATCH 021/153] Add README.md for btcdwebsockets example. --- examples/btcdwebsockets/README.md | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 examples/btcdwebsockets/README.md diff --git a/examples/btcdwebsockets/README.md b/examples/btcdwebsockets/README.md new file mode 100644 index 00000000..1f765fa4 --- /dev/null +++ b/examples/btcdwebsockets/README.md @@ -0,0 +1,37 @@ +btcd Websockets Example +======================= + +This example shows how to use the btcrpcclient package to connect to a btcd +RPC server using TLS-secured websockets, register for block connected and block +disconnected notifications, and get the current block count. + +This example also sets a timer to shutdown the client after 10 seconds to +demonstrate clean shutdown. + +## Running the Example + +The first step is to use `go get` to download and install the btcrpcclient +package: + +```bash +$ go get github.com/conformal/btcrpcclient +``` + +Next, modify the `main.go` source to specify the correct RPC username and +password for the RPC server: + +```Go + User: "yourrpcuser", + Pass: "yourrpcpass", +``` + +Finally, navigate to the example's directory and run it with: + +```bash +$ cd $GOPATH/src/github.com/conformal/btcrpcclient/examples/btcdwebsockets +$ go run *.go +``` + +## License + +This example is licensed under the [copyfree](http://copyfree.org) ISC License. From f666eddf756980f1483b6cb780c73f41ba73dc2b Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 9 May 2014 21:09:09 -0500 Subject: [PATCH 022/153] Add README.md for bitcoincorehttp example. Also, change the port in the example to the mainnet RPC port. --- examples/bitcoincorehttp/README.md | 34 ++++++++++++++++++++++++++++++ examples/bitcoincorehttp/main.go | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 examples/bitcoincorehttp/README.md diff --git a/examples/bitcoincorehttp/README.md b/examples/bitcoincorehttp/README.md new file mode 100644 index 00000000..de01350a --- /dev/null +++ b/examples/bitcoincorehttp/README.md @@ -0,0 +1,34 @@ +Bitcoin Core HTTP POST Example +============================== + +This example shows how to use the btcrpcclient package to connect to a Bitcoin +Core RPC server using HTTP POST mode with TLS disabled and gets the current +block count. + +## Running the Example + +The first step is to use `go get` to download and install the btcrpcclient +package: + +```bash +$ go get github.com/conformal/btcrpcclient +``` + +Next, modify the `main.go` source to specify the correct RPC username and +password for the RPC server: + +```Go + User: "yourrpcuser", + Pass: "yourrpcpass", +``` + +Finally, navigate to the example's directory and run it with: + +```bash +$ cd $GOPATH/src/github.com/conformal/btcrpcclient/examples/bitcoincorehttp +$ go run *.go +``` + +## License + +This example is licensed under the [copyfree](http://copyfree.org) ISC License. diff --git a/examples/bitcoincorehttp/main.go b/examples/bitcoincorehttp/main.go index 605bf844..235a6158 100644 --- a/examples/bitcoincorehttp/main.go +++ b/examples/bitcoincorehttp/main.go @@ -12,7 +12,7 @@ import ( func main() { // Connect to local bitcoin core RPC server using HTTP POST mode. connCfg := &btcrpcclient.ConnConfig{ - Host: "localhost:18332", + Host: "localhost:8332", User: "yourrpcuser", Pass: "yourrpcpass", HttpPostMode: true, // Bitcoin core only supports HTTP POST mode From 7cc356d4c7381676811f6d03377b0b885dc567ca Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 9 May 2014 21:24:25 -0500 Subject: [PATCH 023/153] Convert CreateRawTransaction to higher-level types. Closes #2. --- rawtransactions.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/rawtransactions.go b/rawtransactions.go index 53a29c04..a1ef6cf8 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -241,9 +241,15 @@ func (r FutureCreateRawTransactionResult) Receive() (*btcwire.MsgTx, error) { // 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 { +func (c *Client) CreateRawTransactionAsync(inputs []btcjson.TransactionInput, + amounts map[btcutil.Address]btcutil.Amount) FutureCreateRawTransactionResult { + id := c.NextID() - cmd, err := btcjson.NewCreateRawTransactionCmd(id, inputs, amounts) + convertedAmts := make(map[string]int64, len(amounts)) + for addr, amount := range amounts { + convertedAmts[addr.EncodeAddress()] = int64(amount) + } + cmd, err := btcjson.NewCreateRawTransactionCmd(id, inputs, convertedAmts) if err != nil { return newFutureError(err) } @@ -253,7 +259,9 @@ func (c *Client) CreateRawTransactionAsync(inputs []btcjson.TransactionInput, am // 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) { +func (c *Client) CreateRawTransaction(inputs []btcjson.TransactionInput, + amounts map[btcutil.Address]btcutil.Amount) (*btcwire.MsgTx, error) { + return c.CreateRawTransactionAsync(inputs, amounts).Receive() } From a35c1e8edea856883db0d21592f7626301b4abf1 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 9 May 2014 23:44:43 -0500 Subject: [PATCH 024/153] Auto recreate notification state on reconnect. This commit adds logic to track all registered notifications that have been registered by the client in a notification state when the default automatic reconnect is enabled. The notification state is then used to reregister for all previously registered notifications on reconnect. This allows the caller to continue receiving notifications across reconnect cycles. --- infrastructure.go | 128 ++++++++++++++++++++++++++++++++++++++++++++-- notify.go | 84 ++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+), 3 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index af0af9d8..fd007113 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -14,6 +14,7 @@ import ( "errors" "fmt" "github.com/conformal/btcjson" + "github.com/conformal/btcws" "github.com/conformal/go-socks" "github.com/gorilla/websocket" "net" @@ -127,8 +128,9 @@ type Client struct { requestMap map[uint64]*list.Element requestList *list.List - // Notification handlers. + // Notifications. ntfnHandlers *NotificationHandlers + ntfnState *notificationState // Networking infrastructure. sendChan chan []byte @@ -193,6 +195,42 @@ func (c *Client) removeAllRequests() { c.requestList.Init() } +// trackRegisteredNtfns examines the passed command to see if it is one of +// the notification commands and updates the notification state that is used +// to automatically re-establish registered notifications on reconnects. +func (c *Client) trackRegisteredNtfns(cmd btcjson.Cmd) { + // Nothing to do if the caller is not interested in notifications. + if c.ntfnHandlers == nil { + return + } + + c.ntfnState.Lock() + defer c.ntfnState.Unlock() + + switch bcmd := cmd.(type) { + case *btcws.NotifyBlocksCmd: + c.ntfnState.notifyBlocks = true + + case *btcws.NotifyNewTransactionsCmd: + if bcmd.Verbose { + c.ntfnState.notifyNewTxVerbose = true + } else { + c.ntfnState.notifyNewTx = true + + } + + case *btcws.NotifySpentCmd: + for _, op := range bcmd.OutPoints { + c.ntfnState.notifySpent[op] = struct{}{} + } + + case *btcws.NotifyReceivedCmd: + for _, addr := range bcmd.Addresses { + c.ntfnState.notifyReceived[addr] = struct{}{} + } + } +} + // 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 @@ -259,6 +297,13 @@ func (c *Client) handleMessage(msg []byte) { request.responseChan <- &futureResult{reply: nil, err: err} return } + + // Since the command was successful, examine it to see if it's a + // notification, and if is, add it to the notification state so it + // can automatically be re-established on reconnect. + c.trackRegisteredNtfns(request.cmd) + + // Deliver the reply. request.responseChan <- &futureResult{reply: &reply, err: nil} } @@ -341,10 +386,86 @@ func (c *Client) sendMessage(marshalledJSON []byte) { c.sendChan <- marshalledJSON } +// reregisterNtfns creates and sends commands needed to re-establish the current +// notification state associated with the client. It should only be called on +// on reconnect by the resendCmds function. +func (c *Client) reregisterNtfns() error { + // Nothing to do if the caller is not interested in notifications. + if c.ntfnHandlers == nil { + return nil + } + + // In order to avoid holding the lock on the notification state for the + // entire time of the potentially long running RPCs issued below, make a + // copy of it and work from that. + // + // Also, other commands will be running concurrently which could modify + // the notification state (while not under the lock of course) which + // also register it with the remote RPC server, so this prevents double + // registrations. + stateCopy := c.ntfnState.Copy() + + // Reregister notifyblocks if needed. + if stateCopy.notifyBlocks { + log.Debugf("Reregistering [notifyblocks]") + if err := c.NotifyBlocks(); err != nil { + return err + } + } + + // Reregister notifynewtransactions if needed. + if stateCopy.notifyNewTx || stateCopy.notifyNewTxVerbose { + log.Debugf("Reregistering [notifynewtransactions] (verbose=%v)", + stateCopy.notifyNewTxVerbose) + err := c.NotifyNewTransactions(stateCopy.notifyNewTxVerbose) + if err != nil { + return err + } + } + + // Reregister the combination of all previously registered notifyspent + // outpoints in one command if needed. + nslen := len(stateCopy.notifySpent) + if nslen > 0 { + outpoints := make([]btcws.OutPoint, 0, nslen) + for op := range stateCopy.notifySpent { + outpoints = append(outpoints, op) + } + log.Debugf("Reregistering [notifyspent] outpoints: %v", outpoints) + if err := c.notifySpentInternal(outpoints).Receive(); err != nil { + return err + } + } + + // Reregister the combination of all previously registered + // notifyreceived addresses in one command if needed. + nrlen := len(stateCopy.notifyReceived) + if nrlen > 0 { + addresses := make([]string, 0, nrlen) + for addr := range stateCopy.notifyReceived { + addresses = append(addresses, addr) + } + log.Debugf("Reregistering [notifyreceived] addresses: %v", addresses) + if err := c.notifyReceivedInternal(addresses).Receive(); err != nil { + return err + } + } + + return nil +} + // resendCmds resends any commands that had not completed when the client -// disconnected. It is intended to be called once the client has reconnected -// as a separate goroutine. +// disconnected. It is intended to be called once the client has reconnected as +// a separate goroutine. func (c *Client) resendCmds() { + // Set the notification state back up. If anything goes wrong, + // disconnect the client. + if err := c.reregisterNtfns(); err != nil { + log.Warnf("Unable to re-establish notification state: %v", err) + c.Disconnect() + return + } + // 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 @@ -928,6 +1049,7 @@ func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error requestMap: make(map[uint64]*list.Element), requestList: list.New(), ntfnHandlers: ntfnHandlers, + ntfnState: newNotificationState(), sendChan: make(chan []byte, sendBufferSize), sendPostChan: make(chan *sendPostDetails, sendPostBufferSize), disconnect: make(chan struct{}), diff --git a/notify.go b/notify.go index a3fe3b6f..fa112e7d 100644 --- a/notify.go +++ b/notify.go @@ -12,6 +12,7 @@ import ( "github.com/conformal/btcutil" "github.com/conformal/btcwire" "github.com/conformal/btcws" + "sync" ) var ( @@ -23,6 +24,46 @@ var ( "supported when running in HTTP POST mode") ) +// notificationState is used to track the current state of successfuly +// registered notification so the state can be automatically re-established on +// reconnect. +type notificationState struct { + sync.Mutex + notifyBlocks bool + notifyNewTx bool + notifyNewTxVerbose bool + notifyReceived map[string]struct{} + notifySpent map[btcws.OutPoint]struct{} +} + +// Copy returns a deep copy of the receiver. +// +// This function is safe for concurrent access. +func (s *notificationState) Copy() *notificationState { + s.Lock() + defer s.Unlock() + + stateCopy := *s + stateCopy.notifyReceived = make(map[string]struct{}) + for addr := range s.notifyReceived { + stateCopy.notifyReceived[addr] = struct{}{} + } + stateCopy.notifySpent = make(map[btcws.OutPoint]struct{}) + for op := range s.notifySpent { + stateCopy.notifySpent[op] = struct{}{} + } + + return &stateCopy +} + +// newNotificationState returns a new notification state ready to be populated. +func newNotificationState() *notificationState { + return ¬ificationState{ + notifyReceived: make(map[string]struct{}), + notifySpent: make(map[btcws.OutPoint]struct{}), + } +} + // 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 @@ -371,6 +412,27 @@ func (r FutureNotifySpentResult) Receive() error { return nil } +// notifySpentInternal is the same as notifySpentAsync except it accepts +// the converted outpoints as a parameter so the client can more efficiently +// recreate the previous notification state on reconnect. +func (c *Client) notifySpentInternal(outpoints []btcws.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, outpoints) + + return c.sendCmd(cmd) +} + // 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. @@ -487,6 +549,28 @@ func (r FutureNotifyReceivedResult) Receive() error { return nil } +// notifyReceivedInternal is the same as notifyReceivedAsync except it accepts +// the converted addresses as a parameter so the client can more efficiently +// recreate the previous notification state on reconnect. +func (c *Client) notifyReceivedInternal(addresses []string) 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. + id := c.NextID() + cmd := btcws.NewNotifyReceivedCmd(id, addresses) + + return c.sendCmd(cmd) +} + // 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. From 9bb16e208d8a9c09b4897fd177286d133b328d21 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sat, 10 May 2014 02:47:59 -0500 Subject: [PATCH 025/153] Update README.md. This commit adds a description, status, and major features to the README. --- README.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index db7f1644..fa50cdb1 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,16 @@ btcrpcclient [![GoDoc](https://godoc.org/github.com/conformal/btcrpcclient?status.png)] (http://godoc.org/github.com/conformal/btcrpcclient) -This package is currently under development. +btcrpcclient implements a Websocket-enabled Bitcoin JSON-RPC client package +written in [Go](http://golang.org/). It provides a robust and easy to use +client for interfacing with a Bitcoin RPC server that uses a btcd/bitcoin core +compatible Bitcoin JSON-RPC API. -You really probably don't want to use this yet! +## Status + +This package is currently under active development. It is already stable and +the infrastructure is complete. However, there are still several RPCs left to +implement and the API is not stable yet. ## Documentation @@ -21,6 +28,19 @@ You really probably don't want to use this yet! Connects to a bitcoin core RPC server using HTTP POST mode with TLS disabled and gets the current block count +## Major Features + +* Supports Websockets (btcd/btcwallet) and HTTP POST mode (bitcoin core) +* Provides callback and registration functions for btcd/btcwallet notifications +* Supports btcd extensions +* Translates to and from higher-level and easier to use Go types +* Offers a synchronous (blocking) and asynchrous API +* When running in Websockets mode (the default): + * Automatic reconnect handling (can be disabled) + * Outstanding commands are automatically reissued + * Registered notifications are automatically reregistered + * Back-off support on reconnect attempts + ## Installation ```bash From 0fc9504da4a50e25114e609637f5646192807b3b Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sat, 10 May 2014 02:50:19 -0500 Subject: [PATCH 026/153] Improve overview in doc.go. --- doc.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc.go b/doc.go index 9f3821d6..f79f82eb 100644 --- a/doc.go +++ b/doc.go @@ -7,10 +7,9 @@ Package btcrpcclient implements a websocket-enabled Bitcoin JSON-RPC client. Overview -This client is intended to provide a robust and easy to use client for -interfacing with a Bitcoin RPC server that uses a btcd/bitcoin core compatible -Bitcoin JSON-RPC API. This client has been tested with -btcd (https://github.com/conformal/btcd), +This client provides a robust and easy to use client for interfacing with a +Bitcoin RPC server that uses a btcd/bitcoin core compatible Bitcoin JSON-RPC +API. This client has been tested with btcd (https://github.com/conformal/btcd), btcwallet (https://github.com/conformal/btcwallet), and bitcoin core (https://github.com/bitcoin). From e2ba50eee17743c479475a8ad7b09b21b6d59b42 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sat, 10 May 2014 15:48:14 -0500 Subject: [PATCH 027/153] Fix a couple of typos in README.md. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fa50cdb1..a422e012 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,8 @@ implement and the API is not stable yet. * Supports Websockets (btcd/btcwallet) and HTTP POST mode (bitcoin core) * Provides callback and registration functions for btcd/btcwallet notifications * Supports btcd extensions -* Translates to and from higher-level and easier to use Go types -* Offers a synchronous (blocking) and asynchrous API +* Translates to and from higher-level and easier to use Go types +* Offers a synchronous (blocking) and asynchronous API * When running in Websockets mode (the default): * Automatic reconnect handling (can be disabled) * Outstanding commands are automatically reissued From e230b54427374a8eb9e92e1e7c04201f1e6b5f10 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sun, 11 May 2014 02:34:27 -0500 Subject: [PATCH 028/153] Correct FutureGetBestBlockResult.Receive type. Also correct the error message while here. --- extensions.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions.go b/extensions.go index a5c11745..ea1b9237 100644 --- a/extensions.go +++ b/extensions.go @@ -137,10 +137,10 @@ func (r FutureGetBestBlockResult) Receive() (*btcwire.ShaHash, int32, error) { } // Ensure the returned data is the expected type. - result, ok := reply.(btcws.GetBestBlockResult) + result, ok := reply.(*btcws.GetBestBlockResult) if !ok { return nil, 0, fmt.Errorf("unexpected response type for "+ - "listaddresstransactions: %T\n", reply) + "getbestblock: %T\n", reply) } // Convert hash string. From 267cf94edc7c84f03d67c5b79462d1aead0aaa32 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sun, 11 May 2014 13:30:25 -0500 Subject: [PATCH 029/153] Correct typo in example log statement. --- examples/btcdwebsockets/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/btcdwebsockets/main.go b/examples/btcdwebsockets/main.go index 4d3efa08..b8123775 100644 --- a/examples/btcdwebsockets/main.go +++ b/examples/btcdwebsockets/main.go @@ -66,7 +66,7 @@ func main() { time.AfterFunc(time.Second*10, func() { log.Println("Client shutting down...") client.Shutdown() - log.Println("Client shutting complete.") + log.Println("Client shutdown complete.") }) // Wait until the client either shuts down gracefully (or the user From 85886913b8345ee24aa93a0889bbac6306d58399 Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Mon, 12 May 2014 14:18:09 -0500 Subject: [PATCH 030/153] Implement Client methods for listunspent requests. --- wallet.go | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/wallet.go b/wallet.go index 5d6150ca..b0e1f77f 100644 --- a/wallet.go +++ b/wallet.go @@ -158,6 +158,130 @@ func (c *Client) ListTransactionsCountFrom(account string, count, from int) ([]b return c.ListTransactionsCountFromAsync(account, count, from).Receive() } +// FutureListUnspentResult is a future promise to deliver the result of a +// ListUnspentAsync, ListUnspentMinAsync, ListUnspentMinMaxAsync, or +// ListUnspentMinMaxAddressesAsync RPC invocation (or an applicable error). +type FutureListUnspentResult chan *futureResult + +// Receive waits for the response promised by the future and returns all +// unspent wallet transaction outputs returned by the RPC call. If the +// future wac returnd by a call to ListUnspentMinAsync, ListUnspentMinMaxAsync, +// or ListUnspentMinMaxAddressesAsync, the range may be limited by the +// parameters of the RPC invocation. +func (r FutureListUnspentResult) Receive() ([]btcjson.ListUnspentResult, error) { + reply, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // No unspent transaction outputs. + if reply == nil { + return nil, nil + } + + // Ensure the returned data is the expected type. + unspent, ok := reply.([]btcjson.ListUnspentResult) + if !ok { + return nil, fmt.Errorf("unexpected response type for "+ + "listunspent: %T\n", reply) + } + + return unspent, nil +} + +// ListUnspentAsync 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 ListUnspent for the blocking version and more details. +func (c *Client) ListUnspentAsync() FutureListUnspentResult { + id := c.NextID() + cmd, err := btcjson.NewListUnspentCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListUnspentMinAsync 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 ListUnspentMin for the blocking version and more details. +func (c *Client) ListUnspentMinAsync(minConf int) FutureListUnspentResult { + id := c.NextID() + cmd, err := btcjson.NewListUnspentCmd(id, minConf) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListUnspentMinMaxAsync 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 ListUnspentMinMax for the blocking version and more details. +func (c *Client) ListUnspentMinMaxAsync(minConf, maxConf int) FutureListUnspentResult { + id := c.NextID() + cmd, err := btcjson.NewListUnspentCmd(id, minConf, maxConf) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListUnspentMinMaxAddressesAsync 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 ListUnspentMinMaxAddresses for the blocking version and more details. +func (c *Client) ListUnspentMinMaxAddressesAsync(minConf, maxConf int, addrs []btcutil.Address) FutureListUnspentResult { + addrStrs := make([]string, 0, len(addrs)) + for _, a := range addrs { + addrStrs = append(addrStrs, a.EncodeAddress()) + } + + id := c.NextID() + cmd, err := btcjson.NewListUnspentCmd(id, minConf, maxConf, addrStrs) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListUnspent returns all unspent transaction outputs known to a wallet, using +// the default number of minimum and maximum number of confirmations as a +// filter (1 and 999999, respectively). +func (c *Client) ListUnspent() ([]btcjson.ListUnspentResult, error) { + return c.ListUnspentAsync().Receive() +} + +// ListUnspentMin returns all unspent transaction outputs known to a wallet, +// using the specified number of minimum conformations and default number of +// maximum confiramtions (999999) as a filter. +func (c *Client) ListUnspentMin(minConf int) ([]btcjson.ListUnspentResult, error) { + return c.ListUnspentMinAsync(minConf).Receive() +} + +// ListUnspentMinMax returns all unspent transaction outputs known to a wallet, +// using the specified number of minimum and maximum number of confirmations as +// a filter. +func (c *Client) ListUnspentMinMax(minConf, maxConf int) ([]btcjson.ListUnspentResult, error) { + return c.ListUnspentMinMaxAsync(minConf, maxConf).Receive() +} + +// ListUnspentMinMaxAddresses returns all unspent transaction outputs that pay +// to any of specified addresses in a wallet using the specified number of +// minimum and maximum number of confirmations as a filter. +func (c *Client) ListUnspentMinMaxAddresses(minConf, maxConf int, addrs []btcutil.Address) ([]btcjson.ListUnspentResult, error) { + return c.ListUnspentMinMaxAddressesAsync(minConf, maxConf, addrs).Receive() +} + // FutureListSinceBlockResult is a future promise to deliver the result of a // ListSinceBlockAsync or ListSinceBlockMinConfAsync RPC invocation (or an // applicable error). From 57738b1920388573180039830cfd1b9366aecbcb Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 21 May 2014 19:24:45 -0500 Subject: [PATCH 031/153] gofmt --- wallet.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wallet.go b/wallet.go index b0e1f77f..486fc7be 100644 --- a/wallet.go +++ b/wallet.go @@ -258,28 +258,28 @@ func (c *Client) ListUnspentMinMaxAddressesAsync(minConf, maxConf int, addrs []b // the default number of minimum and maximum number of confirmations as a // filter (1 and 999999, respectively). func (c *Client) ListUnspent() ([]btcjson.ListUnspentResult, error) { - return c.ListUnspentAsync().Receive() + return c.ListUnspentAsync().Receive() } // ListUnspentMin returns all unspent transaction outputs known to a wallet, // using the specified number of minimum conformations and default number of // maximum confiramtions (999999) as a filter. func (c *Client) ListUnspentMin(minConf int) ([]btcjson.ListUnspentResult, error) { - return c.ListUnspentMinAsync(minConf).Receive() + return c.ListUnspentMinAsync(minConf).Receive() } // ListUnspentMinMax returns all unspent transaction outputs known to a wallet, // using the specified number of minimum and maximum number of confirmations as // a filter. func (c *Client) ListUnspentMinMax(minConf, maxConf int) ([]btcjson.ListUnspentResult, error) { - return c.ListUnspentMinMaxAsync(minConf, maxConf).Receive() + return c.ListUnspentMinMaxAsync(minConf, maxConf).Receive() } // ListUnspentMinMaxAddresses returns all unspent transaction outputs that pay // to any of specified addresses in a wallet using the specified number of // minimum and maximum number of confirmations as a filter. func (c *Client) ListUnspentMinMaxAddresses(minConf, maxConf int, addrs []btcutil.Address) ([]btcjson.ListUnspentResult, error) { - return c.ListUnspentMinMaxAddressesAsync(minConf, maxConf, addrs).Receive() + return c.ListUnspentMinMaxAddressesAsync(minConf, maxConf, addrs).Receive() } // FutureListSinceBlockResult is a future promise to deliver the result of a From 9d9c3432477eaf4d5bc02a97bcb4369334b5813c Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 21 May 2014 19:53:46 -0500 Subject: [PATCH 032/153] Make nil pointer handling consistent across RPCs. There are several RPCs which accept a pointer to a hash, transaction, block, etc. Previously not all RPCs handled being passed a nil pointer consistently. Closes #4. --- chain.go | 15 +++++++-- mining.go | 14 +++++--- rawtransactions.go | 79 ++++++++++++++++++++++++++++++---------------- wallet.go | 9 +++++- 4 files changed, 81 insertions(+), 36 deletions(-) diff --git a/chain.go b/chain.go index 9e580824..0b18faa5 100644 --- a/chain.go +++ b/chain.go @@ -96,8 +96,13 @@ func (r FutureGetBlockResult) Receive() (*btcutil.Block, error) { // // See GetBlock for the blocking version and more details. func (c *Client) GetBlockAsync(blockHash *btcwire.ShaHash) FutureGetBlockResult { + hash := "" + if blockHash != nil { + hash = blockHash.String() + } + id := c.NextID() - cmd, err := btcjson.NewGetBlockCmd(id, blockHash.String(), false) + cmd, err := btcjson.NewGetBlockCmd(id, hash, false) if err != nil { return newFutureError(err) } @@ -141,9 +146,13 @@ func (r FutureGetBlockVerboseResult) Receive() (*btcjson.BlockResult, error) { // // See GetBlockVerbose for the blocking version and more details. func (c *Client) GetBlockVerboseAsync(blockHash *btcwire.ShaHash, verboseTx bool) FutureGetBlockVerboseResult { + hash := "" + if blockHash != nil { + hash = blockHash.String() + } + id := c.NextID() - cmd, err := btcjson.NewGetBlockCmd(id, blockHash.String(), true, - verboseTx) + cmd, err := btcjson.NewGetBlockCmd(id, hash, true, verboseTx) if err != nil { return newFutureError(err) } diff --git a/mining.go b/mining.go index 924c3caf..8336d239 100644 --- a/mining.go +++ b/mining.go @@ -384,13 +384,17 @@ func (r FutureSubmitBlockResult) Receive() error { // // 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 := "" + if block != nil { + blockBytes, err := block.Bytes() + if err != nil { + return newFutureError(err) + } + + blockHex = hex.EncodeToString(blockBytes) } - blockHex := hex.EncodeToString(blockBytes) + id := c.NextID() cmd, err := btcjson.NewSubmitBlockCmd(id, blockHex, options) if err != nil { return newFutureError(err) diff --git a/rawtransactions.go b/rawtransactions.go index a1ef6cf8..d1657ff9 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -95,8 +95,13 @@ func (r FutureGetRawTransactionResult) Receive() (*btcutil.Tx, error) { // // See GetRawTransaction for the blocking version and more details. func (c *Client) GetRawTransactionAsync(txHash *btcwire.ShaHash) FutureGetRawTransactionResult { + hash := "" + if txHash != nil { + hash = txHash.String() + } + id := c.NextID() - cmd, err := btcjson.NewGetRawTransactionCmd(id, txHash.String(), 0) + cmd, err := btcjson.NewGetRawTransactionCmd(id, hash, 0) if err != nil { return newFutureError(err) } @@ -141,8 +146,13 @@ func (r FutureGetRawTransactionVerboseResult) Receive() (*btcjson.TxRawResult, e // // See GetRawTransactionVerbose for the blocking version and more details. func (c *Client) GetRawTransactionVerboseAsync(txHash *btcwire.ShaHash) FutureGetRawTransactionVerboseResult { + hash := "" + if txHash != nil { + hash = txHash.String() + } + id := c.NextID() - cmd, err := btcjson.NewGetRawTransactionCmd(id, txHash.String(), 1) + cmd, err := btcjson.NewGetRawTransactionCmd(id, hash, 1) if err != nil { return newFutureError(err) } @@ -294,12 +304,15 @@ func (r FutureSendRawTransactionResult) Receive() (*btcwire.ShaHash, error) { // // 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 := "" + if tx != nil { + // 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()) } - txHex := hex.EncodeToString(buf.Bytes()) id := c.NextID() cmd, err := btcjson.NewSendRawTransactionCmd(id, txHex, allowHighFees) @@ -357,12 +370,15 @@ func (r FutureSignRawTransactionResult) Receive() (*btcwire.MsgTx, bool, error) // // 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 := "" + if tx != nil { + // 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()) } - txHex := hex.EncodeToString(buf.Bytes()) id := c.NextID() cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex) @@ -390,12 +406,15 @@ func (c *Client) SignRawTransaction(tx *btcwire.MsgTx) (*btcwire.MsgTx, bool, er // // 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 := "" + if tx != nil { + // 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()) } - txHex := hex.EncodeToString(buf.Bytes()) id := c.NextID() cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex, inputs) @@ -429,12 +448,15 @@ 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 := "" + if tx != nil { + // 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()) } - txHex := hex.EncodeToString(buf.Bytes()) id := c.NextID() cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex, inputs, @@ -479,12 +501,15 @@ 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 := "" + if tx != nil { + // 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()) } - txHex := hex.EncodeToString(buf.Bytes()) id := c.NextID() cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex, inputs, diff --git a/wallet.go b/wallet.go index 486fc7be..47f812e6 100644 --- a/wallet.go +++ b/wallet.go @@ -44,8 +44,13 @@ func (r FutureGetTransactionResult) Receive() (*btcjson.GetTransactionResult, er // // See GetTransaction for the blocking version and more details. func (c *Client) GetTransactionAsync(txHash *btcwire.ShaHash) FutureGetTransactionResult { + hash := "" + if txHash != nil { + hash = txHash.String() + } + id := c.NextID() - cmd, err := btcjson.NewGetTransactionCmd(id, txHash.String()) + cmd, err := btcjson.NewGetTransactionCmd(id, hash) if err != nil { return newFutureError(err) } @@ -316,6 +321,7 @@ func (c *Client) ListSinceBlockAsync(blockHash *btcwire.ShaHash) FutureListSince if blockHash != nil { hash = blockHash.String() } + id := c.NextID() cmd, err := btcjson.NewListSinceBlockCmd(id, hash) if err != nil { @@ -344,6 +350,7 @@ func (c *Client) ListSinceBlockMinConfAsync(blockHash *btcwire.ShaHash, minConfi if blockHash != nil { hash = blockHash.String() } + id := c.NextID() cmd, err := btcjson.NewListSinceBlockCmd(id, hash, minConfirms) if err != nil { From 6af13826ae8174e99ab26a4d79063e5373116edf Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 21 May 2014 19:59:50 -0500 Subject: [PATCH 033/153] Convert {Dump,Import}PrivKey to use new WIF type. The btcutil package recently exposed a WIF type to provide more functionality and simplify working with WIF strings. This commit changes the DumpPrivKey and ImportPrivKey RPCs to use the new WIF type. --- wallet.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/wallet.go b/wallet.go index 47f812e6..2afb43ae 100644 --- a/wallet.go +++ b/wallet.go @@ -1899,20 +1899,20 @@ 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) { +func (r FutureDumpPrivKeyResult) Receive() (*btcutil.WIF, error) { reply, err := receiveFuture(r) if err != nil { - return "", err + return nil, err } // Ensure the returned data is the expected type. privKeyWIF, ok := reply.(string) if !ok { - return "", fmt.Errorf("unexpected response type for "+ + return nil, fmt.Errorf("unexpected response type for "+ "dumpprivkey: %T\n", reply) } - return privKeyWIF, nil + return btcutil.DecodeWIF(privKeyWIF) } // DumpPrivKeyAsync returns an instance of a type that can be used to get the @@ -1936,7 +1936,7 @@ func (c *Client) DumpPrivKeyAsync(address btcutil.Address) FutureDumpPrivKeyResu // // 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) { +func (c *Client) DumpPrivKey(address btcutil.Address) (*btcutil.WIF, error) { return c.DumpPrivKeyAsync(address).Receive() } @@ -1961,9 +1961,14 @@ func (r FutureImportPrivKeyResult) Receive() error { // returned instance. // // See ImportPrivKey for the blocking version and more details. -func (c *Client) ImportPrivKeyAsync(privKeyWIF string) FutureImportPrivKeyResult { +func (c *Client) ImportPrivKeyAsync(privKeyWIF *btcutil.WIF) FutureImportPrivKeyResult { + wif := "" + if privKeyWIF != nil { + wif = privKeyWIF.String() + } + id := c.NextID() - cmd, err := btcjson.NewImportPrivKeyCmd(id, privKeyWIF) + cmd, err := btcjson.NewImportPrivKeyCmd(id, wif) if err != nil { return newFutureError(err) } @@ -1973,7 +1978,7 @@ func (c *Client) ImportPrivKeyAsync(privKeyWIF string) FutureImportPrivKeyResult // ImportPrivKey imports the passed private key which must be the wallet import // format (WIF). -func (c *Client) ImportPrivKey(privKeyWIF string) error { +func (c *Client) ImportPrivKey(privKeyWIF *btcutil.WIF) error { return c.ImportPrivKeyAsync(privKeyWIF).Receive() } From 0c586634bd3131a2663093c055340eb0850d2b48 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Tue, 27 May 2014 17:03:09 -0500 Subject: [PATCH 034/153] Update for recent btcutil API changes. --- wallet.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/wallet.go b/wallet.go index 2afb43ae..4fe020c9 100644 --- a/wallet.go +++ b/wallet.go @@ -7,6 +7,7 @@ package btcrpcclient import ( "fmt" "github.com/conformal/btcjson" + "github.com/conformal/btcnet" "github.com/conformal/btcutil" "github.com/conformal/btcwire" "github.com/conformal/btcws" @@ -777,7 +778,7 @@ func (r FutureAddMultisigAddressResult) Receive() (btcutil.Address, error) { "addmultisigaddress: %T\n", reply) } - return btcutil.DecodeAddress(addr, btcwire.MainNet) + return btcutil.DecodeAddress(addr, &btcnet.MainNetParams) } // AddMultisigAddressAsync returns an instance of a type that can be used to get @@ -877,7 +878,7 @@ func (r FutureGetNewAddressResult) Receive() (btcutil.Address, error) { "getnewaddress: %T\n", reply) } - return btcutil.DecodeAddress(addr, btcwire.MainNet) + return btcutil.DecodeAddress(addr, &btcnet.MainNetParams) } // GetNewAddressAsync returns an instance of a type that can be used to get the @@ -920,7 +921,7 @@ func (r FutureGetRawChangeAddressResult) Receive() (btcutil.Address, error) { "getrawchangeaddress: %T\n", reply) } - return btcutil.DecodeAddress(addr, btcwire.MainNet) + return btcutil.DecodeAddress(addr, &btcnet.MainNetParams) } // GetRawChangeAddressAsync returns an instance of a type that can be used to @@ -964,7 +965,7 @@ func (r FutureGetAccountAddressResult) Receive() (btcutil.Address, error) { "getaccountaddress: %T\n", reply) } - return btcutil.DecodeAddress(addr, btcwire.MainNet) + return btcutil.DecodeAddress(addr, &btcnet.MainNetParams) } // GetAccountAddressAsync returns an instance of a type that can be used to get @@ -1088,7 +1089,7 @@ func (r FutureGetAddressesByAccountResult) Receive() ([]btcutil.Address, error) addrs := make([]btcutil.Address, 0, len(addrStrings)) for _, addrStr := range addrStrings { - addr, err := btcutil.DecodeAddress(addrStr, btcwire.MainNet) + addr, err := btcutil.DecodeAddress(addrStr, &btcnet.MainNetParams) if err != nil { return nil, err } From 67e94bcaaaf1c3d4f5a2c377ed178fe76dffe1b8 Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Wed, 28 May 2014 11:33:28 -0500 Subject: [PATCH 035/153] Add createencryptedwallet extension support. --- extensions.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/extensions.go b/extensions.go index ea1b9237..eb404a5d 100644 --- a/extensions.go +++ b/extensions.go @@ -36,6 +36,41 @@ func (r FutureDebugLevelResult) Receive() (string, error) { return result, nil } +// FutureCreateEncryptedWalletResult is a future promise to deliver the error +// result of a CreateEncryptedWalletAsync RPC invocation. +type FutureCreateEncryptedWalletResult chan *futureResult + +// Receive waits for and returns the error response promised by the future. +func (r FutureCreateEncryptedWalletResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// CreateEncryptedWalletAsync 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 CreateEncryptedWallet for the blocking version and more details. +// +// NOTE: This is a btcwallet extension. +func (c *Client) CreateEncryptedWalletAsync(passphrase string) FutureCreateEncryptedWalletResult { + id := c.NextID() + cmd := btcws.NewCreateEncryptedWalletCmd(id, passphrase) + return c.sendCmd(cmd) +} + +// CreateEncryptedWallet requests the creation of an encrypted wallet. Wallets +// managed by btcwallet are only written to disk with encrypted private keys, +// and generating wallets on the fly is impossible as it requires user input for +// the encryption passphrase. This RPC specifies the passphrase and instructs +// the wallet creation. This may error if a wallet is already opened, or the +// new wallet cannot be written to disk. +// +// NOTE: This is a btcwallet extension. +func (c *Client) CreateEncryptedWallet(passphrase string) error { + return c.CreateEncryptedWalletAsync(passphrase).Receive() +} + // 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. From a785ef64240b70bbb26a7aeb218713e5fc4fbe9a Mon Sep 17 00:00:00 2001 From: kargakis Date: Mon, 2 Jun 2014 17:31:02 +0300 Subject: [PATCH 036/153] minor syntax fixes --- doc.go | 2 +- infrastructure.go | 2 +- wallet.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc.go b/doc.go index f79f82eb..3917a7db 100644 --- a/doc.go +++ b/doc.go @@ -141,7 +141,7 @@ the type can vary, but usually will be best handled by simply showing/logging it. The third category of errors, that is errors returned by the server, can be -detected by type asserting the error is a *btcjson.Error. For example, to +detected by type asserting the error in a *btcjson.Error. For example, to detect if a command is unimplemented by the remote RPC server: amount, err := client.GetBalance("") diff --git a/infrastructure.go b/infrastructure.go index fd007113..f8ab2695 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -1016,7 +1016,7 @@ func dial(config *ConnConfig) (*websocket.Conn, error) { return wsConn, nil } -// New create a new RPC client based on the provided connection configuration +// New creates 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. diff --git a/wallet.go b/wallet.go index 4fe020c9..5a47cca3 100644 --- a/wallet.go +++ b/wallet.go @@ -600,7 +600,7 @@ func (c *Client) SendFromCommentAsync(fromAccount string, return c.sendCmd(cmd) } -// SendFromMinConf sends the passed amount to the given address using the +// SendFromComment 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 From 879a125a2fee78ca35d40723f91e9c50e3926062 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Mon, 2 Jun 2014 09:50:03 -0500 Subject: [PATCH 037/153] Update CONTRIBUTORS. --- CONTRIBUTORS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 59e93efe..1b09bce3 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -8,3 +8,5 @@ Dave Collins Geert-Johan Riemer +Josh Rickmar +Michalis Kargakis From 4e8e63e0d721a37994263ad25fba173a16a55a28 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 4 Jun 2014 22:12:47 -0500 Subject: [PATCH 038/153] Use bytes.NewReader for all deserialization. Rather than using bytes.NewBuffer, which is a read/write entity (io.ReadWriter), use bytes.NewReader which is only a read entitiy (io.Reader). Benchmarking shows it's slightly faster and it's also technically more accurate since it ensures the data is read-only. --- chain.go | 2 +- rawtransactions.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/chain.go b/chain.go index 0b18faa5..5808dd2a 100644 --- a/chain.go +++ b/chain.go @@ -83,7 +83,7 @@ func (r FutureGetBlockResult) Receive() (*btcutil.Block, error) { // Deserialize the block and return it. var msgBlock btcwire.MsgBlock - msgBlock.Deserialize(bytes.NewBuffer(serializedBlock)) + msgBlock.Deserialize(bytes.NewReader(serializedBlock)) if err != nil { return nil, err } diff --git a/rawtransactions.go b/rawtransactions.go index d1657ff9..95a7abec 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -83,7 +83,7 @@ func (r FutureGetRawTransactionResult) Receive() (*btcutil.Tx, error) { // Deserialize the transaction and return it. var msgTx btcwire.MsgTx - if err := msgTx.Deserialize(bytes.NewBuffer(serializedTx)); err != nil { + if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil { return nil, err } return btcutil.NewTx(&msgTx), nil @@ -240,7 +240,7 @@ func (r FutureCreateRawTransactionResult) Receive() (*btcwire.MsgTx, error) { // Deserialize the transaction and return it. var msgTx btcwire.MsgTx - if err := msgTx.Deserialize(bytes.NewBuffer(serializedTx)); err != nil { + if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil { return nil, err } return &msgTx, nil @@ -357,7 +357,7 @@ func (r FutureSignRawTransactionResult) Receive() (*btcwire.MsgTx, bool, error) // Deserialize the transaction and return it. var msgTx btcwire.MsgTx - if err := msgTx.Deserialize(bytes.NewBuffer(serializedTx)); err != nil { + if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil { return nil, false, err } From e31398a2727aebffa665a4557746810d47a26365 Mon Sep 17 00:00:00 2001 From: Michalis Kargakis Date: Thu, 5 Jun 2014 20:48:56 +0300 Subject: [PATCH 039/153] Add height parameter in disconn notification --- examples/btcdwebsockets/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/btcdwebsockets/main.go b/examples/btcdwebsockets/main.go index b8123775..ccc08970 100644 --- a/examples/btcdwebsockets/main.go +++ b/examples/btcdwebsockets/main.go @@ -24,7 +24,7 @@ func main() { log.Printf("Block connected: %v (%d)", hash, height) }, OnBlockDisconnected: func(hash *btcwire.ShaHash, height int32) { - log.Printf("Block disconnected: %v", hash, height) + log.Printf("Block disconnected: %v (%d)", hash, height) }, } From cd4018b33e2a7369e79a586e49af5569bd7e1e45 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 5 Jun 2014 20:24:03 -0500 Subject: [PATCH 040/153] Add btcwallet websocket example. Reviewed by @jrick who also provided some comment and terminology improvements. --- examples/btcwalletwebsockets/README.md | 38 ++++++++++++++ examples/btcwalletwebsockets/main.go | 71 ++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 examples/btcwalletwebsockets/README.md create mode 100644 examples/btcwalletwebsockets/main.go diff --git a/examples/btcwalletwebsockets/README.md b/examples/btcwalletwebsockets/README.md new file mode 100644 index 00000000..bd9a61fa --- /dev/null +++ b/examples/btcwalletwebsockets/README.md @@ -0,0 +1,38 @@ +btcwallet Websockets Example +============================ + +This example shows how to use the btcrpcclient package to connect to a btcwallet +RPC server using TLS-secured websockets, register for notifications about +changes to account balances, and get a list of unspent transaction outputs +(utxos) the wallet can sign. + +This example also sets a timer to shutdown the client after 10 seconds to +demonstrate clean shutdown. + +## Running the Example + +The first step is to use `go get` to download and install the btcrpcclient +package: + +```bash +$ go get github.com/conformal/btcrpcclient +``` + +Next, modify the `main.go` source to specify the correct RPC username and +password for the RPC server: + +```Go + User: "yourrpcuser", + Pass: "yourrpcpass", +``` + +Finally, navigate to the example's directory and run it with: + +```bash +$ cd $GOPATH/src/github.com/conformal/btcrpcclient/examples/btcwalletwebsockets +$ go run *.go +``` + +## License + +This example is licensed under the [copyfree](http://copyfree.org) ISC License. diff --git a/examples/btcwalletwebsockets/main.go b/examples/btcwalletwebsockets/main.go new file mode 100644 index 00000000..0b62773b --- /dev/null +++ b/examples/btcwalletwebsockets/main.go @@ -0,0 +1,71 @@ +// 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 main + +import ( + "github.com/conformal/btcrpcclient" + "github.com/conformal/btcutil" + "github.com/davecgh/go-spew/spew" + "io/ioutil" + "log" + "path/filepath" + "time" +) + +func main() { + // Only override the handlers for notifications you care about. + // Also note most of the handlers will only be called if you register + // for notifications. See the documentation of the btcrpcclient + // NotificationHandlers type for more details about each handler. + ntfnHandlers := btcrpcclient.NotificationHandlers{ + OnAccountBalance: func(account string, balance btcutil.Amount, confirmed bool) { + log.Printf("New balance for account %s: %v", account, + balance) + }, + } + + // Connect to local btcwallet RPC server using websockets. + certHomeDir := btcutil.AppDataDir("btcwallet", false) + certs, err := ioutil.ReadFile(filepath.Join(certHomeDir, "rpc.cert")) + if err != nil { + log.Fatal(err) + } + connCfg := &btcrpcclient.ConnConfig{ + Host: "localhost:18332", + Endpoint: "frontend", + User: "yourrpcuser", + Pass: "yourrpcpass", + Certificates: certs, + } + client, err := btcrpcclient.New(connCfg, &ntfnHandlers) + if err != nil { + log.Fatal(err) + } + + // Get the list of unspent transaction outputs (utxos) that the + // connected wallet has at least one private key for. + unspent, err := client.ListUnspent() + if err != nil { + log.Fatal(err) + } + log.Printf("Num unspent outputs (utxos): %d", len(unspent)) + if len(unspent) > 0 { + log.Printf("First utxo:\n%v", spew.Sdump(unspent[0])) + } + + // For this example gracefully shutdown the client after 10 seconds. + // Ordinarily when to shutdown the client is highly application + // specific. + log.Println("Client shutdown in 10 seconds...") + time.AfterFunc(time.Second*10, func() { + log.Println("Client shutting down...") + client.Shutdown() + log.Println("Client shutdown complete.") + }) + + // Wait until the client either shuts down gracefully (or the user + // terminates the process with Ctrl+C). + client.WaitForShutdown() +} From 7eea8252a4e7316dba411439330155f5eb70cbb0 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 5 Jun 2014 20:58:47 -0500 Subject: [PATCH 041/153] Add the new example to README.md and doc.go. --- README.md | 10 +++++++--- doc.go | 4 ++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a422e012..b7ecaab7 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,13 @@ implement and the API is not stable yet. * [API Reference](http://godoc.org/github.com/conformal/btcrpcclient) * [btcd Websockets Example](https://github.com/conformal/btcrpcclient/blob/master/examples/btcdwebsockets) - Connects to a btcd RPC server using TLS-secured websockets, - registers for block connected and block disconnected notifications, and gets - the current block count + Connects to a btcd RPC server using TLS-secured websockets, registers for + block connected and block disconnected notifications, and gets the current + block count +* [btcwallet Websockets Example](https://github.com/conformal/btcrpcclient/blob/master/examples/btcwalletwebsockets) + Connects to a btcwallet RPC server using TLS-secured websockets, registers for + notifications about changes to account balances, and gets a list of unspent + transaction outputs (utxos) the wallet can sign * [Bitcoin Core HTTP POST Example](https://github.com/conformal/btcrpcclient/blob/master/examples/bitcoincorehttp) Connects to a bitcoin core RPC server using HTTP POST mode with TLS disabled and gets the current block count diff --git a/doc.go b/doc.go index 3917a7db..b2604723 100644 --- a/doc.go +++ b/doc.go @@ -170,5 +170,9 @@ The following full-blown client examples are in the examples directory: Connects to a btcd RPC server using TLS-secured websockets, registers for block connected and block disconnected notifications, and gets the current block count + - btcwalletwebsockets + Connects to a btcwallet RPC server using TLS-secured websockets, registers + for notifications about changes to account balances, and gets a list of + unspent transaction outputs (utxos) the wallet can sign */ package btcrpcclient From 16024636810157d77765e0762457763f04b731a4 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 5 Jun 2014 23:57:05 -0500 Subject: [PATCH 042/153] Don't split up the group for debuglevel functions. --- extensions.go | 62 +++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/extensions.go b/extensions.go index eb404a5d..7337e40d 100644 --- a/extensions.go +++ b/extensions.go @@ -36,6 +36,37 @@ func (r FutureDebugLevelResult) Receive() (string, error) { 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() +} + // FutureCreateEncryptedWalletResult is a future promise to deliver the error // result of a CreateEncryptedWalletAsync RPC invocation. type FutureCreateEncryptedWalletResult chan *futureResult @@ -71,37 +102,6 @@ func (c *Client) CreateEncryptedWallet(passphrase string) error { return c.CreateEncryptedWalletAsync(passphrase).Receive() } -// 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 From 8e624b20a98af0bd307eb3a4adac103c1377bc8c Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sat, 7 Jun 2014 01:06:11 -0500 Subject: [PATCH 043/153] Switch to conformal vendoring of Gorilla websocket. --- infrastructure.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure.go b/infrastructure.go index f8ab2695..1c1997f4 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -16,7 +16,7 @@ import ( "github.com/conformal/btcjson" "github.com/conformal/btcws" "github.com/conformal/go-socks" - "github.com/gorilla/websocket" + "github.com/conformal/websocket" "net" "net/http" "net/url" From 793e66c785a3a9d1bc435d0dcd53277fa1ffbb0e Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Wed, 4 Jun 2014 14:58:31 -0500 Subject: [PATCH 044/153] Add callback for connects and reconnects. --- infrastructure.go | 10 +++++++++- notify.go | 12 +++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index 1c1997f4..ed6220ab 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -855,7 +855,15 @@ func (c *Client) start() { c.wg.Add(1) go c.sendPostHandler() } else { - c.wg.Add(2) + c.wg.Add(3) + go func() { + if c.ntfnHandlers != nil { + if c.ntfnHandlers.OnClientConnected != nil { + c.ntfnHandlers.OnClientConnected() + } + } + c.wg.Done() + }() go c.wsInHandler() go c.wsOutHandler() } diff --git a/notify.go b/notify.go index fa112e7d..10fd2198 100644 --- a/notify.go +++ b/notify.go @@ -79,10 +79,16 @@ func newNilFutureResult() chan *futureResult { // 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. +// NOTE: Unless otherwise documented, 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 { + // OnClientConnected is invoked when the client connects or reconnects + // to the RPC server. This callback is run async with the rest of the + // notification handlers, and is safe for blocking client requests. + OnClientConnected func() + // 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 From 1ec6dde39ce626d5a12f55c10618adda0acbb911 Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Mon, 9 Jun 2014 19:49:41 -0500 Subject: [PATCH 045/153] Add custom/raw request/response support. --- chain.go | 141 ++++++++-------- extensions.go | 95 ++++++----- infrastructure.go | 226 +++++++++++++------------- mining.go | 94 +++++------ net.go | 80 ++++----- notify.go | 392 ++++++++++++++++++++++++++++++++++++--------- rawrequest.go | 90 +++++++++++ rawtransactions.go | 94 +++++------ wallet.go | 374 +++++++++++++++++++++--------------------- 9 files changed, 948 insertions(+), 638 deletions(-) create mode 100644 rawrequest.go diff --git a/chain.go b/chain.go index 5808dd2a..53e6fdf9 100644 --- a/chain.go +++ b/chain.go @@ -7,7 +7,7 @@ package btcrpcclient import ( "bytes" "encoding/hex" - "fmt" + "encoding/json" "github.com/conformal/btcjson" "github.com/conformal/btcutil" "github.com/conformal/btcwire" @@ -15,23 +15,22 @@ import ( // FutureGetBestBlockHashResult is a future promise to deliver the result of a // GetBestBlockAsync RPC invocation (or an applicable error). -type FutureGetBestBlockHashResult chan *futureResult +type FutureGetBestBlockHashResult chan *response // 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) + res, 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) + // Unmarshal result as a string. + var txHashStr string + err = json.Unmarshal(res, &txHashStr) + if err != nil { + return nil, err } - return btcwire.NewShaHashFromStr(txHashStr) } @@ -58,21 +57,21 @@ func (c *Client) GetBestBlockHash() (*btcwire.ShaHash, error) { // FutureGetBlockResult is a future promise to deliver the result of a // GetBlockAsync RPC invocation (or an applicable error). -type FutureGetBlockResult chan *futureResult +type FutureGetBlockResult chan *response // 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) + res, 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) + // Unmarshal result as a string. + var blockHex string + err = json.Unmarshal(res, &blockHex) + if err != nil { + return nil, err } // Decode the serialized block hex to raw bytes. @@ -120,24 +119,23 @@ func (c *Client) GetBlock(blockHash *btcwire.ShaHash) (*btcutil.Block, error) { // FutureGetBlockVerboseResult is a future promise to deliver the result of a // GetBlockVerboseAsync RPC invocation (or an applicable error). -type FutureGetBlockVerboseResult chan *futureResult +type FutureGetBlockVerboseResult chan *response // 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) + res, 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) + // Unmarshal the raw result into a BlockResult. + var blockResult btcjson.BlockResult + err = json.Unmarshal(res, &blockResult) + if err != nil { + return nil, err } - - return blockResult, nil + return &blockResult, nil } // GetBlockVerboseAsync returns an instance of a type that can be used to get @@ -170,24 +168,23 @@ func (c *Client) GetBlockVerbose(blockHash *btcwire.ShaHash, verboseTx bool) (*b // FutureGetBlockCountResult is a future promise to deliver the result of a // GetBlockCountAsync RPC invocation (or an applicable error). -type FutureGetBlockCountResult chan *futureResult +type FutureGetBlockCountResult chan *response // 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) + res, 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) + // Unmarshal the result as an int64. + var count int64 + err = json.Unmarshal(res, &count) + if err != nil { + return 0, err } - - return int64(count), nil + return count, nil } // GetBlockCountAsync returns an instance of a type that can be used to get the @@ -212,23 +209,22 @@ func (c *Client) GetBlockCount() (int64, error) { // FutureGetDifficultyResult is a future promise to deliver the result of a // GetDifficultyAsync RPC invocation (or an applicable error). -type FutureGetDifficultyResult chan *futureResult +type FutureGetDifficultyResult chan *response // 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) + res, 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) + // Unmarshal the result as a float64. + var difficulty float64 + err = json.Unmarshal(res, &difficulty) + if err != nil { + return 0, err } - return difficulty, nil } @@ -255,23 +251,22 @@ func (c *Client) GetDifficulty() (float64, error) { // FutureGetBlockHashResult is a future promise to deliver the result of a // GetBlockHashAsync RPC invocation (or an applicable error). -type FutureGetBlockHashResult chan *futureResult +type FutureGetBlockHashResult chan *response // 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) + res, 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) + // Unmarshal the result as a string-encoded sha. + var txHashStr string + err = json.Unmarshal(res, &txHashStr) + if err != nil { + return nil, err } - return btcwire.NewShaHashFromStr(txHashStr) } @@ -298,23 +293,24 @@ func (c *Client) GetBlockHash(blockHeight int64) (*btcwire.ShaHash, error) { // FutureGetRawMempoolResult is a future promise to deliver the result of a // GetRawMempoolAsync RPC invocation (or an applicable error). -type FutureGetRawMempoolResult chan *futureResult +type FutureGetRawMempoolResult chan *response // 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) + res, 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) + // Unmarshal the result as an array of strings. + var txHashStrs []string + err = json.Unmarshal(res, &txHashStrs) + if err != nil { + return nil, err } + // Create a slice of ShaHash arrays from the string slice. txHashes := make([]*btcwire.ShaHash, 0, len(txHashStrs)) for _, hashStr := range txHashStrs { txHash, err := btcwire.NewShaHashFromStr(hashStr) @@ -352,24 +348,24 @@ func (c *Client) GetRawMempool() ([]*btcwire.ShaHash, error) { // FutureGetRawMempoolVerboseResult is a future promise to deliver the result of // a GetRawMempoolVerboseAsync RPC invocation (or an applicable error). -type FutureGetRawMempoolVerboseResult chan *futureResult +type FutureGetRawMempoolVerboseResult chan *response // 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) + res, 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) + // Unmarshal the result as a map of strings (tx shas) to their detailed + // results. + var mempoolItems map[string]btcjson.GetRawMempoolResult + err = json.Unmarshal(res, &mempoolItems) + if err != nil { + return nil, err } - return mempoolItems, nil } @@ -400,24 +396,23 @@ func (c *Client) GetRawMempoolVerbose() (map[string]btcjson.GetRawMempoolResult, // FutureVerifyChainResult is a future promise to deliver the result of a // VerifyChainAsync, VerifyChainLevelAsyncRPC, or VerifyChainBlocksAsync // invocation (or an applicable error). -type FutureVerifyChainResult chan *futureResult +type FutureVerifyChainResult chan *response // 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) + res, 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) + // Unmarshal the result as a boolean. + var verified bool + err = json.Unmarshal(res, &verified) + if err != nil { + return false, err } - return verified, nil } diff --git a/extensions.go b/extensions.go index 7337e40d..82a2582d 100644 --- a/extensions.go +++ b/extensions.go @@ -6,6 +6,7 @@ package btcrpcclient import ( "encoding/base64" + "encoding/json" "fmt" "github.com/conformal/btcjson" "github.com/conformal/btcutil" @@ -15,24 +16,23 @@ import ( // FutureDebugLevelResult is a future promise to deliver the result of a // DebugLevelAsync RPC invocation (or an applicable error). -type FutureDebugLevelResult chan *futureResult +type FutureDebugLevelResult chan *response // 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) + res, 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) + // Unmashal the result as a string. + var result string + err = json.Unmarshal(res, &result) + if err != nil { + return "", err } - return result, nil } @@ -69,7 +69,7 @@ func (c *Client) DebugLevel(levelSpec string) (string, error) { // FutureCreateEncryptedWalletResult is a future promise to deliver the error // result of a CreateEncryptedWalletAsync RPC invocation. -type FutureCreateEncryptedWalletResult chan *futureResult +type FutureCreateEncryptedWalletResult chan *response // Receive waits for and returns the error response promised by the future. func (r FutureCreateEncryptedWalletResult) Receive() error { @@ -104,28 +104,22 @@ func (c *Client) CreateEncryptedWallet(passphrase string) error { // FutureListAddressTransactionsResult is a future promise to deliver the result // of a ListAddressTransactionsAsync RPC invocation (or an applicable error). -type FutureListAddressTransactionsResult chan *futureResult +type FutureListAddressTransactionsResult chan *response // 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) + res, err := receiveFuture(r) if err != nil { return nil, err } - // No transactions. - if reply == nil { - return nil, nil + // Unmarshal the result as an array of listtransactions objects. + var transactions []btcjson.ListTransactionsResult + err = json.Unmarshal(res, &transactions) + if err != nil { + return nil, err } - - // 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 } @@ -161,30 +155,30 @@ func (c *Client) ListAddressTransactions(addresses []btcutil.Address, account st // FutureGetBestBlockResult is a future promise to deliver the result of a // GetBestBlockAsync RPC invocation (or an applicable error). -type FutureGetBestBlockResult chan *futureResult +type FutureGetBestBlockResult chan *response // 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) + res, 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 "+ - "getbestblock: %T\n", reply) + // Unmarsal result as a getbestblock result object. + var bestBlock btcws.GetBestBlockResult + err = json.Unmarshal(res, &bestBlock) + if err != nil { + return nil, 0, err } // Convert hash string. - hash, err := btcwire.NewShaHashFromStr(result.Hash) + hash, err := btcwire.NewShaHashFromStr(bestBlock.Hash) if err != nil { return nil, 0, err } - return hash, result.Height, nil + return hash, bestBlock.Height, nil } // GetBestBlockAsync returns an instance of a type that can be used to get the @@ -211,24 +205,24 @@ func (c *Client) GetBestBlock() (*btcwire.ShaHash, int32, error) { // FutureGetCurrentNetResult is a future promise to deliver the result of a // GetCurrentNetAsync RPC invocation (or an applicable error). -type FutureGetCurrentNetResult chan *futureResult +type FutureGetCurrentNetResult chan *response // 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) + res, 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) + // Unmarshal result as an int64. + var net int64 + err = json.Unmarshal(res, &net) + if err != nil { + return 0, err } - return btcwire.BitcoinNet(fnet), nil + return btcwire.BitcoinNet(net), nil } // GetCurrentNetAsync returns an instance of a type that can be used to get the @@ -254,34 +248,35 @@ func (c *Client) GetCurrentNet() (btcwire.BitcoinNet, error) { // FutureExportWatchingWalletResult is a future promise to deliver the result of // an ExportWatchingWalletAsync RPC invocation (or an applicable error). -type FutureExportWatchingWalletResult chan *futureResult +type FutureExportWatchingWalletResult chan *response // 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) + res, 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) + // Unmarshal result as a JSON object. + var obj map[string]interface{} + err = json.Unmarshal(res, &obj) + if err != nil { + return nil, nil, err } - base64Wallet, ok := result["wallet"].(string) + // Check for the wallet and tx string fields in the object. + base64Wallet, ok := obj["wallet"].(string) if !ok { return nil, nil, fmt.Errorf("unexpected response type for "+ "exportwatchingwallet 'wallet' field: %T\n", - result["wallet"]) + obj["wallet"]) } - base64TxStore, ok := result["tx"].(string) + base64TxStore, ok := obj["tx"].(string) if !ok { return nil, nil, fmt.Errorf("unexpected response type for "+ "exportwatchingwallet 'tx' field: %T\n", - result["tx"]) + obj["tx"]) } walletBytes, err := base64.StdEncoding.DecodeString(base64Wallet) diff --git a/infrastructure.go b/infrastructure.go index ed6220ab..c56dc398 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -64,27 +64,20 @@ const ( 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 + responseChan chan *response } // 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 + responseChan chan *response } // Client represents a Bitcoin RPC client which allows easy access to the @@ -231,70 +224,96 @@ func (c *Client) trackRegisteredNtfns(cmd btcjson.Cmd) { } } -// 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. +type ( + // inMessage is the first type that an incoming message is unmarshaled + // into. It supports both requests (for notification support) and + // responses. The partially-unmarshaled message is a notification if + // the embedded ID (from the response) is nil. Otherwise, it is a + // response. + inMessage struct { + ID *uint64 `json:"id"` + *rawNotification + *rawResponse + } + + // rawNotification is a partially-unmarshaled JSON-RPC notification. + rawNotification struct { + Method string `json:"method"` + Params []json.RawMessage `json:"params"` + } + + // rawResponse is a partially-unmarshaled JSON-RPC response. For this + // to be valid (according to JSON-RPC 1.0 spec), ID may not be nil. + rawResponse struct { + Result json.RawMessage `json:"result"` + Error *btcjson.Error `json:"error"` + } +) + +// response is the raw bytes of a JSON-RPC result, or the error if the response +// error object was non-null. +type response struct { + result []byte + err error +} + +// result checks whether the unmarshaled response contains a non-nil error, +// returning an unmarshaled btcjson.Error (or an unmarshaling error) if so. +// If the response is not an error, the raw bytes of the request are +// returned for further unmashaling into specific result types. +func (r rawResponse) result() (result []byte, err error) { + if r.Error != nil { + return nil, r.Error + } + return r.Result, nil +} + +// handleMessage is the main handler for incoming notifications and responses. func (c *Client) handleMessage(msg []byte) { - // Attempt to unmarshal the message as a known JSON-RPC command. - if cmd, err := btcjson.ParseMarshaledCmd(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()) + // Attempt to unmarshal the message as either a notifiation or response. + in := inMessage{} + err := json.Unmarshal(msg, &in) + if err != nil { + log.Warnf("Remote server sent invalid message: %v", err) + return + } + + // JSON-RPC 1.0 notifications are requests with a null id. + if in.ID == nil { + ntfn := in.rawNotification + if ntfn == nil { + log.Warn("Malformed notification: missing " + + "method and parameters") + return + } + if ntfn.Method == "" { + log.Warn("Malformed notification: missing method") + return + } + // params are not optional: nil isn't valid (but len == 0 is) + if ntfn.Params == nil { + log.Warn("Malformed notification: missing params") return } - // Deliver the notification. - log.Tracef("Received notification [%s]", cmd.Method()) - c.handleNotification(cmd) + log.Tracef("Received notification [%s]", in.Method) + c.handleNotification(in.rawNotification) 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") + if in.rawResponse == nil { + log.Warn("Malformed response: missing result and error") 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 := uint64(fid) - log.Tracef("Received response for id %d (result %v)", id, r.Result) + id := *in.ID + log.Tracef("Received response for id %d (result %s)", id, in.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} + log.Warnf("Received unexpected reply: %s (id %d)", in.Result, + id) return } @@ -303,8 +322,9 @@ func (c *Client) handleMessage(msg []byte) { // can automatically be re-established on reconnect. c.trackRegisteredNtfns(request.cmd) - // Deliver the reply. - request.responseChan <- &futureResult{reply: &reply, err: nil} + // Deliver the response. + result, err := in.rawResponse.result() + request.responseChan <- &response{result: result, err: err} } // wsInHandler handles all incoming messages for the websocket connection @@ -576,24 +596,17 @@ func (c *Client) handleSendPostMessage(details *sendPostDetails) { 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} + details.responseChan <- &response{result: nil, err: err} return } // Read the raw bytes and close the response. - respBytes, err := btcjson.GetRaw(httpResponse.Body) + resp, err := btcjson.GetRaw(httpResponse.Body) if err != nil { - details.responseChan <- &futureResult{reply: nil, err: err} + details.responseChan <- &response{result: 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} + details.responseChan <- &response{result: resp, err: nil} } // sendPostHandler handles all outgoing messages when the client is running @@ -620,9 +633,9 @@ cleanup: for { select { case details := <-c.sendPostChan: - details.responseChan <- &futureResult{ - reply: nil, - err: ErrClientShutdown, + details.responseChan <- &response{ + result: nil, + err: ErrClientShutdown, } default: @@ -637,17 +650,17 @@ cleanup: // 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) { +func (c *Client) sendPostRequest(req *http.Request, command btcjson.Cmd, responseChan chan *response) { // Don't send the message if shutting down. select { case <-c.shutdown: - responseChan <- &futureResult{reply: nil, err: ErrClientShutdown} + responseChan <- &response{result: nil, err: ErrClientShutdown} default: } c.sendPostChan <- &sendPostDetails{ - request: req, command: command, + request: req, responseChan: responseChan, } } @@ -655,9 +668,9 @@ func (c *Client) sendPostRequest(req *http.Request, command btcjson.Cmd, respons // 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} +func newFutureError(err error) chan *response { + responseChan := make(chan *response, 1) + responseChan <- &response{err: err} return responseChan } @@ -665,27 +678,10 @@ func newFutureError(err error) chan *futureResult { // 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) { +func receiveFuture(f chan *response) ([]byte, 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 + r := <-f + return r.result, r.err } // marshalAndSendPost marshals the passed command to JSON-RPC and sends it to @@ -694,10 +690,10 @@ func receiveFuture(responseChan chan *futureResult) (interface{}, error) { // 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) { +func (c *Client) marshalAndSendPost(cmd btcjson.Cmd, responseChan chan *response) { marshalledJSON, err := json.Marshal(cmd) if err != nil { - responseChan <- &futureResult{reply: nil, err: err} + responseChan <- &response{result: nil, err: err} return } @@ -709,7 +705,7 @@ func (c *Client) marshalAndSendPost(cmd btcjson.Cmd, responseChan chan *futureRe url := protocol + "://" + c.config.Host req, err := http.NewRequest("POST", url, bytes.NewReader(marshalledJSON)) if err != nil { - responseChan <- &futureResult{reply: nil, err: err} + responseChan <- &response{result: nil, err: err} return } req.Close = true @@ -724,10 +720,10 @@ func (c *Client) marshalAndSendPost(cmd btcjson.Cmd, responseChan chan *futureRe // 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) +func (c *Client) marshalAndSend(cmd btcjson.Cmd, responseChan chan *response) { + marshalledJSON, err := cmd.MarshalJSON() if err != nil { - responseChan <- &futureResult{reply: nil, err: err} + responseChan <- &response{result: nil, err: err} return } @@ -739,12 +735,12 @@ func (c *Client) marshalAndSend(cmd btcjson.Cmd, responseChan chan *futureResult // 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 { +func (c *Client) sendCmd(cmd btcjson.Cmd) chan *response { // 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) + responseChan := make(chan *response, 1) if c.config.HttpPostMode { c.marshalAndSendPost(cmd, responseChan) return responseChan @@ -804,9 +800,9 @@ func (c *Client) Disconnect() { c.requestLock.Lock() for e := c.requestList.Front(); e != nil; e = e.Next() { req := e.Value.(*jsonRequest) - req.responseChan <- &futureResult{ - reply: nil, - err: ErrClientDisconnect, + req.responseChan <- &response{ + result: nil, + err: ErrClientDisconnect, } } c.requestLock.Unlock() @@ -834,9 +830,9 @@ func (c *Client) Shutdown() { c.requestLock.Lock() for e := c.requestList.Front(); e != nil; e = e.Next() { req := e.Value.(*jsonRequest) - req.responseChan <- &futureResult{ - reply: nil, - err: ErrClientShutdown, + req.responseChan <- &response{ + result: nil, + err: ErrClientShutdown, } } c.requestLock.Unlock() diff --git a/mining.go b/mining.go index 8336d239..b98f89a5 100644 --- a/mining.go +++ b/mining.go @@ -6,28 +6,28 @@ package btcrpcclient import ( "encoding/hex" - "fmt" + "encoding/json" "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 +type FutureGetGenerateResult chan *response // 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) + res, 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) + // Unmarshal result as a boolean. + var result bool + err = json.Unmarshal(res, &result) + if err != nil { + return false, err } return result, nil @@ -55,7 +55,7 @@ func (c *Client) GetGenerate() (bool, error) { // FutureSetGenerateResult is a future promise to deliver the result of a // SetGenerateAsync RPC invocation (or an applicable error). -type FutureSetGenerateResult chan *futureResult +type FutureSetGenerateResult chan *response // 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. @@ -90,22 +90,22 @@ func (c *Client) SetGenerate(enable bool, numCPUs int) error { // FutureGetHashesPerSecResult is a future promise to deliver the result of a // GetHashesPerSecAsync RPC invocation (or an applicable error). -type FutureGetHashesPerSecResult chan *futureResult +type FutureGetHashesPerSecResult chan *response // 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) + res, 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) + // Unmarshal result as an int64. + var result int64 + err = json.Unmarshal(res, &result) + if err != nil { + return 0, err } return result, nil @@ -135,24 +135,24 @@ func (c *Client) GetHashesPerSec() (int64, error) { // FutureGetMiningInfoResult is a future promise to deliver the result of a // GetMiningInfoAsync RPC invocation (or an applicable error). -type FutureGetMiningInfoResult chan *futureResult +type FutureGetMiningInfoResult chan *response // 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) + res, 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) + // Unmarshal result as a getmininginfo result object. + var infoResult btcjson.GetMiningInfoResult + err = json.Unmarshal(res, &infoResult) + if err != nil { + return nil, err } - return result, nil + return &infoResult, nil } // GetMiningInfoAsync returns an instance of a type that can be used to get @@ -177,22 +177,22 @@ func (c *Client) GetMiningInfo() (*btcjson.GetMiningInfoResult, error) { // FutureGetNetworkHashPS is a future promise to deliver the result of a // GetNetworkHashPSAsync RPC invocation (or an applicable error). -type FutureGetNetworkHashPS chan *futureResult +type FutureGetNetworkHashPS chan *response // 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) + res, 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) + // Unmarshal result as an int64. + var result int64 + err = json.Unmarshal(res, &result) + if err != nil { + return 0, err } return result, nil @@ -275,24 +275,24 @@ func (c *Client) GetNetworkHashPS3(blocks, height int) (int64, error) { // FutureGetWork is a future promise to deliver the result of a // GetWorkAsync RPC invocation (or an applicable error). -type FutureGetWork chan *futureResult +type FutureGetWork chan *response // 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) + res, 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 (request data): %T\n", reply) + // Unmarshal result as a getwork result object. + var result btcjson.GetWorkResult + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err } - return result, nil + return &result, nil } // GetWorkAsync returns an instance of a type that can be used to get the result @@ -319,21 +319,21 @@ func (c *Client) GetWork() (*btcjson.GetWorkResult, error) { // FutureGetWorkSubmit is a future promise to deliver the result of a // GetWorkSubmitAsync RPC invocation (or an applicable error). -type FutureGetWorkSubmit chan *futureResult +type FutureGetWorkSubmit chan *response // Receive waits for the response promised by the future and returns whether // or not the submitted block header was accepted. func (r FutureGetWorkSubmit) Receive() (bool, error) { - reply, err := receiveFuture(r) + res, err := receiveFuture(r) if err != nil { return false, err } - // Ensure the returned data is the expected type. - accepted, ok := reply.(bool) - if !ok { - return false, fmt.Errorf("unexpected response type for "+ - "getwork (submit data): %T\n", reply) + // Unmarshal result as a boolean. + var accepted bool + err = json.Unmarshal(res, &accepted) + if err != nil { + return false, err } return accepted, nil @@ -364,7 +364,7 @@ func (c *Client) GetWorkSubmit(data string) (bool, error) { // FutureSubmitBlockResult is a future promise to deliver the result of a // SubmitBlockAsync RPC invocation (or an applicable error). -type FutureSubmitBlockResult chan *futureResult +type FutureSubmitBlockResult chan *response // Receive waits for the response promised by the future and returns an error if // any occurred when submitting the block. diff --git a/net.go b/net.go index 5cf6f4d4..3b37515c 100644 --- a/net.go +++ b/net.go @@ -5,7 +5,7 @@ package btcrpcclient import ( - "fmt" + "encoding/json" "github.com/conformal/btcjson" ) @@ -34,7 +34,7 @@ func (cmd AddNodeCommand) String() string { // FutureAddNodeResult is a future promise to deliver the result of an // AddNodeAsync RPC invocation (or an applicable error). -type FutureAddNodeResult chan *futureResult +type FutureAddNodeResult chan *response // Receive waits for the response promised by the future and returns an error if // any occurred when performing the specified command. @@ -73,21 +73,21 @@ func (c *Client) AddNode(host string, command AddNodeCommand) error { // FutureGetAddedNodeInfoResult is a future promise to deliver the result of a // GetAddedNodeInfoAsync RPC invocation (or an applicable error). -type FutureGetAddedNodeInfoResult chan *futureResult +type FutureGetAddedNodeInfoResult chan *response // 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) + res, 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) + // Unmarshal as an array of getaddednodeinfo result objects. + var nodeInfo []btcjson.GetAddedNodeInfoResult + err = json.Unmarshal(res, &nodeInfo) + if err != nil { + return nil, err } return nodeInfo, nil @@ -118,21 +118,21 @@ func (c *Client) GetAddedNodeInfo(peer string) ([]btcjson.GetAddedNodeInfoResult // FutureGetAddedNodeInfoNoDNSResult is a future promise to deliver the result // of a GetAddedNodeInfoNoDNSAsync RPC invocation (or an applicable error). -type FutureGetAddedNodeInfoNoDNSResult chan *futureResult +type FutureGetAddedNodeInfoNoDNSResult chan *response // 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) + res, 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) + // Unmarshal result as an array of strings. + var nodes []string + err = json.Unmarshal(res, &nodes) + if err != nil { + return nil, err } return nodes, nil @@ -164,24 +164,24 @@ func (c *Client) GetAddedNodeInfoNoDNS(peer string) ([]string, error) { // FutureGetConnectionCountResult is a future promise to deliver the result // of a GetConnectionCountAsync RPC invocation (or an applicable error). -type FutureGetConnectionCountResult chan *futureResult +type FutureGetConnectionCountResult chan *response // 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) + res, 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) + // Unmarshal result as an int64. + var count int64 + err = json.Unmarshal(res, &count) + if err != nil { + return 0, err } - return int64(fcount), nil + return count, nil } // GetConnectionCountAsync returns an instance of a type that can be used to get @@ -206,7 +206,7 @@ func (c *Client) GetConnectionCount() (int64, error) { // FuturePingResult is a future promise to deliver the result of a PingAsync RPC // invocation (or an applicable error). -type FuturePingResult chan *futureResult +type FuturePingResult chan *response // Receive waits for the response promised by the future and returns the result // of queueing a ping to be sent to each connected peer. @@ -244,21 +244,21 @@ func (c *Client) Ping() error { // FutureGetPeerInfoResult is a future promise to deliver the result of a // GetPeerInfoAsync RPC invocation (or an applicable error). -type FutureGetPeerInfoResult chan *futureResult +type FutureGetPeerInfoResult chan *response // 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) + res, 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) + // Unmarshal result as an array of getpeerinfo result objects. + var peerInfo []btcjson.GetPeerInfoResult + err = json.Unmarshal(res, &peerInfo) + if err != nil { + return nil, err } return peerInfo, nil @@ -286,24 +286,24 @@ func (c *Client) GetPeerInfo() ([]btcjson.GetPeerInfoResult, error) { // FutureGetNetTotalsResult is a future promise to deliver the result of a // GetNetTotalsAsync RPC invocation (or an applicable error). -type FutureGetNetTotalsResult chan *futureResult +type FutureGetNetTotalsResult chan *response // 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) + res, 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) + // Unmarshal result as a getnettotals result object. + var totals btcjson.GetNetTotalsResult + err = json.Unmarshal(res, &totals) + if err != nil { + return nil, err } - return totals, nil + return &totals, nil } // GetNetTotalsAsync returns an instance of a type that can be used to get the diff --git a/notify.go b/notify.go index 10fd2198..198abb84 100644 --- a/notify.go +++ b/notify.go @@ -7,7 +7,9 @@ package btcrpcclient import ( "bytes" "encoding/hex" + "encoding/json" "errors" + "fmt" "github.com/conformal/btcjson" "github.com/conformal/btcutil" "github.com/conformal/btcwire" @@ -68,9 +70,9 @@ func newNotificationState() *notificationState { // 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} +func newNilFutureResult() chan *response { + responseChan := make(chan *response, 1) + responseChan <- &response{result: nil, err: nil} return responseChan } @@ -161,183 +163,192 @@ type NotificationHandlers struct { // 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{}) + OnUnknownNotification func(method string, params []json.RawMessage) } // 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) { +func (c *Client) handleNotification(ntfn *rawNotification) { // Ignore the notification if the client is not interested in any // notifications. if c.ntfnHandlers == nil { return } - switch ntfn := cmd.(type) { + switch ntfn.Method { // OnBlockConnected - case *btcws.BlockConnectedNtfn: + case btcws.BlockConnectedNtfnMethod: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnBlockConnected == nil { return } - hash, err := btcwire.NewShaHashFromStr(ntfn.Hash) + blockSha, blockHeight, err := parseChainNtfnParams(ntfn.Params) if err != nil { - log.Warnf("Received block connected notification with "+ - "invalid hash string: %q", ntfn.Hash) + log.Warnf("Received invalid block connected "+ + "notification: %v", err) return } - c.ntfnHandlers.OnBlockConnected(hash, ntfn.Height) + c.ntfnHandlers.OnBlockConnected(blockSha, blockHeight) // OnBlockDisconnected - case *btcws.BlockDisconnectedNtfn: + case btcws.BlockDisconnectedNtfnMethod: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnBlockDisconnected == nil { return } - hash, err := btcwire.NewShaHashFromStr(ntfn.Hash) + blockSha, blockHeight, err := parseChainNtfnParams(ntfn.Params) if err != nil { - log.Warnf("Received block disconnected notification "+ - "with invalid hash string: %q", ntfn.Hash) + log.Warnf("Received invalid block connected "+ + "notification: %v", err) return } - c.ntfnHandlers.OnBlockDisconnected(hash, ntfn.Height) + c.ntfnHandlers.OnBlockDisconnected(blockSha, blockHeight) // OnRecvTx - case *btcws.RecvTxNtfn: + case btcws.RecvTxNtfnMethod: // 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) + tx, block, err := parseChainTxNtfnParams(ntfn.Params) 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", + log.Warnf("Received invalid recvtx notification: %v", err) + return } - c.ntfnHandlers.OnRecvTx(btcutil.NewTx(&msgTx), ntfn.Block) + c.ntfnHandlers.OnRecvTx(tx, block) // OnRedeemingTx - case *btcws.RedeemingTxNtfn: + case btcws.RedeemingTxNtfnMethod: // 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) + tx, block, err := parseChainTxNtfnParams(ntfn.Params) if err != nil { - log.Warnf("Received redeemingtx notification with "+ - "invalid transaction hex '%q': %v", ntfn.HexTx, - err) + log.Warnf("Received invalid redeemingtx "+ + "notification: %v", err) + return } - // 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) + c.ntfnHandlers.OnRedeemingTx(tx, block) // OnRescanProgress - case *btcws.RescanProgressNtfn: + case btcws.RescanProgressNtfnMethod: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnRescanProgress == nil { return } - c.ntfnHandlers.OnRescanProgress(ntfn.LastProcessed) + lastProcessed, err := parseRescanProgressNtfnParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid rescanprogress "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnRescanProgress(lastProcessed) // OnTxAccepted - case *btcws.TxAcceptedNtfn: + case btcws.TxAcceptedNtfnMethod: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnTxAccepted == nil { return } - hash, err := btcwire.NewShaHashFromStr(ntfn.TxID) + hash, amt, err := parseTxAcceptedNtfnParams(ntfn.Params) if err != nil { - log.Warnf("Received tx accepted notification with "+ - "invalid hash string: %q", ntfn.TxID) + log.Warnf("Received invalid tx accepted "+ + "notification: %v", err) return } - c.ntfnHandlers.OnTxAccepted(hash, btcutil.Amount(ntfn.Amount)) + c.ntfnHandlers.OnTxAccepted(hash, amt) // OnTxAcceptedVerbose - case *btcws.TxAcceptedVerboseNtfn: + case btcws.TxAcceptedVerboseNtfnMethod: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnTxAcceptedVerbose == nil { return } - c.ntfnHandlers.OnTxAcceptedVerbose(ntfn.RawTx) + rawTx, err := parseTxAcceptedVerboseNtfnParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid tx accepted verbose "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnTxAcceptedVerbose(rawTx) // OnBtcdConnected - case *btcws.BtcdConnectedNtfn: + case btcws.BtcdConnectedNtfnMethod: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnBtcdConnected == nil { return } - c.ntfnHandlers.OnBtcdConnected(ntfn.Connected) + connected, err := parseBtcdConnectedNtfnParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid btcd connected "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnBtcdConnected(connected) // OnAccountBalance - case *btcws.AccountBalanceNtfn: + case btcws.AccountBalanceNtfnMethod: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnAccountBalance == nil { return } - balance, err := btcjson.JSONToAmount(ntfn.Balance) + account, bal, conf, err := parseAccountBalanceNtfnParams(ntfn.Params) if err != nil { - log.Warnf("Received account balance notification with "+ - "an amount that does not parse: %v", - ntfn.Balance) + log.Warnf("Received invalid account balance "+ + "notification: %v", err) return } - c.ntfnHandlers.OnAccountBalance(ntfn.Account, - btcutil.Amount(balance), ntfn.Confirmed) + c.ntfnHandlers.OnAccountBalance(account, bal, conf) // OnWalletLockState - case *btcws.WalletLockStateNtfn: + case btcws.WalletLockStateNtfnMethod: // Ignore the notification is the client is not interested in // it. if c.ntfnHandlers.OnWalletLockState == nil { return } - c.ntfnHandlers.OnWalletLockState(ntfn.Locked) + // The account name is not notified, so the return value is + // discarded. + _, locked, err := parseWalletLockStateNtfnParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid wallet lock state "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnWalletLockState(locked) // OnUnknownNotification default: @@ -345,13 +356,250 @@ func (c *Client) handleNotification(cmd btcjson.Cmd) { return } - c.ntfnHandlers.OnUnknownNotification(ntfn) + c.ntfnHandlers.OnUnknownNotification(ntfn.Method, ntfn.Params) } } +// wrongNumParams is an error type describing an unparseable JSON-RPC +// notificiation due to an incorrect number of parameters for the +// expected notification type. The value is the number of parameters +// of the invalid notification. +type wrongNumParams int + +// Error satisifies the builtin error interface. +func (e wrongNumParams) Error() string { + return fmt.Sprintf("wrong number of parameters (%d)", e) +} + +// parseChainNtfnParams parses out the block hash and height from the parameters +// of blockconnected and blockdisconnected notifications. +func parseChainNtfnParams(params []json.RawMessage) (*btcwire.ShaHash, + int32, error) { + + if len(params) != 2 { + return nil, 0, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as a string. + var blockShaStr string + err := json.Unmarshal(params[0], &blockShaStr) + if err != nil { + return nil, 0, err + } + + // Unmarshal second parameter as an integer. + var blockHeight int32 + err = json.Unmarshal(params[1], &blockHeight) + if err != nil { + return nil, 0, err + } + + // Create ShaHash from block sha string. + blockSha, err := btcwire.NewShaHashFromStr(blockShaStr) + if err != nil { + return nil, 0, err + } + + return blockSha, blockHeight, nil +} + +// parseChainTxNtfnParams parses out the transaction and optional details about +// the block it's mined in from the parameters of recvtx and redeemingtx +// notifications. +func parseChainTxNtfnParams(params []json.RawMessage) (*btcutil.Tx, + *btcws.BlockDetails, error) { + + if len(params) == 0 || len(params) > 2 { + return nil, nil, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as a string. + var txHex string + err := json.Unmarshal(params[0], &txHex) + if err != nil { + return nil, nil, err + } + + // If present, unmarshal second optional parameter as the block details + // JSON object. + var block *btcws.BlockDetails + if len(params) > 1 { + err = json.Unmarshal(params[1], &block) + if err != nil { + return nil, nil, err + } + } + + // Hex decode and deserialize the transaction. + serializedTx, err := hex.DecodeString(txHex) + if err != nil { + return nil, nil, err + } + var msgTx btcwire.MsgTx + err = msgTx.Deserialize(bytes.NewReader(serializedTx)) + if err != nil { + return nil, nil, err + } + + // TODO: Change recvtx and redeemingtx callback signatures to use + // nicer types for details about the block (block sha as a + // btcwire.ShaHash, block time as a time.Time, etc.). + return btcutil.NewTx(&msgTx), block, nil +} + +// parseRescanProgressNtfnParams parses out the height of the last rescanned +// from the parameters of a rescanprogress notification. +func parseRescanProgressNtfnParams(params []json.RawMessage) (int32, error) { + if len(params) != 1 { + return 0, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as an integer. + var height int32 + err := json.Unmarshal(params[0], &height) + if err != nil { + return 0, err + } + + return height, nil +} + +// parseTxAcceptedNtfnParams parses out the transaction hash and total amount +// from the parameters of a txaccepted notification. +func parseTxAcceptedNtfnParams(params []json.RawMessage) (*btcwire.ShaHash, + btcutil.Amount, error) { + + if len(params) != 2 { + return nil, 0, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as a string. + var txShaStr string + err := json.Unmarshal(params[0], &txShaStr) + if err != nil { + return nil, 0, err + } + + // Unmarshal second parameter as an integer. + var amt int64 + err = json.Unmarshal(params[1], &amt) + if err != nil { + return nil, 0, err + } + + // Decode string encoding of transaction sha. + txSha, err := btcwire.NewShaHashFromStr(txShaStr) + if err != nil { + return nil, 0, err + } + + return txSha, btcutil.Amount(amt), nil +} + +// parseTxAcceptedVerboseNtfnParams parses out details about a raw transaction +// from the parameters of a txacceptedverbose notification. +func parseTxAcceptedVerboseNtfnParams(params []json.RawMessage) (*btcjson.TxRawResult, + error) { + + if len(params) != 1 { + return nil, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as a raw transaction result object. + var rawTx btcjson.TxRawResult + err := json.Unmarshal(params[0], &rawTx) + if err != nil { + return nil, err + } + + // TODO: change txacceptedverbose notifiation callbacks to use nicer + // types for all details about the transaction (i.e. decoding hashes + // from their string encoding). + return &rawTx, nil +} + +// parseBtcdConnectedNtfnParams parses out the connection status of btcd +// and btcwallet from the parameters of a btcdconnected notification. +func parseBtcdConnectedNtfnParams(params []json.RawMessage) (bool, error) { + if len(params) != 1 { + return false, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as a boolean. + var connected bool + err := json.Unmarshal(params[0], &connected) + if err != nil { + return false, err + } + + return connected, nil +} + +// parseAccountBalanceNtfnParams parses out the account name, total balance, +// and whether or not the balance is confirmed or unconfirmed from the +// parameters of an accountbalance notification. +func parseAccountBalanceNtfnParams(params []json.RawMessage) (account string, + balance btcutil.Amount, confirmed bool, err error) { + + if len(params) != 3 { + return "", 0, false, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as a string. + err = json.Unmarshal(params[0], &account) + if err != nil { + return "", 0, false, err + } + + // Unmarshal second parameter as a floating point number. + var fbal float64 + err = json.Unmarshal(params[1], &fbal) + if err != nil { + return "", 0, false, err + } + + // Unmarshal third parameter as a boolean. + err = json.Unmarshal(params[2], &confirmed) + if err != nil { + return "", 0, false, err + } + + // Bounds check amount. + bal, err := btcjson.JSONToAmount(fbal) + if err != nil { + return "", 0, false, err + } + + return account, btcutil.Amount(bal), confirmed, nil +} + +// parseWalletLockStateNtfnParams parses out the account name and locked +// state of an account from the parameters of a walletlockstate notification. +func parseWalletLockStateNtfnParams(params []json.RawMessage) (account string, + locked bool, err error) { + + if len(params) != 2 { + return "", false, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as a string. + err = json.Unmarshal(params[0], &account) + if err != nil { + return "", false, err + } + + // Unmarshal second parameter as a boolean. + err = json.Unmarshal(params[1], &locked) + if err != nil { + return "", false, err + } + + return account, locked, nil +} + // FutureNotifyBlocksResult is a future promise to deliver the result of a // NotifyBlocksAsync RPC invocation (or an applicable error). -type FutureNotifyBlocksResult chan *futureResult +type FutureNotifyBlocksResult chan *response // Receive waits for the response promised by the future and returns an error // if the registration was not successful. @@ -405,7 +653,7 @@ func (c *Client) NotifyBlocks() error { // FutureNotifySpentResult is a future promise to deliver the result of a // NotifySpentAsync RPC invocation (or an applicable error). -type FutureNotifySpentResult chan *futureResult +type FutureNotifySpentResult chan *response // Receive waits for the response promised by the future and returns an error // if the registration was not successful. @@ -484,7 +732,7 @@ func (c *Client) NotifySpent(outpoints []*btcwire.OutPoint) error { // FutureNotifyNewTransactionsResult is a future promise to deliver the result // of a NotifyNewTransactionsAsync RPC invocation (or an applicable error). -type FutureNotifyNewTransactionsResult chan *futureResult +type FutureNotifyNewTransactionsResult chan *response // Receive waits for the response promised by the future and returns an error // if the registration was not successful. @@ -542,7 +790,7 @@ func (c *Client) NotifyNewTransactions(verbose bool) error { // FutureNotifyReceivedResult is a future promise to deliver the result of a // NotifyReceivedAsync RPC invocation (or an applicable error). -type FutureNotifyReceivedResult chan *futureResult +type FutureNotifyReceivedResult chan *response // Receive waits for the response promised by the future and returns an error // if the registration was not successful. @@ -630,7 +878,7 @@ func (c *Client) NotifyReceived(addresses []btcutil.Address) error { // FutureRescanResult is a future promise to deliver the result of a RescanAsync // or RescanEndHeightAsync RPC invocation (or an applicable error). -type FutureRescanResult chan *futureResult +type FutureRescanResult chan *response // Receive waits for the response promised by the future and returns an error // if the rescan was not successful. diff --git a/rawrequest.go b/rawrequest.go new file mode 100644 index 00000000..2792a433 --- /dev/null +++ b/rawrequest.go @@ -0,0 +1,90 @@ +// 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/json" + "errors" + "github.com/conformal/btcjson" +) + +// rawRequest satisifies the btcjson.Cmd interface for btcjson raw commands. +// This type exists here rather than making btcjson.RawCmd satisify the Cmd +// interface due to conflict between the Id and Method field names vs method +// names. +type rawRequest struct { + btcjson.RawCmd +} + +// Enforce that rawRequest is a btcjson.Cmd. +var _ btcjson.Cmd = &rawRequest{} + +// Id returns the JSON-RPC id of the request. +func (r *rawRequest) Id() interface{} { + return r.RawCmd.Id +} + +// Method returns the method string of the request. +func (r *rawRequest) Method() string { + return r.RawCmd.Method +} + +// MarshalJSON marshals the raw request as a JSON-RPC 1.0 object. +func (r *rawRequest) MarshalJSON() ([]byte, error) { + return json.Marshal(r.RawCmd) +} + +// UnmarshalJSON unmarshals a JSON-RPC 1.0 object into the raw request. +func (r *rawRequest) UnmarshalJSON(b []byte) error { + return json.Unmarshal(b, &r.RawCmd) +} + +// FutureRawResult is a future promise to deliver the result of a RawRequest RPC +// invocation (or an applicable error). +type FutureRawResult chan *response + +// Receive waits for the response promised by the future and returns the raw +// response, or an error if the request was unsuccessful. +func (r FutureRawResult) Receive() (json.RawMessage, error) { + return receiveFuture(r) +} + +// RawRequestAsync returns an instance of a type that can be used to get the +// result of a custom RPC request at some future time by invoking the Receive +// function on the returned instance. +// +// See RawRequest for the blocking version and more details. +func (c *Client) RawRequestAsync(method string, params []json.RawMessage) FutureRawResult { + // Method may not be empty. + if method == "" { + return newFutureError(errors.New("no method")) + } + + // Marshal parameters as "[]" instead of "null" when no parameters + // are passed. + if params == nil { + params = []json.RawMessage{} + } + + cmd := &rawRequest{ + RawCmd: btcjson.RawCmd{ + Jsonrpc: "1.0", + Id: c.NextID(), + Method: method, + Params: params, + }, + } + + return c.sendCmd(cmd) +} + +// RawRequest allows the caller to send a raw or custom request to the server. +// This method may be used to send and receive requests and responses for +// requests that are not handled by this client package, or to proxy partially +// unmarshaled requests to another JSON-RPC server if a request cannot be +// handled directly. +func (c *Client) RawRequest(method string, params []json.RawMessage) (json.RawMessage, error) { + return c.RawRequestAsync(method, params).Receive() +} diff --git a/rawtransactions.go b/rawtransactions.go index 95a7abec..bfd6cd9c 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -7,7 +7,7 @@ package btcrpcclient import ( "bytes" "encoding/hex" - "fmt" + "encoding/json" "github.com/conformal/btcjson" "github.com/conformal/btcutil" "github.com/conformal/btcwire" @@ -58,21 +58,21 @@ func (s SigHashType) String() string { // FutureGetRawTransactionResult is a future promise to deliver the result of a // GetRawTransactionAsync RPC invocation (or an applicable error). -type FutureGetRawTransactionResult chan *futureResult +type FutureGetRawTransactionResult chan *response // 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) + res, 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) + // Unmarshal result as a string. + var txHex string + err = json.Unmarshal(res, &txHex) + if err != nil { + return nil, err } // Decode the serialized transaction hex to raw bytes. @@ -120,24 +120,24 @@ func (c *Client) GetRawTransaction(txHash *btcwire.ShaHash) (*btcutil.Tx, error) // FutureGetRawTransactionVerboseResult is a future promise to deliver the // result of a GetRawTransactionVerboseAsync RPC invocation (or an applicable // error). -type FutureGetRawTransactionVerboseResult chan *futureResult +type FutureGetRawTransactionVerboseResult chan *response // 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) + res, 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) + // Unmarshal result as a gettrawtransaction result object. + var rawTxResult btcjson.TxRawResult + err = json.Unmarshal(res, &rawTxResult) + if err != nil { + return nil, err } - return result, nil + return &rawTxResult, nil } // GetRawTransactionVerboseAsync returns an instance of a type that can be used @@ -170,24 +170,24 @@ func (c *Client) GetRawTransactionVerbose(txHash *btcwire.ShaHash) (*btcjson.TxR // FutureDecodeRawTransactionResult is a future promise to deliver the result // of a DecodeRawTransactionAsync RPC invocation (or an applicable error). -type FutureDecodeRawTransactionResult chan *futureResult +type FutureDecodeRawTransactionResult chan *response // 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) + res, 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) + // Unmarshal result as a decoderawtransaction result object. + var rawTxResult btcjson.TxRawResult + err = json.Unmarshal(res, &rawTxResult) + if err != nil { + return nil, err } - return result, nil + return &rawTxResult, nil } // DecodeRawTransactionAsync returns an instance of a type that can be used to @@ -214,22 +214,22 @@ func (c *Client) DecodeRawTransaction(serializedTx []byte) (*btcjson.TxRawResult // FutureCreateRawTransactionResult is a future promise to deliver the result // of a CreateRawTransactionAsync RPC invocation (or an applicable error). -type FutureCreateRawTransactionResult chan *futureResult +type FutureCreateRawTransactionResult chan *response // 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) + res, 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) + // Unmarshal result as a string. + var txHex string + err = json.Unmarshal(res, &txHex) + if err != nil { + return nil, err } // Decode the serialized transaction hex to raw bytes. @@ -277,22 +277,22 @@ func (c *Client) CreateRawTransaction(inputs []btcjson.TransactionInput, // FutureSendRawTransactionResult is a future promise to deliver the result // of a SendRawTransactionAsync RPC invocation (or an applicable error). -type FutureSendRawTransactionResult chan *futureResult +type FutureSendRawTransactionResult chan *response // 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) + res, 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) + // Unmarshal result as a string. + var txHashStr string + err = json.Unmarshal(res, &txHashStr) + if err != nil { + return nil, err } return btcwire.NewShaHashFromStr(txHashStr) @@ -332,25 +332,25 @@ func (c *Client) SendRawTransaction(tx *btcwire.MsgTx, allowHighFees bool) (*btc // 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 +type FutureSignRawTransactionResult chan *response // 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) + res, 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) + // Unmarshal as a signrawtransaction result. + var signRawTxResult btcjson.SignRawTransactionResult + err = json.Unmarshal(res, &signRawTxResult) + if err != nil { + return nil, false, err } // Decode the serialized transaction hex to raw bytes. - serializedTx, err := hex.DecodeString(result.Hex) + serializedTx, err := hex.DecodeString(signRawTxResult.Hex) if err != nil { return nil, false, err } @@ -361,7 +361,7 @@ func (r FutureSignRawTransactionResult) Receive() (*btcwire.MsgTx, bool, error) return nil, false, err } - return &msgTx, result.Complete, nil + return &msgTx, signRawTxResult.Complete, nil } // SignRawTransactionAsync returns an instance of a type that can be used to get diff --git a/wallet.go b/wallet.go index 5a47cca3..4ce4b2df 100644 --- a/wallet.go +++ b/wallet.go @@ -5,7 +5,7 @@ package btcrpcclient import ( - "fmt" + "encoding/json" "github.com/conformal/btcjson" "github.com/conformal/btcnet" "github.com/conformal/btcutil" @@ -19,24 +19,24 @@ import ( // FutureGetTransactionResult is a future promise to deliver the result // of a GetTransactionAsync RPC invocation (or an applicable error). -type FutureGetTransactionResult chan *futureResult +type FutureGetTransactionResult chan *response // 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) + res, 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) + // Unmarshal result as a gettransaction result object + var getTx btcjson.GetTransactionResult + err = json.Unmarshal(res, &getTx) + if err != nil { + return nil, err } - return result, nil + return &getTx, nil } // GetTransactionAsync returns an instance of a type that can be used to get the @@ -69,26 +69,21 @@ func (c *Client) GetTransaction(txHash *btcwire.ShaHash) (*btcjson.GetTransactio // 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 +type FutureListTransactionsResult chan *response // 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) + res, 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) + // Unmarshal result as an array of listtransaction result objects. + var transactions []btcjson.ListTransactionsResult + err = json.Unmarshal(res, &transactions) + if err != nil { + return nil, err } return transactions, nil @@ -167,7 +162,7 @@ func (c *Client) ListTransactionsCountFrom(account string, count, from int) ([]b // FutureListUnspentResult is a future promise to deliver the result of a // ListUnspentAsync, ListUnspentMinAsync, ListUnspentMinMaxAsync, or // ListUnspentMinMaxAddressesAsync RPC invocation (or an applicable error). -type FutureListUnspentResult chan *futureResult +type FutureListUnspentResult chan *response // Receive waits for the response promised by the future and returns all // unspent wallet transaction outputs returned by the RPC call. If the @@ -175,21 +170,16 @@ type FutureListUnspentResult chan *futureResult // or ListUnspentMinMaxAddressesAsync, the range may be limited by the // parameters of the RPC invocation. func (r FutureListUnspentResult) Receive() ([]btcjson.ListUnspentResult, error) { - reply, err := receiveFuture(r) + res, err := receiveFuture(r) if err != nil { return nil, err } - // No unspent transaction outputs. - if reply == nil { - return nil, nil - } - - // Ensure the returned data is the expected type. - unspent, ok := reply.([]btcjson.ListUnspentResult) - if !ok { - return nil, fmt.Errorf("unexpected response type for "+ - "listunspent: %T\n", reply) + // Unmarshal result as an array of listunspent results. + var unspent []btcjson.ListUnspentResult + err = json.Unmarshal(res, &unspent) + if err != nil { + return nil, err } return unspent, nil @@ -291,25 +281,25 @@ func (c *Client) ListUnspentMinMaxAddresses(minConf, maxConf int, addrs []btcuti // FutureListSinceBlockResult is a future promise to deliver the result of a // ListSinceBlockAsync or ListSinceBlockMinConfAsync RPC invocation (or an // applicable error). -type FutureListSinceBlockResult chan *futureResult +type FutureListSinceBlockResult chan *response // 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) + res, 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) + // Unmarshal result as a listsinceblock result object. + var listResult btcjson.ListSinceBlockResult + err = json.Unmarshal(res, &listResult) + if err != nil { + return nil, err } - return listResult, nil + return &listResult, nil } // ListSinceBlockAsync returns an instance of a type that can be used to get @@ -376,7 +366,7 @@ func (c *Client) ListSinceBlockMinConf(blockHash *btcwire.ShaHash, minConfirms i // FutureSetTxFeeResult is a future promise to deliver the result of a // SetTxFeeAsync RPC invocation (or an applicable error). -type FutureSetTxFeeResult chan *futureResult +type FutureSetTxFeeResult chan *response // 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 @@ -413,20 +403,21 @@ func (c *Client) SetTxFee(fee btcutil.Amount) error { // FutureSendToAddressResult is a future promise to deliver the result of a // SendToAddressAsync RPC invocation (or an applicable error). -type FutureSendToAddressResult chan *futureResult +type FutureSendToAddressResult chan *response // 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) + res, 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) + // Unmarshal result as a string. + var txHash string + err = json.Unmarshal(res, &txHash) + if err != nil { + return nil, err } return btcwire.NewShaHashFromStr(txHash) @@ -500,22 +491,22 @@ func (c *Client) SendToAddressComment(address btcutil.Address, amount btcutil.Am // 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 +type FutureSendFromResult chan *response // 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) + res, 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) + // Unmarshal result as a string. + var txHash string + err = json.Unmarshal(res, &txHash) + if err != nil { + return nil, err } return btcwire.NewShaHashFromStr(txHash) @@ -622,22 +613,22 @@ func (c *Client) SendFromComment(fromAccount string, toAddress btcutil.Address, // 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 +type FutureSendManyResult chan *response // 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) + res, 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) + // Unmashal result as a string. + var txHash string + err = json.Unmarshal(res, &txHash) + if err != nil { + return nil, err } return btcwire.NewShaHashFromStr(txHash) @@ -760,22 +751,22 @@ func (c *Client) SendManyComment(fromAccount string, // FutureAddMultisigAddressResult is a future promise to deliver the result of a // AddMultisigAddressAsync RPC invocation (or an applicable error). -type FutureAddMultisigAddressResult chan *futureResult +type FutureAddMultisigAddressResult chan *response // 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) + res, 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) + // Unmarshal result as a string. + var addr string + err = json.Unmarshal(res, &addr) + if err != nil { + return nil, err } return btcutil.DecodeAddress(addr, &btcnet.MainNetParams) @@ -811,24 +802,24 @@ func (c *Client) AddMultisigAddress(requiredSigs int, addresses []btcutil.Addres // FutureCreateMultisigResult is a future promise to deliver the result of a // CreateMultisigAsync RPC invocation (or an applicable error). -type FutureCreateMultisigResult chan *futureResult +type FutureCreateMultisigResult chan *response // 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) + res, 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) + // Unmarshal result as a createmultisig result object. + var multisigRes btcjson.CreateMultiSigResult + err = json.Unmarshal(res, &multisigRes) + if err != nil { + return nil, err } - return result, nil + return &multisigRes, nil } // CreateMultisigAsync returns an instance of a type that can be used to get @@ -861,21 +852,21 @@ func (c *Client) CreateMultisig(requiredSigs int, addresses []btcutil.Address) ( // FutureGetNewAddressResult is a future promise to deliver the result of a // GetNewAddressAsync RPC invocation (or an applicable error). -type FutureGetNewAddressResult chan *futureResult +type FutureGetNewAddressResult chan *response // 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) + res, 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) + // Unmarshal result as a string. + var addr string + err = json.Unmarshal(res, &addr) + if err != nil { + return nil, err } return btcutil.DecodeAddress(addr, &btcnet.MainNetParams) @@ -903,22 +894,22 @@ func (c *Client) GetNewAddress() (btcutil.Address, error) { // FutureGetRawChangeAddressResult is a future promise to deliver the result of // a GetRawChangeAddressAsync RPC invocation (or an applicable error). -type FutureGetRawChangeAddressResult chan *futureResult +type FutureGetRawChangeAddressResult chan *response // 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) + res, 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) + // Unmarshal result as a string. + var addr string + err = json.Unmarshal(res, &addr) + if err != nil { + return nil, err } return btcutil.DecodeAddress(addr, &btcnet.MainNetParams) @@ -948,21 +939,21 @@ func (c *Client) GetRawChangeAddress(account string) (btcutil.Address, error) { // FutureGetAccountAddressResult is a future promise to deliver the result of a // GetAccountAddressAsync RPC invocation (or an applicable error). -type FutureGetAccountAddressResult chan *futureResult +type FutureGetAccountAddressResult chan *response // 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) + res, 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) + // Unmarshal result as a string. + var addr string + err = json.Unmarshal(res, &addr) + if err != nil { + return nil, err } return btcutil.DecodeAddress(addr, &btcnet.MainNetParams) @@ -991,21 +982,21 @@ func (c *Client) GetAccountAddress(account string) (btcutil.Address, error) { // FutureGetAccountResult is a future promise to deliver the result of a // GetAccountAsync RPC invocation (or an applicable error). -type FutureGetAccountResult chan *futureResult +type FutureGetAccountResult chan *response // 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) + res, 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) + // Unmarshal result as a string. + var account string + err = json.Unmarshal(res, &account) + if err != nil { + return "", err } return account, nil @@ -1034,7 +1025,7 @@ func (c *Client) GetAccount(address btcutil.Address) (string, error) { // FutureSetAccountResult is a future promise to deliver the result of a // SetAccountAsync RPC invocation (or an applicable error). -type FutureSetAccountResult chan *futureResult +type FutureSetAccountResult chan *response // Receive waits for the response promised by the future and returns the result // of setting the account to be associated with the passed address. @@ -1070,21 +1061,21 @@ func (c *Client) SetAccount(address btcutil.Address, account string) error { // FutureGetAddressesByAccountResult is a future promise to deliver the result // of a GetAddressesByAccountAsync RPC invocation (or an applicable error). -type FutureGetAddressesByAccountResult chan *futureResult +type FutureGetAddressesByAccountResult chan *response // 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) + res, 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) + // Unmashal result as an array of string. + var addrStrings []string + err = json.Unmarshal(res, &addrStrings) + if err != nil { + return nil, err } addrs := make([]btcutil.Address, 0, len(addrStrings)) @@ -1122,24 +1113,24 @@ func (c *Client) GetAddressesByAccount(account string) ([]btcutil.Address, error // FutureValidateAddressResult is a future promise to deliver the result of a // ValidateAddressAsync RPC invocation (or an applicable error). -type FutureValidateAddressResult chan *futureResult +type FutureValidateAddressResult chan *response // 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) + res, 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) + // Unmarshal result as a validateaddress result object. + var addrResult btcjson.ValidateAddressResult + err = json.Unmarshal(res, &addrResult) + if err != nil { + return nil, err } - return result, nil + return &addrResult, nil } // ValidateAddressAsync returns an instance of a type that can be used to get @@ -1165,7 +1156,7 @@ func (c *Client) ValidateAddress(address btcutil.Address) (*btcjson.ValidateAddr // FutureKeyPoolRefillResult is a future promise to deliver the result of a // KeyPoolRefillAsync RPC invocation (or an applicable error). -type FutureKeyPoolRefillResult chan *futureResult +type FutureKeyPoolRefillResult chan *response // Receive waits for the response promised by the future and returns the result // of refilling the key pool. @@ -1228,21 +1219,21 @@ func (c *Client) KeyPoolRefillSize(newSize uint) error { // FutureListAccountsResult is a future promise to deliver the result of a // ListAccountsAsync or ListAccountsMinConfAsync RPC invocation (or an // applicable error). -type FutureListAccountsResult chan *futureResult +type FutureListAccountsResult chan *response // 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) + res, 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) + // Unmarshal result as a json object. + var accounts map[string]float64 + err = json.Unmarshal(res, &accounts) + if err != nil { + return nil, err } accountsMap := make(map[string]btcutil.Amount) @@ -1307,21 +1298,21 @@ func (c *Client) ListAccountsMinConf(minConfirms int) (map[string]btcutil.Amount // FutureGetBalanceResult is a future promise to deliver the result of a // GetBalanceAsync or GetBalanceMinConfAsync RPC invocation (or an applicable // error). -type FutureGetBalanceResult chan *futureResult +type FutureGetBalanceResult chan *response // 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) + res, 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) + // Unmarshal result as a floating point number. + var balance float64 + err = json.Unmarshal(res, &balance) + if err != nil { + return 0, err } satoshi, err := btcjson.JSONToAmount(balance) @@ -1391,21 +1382,21 @@ func (c *Client) GetBalanceMinConf(account string, minConfirms int) (btcutil.Amo // FutureGetReceivedByAccountResult is a future promise to deliver the result of // a GetReceivedByAccountAsync or GetReceivedByAccountMinConfAsync RPC // invocation (or an applicable error). -type FutureGetReceivedByAccountResult chan *futureResult +type FutureGetReceivedByAccountResult chan *response // 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) + res, 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) + // Unmarshal result as a floating point number. + var balance float64 + err = json.Unmarshal(res, &balance) + if err != nil { + return 0, err } satoshi, err := btcjson.JSONToAmount(balance) @@ -1466,21 +1457,21 @@ func (c *Client) GetReceivedByAccountMinConf(account string, minConfirms int) (b // FutureGetUnconfirmedBalanceResult is a future promise to deliver the result // of a GetUnconfirmedBalanceAsync RPC invocation (or an applicable error). -type FutureGetUnconfirmedBalanceResult chan *futureResult +type FutureGetUnconfirmedBalanceResult chan *response // 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) + res, 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) + // Unmarshal result as a floating point number. + var balance float64 + err = json.Unmarshal(res, &balance) + if err != nil { + return 0, err } satoshi, err := btcjson.JSONToAmount(balance) @@ -1515,21 +1506,21 @@ func (c *Client) GetUnconfirmedBalance(account string) (btcutil.Amount, error) { // FutureGetReceivedByAddressResult is a future promise to deliver the result of // a GetReceivedByAddressAsync or GetReceivedByAddressMinConfAsync RPC // invocation (or an applicable error). -type FutureGetReceivedByAddressResult chan *futureResult +type FutureGetReceivedByAddressResult chan *response // 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) + res, 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) + // Unmarshal result as a floating point number. + var balance float64 + err = json.Unmarshal(res, &balance) + if err != nil { + return 0, err } satoshi, err := btcjson.JSONToAmount(balance) @@ -1594,29 +1585,24 @@ func (c *Client) GetReceivedByAddressMinConf(address btcutil.Address, minConfirm // of a ListReceivedByAccountAsync, ListReceivedByAccountMinConfAsync, or // ListReceivedByAccountIncludeEmptyAsync RPC invocation (or an applicable // error). -type FutureListReceivedByAccountResult chan *futureResult +type FutureListReceivedByAccountResult chan *response // 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) + res, err := receiveFuture(r) if err != nil { return nil, err } - // No results. - if reply == nil { - return nil, nil + // Unmarshal as an array of listreceivedbyaccount result objects. + var received []btcjson.ListReceivedByAccountResult + err = json.Unmarshal(res, &received) + if err != nil { + return nil, err } - // 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 + return received, nil } // ListReceivedByAccountAsync returns an instance of a type that can be used to @@ -1702,7 +1688,7 @@ func (c *Client) ListReceivedByAccountIncludeEmpty(minConfirms int, includeEmpty // FutureWalletLockResult is a future promise to deliver the result of a // WalletLockAsync RPC invocation (or an applicable error). -type FutureWalletLockResult chan *futureResult +type FutureWalletLockResult chan *response // Receive waits for the response promised by the future and returns the result // of locking the wallet. @@ -1759,7 +1745,7 @@ func (c *Client) WalletPassphrase(passphrase string, timeoutSecs int64) error { // FutureWalletPassphraseChangeResult is a future promise to deliver the result // of a WalletPassphraseChangeAsync RPC invocation (or an applicable error). -type FutureWalletPassphraseChangeResult chan *futureResult +type FutureWalletPassphraseChangeResult chan *response // Receive waits for the response promised by the future and returns the result // of changing the wallet passphrase. @@ -1799,21 +1785,21 @@ func (c *Client) WalletPassphraseChange(old, new string) error { // FutureSignMessageResult is a future promise to deliver the result of a // SignMessageAsync RPC invocation (or an applicable error). -type FutureSignMessageResult chan *futureResult +type FutureSignMessageResult chan *response // 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) + res, 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) + // Unmarshal result as a string. + var b64 string + err = json.Unmarshal(res, &b64) + if err != nil { + return "", err } return b64, nil @@ -1845,21 +1831,21 @@ func (c *Client) SignMessage(address btcutil.Address, message string) (string, e // FutureVerifyMessageResult is a future promise to deliver the result of a // VerifyMessageAsync RPC invocation (or an applicable error). -type FutureVerifyMessageResult chan *futureResult +type FutureVerifyMessageResult chan *response // 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) + res, 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) + // Unmarshal result as a boolean. + var verified bool + err = json.Unmarshal(res, &verified) + if err != nil { + return false, err } return verified, nil @@ -1895,22 +1881,22 @@ func (c *Client) VerifyMessage(address btcutil.Address, signature, message strin // FutureDumpPrivKeyResult is a future promise to deliver the result of a // DumpPrivKeyAsync RPC invocation (or an applicable error). -type FutureDumpPrivKeyResult chan *futureResult +type FutureDumpPrivKeyResult chan *response // 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() (*btcutil.WIF, error) { - reply, err := receiveFuture(r) + res, err := receiveFuture(r) if err != nil { return nil, err } - // Ensure the returned data is the expected type. - privKeyWIF, ok := reply.(string) - if !ok { - return nil, fmt.Errorf("unexpected response type for "+ - "dumpprivkey: %T\n", reply) + // Unmarshal result as a string. + var privKeyWIF string + err = json.Unmarshal(res, &privKeyWIF) + if err != nil { + return nil, err } return btcutil.DecodeWIF(privKeyWIF) @@ -1943,7 +1929,7 @@ func (c *Client) DumpPrivKey(address btcutil.Address) (*btcutil.WIF, error) { // FutureImportPrivKeyResult is a future promise to deliver the result of an // ImportPrivKeyAsync RPC invocation (or an applicable error). -type FutureImportPrivKeyResult chan *futureResult +type FutureImportPrivKeyResult chan *response // 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 From cfb46e2c439b929944ce0dfd634990b9217658e7 Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Wed, 11 Jun 2014 09:30:04 -0500 Subject: [PATCH 046/153] Add getinfo support. --- wallet.go | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/wallet.go b/wallet.go index 4ce4b2df..a937b146 100644 --- a/wallet.go +++ b/wallet.go @@ -1969,6 +1969,57 @@ func (c *Client) ImportPrivKey(privKeyWIF *btcutil.WIF) error { return c.ImportPrivKeyAsync(privKeyWIF).Receive() } +// *********************** +// Miscellaneous Functions +// *********************** + +// NOTE: While getinfo is implemented here (in wallet.go), a btcd chain server +// will respond to getinfo requests as well, excluding any wallet information. + +// FutureGetInfoResult is a future promise to deliver the result of a +// GetInfoAsync RPC invocation (or an applicable error). +type FutureGetInfoResult chan *response + +// Receive waits for the response promised by the future and returns the info +// provided by the server. +func (r FutureGetInfoResult) Receive() (*btcjson.InfoResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a getinfo result object. + var infoRes btcjson.InfoResult + err = json.Unmarshal(res, &infoRes) + if err != nil { + return nil, err + } + + return &infoRes, nil +} + +// GetInfoAsync 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 GetInfo for the blocking version and more details. +func (c *Client) GetInfoAsync() FutureGetInfoResult { + id := c.NextID() + cmd, err := btcjson.NewGetInfoCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// GetInfo returns miscellaneous info regarding the RPC server. The returned +// info object may be void of wallet information if the remote server does +// not include wallet functionality. +func (c *Client) GetInfo() (*btcjson.InfoResult, error) { + return c.GetInfoAsync().Receive() +} + // TODO(davec): Implement // backupwallet (NYI in btcwallet) // encryptwallet (Won't be supported by btcwallet since it's always encrypted) From 4ac778d72a5d400d9d013b309e95c944e08aff5f Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Wed, 11 Jun 2014 13:23:21 -0500 Subject: [PATCH 047/153] Do not reissue rescan requests on reconnect. --- infrastructure.go | 21 +++++++++++++++++++-- notify.go | 12 ++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index c56dc398..ee58b9d6 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -474,6 +474,12 @@ func (c *Client) reregisterNtfns() error { return nil } +// ignoreResends is a set of all methods for requests that are "long running" +// are not be reissued by the client on reconnect. +var ignoreResends = map[string]struct{}{ + "rescan": struct{}{}, +} + // resendCmds resends any commands that had not completed when the client // disconnected. It is intended to be called once the client has reconnected as // a separate goroutine. @@ -492,9 +498,20 @@ func (c *Client) resendCmds() { // 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() { + var nextElem *list.Element + for e := c.requestList.Front(); e != nil; e = nextElem { + nextElem = e.Next() + req := e.Value.(*jsonRequest) - resendCmds = append(resendCmds, req) + if _, ok := ignoreResends[req.cmd.Method()]; ok { + // If a request is not sent on reconnect, remove it + // from the request structures, since no reply is + // expected. + delete(c.requestMap, req.cmd.Id().(uint64)) + c.requestList.Remove(e) + } else { + resendCmds = append(resendCmds, req) + } } c.requestLock.Unlock() diff --git a/notify.go b/notify.go index 198abb84..28c7e686 100644 --- a/notify.go +++ b/notify.go @@ -897,6 +897,12 @@ func (r FutureRescanResult) Receive() error { // // See Rescan for the blocking version and more details. // +// NOTE: Rescan requests are not issued on client reconnect and must be +// performed manually (ideally with a new start height based on the last +// rescan progress notification). See the OnClientConnected notification +// callback for a good callsite to reissue rescan requests on connect and +// reconnect. +// // NOTE: This is a btcd extension and requires a websocket connection. func (c *Client) RescanAsync(startHeight int32, addresses []btcutil.Address, outpoints []*btcwire.OutPoint) FutureRescanResult { @@ -952,6 +958,12 @@ func (c *Client) RescanAsync(startHeight int32, addresses []btcutil.Address, // See RescanEndHeight to also specify a block height at which to stop the // rescan if a bounded rescan is desired instead. // +// NOTE: Rescan requests are not issued on client reconnect and must be +// performed manually (ideally with a new start height based on the last +// rescan progress notification). See the OnClientConnected notification +// callback for a good callsite to reissue rescan requests on connect and +// reconnect. +// // NOTE: This is a btcd extension and requires a websocket connection. func (c *Client) Rescan(startHeight int32, addresses []btcutil.Address, outpoints []*btcwire.OutPoint) error { From 28c0a3c8c738a47dbb8fb8871742046f7dcdc105 Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Thu, 12 Jun 2014 13:00:42 -0500 Subject: [PATCH 048/153] Modify doco and example for btcwallet ws endpoint. --- examples/btcwalletwebsockets/main.go | 2 +- infrastructure.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/btcwalletwebsockets/main.go b/examples/btcwalletwebsockets/main.go index 0b62773b..c46e90ac 100644 --- a/examples/btcwalletwebsockets/main.go +++ b/examples/btcwalletwebsockets/main.go @@ -34,7 +34,7 @@ func main() { } connCfg := &btcrpcclient.ConnConfig{ Host: "localhost:18332", - Endpoint: "frontend", + Endpoint: "ws", User: "yourrpcuser", Pass: "yourrpcpass", Certificates: certs, diff --git a/infrastructure.go b/infrastructure.go index ee58b9d6..8075e864 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -896,7 +896,7 @@ type ConnConfig struct { Host string // Endpoint is the websocket endpoint on the RPC server. This is - // typically "ws" or "frontend". + // typically "ws". Endpoint string // User is the username to use to authenticate to the RPC server. From 67d570e660be35a63c8a4151529b120eebf2173f Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 12 Jun 2014 22:50:47 -0500 Subject: [PATCH 049/153] Correct a few cases of address encoding. There are certain RPCs where an address should be sent as the raw string instead of the encoded bitcoin address since the RPC handler on the other end expects "keys" instead of Bitcoin addresses. For example, the multisignature RPCs addmultisigaddress and createmultisig can work with pubkeys (compressed, uncompressed, or hybrid) as well as Bitcoin addresses. The original issue which prompted these changes was report by Paul Snow on IRC. --- notify.go | 6 +++--- rawtransactions.go | 2 +- wallet.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/notify.go b/notify.go index 28c7e686..57599fd6 100644 --- a/notify.go +++ b/notify.go @@ -847,7 +847,7 @@ func (c *Client) NotifyReceivedAsync(addresses []btcutil.Address) FutureNotifyRe // Convert addresses to strings. addrs := make([]string, 0, len(addresses)) for _, addr := range addresses { - addrs = append(addrs, addr.EncodeAddress()) + addrs = append(addrs, addr.String()) } id := c.NextID() cmd := btcws.NewNotifyReceivedCmd(id, addrs) @@ -921,7 +921,7 @@ func (c *Client) RescanAsync(startHeight int32, addresses []btcutil.Address, // Convert addresses to strings. addrs := make([]string, 0, len(addresses)) for _, addr := range addresses { - addrs = append(addrs, addr.EncodeAddress()) + addrs = append(addrs, addr.String()) } // Convert outpoints. @@ -996,7 +996,7 @@ func (c *Client) RescanEndHeightAsync(startHeight int32, // Convert addresses to strings. addrs := make([]string, 0, len(addresses)) for _, addr := range addresses { - addrs = append(addrs, addr.EncodeAddress()) + addrs = append(addrs, addr.String()) } // Convert outpoints. diff --git a/rawtransactions.go b/rawtransactions.go index bfd6cd9c..913e8b06 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -257,7 +257,7 @@ func (c *Client) CreateRawTransactionAsync(inputs []btcjson.TransactionInput, id := c.NextID() convertedAmts := make(map[string]int64, len(amounts)) for addr, amount := range amounts { - convertedAmts[addr.EncodeAddress()] = int64(amount) + convertedAmts[addr.String()] = int64(amount) } cmd, err := btcjson.NewCreateRawTransactionCmd(id, inputs, convertedAmts) if err != nil { diff --git a/wallet.go b/wallet.go index a937b146..7c74fd93 100644 --- a/wallet.go +++ b/wallet.go @@ -782,7 +782,7 @@ func (c *Client) AddMultisigAddressAsync(requiredSigs int, addresses []btcutil.A addrs := make([]string, 0, len(addresses)) for _, addr := range addresses { - addrs = append(addrs, addr.EncodeAddress()) + addrs = append(addrs, addr.String()) } cmd, err := btcjson.NewAddMultisigAddressCmd(id, requiredSigs, addrs, account) @@ -832,7 +832,7 @@ func (c *Client) CreateMultisigAsync(requiredSigs int, addresses []btcutil.Addre addrs := make([]string, 0, len(addresses)) for _, addr := range addresses { - addrs = append(addrs, addr.EncodeAddress()) + addrs = append(addrs, addr.String()) } cmd, err := btcjson.NewCreateMultisigCmd(id, requiredSigs, addrs) From 7448f9555c2f06bce1794e750d18d36bd30390ee Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 12 Jun 2014 23:00:03 -0500 Subject: [PATCH 050/153] Make golint happier. --- infrastructure.go | 6 +++--- rawtransactions.go | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index 8075e864..b3bf7bdf 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -943,9 +943,9 @@ type ConnConfig struct { HttpPostMode bool } -// newHttpClient returns a new http client that is configured according to the +// 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) { +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 != "" { @@ -1051,7 +1051,7 @@ func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error ntfnHandlers = nil var err error - httpClient, err = newHttpClient(config) + httpClient, err = newHTTPClient(config) if err != nil { return nil, err } diff --git a/rawtransactions.go b/rawtransactions.go index 913e8b06..8a8b0ca7 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -44,10 +44,10 @@ const ( // 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 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" ) From aceaed82c516e85263a4732ed85dd63b98d911c0 Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Mon, 16 Jun 2014 09:42:29 -0500 Subject: [PATCH 051/153] Add support for new rescanfinished notification. ok @davecgh --- notify.go | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/notify.go b/notify.go index 57599fd6..78cbb7c6 100644 --- a/notify.go +++ b/notify.go @@ -122,6 +122,13 @@ type NotificationHandlers struct { // this to invoked indirectly as the result of a NotifyReceived call. OnRedeemingTx func(transaction *btcutil.Tx, details *btcws.BlockDetails) + // OnRescanFinished is invoked after a rescan finishes due to a previous + // call to Rescan or RescanEndHeight. Finished rescans should be + // signaled on this notification, rather than relying on the return + // result of a rescan request, due to how btcd may send various rescan + // notifications after the rescan request has already returned. + OnRescanFinished func(lastProcessedHeight int32) + // 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. @@ -246,6 +253,23 @@ func (c *Client) handleNotification(ntfn *rawNotification) { c.ntfnHandlers.OnRedeemingTx(tx, block) + // OnRescanFinished + case btcws.RescanFinishedNtfnMethod: + // Ignore the notification is the client is not interested in + // it. + if c.ntfnHandlers.OnRescanFinished == nil { + return + } + + lastProcessed, err := parseRescanHeightParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid rescanfinished "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnRescanFinished(lastProcessed) + // OnRescanProgress case btcws.RescanProgressNtfnMethod: // Ignore the notification is the client is not interested in @@ -254,7 +278,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { return } - lastProcessed, err := parseRescanProgressNtfnParams(ntfn.Params) + lastProcessed, err := parseRescanHeightParams(ntfn.Params) if err != nil { log.Warnf("Received invalid rescanprogress "+ "notification: %v", err) @@ -447,9 +471,9 @@ func parseChainTxNtfnParams(params []json.RawMessage) (*btcutil.Tx, return btcutil.NewTx(&msgTx), block, nil } -// parseRescanProgressNtfnParams parses out the height of the last rescanned -// from the parameters of a rescanprogress notification. -func parseRescanProgressNtfnParams(params []json.RawMessage) (int32, error) { +// parseRescanHeightParams parses out the height of the last rescanned block +// from the parameters of rescanfinished and rescanprogress notifications. +func parseRescanHeightParams(params []json.RawMessage) (int32, error) { if len(params) != 1 { return 0, wrongNumParams(len(params)) } From 22b6af14000cb451983297cfab3d0e2061b6a69d Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Wed, 18 Jun 2014 10:16:09 -0500 Subject: [PATCH 052/153] Fix unmarshaling of HTTP POST responses. If connecting to a bitcoin RPC server as an HTTP POST client, full response objects rather than just the raw result bytes were being passed to the specific result unmarshalers, causing unmarshal errors for the incorrect JSON types. To fix this, first unmarshal the response body into a rawResponse, and pass only the raw result bytes (or an error) to the specific handlers. This was reported by nskelsey on IRC. ok @davecgh --- infrastructure.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index b3bf7bdf..bb8b60d0 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -271,7 +271,7 @@ func (r rawResponse) result() (result []byte, err error) { // handleMessage is the main handler for incoming notifications and responses. func (c *Client) handleMessage(msg []byte) { // Attempt to unmarshal the message as either a notifiation or response. - in := inMessage{} + var in inMessage err := json.Unmarshal(msg, &in) if err != nil { log.Warnf("Remote server sent invalid message: %v", err) @@ -618,12 +618,19 @@ func (c *Client) handleSendPostMessage(details *sendPostDetails) { } // Read the raw bytes and close the response. - resp, err := btcjson.GetRaw(httpResponse.Body) + respBytes, err := btcjson.GetRaw(httpResponse.Body) if err != nil { details.responseChan <- &response{result: nil, err: err} return } - details.responseChan <- &response{result: resp, err: nil} + var resp rawResponse + err = json.Unmarshal(respBytes, &resp) + if err != nil { + details.responseChan <- &response{result: nil, err: err} + return + } + res, err := resp.result() + details.responseChan <- &response{result: res, err: err} } // sendPostHandler handles all outgoing messages when the client is running From 0ae3676a7d8da77a48a25b4d1a1d760842e32485 Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 25 Jun 2014 12:56:47 -0400 Subject: [PATCH 053/153] Log when the initial connection has been made. --- infrastructure.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/infrastructure.go b/infrastructure.go index bb8b60d0..511202c8 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -1069,6 +1069,8 @@ func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error return nil, err } } + log.Infof("Established connection to RPC server %s", + config.Host) client := &Client{ config: config, From a9e1b8fb84a8a4062225f0d05e2aaf4bd5b4fa2d Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 25 Jun 2014 21:48:40 -0500 Subject: [PATCH 054/153] Use system CAs when Certificates are not specified. This commit modifies the TLS setup to only override the RootCAs for the TLS connection if certificates are specified. This allows the Certificates parameter to be ommitted from the connection config to use the system CAs. --- infrastructure.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index 511202c8..b45009b2 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -966,10 +966,12 @@ func newHTTPClient(config *ConnConfig) (*http.Client, error) { // Configure TLS if needed. var tlsConfig *tls.Config if !config.DisableTLS { - pool := x509.NewCertPool() - pool.AppendCertsFromPEM(config.Certificates) - tlsConfig = &tls.Config{ - RootCAs: pool, + if len(config.Certificates) > 0 { + pool := x509.NewCertPool() + pool.AppendCertsFromPEM(config.Certificates) + tlsConfig = &tls.Config{ + RootCAs: pool, + } } } @@ -990,12 +992,14 @@ func dial(config *ConnConfig) (*websocket.Conn, error) { var tlsConfig *tls.Config var scheme = "ws" if !config.DisableTLS { - pool := x509.NewCertPool() - pool.AppendCertsFromPEM(config.Certificates) tlsConfig = &tls.Config{ - RootCAs: pool, MinVersion: tls.VersionTLS12, } + if len(config.Certificates) > 0 { + pool := x509.NewCertPool() + pool.AppendCertsFromPEM(config.Certificates) + tlsConfig.RootCAs = pool + } scheme = "wss" } From cb6797251245f61ca437f4cfaf24e07db6484975 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 26 Jun 2014 18:04:01 -0500 Subject: [PATCH 055/153] Detect error strings on SumitBlock. The submitblock RPC returns a string containing an error if it failed for any reason. This comment detects a non-null return from submitblock and converts that string to an error which is returned. --- mining.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mining.go b/mining.go index b98f89a5..846de39a 100644 --- a/mining.go +++ b/mining.go @@ -369,11 +369,16 @@ type FutureSubmitBlockResult chan *response // 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) + res, err := receiveFuture(r) if err != nil { return err } + resStr := string(res) + if resStr != "null" { + return errors.New(resStr) + } + return nil } From 0f6726b9c36ac4dda48eb35daf2f24f87b9cae53 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 26 Jun 2014 20:24:40 -0500 Subject: [PATCH 056/153] Add missing import. --- mining.go | 1 + 1 file changed, 1 insertion(+) diff --git a/mining.go b/mining.go index 846de39a..d35db716 100644 --- a/mining.go +++ b/mining.go @@ -7,6 +7,7 @@ package btcrpcclient import ( "encoding/hex" "encoding/json" + "errors" "github.com/conformal/btcjson" "github.com/conformal/btcutil" ) From 894d5e23aa1680cbf329fd7d1d610a0742589a6f Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 26 Jun 2014 21:47:29 -0500 Subject: [PATCH 057/153] Unmarshal the SubmitBlock error. The string needs to be unmarshaled rather than returned directly otherwise it will have an extra set of quotes plus it's safter to properly unmarshal it. Pointed out by @jrick. --- mining.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/mining.go b/mining.go index d35db716..d299776f 100644 --- a/mining.go +++ b/mining.go @@ -375,9 +375,14 @@ func (r FutureSubmitBlockResult) Receive() error { return err } - resStr := string(res) - if resStr != "null" { - return errors.New(resStr) + if string(res) != "null" { + var result string + err = json.Unmarshal(res, &result) + if err != nil { + return err + } + + return errors.New(result) } return nil From f1bfb5dc1cb6300ae657a8e9430e2ccc4fd1685b Mon Sep 17 00:00:00 2001 From: GeertJohan Date: Thu, 26 Jun 2014 21:26:53 +0200 Subject: [PATCH 058/153] Add ImportPrivKey versions with Label and Rescan arguments. --- wallet.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/wallet.go b/wallet.go index 7c74fd93..fdfaba01 100644 --- a/wallet.go +++ b/wallet.go @@ -1969,6 +1969,59 @@ func (c *Client) ImportPrivKey(privKeyWIF *btcutil.WIF) error { return c.ImportPrivKeyAsync(privKeyWIF).Receive() } +// ImportPrivKeyLabelAsync 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) ImportPrivKeyLabelAsync(privKeyWIF *btcutil.WIF, label string) FutureImportPrivKeyResult { + wif := "" + if privKeyWIF != nil { + wif = privKeyWIF.String() + } + + id := c.NextID() + cmd, err := btcjson.NewImportPrivKeyCmd(id, wif, label) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ImportPrivKeyLabel imports the passed private key which must be the wallet import +// format (WIF). It sets the account label to the one provided. +func (c *Client) ImportPrivKeyLabel(privKeyWIF *btcutil.WIF, label string) error { + return c.ImportPrivKeyLabelAsync(privKeyWIF, label).Receive() +} + +// ImportPrivKeyRescanAsync 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) ImportPrivKeyRescanAsync(privKeyWIF *btcutil.WIF, label string, rescan bool) FutureImportPrivKeyResult { + wif := "" + if privKeyWIF != nil { + wif = privKeyWIF.String() + } + + id := c.NextID() + cmd, err := btcjson.NewImportPrivKeyCmd(id, wif, label, rescan) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ImportPrivKeyRescan imports the passed private key which must be the wallet import +// format (WIF). It sets the account label to the one provided. When rescan is true, +// the block history is scanned for transactions addressed to provided privKey. +func (c *Client) ImportPrivKeyRescan(privKeyWIF *btcutil.WIF, label string, rescan bool) error { + return c.ImportPrivKeyRescanAsync(privKeyWIF, label, rescan).Receive() +} + // *********************** // Miscellaneous Functions // *********************** From 4c045cc1ec1c6b08e059bfc6cf9d4eea02191820 Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Mon, 30 Jun 2014 09:08:45 -0500 Subject: [PATCH 059/153] Add LockUnspent support. Closes #10. ok @davecgh --- wallet.go | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/wallet.go b/wallet.go index fdfaba01..016877e1 100644 --- a/wallet.go +++ b/wallet.go @@ -364,6 +364,60 @@ func (c *Client) ListSinceBlockMinConf(blockHash *btcwire.ShaHash, minConfirms i // Transaction Send Functions // ************************** +// FutureLockUnspentResult is a future promise to deliver the error result of a +// LockUnspentAsync RPC invocation. +type FutureLockUnspentResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of locking or unlocking the unspent output(s). +func (r FutureLockUnspentResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// LockUnspentAsync 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 LockUnspent for the blocking version and more details. +func (c *Client) LockUnspentAsync(unlock bool, ops []*btcwire.OutPoint) FutureLockUnspentResult { + id := c.NextID() + outputs := make([]btcjson.TransactionInput, len(ops)) + for i, op := range ops { + outputs[i] = btcjson.TransactionInput{ + Txid: op.Hash.String(), + Vout: op.Index, + } + } + cmd, err := btcjson.NewLockUnspentCmd(id, unlock, outputs) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// LockUnspent marks outputs as locked or unlocked, depending on the value of +// the unlock bool. When locked, the unspent output will not be selected as +// input for newly created, non-raw transactions, and will not be returned in +// future ListUnspent results, until the output is marked unlocked again. +// +// If unlock is false, each outpoint in ops will be marked locked. If unlocked +// is true and specific outputs are specified in ops (len != 0), exactly those +// outputs will be marked unlocked. If unlocked is true and no outpoints are +// specified, all previous locked outputs are marked unlocked. +// +// The locked or unlocked state of outputs are not written to disk and after +// restarting a wallet process, this data will be reset (every output unlocked). +// +// NOTE: While this method would be a bit more readable if the unlock bool was +// reversed (that is, LockUnspent(true, ...) locked the outputs), it has been +// left as unlock to keep compatibility with the reference client API and to +// avoid confusion for those who are already familiar with the lockunspent RPC. +func (c *Client) LockUnspent(unlock bool, ops []*btcwire.OutPoint) error { + return c.LockUnspentAsync(unlock, ops).Receive() +} + // FutureSetTxFeeResult is a future promise to deliver the result of a // SetTxFeeAsync RPC invocation (or an applicable error). type FutureSetTxFeeResult chan *response @@ -2081,7 +2135,6 @@ func (c *Client) GetInfo() (*btcjson.InfoResult, error) { // listlockunspent (NYI in btcwallet) // listreceivedbyaddress (NYI in btcwallet) // listreceivedbyaccount (NYI in btcwallet) -// lockunspent (NYI in btcwallet) // move (NYI in btcwallet) // DUMP From 130edaec651dcd5c6f2d7f18173a23e0c2c67d88 Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Mon, 30 Jun 2014 10:08:50 -0500 Subject: [PATCH 060/153] Add ListLockUnspent support. Closes #11. ok @davecgh --- wallet.go | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/wallet.go b/wallet.go index 016877e1..7cdaa0b8 100644 --- a/wallet.go +++ b/wallet.go @@ -418,6 +418,60 @@ func (c *Client) LockUnspent(unlock bool, ops []*btcwire.OutPoint) error { return c.LockUnspentAsync(unlock, ops).Receive() } +// FutureListLockUnspentResult is a future promise to deliver the result of a +// ListLockUnspentAsync RPC invocation (or an applicable error). +type FutureListLockUnspentResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of all currently locked unspent outputs. +func (r FutureListLockUnspentResult) Receive() ([]*btcwire.OutPoint, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal as an array of transaction inputs. + var inputs []btcjson.TransactionInput + err = json.Unmarshal(res, &inputs) + if err != nil { + return nil, err + } + + // Create a slice of outpoints from the transaction input structs. + ops := make([]*btcwire.OutPoint, len(inputs)) + for i, input := range inputs { + sha, err := btcwire.NewShaHashFromStr(input.Txid) + if err != nil { + return nil, err + } + ops[i] = btcwire.NewOutPoint(sha, input.Vout) + } + + return ops, nil +} + +// ListLockUnspentAsync 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 ListLockUnspent for the blocking version and more details. +func (c *Client) ListLockUnspentAsync() FutureListLockUnspentResult { + id := c.NextID() + cmd, err := btcjson.NewListLockUnspentCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListLockUnspent returns a slice of outpoints for all unspent outputs marked +// as locked by a wallet. Unspent outputs may be marked locked using +// LockOutput. +func (c *Client) ListLockUnspent() ([]*btcwire.OutPoint, error) { + return c.ListLockUnspentAsync().Receive() +} + // FutureSetTxFeeResult is a future promise to deliver the result of a // SetTxFeeAsync RPC invocation (or an applicable error). type FutureSetTxFeeResult chan *response @@ -2132,7 +2186,6 @@ func (c *Client) GetInfo() (*btcjson.InfoResult, error) { // 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) // move (NYI in btcwallet) From 0e463baf955fc31401f840e3a235163e31439d8e Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 2 Jul 2014 19:33:54 -0500 Subject: [PATCH 061/153] goimports -w . --- chain.go | 1 + examples/bitcoincorehttp/main.go | 3 ++- examples/btcdwebsockets/main.go | 7 ++++--- examples/btcwalletwebsockets/main.go | 7 ++++--- extensions.go | 1 + infrastructure.go | 9 +++++---- log.go | 3 ++- mining.go | 1 + net.go | 1 + notify.go | 3 ++- rawrequest.go | 1 + rawtransactions.go | 1 + wallet.go | 1 + 13 files changed, 26 insertions(+), 13 deletions(-) diff --git a/chain.go b/chain.go index 53e6fdf9..7a89cd01 100644 --- a/chain.go +++ b/chain.go @@ -8,6 +8,7 @@ import ( "bytes" "encoding/hex" "encoding/json" + "github.com/conformal/btcjson" "github.com/conformal/btcutil" "github.com/conformal/btcwire" diff --git a/examples/bitcoincorehttp/main.go b/examples/bitcoincorehttp/main.go index 235a6158..6c14fe7d 100644 --- a/examples/bitcoincorehttp/main.go +++ b/examples/bitcoincorehttp/main.go @@ -5,8 +5,9 @@ package main import ( - "github.com/conformal/btcrpcclient" "log" + + "github.com/conformal/btcrpcclient" ) func main() { diff --git a/examples/btcdwebsockets/main.go b/examples/btcdwebsockets/main.go index ccc08970..92fd6f9f 100644 --- a/examples/btcdwebsockets/main.go +++ b/examples/btcdwebsockets/main.go @@ -5,13 +5,14 @@ package main import ( - "github.com/conformal/btcrpcclient" - "github.com/conformal/btcutil" - "github.com/conformal/btcwire" "io/ioutil" "log" "path/filepath" "time" + + "github.com/conformal/btcrpcclient" + "github.com/conformal/btcutil" + "github.com/conformal/btcwire" ) func main() { diff --git a/examples/btcwalletwebsockets/main.go b/examples/btcwalletwebsockets/main.go index c46e90ac..c87b1c7b 100644 --- a/examples/btcwalletwebsockets/main.go +++ b/examples/btcwalletwebsockets/main.go @@ -5,13 +5,14 @@ package main import ( - "github.com/conformal/btcrpcclient" - "github.com/conformal/btcutil" - "github.com/davecgh/go-spew/spew" "io/ioutil" "log" "path/filepath" "time" + + "github.com/conformal/btcrpcclient" + "github.com/conformal/btcutil" + "github.com/davecgh/go-spew/spew" ) func main() { diff --git a/extensions.go b/extensions.go index 82a2582d..f344c625 100644 --- a/extensions.go +++ b/extensions.go @@ -8,6 +8,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "github.com/conformal/btcjson" "github.com/conformal/btcutil" "github.com/conformal/btcwire" diff --git a/infrastructure.go b/infrastructure.go index b45009b2..e6ba33b2 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -13,16 +13,17 @@ import ( "encoding/json" "errors" "fmt" - "github.com/conformal/btcjson" - "github.com/conformal/btcws" - "github.com/conformal/go-socks" - "github.com/conformal/websocket" "net" "net/http" "net/url" "sync" "sync/atomic" "time" + + "github.com/conformal/btcjson" + "github.com/conformal/btcws" + "github.com/conformal/go-socks" + "github.com/conformal/websocket" ) var ( diff --git a/log.go b/log.go index 204ee66a..fa2d59f9 100644 --- a/log.go +++ b/log.go @@ -6,8 +6,9 @@ package btcrpcclient import ( "errors" - "github.com/conformal/btclog" "io" + + "github.com/conformal/btclog" ) // log is a logger that is initialized with no output filters. This diff --git a/mining.go b/mining.go index d299776f..7e584da5 100644 --- a/mining.go +++ b/mining.go @@ -8,6 +8,7 @@ import ( "encoding/hex" "encoding/json" "errors" + "github.com/conformal/btcjson" "github.com/conformal/btcutil" ) diff --git a/net.go b/net.go index 3b37515c..1c2ab428 100644 --- a/net.go +++ b/net.go @@ -6,6 +6,7 @@ package btcrpcclient import ( "encoding/json" + "github.com/conformal/btcjson" ) diff --git a/notify.go b/notify.go index 78cbb7c6..469b2629 100644 --- a/notify.go +++ b/notify.go @@ -10,11 +10,12 @@ import ( "encoding/json" "errors" "fmt" + "sync" + "github.com/conformal/btcjson" "github.com/conformal/btcutil" "github.com/conformal/btcwire" "github.com/conformal/btcws" - "sync" ) var ( diff --git a/rawrequest.go b/rawrequest.go index 2792a433..40339f09 100644 --- a/rawrequest.go +++ b/rawrequest.go @@ -7,6 +7,7 @@ package btcrpcclient import ( "encoding/json" "errors" + "github.com/conformal/btcjson" ) diff --git a/rawtransactions.go b/rawtransactions.go index 8a8b0ca7..10496e1f 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -8,6 +8,7 @@ import ( "bytes" "encoding/hex" "encoding/json" + "github.com/conformal/btcjson" "github.com/conformal/btcutil" "github.com/conformal/btcwire" diff --git a/wallet.go b/wallet.go index 7cdaa0b8..d3c03ef3 100644 --- a/wallet.go +++ b/wallet.go @@ -6,6 +6,7 @@ package btcrpcclient import ( "encoding/json" + "github.com/conformal/btcjson" "github.com/conformal/btcnet" "github.com/conformal/btcutil" From 77fdb1011b9cc93fa5968af14e330f08f8d47da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Senart?= Date: Thu, 3 Jul 2014 14:04:58 +0200 Subject: [PATCH 062/153] Handle non successfull HTTP responses This change set equips the RPC client with handling of non successful HTTP responses. An HTTP response is considered non successful when its status code is not in the range 200..299 --- infrastructure.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index e6ba33b2..707ae6fd 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -614,22 +614,30 @@ func (c *Client) handleSendPostMessage(details *sendPostDetails) { log.Tracef("Sending command [%s] with id %d", cmd.Method(), cmd.Id()) httpResponse, err := c.httpClient.Do(details.request) if err != nil { - details.responseChan <- &response{result: nil, err: err} + details.responseChan <- &response{err: err} return } // Read the raw bytes and close the response. respBytes, err := btcjson.GetRaw(httpResponse.Body) if err != nil { - details.responseChan <- &response{result: nil, err: err} + details.responseChan <- &response{err: err} return } + + // Handle unsuccessful HTTP responses + if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 { + details.responseChan <- &response{err: errors.New(string(respBytes))} + return + } + var resp rawResponse err = json.Unmarshal(respBytes, &resp) if err != nil { - details.responseChan <- &response{result: nil, err: err} + details.responseChan <- &response{err: err} return } + res, err := resp.result() details.responseChan <- &response{result: res, err: err} } From 1a866200e3611e99b7bd5e7517f87073cc765080 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 3 Jul 2014 12:26:45 -0500 Subject: [PATCH 063/153] Improve websocket connection error handling. This commit modifies the error handling for websocket connections to fall back to returning the status text returned from the server if the handshake fails and it's not due to an authentication or invalid endpoint. --- infrastructure.go | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index 707ae6fd..f6d6692c 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -1037,22 +1037,26 @@ func dial(config *ConnConfig) (*websocket.Conn, error) { url := fmt.Sprintf("%s://%s/%s", scheme, config.Host, config.Endpoint) wsConn, resp, err := dialer.Dial(url, requestHeader) if err != nil { - if err == websocket.ErrBadHandshake { - // Detect HTTP authentication error status codes. - if resp != nil && - (resp.StatusCode == http.StatusUnauthorized || - resp.StatusCode == http.StatusForbidden) { + if err != websocket.ErrBadHandshake || resp == nil { + return nil, err + } - return nil, ErrInvalidAuth - } + // Detect HTTP authentication error status codes. + if resp.StatusCode == http.StatusUnauthorized || + resp.StatusCode == http.StatusForbidden { + return nil, ErrInvalidAuth + } - // The connection was authenticated, but the websocket - // handshake still failed, so the endpoint is invalid - // in some way. + // The connection was authenticated and the status response was + // ok, but the websocket handshake still failed, so the endpoint + // is invalid in some way. + if resp.StatusCode == http.StatusOK { return nil, ErrInvalidEndpoint } - return nil, err + // Return the status text from the server if none of the special + // cases above apply. + return nil, errors.New(resp.Status) } return wsConn, nil } From 1a23feb53e1b3426258edd87026624b8ba5b831a Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Mon, 7 Jul 2014 17:48:38 -0500 Subject: [PATCH 064/153] Fix hang on new request receives after shutdown. Previously, requests could still be sent to a shutdown client and added to the client's internal data structures, without ever responding to the future with an error for a shutdown client (causing hangs when blocking on the future receive). This change fixes this by performing a non-blocking read of the client's shutdown channel before adding a request, and responding with the shutdown error if the client has begun or completed its shutdown. ok @davecgh --- infrastructure.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index f6d6692c..02b57665 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -145,17 +145,33 @@ func (c *Client) NextID() uint64 { } // 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. +// the response from the remote server to be unmarshalled to the appropiate type +// and sent to the specified channel when it is received. +// +// If the client has already begun shutting down, ErrClientShutdown is returned +// and the request is not added. // // This function is safe for concurrent access. -func (c *Client) addRequest(id uint64, request *jsonRequest) { +func (c *Client) addRequest(id uint64, request *jsonRequest) error { c.requestLock.Lock() defer c.requestLock.Unlock() + // A non-blocking read of the shutdown channel with the request lock + // held avoids adding the request to the client's internal data + // structures if the client is in the process of shutting down (and + // has not yet grabbed the request lock), or has finished shutdown + // already (responding to each outstanding request with + // ErrClientShutdown). + select { + case <-c.shutdown: + return ErrClientShutdown + default: + } + // TODO(davec): Already there? element := c.requestList.PushBack(request) c.requestMap[id] = element + return nil } // removeRequest returns and removes the jsonRequest which contains the response @@ -779,10 +795,14 @@ func (c *Client) sendCmd(cmd btcjson.Cmd) chan *response { return responseChan } - c.addRequest(cmd.Id().(uint64), &jsonRequest{ + err := c.addRequest(cmd.Id().(uint64), &jsonRequest{ cmd: cmd, responseChan: responseChan, }) + if err != nil { + responseChan <- &response{err: err} + return responseChan + } c.marshalAndSend(cmd, responseChan) return responseChan } From 656fa8699bf68d7c5aa58cfa6dab7eb846b1d808 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Mon, 7 Jul 2014 20:11:01 -0500 Subject: [PATCH 065/153] Improve disconnect and shutdown handling. Previously the exported Disconnect and Shutdown functions called each other and therefore needed to release and reacquire the locks which could potentionally allow a request to sneak in between the lock and unlock. This commit changes the exported Shutdown function so that everything is done under the request lock to prevent this possibility. In order to support this, the shared code between the Disconnect and Shutdown functions has been refactored into unexported functions and the locking has been altered accordingly. Also, replace the sendMessage function with a select over the send and disconnect channels to prevent any issues with queued up messages. joint debugging effort between myself and @jrick --- infrastructure.go | 84 +++++++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index 02b57665..8102fc46 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -145,8 +145,8 @@ func (c *Client) NextID() uint64 { } // addRequest associates the passed jsonRequest with the passed id. This allows -// the response from the remote server to be unmarshalled to the appropiate type -// and sent to the specified channel when it is received. +// the response from the remote server to be unmarshalled to the appropriate +// type and sent to the specified channel when it is received. // // If the client has already begun shutting down, ErrClientShutdown is returned // and the request is not added. @@ -196,11 +196,8 @@ func (c *Client) removeRequest(id uint64) *jsonRequest { // removeAllRequests removes all the jsonRequests which contain the response // channels for outstanding requests. // -// This function is safe for concurrent access. +// This function MUST be called with the request lock held. func (c *Client) removeAllRequests() { - c.requestLock.Lock() - defer c.requestLock.Unlock() - c.requestMap = make(map[uint64]*list.Element) c.requestList.Init() } @@ -416,11 +413,11 @@ cleanup: // block until the send channel is full. func (c *Client) sendMessage(marshalledJSON []byte) { // Don't send the message if disconnected. - if c.Disconnected() { + select { + case c.sendChan <- marshalledJSON: + case <-c.disconnect: return } - - c.sendChan <- marshalledJSON } // reregisterNtfns creates and sends commands needed to re-establish the current @@ -824,14 +821,14 @@ func (c *Client) Disconnected() bool { return c.disconnected } -// Disconnect disconnects the current websocket associated with the client. The -// connection will automatically be re-established unless the client was -// created with the DisableAutoReconnect flag. +// doDisconnect disconnects the websocket associated with the client if it +// hasn't already been disconnected. It will return false if the disconnect is +// not needed or the client is running in HTTP POST mode. // -// This function has no effect when the client is running in HTTP POST mode. -func (c *Client) Disconnect() { +// This function is safe for concurrent access. +func (c *Client) doDisconnect() bool { if c.config.HttpPostMode { - return + return false } c.mtx.Lock() @@ -839,18 +836,51 @@ func (c *Client) Disconnect() { // Nothing to do if already disconnected. if c.disconnected { - return + return false } log.Tracef("Disconnecting RPC client %s", c.config.Host) close(c.disconnect) c.wsConn.Close() c.disconnected = true + return true +} + +// doShutdown closes the shutdown channel and logs the shutdown unless shutdown +// is already in progress. It will return false if the shutdown is not needed. +// +// This function is safe for concurrent access. +func (c *Client) doShutdown() bool { + // Ignore the shutdown request if the client is already in the process + // of shutting down or already shutdown. + select { + case <-c.shutdown: + return false + default: + } + + log.Tracef("Shutting down RPC client %s", c.config.Host) + close(c.shutdown) + return true +} + +// Disconnect disconnects the current websocket associated with the client. The +// connection will automatically be re-established unless the client was +// created with the DisableAutoReconnect flag. +// +// This function has no effect when the client is running in HTTP POST mode. +func (c *Client) Disconnect() { + // Nothing to do if already disconnected or running in HTTP POST mode. + if !c.doDisconnect() { + return + } + + c.requestLock.Lock() + defer c.requestLock.Unlock() // 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 <- &response{ @@ -858,9 +888,8 @@ func (c *Client) Disconnect() { err: ErrClientDisconnect, } } - c.requestLock.Unlock() c.removeAllRequests() - c.Shutdown() + c.doShutdown() } } @@ -868,19 +897,18 @@ func (c *Client) Disconnect() { // with the client and, when automatic reconnect is enabled, preventing future // attempts to reconnect. It also stops all goroutines. func (c *Client) Shutdown() { + // Do the shutdown under the request lock to prevent clients from + // adding new requests while the client shutdown process is initiated. + c.requestLock.Lock() + defer c.requestLock.Unlock() + // Ignore the shutdown request if the client is already in the process // of shutting down or already shutdown. - select { - case <-c.shutdown: + if !c.doShutdown() { 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 <- &response{ @@ -888,10 +916,10 @@ func (c *Client) Shutdown() { err: ErrClientShutdown, } } - c.requestLock.Unlock() c.removeAllRequests() - c.Disconnect() + // Disconnect the client if needed. + c.doDisconnect() } // Start begins processing input and output messages. From 406dd2fcb73e90b299a00e07d64388095f77c299 Mon Sep 17 00:00:00 2001 From: Michalis Kargakis Date: Tue, 8 Jul 2014 15:40:53 +0300 Subject: [PATCH 066/153] Fix typos --- notify.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/notify.go b/notify.go index 469b2629..402f0eb0 100644 --- a/notify.go +++ b/notify.go @@ -188,7 +188,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { switch ntfn.Method { // OnBlockConnected case btcws.BlockConnectedNtfnMethod: - // Ignore the notification is the client is not interested in + // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnBlockConnected == nil { return @@ -205,7 +205,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { // OnBlockDisconnected case btcws.BlockDisconnectedNtfnMethod: - // Ignore the notification is the client is not interested in + // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnBlockDisconnected == nil { return @@ -222,7 +222,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { // OnRecvTx case btcws.RecvTxNtfnMethod: - // Ignore the notification is the client is not interested in + // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnRecvTx == nil { return @@ -239,7 +239,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { // OnRedeemingTx case btcws.RedeemingTxNtfnMethod: - // Ignore the notification is the client is not interested in + // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnRedeemingTx == nil { return @@ -256,7 +256,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { // OnRescanFinished case btcws.RescanFinishedNtfnMethod: - // Ignore the notification is the client is not interested in + // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnRescanFinished == nil { return @@ -273,7 +273,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { // OnRescanProgress case btcws.RescanProgressNtfnMethod: - // Ignore the notification is the client is not interested in + // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnRescanProgress == nil { return @@ -290,7 +290,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { // OnTxAccepted case btcws.TxAcceptedNtfnMethod: - // Ignore the notification is the client is not interested in + // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnTxAccepted == nil { return @@ -307,7 +307,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { // OnTxAcceptedVerbose case btcws.TxAcceptedVerboseNtfnMethod: - // Ignore the notification is the client is not interested in + // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnTxAcceptedVerbose == nil { return @@ -324,7 +324,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { // OnBtcdConnected case btcws.BtcdConnectedNtfnMethod: - // Ignore the notification is the client is not interested in + // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnBtcdConnected == nil { return @@ -341,7 +341,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { // OnAccountBalance case btcws.AccountBalanceNtfnMethod: - // Ignore the notification is the client is not interested in + // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnAccountBalance == nil { return @@ -358,7 +358,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { // OnWalletLockState case btcws.WalletLockStateNtfnMethod: - // Ignore the notification is the client is not interested in + // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnWalletLockState == nil { return From a07dadb600317b322ea763c6698131e8d89e5a8e Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 9 Jul 2014 21:17:58 -0500 Subject: [PATCH 067/153] Add Move support. This commit adds the Move, MoveAsync, MoveMinConf, MoveMinConfAsync, MoveComment, and MoveCommentAsync functions for issuing the "move" RPC with various optional parameters. Closes #16. --- wallet.go | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-) diff --git a/wallet.go b/wallet.go index d3c03ef3..7ad3b4fd 100644 --- a/wallet.go +++ b/wallet.go @@ -1220,6 +1220,111 @@ func (c *Client) GetAddressesByAccount(account string) ([]btcutil.Address, error return c.GetAddressesByAccountAsync(account).Receive() } +// FutureMoveResult is a future promise to deliver the result of a MoveAsync, +// MoveMinConfAsync, or MoveCommentAsync RPC invocation (or an applicable +// error). +type FutureMoveResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of the move operation. +func (r FutureMoveResult) Receive() (bool, error) { + res, err := receiveFuture(r) + if err != nil { + return false, err + } + + // Unmarshal result as a boolean. + var moveResult bool + err = json.Unmarshal(res, &moveResult) + if err != nil { + return false, err + } + + return moveResult, nil +} + +// MoveAsync 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 Move for the blocking version and more details. +func (c *Client) MoveAsync(fromAccount, toAccount string, amount btcutil.Amount) FutureMoveResult { + id := c.NextID() + cmd, err := btcjson.NewMoveCmd(id, fromAccount, toAccount, int64(amount)) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// Move moves specified amount from one account in your wallet to another. Only +// funds with the default number of minimum confirmations will be used. +// +// See MoveMinConf and MoveComment for different options. +func (c *Client) Move(fromAccount, toAccount string, amount btcutil.Amount) (bool, error) { + return c.MoveAsync(fromAccount, toAccount, amount).Receive() +} + +// MoveMinConfAsync 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 MoveMinConf for the blocking version and more details. +func (c *Client) MoveMinConfAsync(fromAccount, toAccount string, + amount btcutil.Amount, minConfirms int) FutureMoveResult { + + id := c.NextID() + cmd, err := btcjson.NewMoveCmd(id, fromAccount, toAccount, int64(amount), + minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// MoveMinConf moves specified amount from one account in your wallet to +// another. Only funds with the passed number of minimum confirmations will be +// used. +// +// See Move to use the default number of minimum confirmations and MoveComment +// for additional options. +func (c *Client) MoveMinConf(fromAccount, toAccount string, amount btcutil.Amount, minConf int) (bool, error) { + return c.MoveMinConfAsync(fromAccount, toAccount, amount, minConf).Receive() +} + +// MoveCommentAsync 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 MoveComment for the blocking version and more details. +func (c *Client) MoveCommentAsync(fromAccount, toAccount string, + amount btcutil.Amount, minConfirms int, comment string) FutureMoveResult { + + id := c.NextID() + cmd, err := btcjson.NewMoveCmd(id, fromAccount, toAccount, int64(amount), + minConfirms, comment) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// MoveComment moves specified amount from one account in your wallet to +// another and stores the provided comment in the wallet. The comment +// parameter is only available in the wallet. Only funds with the passed number +// of minimum confirmations will be used. +// +// See Move and MoveMinConf to use defaults. +func (c *Client) MoveComment(fromAccount, toAccount string, amount btcutil.Amount, + minConf int, comment string) (bool, error) { + + return c.MoveCommentAsync(fromAccount, toAccount, amount, minConf, + comment).Receive() +} + // FutureValidateAddressResult is a future promise to deliver the result of a // ValidateAddressAsync RPC invocation (or an applicable error). type FutureValidateAddressResult chan *response @@ -2189,7 +2294,6 @@ func (c *Client) GetInfo() (*btcjson.InfoResult, error) { // listaddressgroupings (NYI in btcwallet) // listreceivedbyaddress (NYI in btcwallet) // listreceivedbyaccount (NYI in btcwallet) -// move (NYI in btcwallet) // DUMP // importwallet (NYI in btcwallet) From 6b8ff7f52feb3e454e007d01af29e9d4b328bdbb Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Thu, 17 Jul 2014 13:37:54 -0500 Subject: [PATCH 068/153] Use block shas for rescan begin/end. Also send block sha and block time for rescanprogress/finished. ok @davecgh --- notify.go | 112 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 76 insertions(+), 36 deletions(-) diff --git a/notify.go b/notify.go index 402f0eb0..33899be3 100644 --- a/notify.go +++ b/notify.go @@ -11,6 +11,7 @@ import ( "errors" "fmt" "sync" + "time" "github.com/conformal/btcjson" "github.com/conformal/btcutil" @@ -128,12 +129,12 @@ type NotificationHandlers struct { // signaled on this notification, rather than relying on the return // result of a rescan request, due to how btcd may send various rescan // notifications after the rescan request has already returned. - OnRescanFinished func(lastProcessedHeight int32) + OnRescanFinished func(hash *btcwire.ShaHash, height int32, blkTime time.Time) // 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) + OnRescanProgress func(hash *btcwire.ShaHash, height int32, blkTime time.Time) // OnTxAccepted is invoked when a transaction is accepted into the // memory pool. It will only be invoked if a preceding call to @@ -262,14 +263,14 @@ func (c *Client) handleNotification(ntfn *rawNotification) { return } - lastProcessed, err := parseRescanHeightParams(ntfn.Params) + hash, height, blkTime, err := parseRescanProgressParams(ntfn.Params) if err != nil { log.Warnf("Received invalid rescanfinished "+ "notification: %v", err) return } - c.ntfnHandlers.OnRescanFinished(lastProcessed) + c.ntfnHandlers.OnRescanFinished(hash, height, blkTime) // OnRescanProgress case btcws.RescanProgressNtfnMethod: @@ -279,14 +280,14 @@ func (c *Client) handleNotification(ntfn *rawNotification) { return } - lastProcessed, err := parseRescanHeightParams(ntfn.Params) + hash, height, blkTime, err := parseRescanProgressParams(ntfn.Params) if err != nil { log.Warnf("Received invalid rescanprogress "+ "notification: %v", err) return } - c.ntfnHandlers.OnRescanProgress(lastProcessed) + c.ntfnHandlers.OnRescanProgress(hash, height, blkTime) // OnTxAccepted case btcws.TxAcceptedNtfnMethod: @@ -472,21 +473,41 @@ func parseChainTxNtfnParams(params []json.RawMessage) (*btcutil.Tx, return btcutil.NewTx(&msgTx), block, nil } -// parseRescanHeightParams parses out the height of the last rescanned block +// parseRescanProgressParams parses out the height of the last rescanned block // from the parameters of rescanfinished and rescanprogress notifications. -func parseRescanHeightParams(params []json.RawMessage) (int32, error) { - if len(params) != 1 { - return 0, wrongNumParams(len(params)) +func parseRescanProgressParams(params []json.RawMessage) (*btcwire.ShaHash, int32, time.Time, error) { + if len(params) != 3 { + return nil, 0, time.Time{}, wrongNumParams(len(params)) } - // Unmarshal first parameter as an integer. - var height int32 - err := json.Unmarshal(params[0], &height) + // Unmarshal first parameter as an string. + var hashStr string + err := json.Unmarshal(params[0], &hashStr) if err != nil { - return 0, err + return nil, 0, time.Time{}, err } - return height, nil + // Unmarshal second parameter as an integer. + var height int32 + err = json.Unmarshal(params[1], &height) + if err != nil { + return nil, 0, time.Time{}, err + } + + // Unmarshal third parameter as an integer. + var blkTime int64 + err = json.Unmarshal(params[2], &blkTime) + if err != nil { + return nil, 0, time.Time{}, err + } + + // Decode string encoding of block hash. + hash, err := btcwire.NewShaHashFromStr(hashStr) + if err != nil { + return nil, 0, time.Time{}, err + } + + return hash, height, time.Unix(blkTime, 0), nil } // parseTxAcceptedNtfnParams parses out the transaction hash and total amount @@ -929,7 +950,8 @@ func (r FutureRescanResult) Receive() error { // reconnect. // // NOTE: This is a btcd extension and requires a websocket connection. -func (c *Client) RescanAsync(startHeight int32, addresses []btcutil.Address, +func (c *Client) RescanAsync(startBlock *btcwire.ShaHash, + addresses []btcutil.Address, outpoints []*btcwire.OutPoint) FutureRescanResult { // Not supported in HTTP POST mode. @@ -943,6 +965,12 @@ func (c *Client) RescanAsync(startHeight int32, addresses []btcutil.Address, return newNilFutureResult() } + // Convert block hashes to strings. + var startBlockShaStr string + if startBlock != nil { + startBlockShaStr = startBlock.String() + } + // Convert addresses to strings. addrs := make([]string, 0, len(addresses)) for _, addr := range addresses { @@ -956,7 +984,7 @@ func (c *Client) RescanAsync(startHeight int32, addresses []btcutil.Address, } id := c.NextID() - cmd, err := btcws.NewRescanCmd(id, startHeight, addrs, ops) + cmd, err := btcws.NewRescanCmd(id, startBlockShaStr, addrs, ops) if err != nil { return newFutureError(err) } @@ -964,9 +992,9 @@ func (c *Client) RescanAsync(startHeight int32, addresses []btcutil.Address, 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. +// Rescan rescans the block chain starting from the provided starting block 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 @@ -980,8 +1008,8 @@ func (c *Client) RescanAsync(startHeight int32, addresses []btcutil.Address, // 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. +// See RescanEndBlock to also specify an ending block to finish the rescan +// without continuing through the best block on the main chain. // // NOTE: Rescan requests are not issued on client reconnect and must be // performed manually (ideally with a new start height based on the last @@ -990,22 +1018,23 @@ func (c *Client) RescanAsync(startHeight int32, addresses []btcutil.Address, // reconnect. // // NOTE: This is a btcd extension and requires a websocket connection. -func (c *Client) Rescan(startHeight int32, addresses []btcutil.Address, +func (c *Client) Rescan(startBlock *btcwire.ShaHash, + addresses []btcutil.Address, outpoints []*btcwire.OutPoint) error { - return c.RescanAsync(startHeight, addresses, outpoints).Receive() + return c.RescanAsync(startBlock, addresses, outpoints).Receive() } -// RescanEndHeightAsync returns an instance of a type that can be used to get +// RescanEndBlockAsync 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. +// See RescanEndBlock for the blocking version and more details. // // NOTE: This is a btcd extension and requires a websocket connection. -func (c *Client) RescanEndHeightAsync(startHeight int32, +func (c *Client) RescanEndBlockAsync(startBlock *btcwire.ShaHash, addresses []btcutil.Address, outpoints []*btcwire.OutPoint, - endHeight int64) FutureRescanResult { + endBlock *btcwire.ShaHash) FutureRescanResult { // Not supported in HTTP POST mode. if c.config.HttpPostMode { @@ -1018,6 +1047,15 @@ func (c *Client) RescanEndHeightAsync(startHeight int32, return newNilFutureResult() } + // Convert block hashes to strings. + var startBlockShaStr, endBlockShaStr string + if startBlock != nil { + startBlockShaStr = startBlock.String() + } + if endBlock != nil { + endBlockShaStr = endBlock.String() + } + // Convert addresses to strings. addrs := make([]string, 0, len(addresses)) for _, addr := range addresses { @@ -1031,7 +1069,8 @@ func (c *Client) RescanEndHeightAsync(startHeight int32, } id := c.NextID() - cmd, err := btcws.NewRescanCmd(id, startHeight, addrs, ops, endHeight) + cmd, err := btcws.NewRescanCmd(id, startBlockShaStr, addrs, ops, + endBlockShaStr) if err != nil { return newFutureError(err) } @@ -1039,9 +1078,9 @@ func (c *Client) RescanEndHeightAsync(startHeight int32, 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. +// RescanEndBlock rescans the block chain starting from the provided starting +// block up to the provided ending block 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 @@ -1058,9 +1097,10 @@ func (c *Client) RescanEndHeightAsync(startHeight int32, // 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 { +func (c *Client) RescanEndHeight(startBlock *btcwire.ShaHash, + addresses []btcutil.Address, outpoints []*btcwire.OutPoint, + endBlock *btcwire.ShaHash) error { - return c.RescanEndHeightAsync(startHeight, addresses, outpoints, - endHeight).Receive() + return c.RescanEndBlockAsync(startBlock, addresses, outpoints, + endBlock).Receive() } From 160a8431716c918070c8efaffbad4591ae604471 Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Thu, 10 Jul 2014 13:11:57 -0500 Subject: [PATCH 069/153] Allow websocket conns to be established after New. ok @davecgh --- infrastructure.go | 163 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 135 insertions(+), 28 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index 8102fc46..95eae990 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -37,6 +37,12 @@ var ( ErrInvalidEndpoint = errors.New("the endpoint either does not support " + "websockets or does not exist") + // ErrClientNotConnected is an error to describe the condition where a + // websocket client has been created, but the connection was never + // established. This condition differs from ErrClientDisconnect, which + // represents an established connection that was lost. + ErrClientNotConnected = errors.New("the client was never connected") + // 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 @@ -49,6 +55,18 @@ var ( // 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") + + // ErrNotWebsocketClient is an error to describe the condition of + // calling a Client method intended for a websocket client when the + // client has been configured to run in HTTP POST mode instead. + ErrNotWebsocketClient = errors.New("client is not configured for " + + "websockets") + + // ErrClientAlreadyConnected is an error to describe the condition where + // a new client connection cannot be established due to a websocket + // client having already connected to the RPC server. + ErrClientAlreadyConnected = errors.New("websocket client has already " + + "connected") ) const ( @@ -127,11 +145,12 @@ type Client struct { ntfnState *notificationState // Networking infrastructure. - sendChan chan []byte - sendPostChan chan *sendPostDetails - disconnect chan struct{} - shutdown chan struct{} - wg sync.WaitGroup + sendChan chan []byte + sendPostChan chan *sendPostDetails + connEstablished chan struct{} + disconnect chan struct{} + shutdown chan struct{} + wg sync.WaitGroup } // NextID returns the next id to be used when sending a JSON-RPC message. This @@ -792,6 +811,15 @@ func (c *Client) sendCmd(cmd btcjson.Cmd) chan *response { return responseChan } + // Check whether the websocket connection has never been established, + // in which case the handler goroutines are not running. + select { + case <-c.connEstablished: + default: + responseChan <- &response{err: ErrClientNotConnected} + return responseChan + } + err := c.addRequest(cmd.Id().(uint64), &jsonRequest{ cmd: cmd, responseChan: responseChan, @@ -813,12 +841,18 @@ func (c *Client) sendCmdAndWait(cmd btcjson.Cmd) (interface{}, error) { return receiveFuture(c.sendCmd(cmd)) } -// Disconnected returns whether or not the server is disconnected. +// Disconnected returns whether or not the server is disconnected. If a +// websocket client was created but never connected, this also returns false. func (c *Client) Disconnected() bool { c.mtx.Lock() defer c.mtx.Unlock() - return c.disconnected + select { + case <-c.connEstablished: + return c.disconnected + default: + return false + } } // doDisconnect disconnects the websocket associated with the client if it @@ -841,7 +875,9 @@ func (c *Client) doDisconnect() bool { log.Tracef("Disconnecting RPC client %s", c.config.Host) close(c.disconnect) - c.wsConn.Close() + if c.wsConn != nil { + c.wsConn.Close() + } c.disconnected = true return true } @@ -922,7 +958,7 @@ func (c *Client) Shutdown() { c.doDisconnect() } -// Start begins processing input and output messages. +// start begins processing input and output messages. func (c *Client) start() { log.Tracef("Starting RPC client %s", c.config.Host) @@ -998,6 +1034,12 @@ type ConnConfig struct { // try to reconnect to the server when it has been disconnected. DisableAutoReconnect bool + // DisableConnectOnNew specifies that a websocket client connection + // should not be tried when creating the client with New. Instead, the + // client is created and returned unconnected, and Connect must be + // called manually. + DisableConnectOnNew 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 @@ -1119,8 +1161,11 @@ func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error // when running in HTTP POST mode. var wsConn *websocket.Conn var httpClient *http.Client + connEstablished := make(chan struct{}) + var start bool if config.HttpPostMode { ntfnHandlers = nil + start = true var err error httpClient, err = newHTTPClient(config) @@ -1128,34 +1173,96 @@ func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error return nil, err } } else { - var err error - wsConn, err = dial(config) - if err != nil { - return nil, err + if !config.DisableConnectOnNew { + var err error + wsConn, err = dial(config) + if err != nil { + return nil, err + } + start = true } } log.Infof("Established connection to RPC server %s", config.Host) client := &Client{ - config: config, - wsConn: wsConn, - httpClient: httpClient, - requestMap: make(map[uint64]*list.Element), - requestList: list.New(), - ntfnHandlers: ntfnHandlers, - ntfnState: newNotificationState(), - sendChan: make(chan []byte, sendBufferSize), - sendPostChan: make(chan *sendPostDetails, sendPostBufferSize), - disconnect: make(chan struct{}), - shutdown: make(chan struct{}), + config: config, + wsConn: wsConn, + httpClient: httpClient, + requestMap: make(map[uint64]*list.Element), + requestList: list.New(), + ntfnHandlers: ntfnHandlers, + ntfnState: newNotificationState(), + sendChan: make(chan []byte, sendBufferSize), + sendPostChan: make(chan *sendPostDetails, sendPostBufferSize), + connEstablished: connEstablished, + disconnect: make(chan struct{}), + shutdown: make(chan struct{}), } - client.start() - if !client.config.HttpPostMode && !client.config.DisableAutoReconnect { - client.wg.Add(1) - go client.wsReconnectHandler() + if start { + close(connEstablished) + client.start() + if !client.config.HttpPostMode && !client.config.DisableAutoReconnect { + client.wg.Add(1) + go client.wsReconnectHandler() + } } return client, nil } + +// Connect establishes the initial websocket connection. This is necessary when +// a client was created after setting the DisableConnectOnNew field of the +// Config struct. +// +// Up to tries number of connections (each after an increasing backoff) will +// be tried if the connection can not be established. The special value of 0 +// indicates an unlimited number of connection attempts. +// +// This method will error if the client is not configured for websockets, if the +// connection has already been established, or if none of the connection +// attempts were successful. +func (c *Client) Connect(tries int) error { + c.mtx.Lock() + defer c.mtx.Unlock() + + if c.config.HttpPostMode { + return ErrNotWebsocketClient + } + if c.wsConn != nil { + return ErrClientAlreadyConnected + } + + // Begin connection attempts. Increase the backoff after each failed + // attempt, up to a maximum of one minute. + var err error + var backoff time.Duration + for i := 0; tries == 0 || i < tries; i++ { + var wsConn *websocket.Conn + wsConn, err = dial(c.config) + if err != nil { + backoff = connectionRetryInterval * time.Duration(i+1) + if backoff > time.Minute { + backoff = time.Minute + } + time.Sleep(backoff) + continue + } + + // Connection was established. Set the websocket connection + // member of the client and start the goroutines necessary + // to run the client. + c.wsConn = wsConn + close(c.connEstablished) + c.start() + if !c.config.DisableAutoReconnect { + c.wg.Add(1) + go c.wsReconnectHandler() + } + return nil + } + + // All connection attempts failed, so return the last error. + return err +} From 331f25f506b7ed26079af1712bc4cde30d3cc1fd Mon Sep 17 00:00:00 2001 From: Javed Khan Date: Wed, 10 Sep 2014 21:10:47 +0530 Subject: [PATCH 070/153] compatiblity hack for blockchain.info getbalance --- infrastructure.go | 4 ++++ wallet.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/infrastructure.go b/infrastructure.go index 95eae990..a8636dc7 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -1047,6 +1047,10 @@ type ConnConfig struct { // 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 + + // EnableBCInfoHacks is an option provided to enable compatiblity hacks + // when connecting to blockchain.info RPC server + EnableBCInfoHacks bool } // newHTTPClient returns a new http client that is configured according to the diff --git a/wallet.go b/wallet.go index 7ad3b4fd..6ae4e92f 100644 --- a/wallet.go +++ b/wallet.go @@ -6,6 +6,7 @@ package btcrpcclient import ( "encoding/json" + "strconv" "github.com/conformal/btcjson" "github.com/conformal/btcnet" @@ -1537,6 +1538,39 @@ func (r FutureGetBalanceResult) Receive() (btcutil.Amount, error) { return btcutil.Amount(satoshi), nil } +// FutureGetBalanceParseResult is same as FutureGetBalanceResult except +// that the result is expected to be a string which is then parsed into +// a float64 value +// This is required for compatiblity with servers like blockchain.info +type FutureGetBalanceParseResult chan *response + +// Receive waits for the response promised by the future and returns the +// available balance from the server for the specified account. +func (r FutureGetBalanceParseResult) Receive() (btcutil.Amount, error) { + res, err := receiveFuture(r) + if err != nil { + return 0, err + } + + // Unmarshal result as a string + var balanceString string + err = json.Unmarshal(res, &balanceString) + if err != nil { + return 0, err + } + + balance, err := strconv.ParseFloat(balanceString, 64) + if err != nil { + return 0, err + } + 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. @@ -1590,6 +1624,10 @@ func (c *Client) GetBalanceMinConfAsync(account string, minConfirms int) FutureG // // See GetBalance to use the default minimum number of confirmations. func (c *Client) GetBalanceMinConf(account string, minConfirms int) (btcutil.Amount, error) { + if c.config.EnableBCInfoHacks { + response := c.GetBalanceMinConfAsync(account, minConfirms) + return FutureGetBalanceParseResult(response).Receive() + } return c.GetBalanceMinConfAsync(account, minConfirms).Receive() } From a8dda1fabff762c132aadcccf8747fe1410a386d Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sun, 26 Oct 2014 17:09:41 -0500 Subject: [PATCH 071/153] Add ListReceivedByAddress support. This commit also fixes a couple of comment typos found while adding support for ListReceivedByAddress. Closes #20. --- wallet.go | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 106 insertions(+), 5 deletions(-) diff --git a/wallet.go b/wallet.go index 6ae4e92f..00802eed 100644 --- a/wallet.go +++ b/wallet.go @@ -1899,12 +1899,12 @@ func (c *Client) ListReceivedByAccountMinConfAsync(minConfirms int) FutureListRe } // ListReceivedByAccountMinConf lists balances by account using the specified -// number of minimum confirmations and including accounts that haven't received +// number of minimum confirmations not 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. +// and ListReceivedByAccountIncludeEmpty to also include accounts that haven't +// received any payments in the results. func (c *Client) ListReceivedByAccountMinConf(minConfirms int) ([]btcjson.ListReceivedByAccountResult, error) { return c.ListReceivedByAccountMinConfAsync(minConfirms).Receive() } @@ -1913,7 +1913,7 @@ func (c *Client) ListReceivedByAccountMinConf(minConfirms int) ([]btcjson.ListRe // 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. +// See ListReceivedByAccountIncludeEmpty 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) @@ -1934,6 +1934,108 @@ func (c *Client) ListReceivedByAccountIncludeEmpty(minConfirms int, includeEmpty includeEmpty).Receive() } +// FutureListReceivedByAddressResult is a future promise to deliver the result +// of a ListReceivedByAddressAsync, ListReceivedByAddressMinConfAsync, or +// ListReceivedByAddressIncludeEmptyAsync RPC invocation (or an applicable +// error). +type FutureListReceivedByAddressResult chan *response + +// Receive waits for the response promised by the future and returns a list of +// balances by address. +func (r FutureListReceivedByAddressResult) Receive() ([]btcjson.ListReceivedByAddressResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal as an array of listreceivedbyaddress result objects. + var received []btcjson.ListReceivedByAddressResult + err = json.Unmarshal(res, &received) + if err != nil { + return nil, err + } + + return received, nil +} + +// ListReceivedByAddressAsync 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 ListReceivedByAddress for the blocking version and more details. +func (c *Client) ListReceivedByAddressAsync() FutureListReceivedByAddressResult { + id := c.NextID() + cmd, err := btcjson.NewListReceivedByAddressCmd(id) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListReceivedByAddress lists balances by address using the default number +// of minimum confirmations not including addresses that haven't received any +// payments or watching only addresses. +// +// See ListReceivedByAddressMinConf to override the minimum number of +// confirmations and ListReceivedByAddressIncludeEmpty to also include addresses +// that haven't received any payments in the results. +func (c *Client) ListReceivedByAddress() ([]btcjson.ListReceivedByAddressResult, error) { + return c.ListReceivedByAddressAsync().Receive() +} + +// ListReceivedByAddressMinConfAsync 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 ListReceivedByAddressMinConf for the blocking version and more details. +func (c *Client) ListReceivedByAddressMinConfAsync(minConfirms int) FutureListReceivedByAddressResult { + id := c.NextID() + cmd, err := btcjson.NewListReceivedByAddressCmd(id, minConfirms) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListReceivedByAddressMinConf lists balances by address using the specified +// number of minimum confirmations not including addresses that haven't received +// any payments. +// +// See ListReceivedByAddress to use the default minimum number of confirmations +// and ListReceivedByAddressIncludeEmpty to also include addresses that haven't +// received any payments in the results. +func (c *Client) ListReceivedByAddressMinConf(minConfirms int) ([]btcjson.ListReceivedByAddressResult, error) { + return c.ListReceivedByAddressMinConfAsync(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 ListReceivedByAccountIncludeEmpty for the blocking version and more details. +func (c *Client) ListReceivedByAddressIncludeEmptyAsync(minConfirms int, includeEmpty bool) FutureListReceivedByAddressResult { + id := c.NextID() + cmd, err := btcjson.NewListReceivedByAddressCmd(id, minConfirms, + includeEmpty) + if err != nil { + return newFutureError(err) + } + + return c.sendCmd(cmd) +} + +// ListReceivedByAddressIncludeEmpty lists balances by address using the +// specified number of minimum confirmations and including addresses that +// haven't received any payments depending on specified flag. +// +// See ListReceivedByAddress and ListReceivedByAddressMinConf to use defaults. +func (c *Client) ListReceivedByAddressIncludeEmpty(minConfirms int, includeEmpty bool) ([]btcjson.ListReceivedByAddressResult, error) { + return c.ListReceivedByAddressIncludeEmptyAsync(minConfirms, + includeEmpty).Receive() +} + // ************************ // Wallet Locking Functions // ************************ @@ -2330,7 +2432,6 @@ func (c *Client) GetInfo() (*btcjson.InfoResult, error) { // encryptwallet (Won't be supported by btcwallet since it's always encrypted) // getwalletinfo (NYI in btcwallet or btcjson) // listaddressgroupings (NYI in btcwallet) -// listreceivedbyaddress (NYI in btcwallet) // listreceivedbyaccount (NYI in btcwallet) // DUMP From 59e825d79637a9a536ee2c3421de1f2c5638ead7 Mon Sep 17 00:00:00 2001 From: Jimmy Song Date: Fri, 17 Oct 2014 15:39:45 -0500 Subject: [PATCH 072/153] Added GetTxOut as a possible command to btcrpcclient. Put into chain.go vs other places as per @davecgh --- chain.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/chain.go b/chain.go index 7a89cd01..69305f90 100644 --- a/chain.go +++ b/chain.go @@ -497,3 +497,56 @@ func (c *Client) VerifyChainBlocksAsync(checkLevel, numBlocks int32) FutureVerif func (c *Client) VerifyChainBlocks(checkLevel, numBlocks int32) (bool, error) { return c.VerifyChainBlocksAsync(checkLevel, numBlocks).Receive() } + +// FutureGetTxOutResult is a future promise to deliver the result of a +// GetTxOutAsync RPC invocation (or an applicable error). +type FutureGetTxOutResult chan *response + +// Receive waits for the response promised by the future and returns a +// transaction given its hash. +func (r FutureGetTxOutResult) Receive() (*btcjson.GetTxOutResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // take care of the special case where the output has been spent already + // it should return the string "null" + if string(res) == "null" { + return nil, nil + } + + // Unmarshal result as an gettxout result object. + var txOutInfo *btcjson.GetTxOutResult + err = json.Unmarshal(res, &txOutInfo) + if err != nil { + return nil, err + } + + return txOutInfo, nil +} + +// GetTxOutAsync 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 GetTxOut for the blocking version and more details. +func (c *Client) GetTxOutAsync(txHash *btcwire.ShaHash, index int, mempool bool) FutureGetTxOutResult { + hash := "" + if txHash != nil { + hash = txHash.String() + } + + id := c.NextID() + cmd, err := btcjson.NewGetTxOutCmd(id, hash, index, mempool) + if err != nil { + return newFutureError(err) + } + return c.sendCmd(cmd) +} + +// GetTxOut returns the transaction output info if it's unspent and +// nil, otherwise. +func (c *Client) GetTxOut(txHash *btcwire.ShaHash, index int, mempool bool) (*btcjson.GetTxOutResult, error) { + return c.GetTxOutAsync(txHash, index, mempool).Receive() +} From 37fbff291ca895cff281db27d1356c8cb24a3ddd Mon Sep 17 00:00:00 2001 From: David Hill Date: Sat, 13 Dec 2014 10:02:59 -0500 Subject: [PATCH 073/153] Add missing error check --- chain.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain.go b/chain.go index 69305f90..395002cb 100644 --- a/chain.go +++ b/chain.go @@ -83,7 +83,7 @@ func (r FutureGetBlockResult) Receive() (*btcutil.Block, error) { // Deserialize the block and return it. var msgBlock btcwire.MsgBlock - msgBlock.Deserialize(bytes.NewReader(serializedBlock)) + err = msgBlock.Deserialize(bytes.NewReader(serializedBlock)) if err != nil { return nil, err } From c74bb4e0875b6d62903d1c3847b6b4a14a3dee0e Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sat, 3 Jan 2015 00:52:21 -0600 Subject: [PATCH 074/153] Update license year to 2015. --- LICENSE | 2 +- chain.go | 2 +- examples/bitcoincorehttp/main.go | 2 +- examples/btcdwebsockets/main.go | 2 +- examples/btcwalletwebsockets/main.go | 2 +- extensions.go | 2 +- infrastructure.go | 2 +- log.go | 2 +- mining.go | 2 +- net.go | 2 +- notify.go | 2 +- rawrequest.go | 2 +- rawtransactions.go | 2 +- wallet.go | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/LICENSE b/LICENSE index 315cb819..809b8fe7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014 Conformal Systems LLC. +Copyright (c) 2014-2015 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 diff --git a/chain.go b/chain.go index 395002cb..701a0627 100644 --- a/chain.go +++ b/chain.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014 Conformal Systems LLC. +// Copyright (c) 2014-2015 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/examples/bitcoincorehttp/main.go b/examples/bitcoincorehttp/main.go index 6c14fe7d..6db29906 100644 --- a/examples/bitcoincorehttp/main.go +++ b/examples/bitcoincorehttp/main.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014 Conformal Systems LLC. +// Copyright (c) 2014-2015 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/examples/btcdwebsockets/main.go b/examples/btcdwebsockets/main.go index 92fd6f9f..a7a24367 100644 --- a/examples/btcdwebsockets/main.go +++ b/examples/btcdwebsockets/main.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014 Conformal Systems LLC. +// Copyright (c) 2014-2015 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/examples/btcwalletwebsockets/main.go b/examples/btcwalletwebsockets/main.go index c87b1c7b..ffe3e4ab 100644 --- a/examples/btcwalletwebsockets/main.go +++ b/examples/btcwalletwebsockets/main.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014 Conformal Systems LLC. +// Copyright (c) 2014-2015 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/extensions.go b/extensions.go index f344c625..213b433c 100644 --- a/extensions.go +++ b/extensions.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014 Conformal Systems LLC. +// Copyright (c) 2014-2015 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/infrastructure.go b/infrastructure.go index a8636dc7..1c2cd709 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014 Conformal Systems LLC. +// Copyright (c) 2014-2015 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/log.go b/log.go index fa2d59f9..631ef411 100644 --- a/log.go +++ b/log.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014 Conformal Systems LLC. +// Copyright (c) 2014-2015 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/mining.go b/mining.go index 7e584da5..a8d7e01a 100644 --- a/mining.go +++ b/mining.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014 Conformal Systems LLC. +// Copyright (c) 2014-2015 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/net.go b/net.go index 1c2ab428..12a3d4df 100644 --- a/net.go +++ b/net.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014 Conformal Systems LLC. +// Copyright (c) 2014-2015 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/notify.go b/notify.go index 33899be3..ff256f78 100644 --- a/notify.go +++ b/notify.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014 Conformal Systems LLC. +// Copyright (c) 2014-2015 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/rawrequest.go b/rawrequest.go index 40339f09..4decdfa3 100644 --- a/rawrequest.go +++ b/rawrequest.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014 Conformal Systems LLC. +// Copyright (c) 2014-2015 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/rawtransactions.go b/rawtransactions.go index 10496e1f..56da1c7a 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014 Conformal Systems LLC. +// Copyright (c) 2014-2015 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/wallet.go b/wallet.go index 00802eed..eb098376 100644 --- a/wallet.go +++ b/wallet.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014 Conformal Systems LLC. +// Copyright (c) 2014-2015 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. From 3dfefc978f570b2e2270b1d9f857be2158b6fc68 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 15 Jan 2015 10:41:20 -0600 Subject: [PATCH 075/153] Update btcutil import paths to new location. --- chain.go | 2 +- examples/btcdwebsockets/main.go | 2 +- examples/btcwalletwebsockets/main.go | 2 +- extensions.go | 2 +- mining.go | 2 +- notify.go | 2 +- rawtransactions.go | 2 +- wallet.go | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/chain.go b/chain.go index 701a0627..07241a67 100644 --- a/chain.go +++ b/chain.go @@ -9,8 +9,8 @@ import ( "encoding/hex" "encoding/json" + "github.com/btcsuite/btcutil" "github.com/conformal/btcjson" - "github.com/conformal/btcutil" "github.com/conformal/btcwire" ) diff --git a/examples/btcdwebsockets/main.go b/examples/btcdwebsockets/main.go index a7a24367..e5ac2171 100644 --- a/examples/btcdwebsockets/main.go +++ b/examples/btcdwebsockets/main.go @@ -10,8 +10,8 @@ import ( "path/filepath" "time" + "github.com/btcsuite/btcutil" "github.com/conformal/btcrpcclient" - "github.com/conformal/btcutil" "github.com/conformal/btcwire" ) diff --git a/examples/btcwalletwebsockets/main.go b/examples/btcwalletwebsockets/main.go index ffe3e4ab..47aada2a 100644 --- a/examples/btcwalletwebsockets/main.go +++ b/examples/btcwalletwebsockets/main.go @@ -10,8 +10,8 @@ import ( "path/filepath" "time" + "github.com/btcsuite/btcutil" "github.com/conformal/btcrpcclient" - "github.com/conformal/btcutil" "github.com/davecgh/go-spew/spew" ) diff --git a/extensions.go b/extensions.go index 213b433c..5d281e51 100644 --- a/extensions.go +++ b/extensions.go @@ -9,8 +9,8 @@ import ( "encoding/json" "fmt" + "github.com/btcsuite/btcutil" "github.com/conformal/btcjson" - "github.com/conformal/btcutil" "github.com/conformal/btcwire" "github.com/conformal/btcws" ) diff --git a/mining.go b/mining.go index a8d7e01a..5373e561 100644 --- a/mining.go +++ b/mining.go @@ -9,8 +9,8 @@ import ( "encoding/json" "errors" + "github.com/btcsuite/btcutil" "github.com/conformal/btcjson" - "github.com/conformal/btcutil" ) // FutureGetGenerateResult is a future promise to deliver the result of a diff --git a/notify.go b/notify.go index ff256f78..e391cb0c 100644 --- a/notify.go +++ b/notify.go @@ -13,8 +13,8 @@ import ( "sync" "time" + "github.com/btcsuite/btcutil" "github.com/conformal/btcjson" - "github.com/conformal/btcutil" "github.com/conformal/btcwire" "github.com/conformal/btcws" ) diff --git a/rawtransactions.go b/rawtransactions.go index 56da1c7a..2cfec5e9 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -9,8 +9,8 @@ import ( "encoding/hex" "encoding/json" + "github.com/btcsuite/btcutil" "github.com/conformal/btcjson" - "github.com/conformal/btcutil" "github.com/conformal/btcwire" ) diff --git a/wallet.go b/wallet.go index eb098376..5989676e 100644 --- a/wallet.go +++ b/wallet.go @@ -8,9 +8,9 @@ import ( "encoding/json" "strconv" + "github.com/btcsuite/btcutil" "github.com/conformal/btcjson" "github.com/conformal/btcnet" - "github.com/conformal/btcutil" "github.com/conformal/btcwire" "github.com/conformal/btcws" ) From bbec5f3a91e78a451d5216657eaa2bfcb970b5a1 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 15 Jan 2015 17:25:14 -0600 Subject: [PATCH 076/153] Update websocket import paths to new location. --- infrastructure.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure.go b/infrastructure.go index 1c2cd709..7338c424 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -20,10 +20,10 @@ import ( "sync/atomic" "time" + "github.com/btcsuite/websocket" "github.com/conformal/btcjson" "github.com/conformal/btcws" "github.com/conformal/go-socks" - "github.com/conformal/websocket" ) var ( From d5cbdf849274b0b34af0fbd98d72118df97add50 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 15 Jan 2015 18:09:18 -0600 Subject: [PATCH 077/153] Fix a couple of issues found by golint. --- notify.go | 2 +- wallet.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/notify.go b/notify.go index e391cb0c..c7154cdf 100644 --- a/notify.go +++ b/notify.go @@ -1078,7 +1078,7 @@ func (c *Client) RescanEndBlockAsync(startBlock *btcwire.ShaHash, return c.sendCmd(cmd) } -// RescanEndBlock rescans the block chain starting from the provided starting +// RescanEndHeight rescans the block chain starting from the provided starting // block up to the provided ending block for transactions that pay to the // passed addresses and transactions which spend the passed outpoints. // diff --git a/wallet.go b/wallet.go index 5989676e..01ec19af 100644 --- a/wallet.go +++ b/wallet.go @@ -2010,7 +2010,7 @@ func (c *Client) ListReceivedByAddressMinConf(minConfirms int) ([]btcjson.ListRe return c.ListReceivedByAddressMinConfAsync(minConfirms).Receive() } -// ListReceivedByAccountIncludeEmptyAsync returns an instance of a type that can +// ListReceivedByAddressIncludeEmptyAsync 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. // From d3d5618ef92a1649a2c3fd88de32d6efd3ac022b Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 15 Jan 2015 17:58:47 -0600 Subject: [PATCH 078/153] Update TravisCI to goclean script. Also update to use the new container-based builds For now, golint has been disabled since it fails due to wanting HTTPPostMode. This change will be made as a part of the upgrade to use btcjson2 since it involves an API change. --- .travis.yml | 14 +++++++++++++- goclean.sh | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100755 goclean.sh diff --git a/.travis.yml b/.travis.yml index ae71c02f..eb5922f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,15 @@ language: go go: release -install: go get -d -t -v ./... +sudo: false +before_install: + - gotools=golang.org/x/tools + - if [ "$TRAVIS_GO_VERSION" = "release" ]; then gotools=code.google.com/p/go.tools; fi +install: + - go get -d -t -v ./... + - go get -v $gotools/cmd/cover + - go get -v $gotools/cmd/vet + - go get -v github.com/bradfitz/goimports + - go get -v github.com/golang/lint/golint +script: + - export PATH=$PATH:$HOME/gopath/bin + - ./goclean.sh diff --git a/goclean.sh b/goclean.sh new file mode 100755 index 00000000..47aa7956 --- /dev/null +++ b/goclean.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# The script does automatic checking on a Go package and its sub-packages, including: +# 1. gofmt (http://golang.org/cmd/gofmt/) +# 2. goimports (https://github.com/bradfitz/goimports) +# 3. golint (https://github.com/golang/lint) +# 4. go vet (http://golang.org/cmd/vet) +# 5. test coverage (http://blog.golang.org/cover) + +set -e + +# Automatic checks +test -z "$(gofmt -l -w . | tee /dev/stderr)" +test -z "$(goimports -l -w . | tee /dev/stderr)" +#test -z "$(golint . | tee /dev/stderr)" +go vet ./... +env GORACE="halt_on_error=1" go test -v -race ./... + +# Run test coverage on each subdirectories and merge the coverage profile. + +echo "mode: count" > profile.cov + +# Standard go tooling behavior is to ignore dirs with leading underscores. +for dir in $(find . -maxdepth 10 -not -path './.git*' -not -path '*/_*' -type d); +do +if ls $dir/*.go &> /dev/null; then + go test -covermode=count -coverprofile=$dir/profile.tmp $dir + if [ -f $dir/profile.tmp ]; then + cat $dir/profile.tmp | tail -n +2 >> profile.cov + rm $dir/profile.tmp + fi +fi +done + +go tool cover -func profile.cov + +# To submit the test coverage result to coveralls.io, +# use goveralls (https://github.com/mattn/goveralls) +# goveralls -coverprofile=profile.cov -service=travis-ci From 62b2ec081abef005ee71e1f72f72c442c75f159d Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 15 Jan 2015 21:21:20 -0600 Subject: [PATCH 079/153] Update btcrpcclient import paths to new location. --- README.md | 18 +++++++++--------- examples/bitcoincorehttp/README.md | 4 ++-- examples/bitcoincorehttp/main.go | 2 +- examples/btcdwebsockets/README.md | 4 ++-- examples/btcdwebsockets/main.go | 2 +- examples/btcwalletwebsockets/README.md | 4 ++-- examples/btcwalletwebsockets/main.go | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index b7ecaab7..6ee8e377 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ btcrpcclient ============ -[![Build Status](https://travis-ci.org/conformal/btcrpcclient.png?branch=master)] -(https://travis-ci.org/conformal/btcrpcclient) -[![GoDoc](https://godoc.org/github.com/conformal/btcrpcclient?status.png)] -(http://godoc.org/github.com/conformal/btcrpcclient) +[![Build Status](https://travis-ci.org/btcsuite/btcrpcclient.png?branch=master)] +(https://travis-ci.org/btcsuite/btcrpcclient) +[![GoDoc](https://godoc.org/github.com/btcsuite/btcrpcclient?status.png)] +(http://godoc.org/github.com/btcsuite/btcrpcclient) btcrpcclient implements a Websocket-enabled Bitcoin JSON-RPC client package written in [Go](http://golang.org/). It provides a robust and easy to use @@ -19,16 +19,16 @@ implement and the API is not stable yet. ## Documentation -* [API Reference](http://godoc.org/github.com/conformal/btcrpcclient) -* [btcd Websockets Example](https://github.com/conformal/btcrpcclient/blob/master/examples/btcdwebsockets) +* [API Reference](http://godoc.org/github.com/btcsuite/btcrpcclient) +* [btcd Websockets Example](https://github.com/btcsuite/btcrpcclient/blob/master/examples/btcdwebsockets) Connects to a btcd RPC server using TLS-secured websockets, registers for block connected and block disconnected notifications, and gets the current block count -* [btcwallet Websockets Example](https://github.com/conformal/btcrpcclient/blob/master/examples/btcwalletwebsockets) +* [btcwallet Websockets Example](https://github.com/btcsuite/btcrpcclient/blob/master/examples/btcwalletwebsockets) Connects to a btcwallet RPC server using TLS-secured websockets, registers for notifications about changes to account balances, and gets a list of unspent transaction outputs (utxos) the wallet can sign -* [Bitcoin Core HTTP POST Example](https://github.com/conformal/btcrpcclient/blob/master/examples/bitcoincorehttp) +* [Bitcoin Core HTTP POST Example](https://github.com/btcsuite/btcrpcclient/blob/master/examples/bitcoincorehttp) Connects to a bitcoin core RPC server using HTTP POST mode with TLS disabled and gets the current block count @@ -48,7 +48,7 @@ implement and the API is not stable yet. ## Installation ```bash -$ go get github.com/conformal/btcrpcclient +$ go get github.com/btcsuite/btcrpcclient ``` ## License diff --git a/examples/bitcoincorehttp/README.md b/examples/bitcoincorehttp/README.md index de01350a..cc1dfa30 100644 --- a/examples/bitcoincorehttp/README.md +++ b/examples/bitcoincorehttp/README.md @@ -11,7 +11,7 @@ The first step is to use `go get` to download and install the btcrpcclient package: ```bash -$ go get github.com/conformal/btcrpcclient +$ go get github.com/btcsuite/btcrpcclient ``` Next, modify the `main.go` source to specify the correct RPC username and @@ -25,7 +25,7 @@ password for the RPC server: Finally, navigate to the example's directory and run it with: ```bash -$ cd $GOPATH/src/github.com/conformal/btcrpcclient/examples/bitcoincorehttp +$ cd $GOPATH/src/github.com/btcsuite/btcrpcclient/examples/bitcoincorehttp $ go run *.go ``` diff --git a/examples/bitcoincorehttp/main.go b/examples/bitcoincorehttp/main.go index 6db29906..3caddb3b 100644 --- a/examples/bitcoincorehttp/main.go +++ b/examples/bitcoincorehttp/main.go @@ -7,7 +7,7 @@ package main import ( "log" - "github.com/conformal/btcrpcclient" + "github.com/btcsuite/btcrpcclient" ) func main() { diff --git a/examples/btcdwebsockets/README.md b/examples/btcdwebsockets/README.md index 1f765fa4..b6050fef 100644 --- a/examples/btcdwebsockets/README.md +++ b/examples/btcdwebsockets/README.md @@ -14,7 +14,7 @@ The first step is to use `go get` to download and install the btcrpcclient package: ```bash -$ go get github.com/conformal/btcrpcclient +$ go get github.com/btcsuite/btcrpcclient ``` Next, modify the `main.go` source to specify the correct RPC username and @@ -28,7 +28,7 @@ password for the RPC server: Finally, navigate to the example's directory and run it with: ```bash -$ cd $GOPATH/src/github.com/conformal/btcrpcclient/examples/btcdwebsockets +$ cd $GOPATH/src/github.com/btcsuite/btcrpcclient/examples/btcdwebsockets $ go run *.go ``` diff --git a/examples/btcdwebsockets/main.go b/examples/btcdwebsockets/main.go index e5ac2171..5ae486b7 100644 --- a/examples/btcdwebsockets/main.go +++ b/examples/btcdwebsockets/main.go @@ -10,8 +10,8 @@ import ( "path/filepath" "time" + "github.com/btcsuite/btcrpcclient" "github.com/btcsuite/btcutil" - "github.com/conformal/btcrpcclient" "github.com/conformal/btcwire" ) diff --git a/examples/btcwalletwebsockets/README.md b/examples/btcwalletwebsockets/README.md index bd9a61fa..3d41e944 100644 --- a/examples/btcwalletwebsockets/README.md +++ b/examples/btcwalletwebsockets/README.md @@ -15,7 +15,7 @@ The first step is to use `go get` to download and install the btcrpcclient package: ```bash -$ go get github.com/conformal/btcrpcclient +$ go get github.com/btcsuite/btcrpcclient ``` Next, modify the `main.go` source to specify the correct RPC username and @@ -29,7 +29,7 @@ password for the RPC server: Finally, navigate to the example's directory and run it with: ```bash -$ cd $GOPATH/src/github.com/conformal/btcrpcclient/examples/btcwalletwebsockets +$ cd $GOPATH/src/github.com/btcsuite/btcrpcclient/examples/btcwalletwebsockets $ go run *.go ``` diff --git a/examples/btcwalletwebsockets/main.go b/examples/btcwalletwebsockets/main.go index 47aada2a..38833931 100644 --- a/examples/btcwalletwebsockets/main.go +++ b/examples/btcwalletwebsockets/main.go @@ -10,8 +10,8 @@ import ( "path/filepath" "time" + "github.com/btcsuite/btcrpcclient" "github.com/btcsuite/btcutil" - "github.com/conformal/btcrpcclient" "github.com/davecgh/go-spew/spew" ) From 231854f24baff67fc8a7166ddd9e837684636466 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 16 Jan 2015 01:25:00 -0600 Subject: [PATCH 080/153] Update go-socks import paths to new location. --- infrastructure.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure.go b/infrastructure.go index 7338c424..31624281 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -20,10 +20,10 @@ import ( "sync/atomic" "time" + "github.com/btcsuite/go-socks/socks" "github.com/btcsuite/websocket" "github.com/conformal/btcjson" "github.com/conformal/btcws" - "github.com/conformal/go-socks" ) var ( From a02ba2b4b1b045c95cf5d8200ae2daa81d5c06e8 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 16 Jan 2015 13:08:45 -0600 Subject: [PATCH 081/153] Update btclog import paths to new location. --- log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log.go b/log.go index 631ef411..0b05f1ca 100644 --- a/log.go +++ b/log.go @@ -8,7 +8,7 @@ import ( "errors" "io" - "github.com/conformal/btclog" + "github.com/btcsuite/btclog" ) // log is a logger that is initialized with no output filters. This From 132207cb3f75a5c2e32bcc95cf58edf5ad61303a Mon Sep 17 00:00:00 2001 From: Javed Khan Date: Mon, 10 Nov 2014 22:40:31 +0530 Subject: [PATCH 082/153] Added CreateNewAccount, RenameAccount --- wallet.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/wallet.go b/wallet.go index 01ec19af..916e9100 100644 --- a/wallet.go +++ b/wallet.go @@ -960,6 +960,17 @@ func (c *Client) CreateMultisig(requiredSigs int, addresses []btcutil.Address) ( return c.CreateMultisigAsync(requiredSigs, addresses).Receive() } +// CreateNewAccount creates a new wallet account. +func (c *Client) CreateNewAccount(account string) error { + id := c.NextID() + cmd := btcws.NewCreateNewAccountCmd(id, account) + _, err := c.sendCmdAndWait(cmd) + if err != nil { + return err + } + return nil +} + // FutureGetNewAddressResult is a future promise to deliver the result of a // GetNewAddressAsync RPC invocation (or an applicable error). type FutureGetNewAddressResult chan *response @@ -1326,6 +1337,17 @@ func (c *Client) MoveComment(fromAccount, toAccount string, amount btcutil.Amoun comment).Receive() } +// RenameAccount renames a wallet account. +func (c *Client) RenameAccount(oldaccount, newaccount string) error { + id := c.NextID() + cmd := btcws.NewRenameAccountCmd(id, oldaccount, newaccount) + _, err := c.sendCmdAndWait(cmd) + if err != nil { + return err + } + return nil +} + // FutureValidateAddressResult is a future promise to deliver the result of a // ValidateAddressAsync RPC invocation (or an applicable error). type FutureValidateAddressResult chan *response From c1df139e32ab36a69a9499b35c2c35959b260b47 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 16 Jan 2015 15:28:53 -0600 Subject: [PATCH 083/153] Update btcwire import paths to new location. --- chain.go | 2 +- examples/btcdwebsockets/main.go | 2 +- extensions.go | 2 +- notify.go | 2 +- rawtransactions.go | 2 +- wallet.go | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/chain.go b/chain.go index 07241a67..6f929f32 100644 --- a/chain.go +++ b/chain.go @@ -10,8 +10,8 @@ import ( "encoding/json" "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcwire" "github.com/conformal/btcjson" - "github.com/conformal/btcwire" ) // FutureGetBestBlockHashResult is a future promise to deliver the result of a diff --git a/examples/btcdwebsockets/main.go b/examples/btcdwebsockets/main.go index 5ae486b7..3dd2b62f 100644 --- a/examples/btcdwebsockets/main.go +++ b/examples/btcdwebsockets/main.go @@ -12,7 +12,7 @@ import ( "github.com/btcsuite/btcrpcclient" "github.com/btcsuite/btcutil" - "github.com/conformal/btcwire" + "github.com/btcsuite/btcwire" ) func main() { diff --git a/extensions.go b/extensions.go index 5d281e51..0c881a4f 100644 --- a/extensions.go +++ b/extensions.go @@ -10,8 +10,8 @@ import ( "fmt" "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcwire" "github.com/conformal/btcjson" - "github.com/conformal/btcwire" "github.com/conformal/btcws" ) diff --git a/notify.go b/notify.go index c7154cdf..d84f6ce3 100644 --- a/notify.go +++ b/notify.go @@ -14,8 +14,8 @@ import ( "time" "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcwire" "github.com/conformal/btcjson" - "github.com/conformal/btcwire" "github.com/conformal/btcws" ) diff --git a/rawtransactions.go b/rawtransactions.go index 2cfec5e9..6ca9ce09 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -10,8 +10,8 @@ import ( "encoding/json" "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcwire" "github.com/conformal/btcjson" - "github.com/conformal/btcwire" ) // SigHashType enumerates the available signature hashing types that the diff --git a/wallet.go b/wallet.go index 916e9100..75849e67 100644 --- a/wallet.go +++ b/wallet.go @@ -9,9 +9,9 @@ import ( "strconv" "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcwire" "github.com/conformal/btcjson" "github.com/conformal/btcnet" - "github.com/conformal/btcwire" "github.com/conformal/btcws" ) From fccfbbb347cf91a224e8946196d72157d4015bfe Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 16 Jan 2015 17:36:51 -0600 Subject: [PATCH 084/153] Update btcnet import paths to new location. --- wallet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet.go b/wallet.go index 75849e67..44d472b4 100644 --- a/wallet.go +++ b/wallet.go @@ -8,10 +8,10 @@ import ( "encoding/json" "strconv" + "github.com/btcsuite/btcnet" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcwire" "github.com/conformal/btcjson" - "github.com/conformal/btcnet" "github.com/conformal/btcws" ) From 9e86f74c50310f8d26eb63428fa3bd693f67a3ab Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 16 Jan 2015 23:38:11 -0600 Subject: [PATCH 085/153] Update btcws import paths to new location. --- extensions.go | 2 +- infrastructure.go | 2 +- notify.go | 2 +- wallet.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions.go b/extensions.go index 0c881a4f..af8cd3e0 100644 --- a/extensions.go +++ b/extensions.go @@ -11,8 +11,8 @@ import ( "github.com/btcsuite/btcutil" "github.com/btcsuite/btcwire" + "github.com/btcsuite/btcws" "github.com/conformal/btcjson" - "github.com/conformal/btcws" ) // FutureDebugLevelResult is a future promise to deliver the result of a diff --git a/infrastructure.go b/infrastructure.go index 31624281..8dff70f5 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -20,10 +20,10 @@ import ( "sync/atomic" "time" + "github.com/btcsuite/btcws" "github.com/btcsuite/go-socks/socks" "github.com/btcsuite/websocket" "github.com/conformal/btcjson" - "github.com/conformal/btcws" ) var ( diff --git a/notify.go b/notify.go index d84f6ce3..b6f329cd 100644 --- a/notify.go +++ b/notify.go @@ -15,8 +15,8 @@ import ( "github.com/btcsuite/btcutil" "github.com/btcsuite/btcwire" + "github.com/btcsuite/btcws" "github.com/conformal/btcjson" - "github.com/conformal/btcws" ) var ( diff --git a/wallet.go b/wallet.go index 44d472b4..40d4254d 100644 --- a/wallet.go +++ b/wallet.go @@ -11,8 +11,8 @@ import ( "github.com/btcsuite/btcnet" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcwire" + "github.com/btcsuite/btcws" "github.com/conformal/btcjson" - "github.com/conformal/btcws" ) // ***************************** From 64231158a0de6f13c15ba20b320fad2f12035729 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 16 Jan 2015 23:56:08 -0600 Subject: [PATCH 086/153] Update btcjson import paths to new location. --- chain.go | 2 +- extensions.go | 2 +- infrastructure.go | 2 +- mining.go | 2 +- net.go | 2 +- notify.go | 2 +- rawrequest.go | 2 +- rawtransactions.go | 2 +- wallet.go | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/chain.go b/chain.go index 6f929f32..97bc4a08 100644 --- a/chain.go +++ b/chain.go @@ -9,9 +9,9 @@ import ( "encoding/hex" "encoding/json" + "github.com/btcsuite/btcjson" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcwire" - "github.com/conformal/btcjson" ) // FutureGetBestBlockHashResult is a future promise to deliver the result of a diff --git a/extensions.go b/extensions.go index af8cd3e0..5c00187f 100644 --- a/extensions.go +++ b/extensions.go @@ -9,10 +9,10 @@ import ( "encoding/json" "fmt" + "github.com/btcsuite/btcjson" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcwire" "github.com/btcsuite/btcws" - "github.com/conformal/btcjson" ) // FutureDebugLevelResult is a future promise to deliver the result of a diff --git a/infrastructure.go b/infrastructure.go index 8dff70f5..d14b4dc2 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -20,10 +20,10 @@ import ( "sync/atomic" "time" + "github.com/btcsuite/btcjson" "github.com/btcsuite/btcws" "github.com/btcsuite/go-socks/socks" "github.com/btcsuite/websocket" - "github.com/conformal/btcjson" ) var ( diff --git a/mining.go b/mining.go index 5373e561..c201e2ab 100644 --- a/mining.go +++ b/mining.go @@ -9,8 +9,8 @@ import ( "encoding/json" "errors" + "github.com/btcsuite/btcjson" "github.com/btcsuite/btcutil" - "github.com/conformal/btcjson" ) // FutureGetGenerateResult is a future promise to deliver the result of a diff --git a/net.go b/net.go index 12a3d4df..ace15c88 100644 --- a/net.go +++ b/net.go @@ -7,7 +7,7 @@ package btcrpcclient import ( "encoding/json" - "github.com/conformal/btcjson" + "github.com/btcsuite/btcjson" ) // AddNodeCommand enumerates the available commands that the AddNode function diff --git a/notify.go b/notify.go index b6f329cd..04d6b31d 100644 --- a/notify.go +++ b/notify.go @@ -13,10 +13,10 @@ import ( "sync" "time" + "github.com/btcsuite/btcjson" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcwire" "github.com/btcsuite/btcws" - "github.com/conformal/btcjson" ) var ( diff --git a/rawrequest.go b/rawrequest.go index 4decdfa3..d18f10b4 100644 --- a/rawrequest.go +++ b/rawrequest.go @@ -8,7 +8,7 @@ import ( "encoding/json" "errors" - "github.com/conformal/btcjson" + "github.com/btcsuite/btcjson" ) // rawRequest satisifies the btcjson.Cmd interface for btcjson raw commands. diff --git a/rawtransactions.go b/rawtransactions.go index 6ca9ce09..a345848b 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -9,9 +9,9 @@ import ( "encoding/hex" "encoding/json" + "github.com/btcsuite/btcjson" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcwire" - "github.com/conformal/btcjson" ) // SigHashType enumerates the available signature hashing types that the diff --git a/wallet.go b/wallet.go index 40d4254d..aeea2c2b 100644 --- a/wallet.go +++ b/wallet.go @@ -8,11 +8,11 @@ import ( "encoding/json" "strconv" + "github.com/btcsuite/btcjson" "github.com/btcsuite/btcnet" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcwire" "github.com/btcsuite/btcws" - "github.com/conformal/btcjson" ) // ***************************** From 04f541082afd94a72e1c21a5603b26da9fb44ec4 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sat, 17 Jan 2015 00:30:49 -0600 Subject: [PATCH 087/153] Update btcwallet import paths to new location. --- doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc.go b/doc.go index b2604723..15fd91c4 100644 --- a/doc.go +++ b/doc.go @@ -10,7 +10,7 @@ Overview This client provides a robust and easy to use client for interfacing with a Bitcoin RPC server that uses a btcd/bitcoin core compatible Bitcoin JSON-RPC API. This client has been tested with btcd (https://github.com/conformal/btcd), -btcwallet (https://github.com/conformal/btcwallet), and +btcwallet (https://github.com/btcsuite/btcwallet), and bitcoin core (https://github.com/bitcoin). In addition to the compatible standard HTTP POST JSON-RPC API, btcd and From bc36ac6d52a2719ab8f9847c4e9cea8b892c2b61 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sat, 17 Jan 2015 01:18:22 -0600 Subject: [PATCH 088/153] Update btcd import paths to new location. --- doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc.go b/doc.go index 15fd91c4..8f7a0589 100644 --- a/doc.go +++ b/doc.go @@ -9,7 +9,7 @@ Overview This client provides a robust and easy to use client for interfacing with a Bitcoin RPC server that uses a btcd/bitcoin core compatible Bitcoin JSON-RPC -API. This client has been tested with btcd (https://github.com/conformal/btcd), +API. This client has been tested with btcd (https://github.com/btcsuite/btcd), btcwallet (https://github.com/btcsuite/btcwallet), and bitcoin core (https://github.com/bitcoin). From 20ff0689d8fcc0fc164d1b53dccb5eae07facd37 Mon Sep 17 00:00:00 2001 From: Javed Khan Date: Fri, 23 Jan 2015 17:32:22 +0530 Subject: [PATCH 089/153] Added async version for CreateNewAccount, RenameAccount --- wallet.go | 60 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/wallet.go b/wallet.go index aeea2c2b..fc23a428 100644 --- a/wallet.go +++ b/wallet.go @@ -960,17 +960,37 @@ func (c *Client) CreateMultisig(requiredSigs int, addresses []btcutil.Address) ( return c.CreateMultisigAsync(requiredSigs, addresses).Receive() } -// CreateNewAccount creates a new wallet account. -func (c *Client) CreateNewAccount(account string) error { - id := c.NextID() - cmd := btcws.NewCreateNewAccountCmd(id, account) - _, err := c.sendCmdAndWait(cmd) +// FutureCreateNewAccountResult is a future promise to deliver the result of a +// CreateNewAccountAsync RPC invocation (or an applicable error). +type FutureCreateNewAccountResult chan *response + +// Receive waits for the response promised by the future and returns the +// result of creating new account. +func (r FutureCreateNewAccountResult) Receive() error { + _, err := receiveFuture(r) if err != nil { return err } + return nil } +// CreateNewAccountAsync 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 CreateNewAccount for the blocking version and more details. +func (c *Client) CreateNewAccountAsync(account string) FutureCreateNewAccountResult { + id := c.NextID() + cmd := btcws.NewCreateNewAccountCmd(id, account) + return c.sendCmd(cmd) +} + +// CreateNewAccount creates a new wallet account. +func (c *Client) CreateNewAccount(account string) error { + return c.CreateNewAccountAsync(account).Receive() +} + // FutureGetNewAddressResult is a future promise to deliver the result of a // GetNewAddressAsync RPC invocation (or an applicable error). type FutureGetNewAddressResult chan *response @@ -1337,17 +1357,37 @@ func (c *Client) MoveComment(fromAccount, toAccount string, amount btcutil.Amoun comment).Receive() } -// RenameAccount renames a wallet account. -func (c *Client) RenameAccount(oldaccount, newaccount string) error { - id := c.NextID() - cmd := btcws.NewRenameAccountCmd(id, oldaccount, newaccount) - _, err := c.sendCmdAndWait(cmd) +// FutureRenameAccountResult is a future promise to deliver the result of a +// RenameAccountAsync RPC invocation (or an applicable error). +type FutureRenameAccountResult chan *response + +// Receive waits for the response promised by the future and returns the +// result of creating new account. +func (r FutureRenameAccountResult) Receive() error { + _, err := receiveFuture(r) if err != nil { return err } + return nil } +// RenameAccountAsync 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 RenameAccount for the blocking version and more details. +func (c *Client) RenameAccountAsync(oldaccount, newaccount string) FutureRenameAccountResult { + id := c.NextID() + cmd := btcws.NewRenameAccountCmd(id, oldaccount, newaccount) + return c.sendCmd(cmd) +} + +// RenameAccount creates a new wallet account. +func (c *Client) RenameAccount(oldaccount, newaccount string) error { + return c.RenameAccountAsync(oldaccount, newaccount).Receive() +} + // FutureValidateAddressResult is a future promise to deliver the result of a // ValidateAddressAsync RPC invocation (or an applicable error). type FutureValidateAddressResult chan *response From ed821410cb544aad045c419f3f2e74910e9ed2d0 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 5 Feb 2015 15:50:03 -0600 Subject: [PATCH 090/153] Update btcwire path import paths to new location. --- chain.go | 36 ++++++++++----------- examples/btcdwebsockets/main.go | 6 ++-- extensions.go | 14 ++++----- notify.go | 52 +++++++++++++++--------------- rawtransactions.go | 50 ++++++++++++++--------------- wallet.go | 56 ++++++++++++++++----------------- 6 files changed, 107 insertions(+), 107 deletions(-) diff --git a/chain.go b/chain.go index 97bc4a08..0638c6e7 100644 --- a/chain.go +++ b/chain.go @@ -9,9 +9,9 @@ import ( "encoding/hex" "encoding/json" + "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcjson" "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcwire" ) // FutureGetBestBlockHashResult is a future promise to deliver the result of a @@ -20,7 +20,7 @@ type FutureGetBestBlockHashResult chan *response // 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) { +func (r FutureGetBestBlockHashResult) Receive() (*wire.ShaHash, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -32,7 +32,7 @@ func (r FutureGetBestBlockHashResult) Receive() (*btcwire.ShaHash, error) { if err != nil { return nil, err } - return btcwire.NewShaHashFromStr(txHashStr) + return wire.NewShaHashFromStr(txHashStr) } // GetBestBlockHashAsync returns an instance of a type that can be used to get @@ -52,7 +52,7 @@ func (c *Client) GetBestBlockHashAsync() FutureGetBestBlockHashResult { // GetBestBlockHash returns the hash of the best block in the longest block // chain. -func (c *Client) GetBestBlockHash() (*btcwire.ShaHash, error) { +func (c *Client) GetBestBlockHash() (*wire.ShaHash, error) { return c.GetBestBlockHashAsync().Receive() } @@ -82,7 +82,7 @@ func (r FutureGetBlockResult) Receive() (*btcutil.Block, error) { } // Deserialize the block and return it. - var msgBlock btcwire.MsgBlock + var msgBlock wire.MsgBlock err = msgBlock.Deserialize(bytes.NewReader(serializedBlock)) if err != nil { return nil, err @@ -95,7 +95,7 @@ func (r FutureGetBlockResult) Receive() (*btcutil.Block, error) { // returned instance. // // See GetBlock for the blocking version and more details. -func (c *Client) GetBlockAsync(blockHash *btcwire.ShaHash) FutureGetBlockResult { +func (c *Client) GetBlockAsync(blockHash *wire.ShaHash) FutureGetBlockResult { hash := "" if blockHash != nil { hash = blockHash.String() @@ -114,7 +114,7 @@ func (c *Client) GetBlockAsync(blockHash *btcwire.ShaHash) FutureGetBlockResult // // See GetBlockVerbose to retrieve a data structure with information about the // block instead. -func (c *Client) GetBlock(blockHash *btcwire.ShaHash) (*btcutil.Block, error) { +func (c *Client) GetBlock(blockHash *wire.ShaHash) (*btcutil.Block, error) { return c.GetBlockAsync(blockHash).Receive() } @@ -144,7 +144,7 @@ func (r FutureGetBlockVerboseResult) Receive() (*btcjson.BlockResult, error) { // the returned instance. // // See GetBlockVerbose for the blocking version and more details. -func (c *Client) GetBlockVerboseAsync(blockHash *btcwire.ShaHash, verboseTx bool) FutureGetBlockVerboseResult { +func (c *Client) GetBlockVerboseAsync(blockHash *wire.ShaHash, verboseTx bool) FutureGetBlockVerboseResult { hash := "" if blockHash != nil { hash = blockHash.String() @@ -163,7 +163,7 @@ func (c *Client) GetBlockVerboseAsync(blockHash *btcwire.ShaHash, verboseTx bool // 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) { +func (c *Client) GetBlockVerbose(blockHash *wire.ShaHash, verboseTx bool) (*btcjson.BlockResult, error) { return c.GetBlockVerboseAsync(blockHash, verboseTx).Receive() } @@ -256,7 +256,7 @@ type FutureGetBlockHashResult chan *response // 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) { +func (r FutureGetBlockHashResult) Receive() (*wire.ShaHash, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -268,7 +268,7 @@ func (r FutureGetBlockHashResult) Receive() (*btcwire.ShaHash, error) { if err != nil { return nil, err } - return btcwire.NewShaHashFromStr(txHashStr) + return wire.NewShaHashFromStr(txHashStr) } // GetBlockHashAsync returns an instance of a type that can be used to get the @@ -288,7 +288,7 @@ func (c *Client) GetBlockHashAsync(blockHeight int64) FutureGetBlockHashResult { // 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) { +func (c *Client) GetBlockHash(blockHeight int64) (*wire.ShaHash, error) { return c.GetBlockHashAsync(blockHeight).Receive() } @@ -298,7 +298,7 @@ type FutureGetRawMempoolResult chan *response // 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) { +func (r FutureGetRawMempoolResult) Receive() ([]*wire.ShaHash, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -312,9 +312,9 @@ func (r FutureGetRawMempoolResult) Receive() ([]*btcwire.ShaHash, error) { } // Create a slice of ShaHash arrays from the string slice. - txHashes := make([]*btcwire.ShaHash, 0, len(txHashStrs)) + txHashes := make([]*wire.ShaHash, 0, len(txHashStrs)) for _, hashStr := range txHashStrs { - txHash, err := btcwire.NewShaHashFromStr(hashStr) + txHash, err := wire.NewShaHashFromStr(hashStr) if err != nil { return nil, err } @@ -343,7 +343,7 @@ func (c *Client) GetRawMempoolAsync() FutureGetRawMempoolResult { // // See GetRawMempoolVerbose to retrieve data structures with information about // the transactions instead. -func (c *Client) GetRawMempool() ([]*btcwire.ShaHash, error) { +func (c *Client) GetRawMempool() ([]*wire.ShaHash, error) { return c.GetRawMempoolAsync().Receive() } @@ -531,7 +531,7 @@ func (r FutureGetTxOutResult) Receive() (*btcjson.GetTxOutResult, error) { // the returned instance. // // See GetTxOut for the blocking version and more details. -func (c *Client) GetTxOutAsync(txHash *btcwire.ShaHash, index int, mempool bool) FutureGetTxOutResult { +func (c *Client) GetTxOutAsync(txHash *wire.ShaHash, index int, mempool bool) FutureGetTxOutResult { hash := "" if txHash != nil { hash = txHash.String() @@ -547,6 +547,6 @@ func (c *Client) GetTxOutAsync(txHash *btcwire.ShaHash, index int, mempool bool) // GetTxOut returns the transaction output info if it's unspent and // nil, otherwise. -func (c *Client) GetTxOut(txHash *btcwire.ShaHash, index int, mempool bool) (*btcjson.GetTxOutResult, error) { +func (c *Client) GetTxOut(txHash *wire.ShaHash, index int, mempool bool) (*btcjson.GetTxOutResult, error) { return c.GetTxOutAsync(txHash, index, mempool).Receive() } diff --git a/examples/btcdwebsockets/main.go b/examples/btcdwebsockets/main.go index 3dd2b62f..92978305 100644 --- a/examples/btcdwebsockets/main.go +++ b/examples/btcdwebsockets/main.go @@ -10,9 +10,9 @@ import ( "path/filepath" "time" + "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcrpcclient" "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcwire" ) func main() { @@ -21,10 +21,10 @@ func main() { // for notifications. See the documentation of the btcrpcclient // NotificationHandlers type for more details about each handler. ntfnHandlers := btcrpcclient.NotificationHandlers{ - OnBlockConnected: func(hash *btcwire.ShaHash, height int32) { + OnBlockConnected: func(hash *wire.ShaHash, height int32) { log.Printf("Block connected: %v (%d)", hash, height) }, - OnBlockDisconnected: func(hash *btcwire.ShaHash, height int32) { + OnBlockDisconnected: func(hash *wire.ShaHash, height int32) { log.Printf("Block disconnected: %v (%d)", hash, height) }, } diff --git a/extensions.go b/extensions.go index 5c00187f..033ec914 100644 --- a/extensions.go +++ b/extensions.go @@ -9,9 +9,9 @@ import ( "encoding/json" "fmt" + "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcjson" "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcwire" "github.com/btcsuite/btcws" ) @@ -160,7 +160,7 @@ type FutureGetBestBlockResult chan *response // 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) { +func (r FutureGetBestBlockResult) Receive() (*wire.ShaHash, int32, error) { res, err := receiveFuture(r) if err != nil { return nil, 0, err @@ -174,7 +174,7 @@ func (r FutureGetBestBlockResult) Receive() (*btcwire.ShaHash, int32, error) { } // Convert hash string. - hash, err := btcwire.NewShaHashFromStr(bestBlock.Hash) + hash, err := wire.NewShaHashFromStr(bestBlock.Hash) if err != nil { return nil, 0, err } @@ -200,7 +200,7 @@ func (c *Client) GetBestBlockAsync() FutureGetBestBlockResult { // chain. // // NOTE: This is a btcd extension. -func (c *Client) GetBestBlock() (*btcwire.ShaHash, int32, error) { +func (c *Client) GetBestBlock() (*wire.ShaHash, int32, error) { return c.GetBestBlockAsync().Receive() } @@ -210,7 +210,7 @@ type FutureGetCurrentNetResult chan *response // 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) { +func (r FutureGetCurrentNetResult) Receive() (wire.BitcoinNet, error) { res, err := receiveFuture(r) if err != nil { return 0, err @@ -223,7 +223,7 @@ func (r FutureGetCurrentNetResult) Receive() (btcwire.BitcoinNet, error) { return 0, err } - return btcwire.BitcoinNet(net), nil + return wire.BitcoinNet(net), nil } // GetCurrentNetAsync returns an instance of a type that can be used to get the @@ -243,7 +243,7 @@ func (c *Client) GetCurrentNetAsync() FutureGetCurrentNetResult { // GetCurrentNet returns the network the server is running on. // // NOTE: This is a btcd extension. -func (c *Client) GetCurrentNet() (btcwire.BitcoinNet, error) { +func (c *Client) GetCurrentNet() (wire.BitcoinNet, error) { return c.GetCurrentNetAsync().Receive() } diff --git a/notify.go b/notify.go index 04d6b31d..557cb76b 100644 --- a/notify.go +++ b/notify.go @@ -13,9 +13,9 @@ import ( "sync" "time" + "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcjson" "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcwire" "github.com/btcsuite/btcws" ) @@ -97,13 +97,13 @@ type NotificationHandlers struct { // (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) + OnBlockConnected func(hash *wire.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) + OnBlockDisconnected func(hash *wire.ShaHash, height int32) // OnRecvTx is invoked when a transaction that receives funds to a // registered address is received into the memory pool and also @@ -129,18 +129,18 @@ type NotificationHandlers struct { // signaled on this notification, rather than relying on the return // result of a rescan request, due to how btcd may send various rescan // notifications after the rescan request has already returned. - OnRescanFinished func(hash *btcwire.ShaHash, height int32, blkTime time.Time) + OnRescanFinished func(hash *wire.ShaHash, height int32, blkTime time.Time) // 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(hash *btcwire.ShaHash, height int32, blkTime time.Time) + OnRescanProgress func(hash *wire.ShaHash, height int32, blkTime time.Time) // 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 func(hash *wire.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 @@ -399,7 +399,7 @@ func (e wrongNumParams) Error() string { // parseChainNtfnParams parses out the block hash and height from the parameters // of blockconnected and blockdisconnected notifications. -func parseChainNtfnParams(params []json.RawMessage) (*btcwire.ShaHash, +func parseChainNtfnParams(params []json.RawMessage) (*wire.ShaHash, int32, error) { if len(params) != 2 { @@ -421,7 +421,7 @@ func parseChainNtfnParams(params []json.RawMessage) (*btcwire.ShaHash, } // Create ShaHash from block sha string. - blockSha, err := btcwire.NewShaHashFromStr(blockShaStr) + blockSha, err := wire.NewShaHashFromStr(blockShaStr) if err != nil { return nil, 0, err } @@ -461,7 +461,7 @@ func parseChainTxNtfnParams(params []json.RawMessage) (*btcutil.Tx, if err != nil { return nil, nil, err } - var msgTx btcwire.MsgTx + var msgTx wire.MsgTx err = msgTx.Deserialize(bytes.NewReader(serializedTx)) if err != nil { return nil, nil, err @@ -469,13 +469,13 @@ func parseChainTxNtfnParams(params []json.RawMessage) (*btcutil.Tx, // TODO: Change recvtx and redeemingtx callback signatures to use // nicer types for details about the block (block sha as a - // btcwire.ShaHash, block time as a time.Time, etc.). + // wire.ShaHash, block time as a time.Time, etc.). return btcutil.NewTx(&msgTx), block, nil } // parseRescanProgressParams parses out the height of the last rescanned block // from the parameters of rescanfinished and rescanprogress notifications. -func parseRescanProgressParams(params []json.RawMessage) (*btcwire.ShaHash, int32, time.Time, error) { +func parseRescanProgressParams(params []json.RawMessage) (*wire.ShaHash, int32, time.Time, error) { if len(params) != 3 { return nil, 0, time.Time{}, wrongNumParams(len(params)) } @@ -502,7 +502,7 @@ func parseRescanProgressParams(params []json.RawMessage) (*btcwire.ShaHash, int3 } // Decode string encoding of block hash. - hash, err := btcwire.NewShaHashFromStr(hashStr) + hash, err := wire.NewShaHashFromStr(hashStr) if err != nil { return nil, 0, time.Time{}, err } @@ -512,7 +512,7 @@ func parseRescanProgressParams(params []json.RawMessage) (*btcwire.ShaHash, int3 // parseTxAcceptedNtfnParams parses out the transaction hash and total amount // from the parameters of a txaccepted notification. -func parseTxAcceptedNtfnParams(params []json.RawMessage) (*btcwire.ShaHash, +func parseTxAcceptedNtfnParams(params []json.RawMessage) (*wire.ShaHash, btcutil.Amount, error) { if len(params) != 2 { @@ -534,7 +534,7 @@ func parseTxAcceptedNtfnParams(params []json.RawMessage) (*btcwire.ShaHash, } // Decode string encoding of transaction sha. - txSha, err := btcwire.NewShaHashFromStr(txShaStr) + txSha, err := wire.NewShaHashFromStr(txShaStr) if err != nil { return nil, 0, err } @@ -740,7 +740,7 @@ func (c *Client) notifySpentInternal(outpoints []btcws.OutPoint) FutureNotifySpe // See NotifySpent for the blocking version and more details. // // NOTE: This is a btcd extension and requires a websocket connection. -func (c *Client) NotifySpentAsync(outpoints []*btcwire.OutPoint) FutureNotifySpentResult { +func (c *Client) NotifySpentAsync(outpoints []*wire.OutPoint) FutureNotifySpentResult { // Not supported in HTTP POST mode. if c.config.HttpPostMode { return newFutureError(ErrNotificationsNotSupported) @@ -772,7 +772,7 @@ func (c *Client) NotifySpentAsync(outpoints []*btcwire.OutPoint) FutureNotifySpe // OnRedeemingTx. // // NOTE: This is a btcd extension and requires a websocket connection. -func (c *Client) NotifySpent(outpoints []*btcwire.OutPoint) error { +func (c *Client) NotifySpent(outpoints []*wire.OutPoint) error { return c.NotifySpentAsync(outpoints).Receive() } @@ -950,9 +950,9 @@ func (r FutureRescanResult) Receive() error { // reconnect. // // NOTE: This is a btcd extension and requires a websocket connection. -func (c *Client) RescanAsync(startBlock *btcwire.ShaHash, +func (c *Client) RescanAsync(startBlock *wire.ShaHash, addresses []btcutil.Address, - outpoints []*btcwire.OutPoint) FutureRescanResult { + outpoints []*wire.OutPoint) FutureRescanResult { // Not supported in HTTP POST mode. if c.config.HttpPostMode { @@ -1018,9 +1018,9 @@ func (c *Client) RescanAsync(startBlock *btcwire.ShaHash, // reconnect. // // NOTE: This is a btcd extension and requires a websocket connection. -func (c *Client) Rescan(startBlock *btcwire.ShaHash, +func (c *Client) Rescan(startBlock *wire.ShaHash, addresses []btcutil.Address, - outpoints []*btcwire.OutPoint) error { + outpoints []*wire.OutPoint) error { return c.RescanAsync(startBlock, addresses, outpoints).Receive() } @@ -1032,9 +1032,9 @@ func (c *Client) Rescan(startBlock *btcwire.ShaHash, // See RescanEndBlock for the blocking version and more details. // // NOTE: This is a btcd extension and requires a websocket connection. -func (c *Client) RescanEndBlockAsync(startBlock *btcwire.ShaHash, - addresses []btcutil.Address, outpoints []*btcwire.OutPoint, - endBlock *btcwire.ShaHash) FutureRescanResult { +func (c *Client) RescanEndBlockAsync(startBlock *wire.ShaHash, + addresses []btcutil.Address, outpoints []*wire.OutPoint, + endBlock *wire.ShaHash) FutureRescanResult { // Not supported in HTTP POST mode. if c.config.HttpPostMode { @@ -1097,9 +1097,9 @@ func (c *Client) RescanEndBlockAsync(startBlock *btcwire.ShaHash, // 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(startBlock *btcwire.ShaHash, - addresses []btcutil.Address, outpoints []*btcwire.OutPoint, - endBlock *btcwire.ShaHash) error { +func (c *Client) RescanEndHeight(startBlock *wire.ShaHash, + addresses []btcutil.Address, outpoints []*wire.OutPoint, + endBlock *wire.ShaHash) error { return c.RescanEndBlockAsync(startBlock, addresses, outpoints, endBlock).Receive() diff --git a/rawtransactions.go b/rawtransactions.go index a345848b..5dec6299 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -9,9 +9,9 @@ import ( "encoding/hex" "encoding/json" + "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcjson" "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcwire" ) // SigHashType enumerates the available signature hashing types that the @@ -83,7 +83,7 @@ func (r FutureGetRawTransactionResult) Receive() (*btcutil.Tx, error) { } // Deserialize the transaction and return it. - var msgTx btcwire.MsgTx + var msgTx wire.MsgTx if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil { return nil, err } @@ -95,7 +95,7 @@ func (r FutureGetRawTransactionResult) Receive() (*btcutil.Tx, error) { // the returned instance. // // See GetRawTransaction for the blocking version and more details. -func (c *Client) GetRawTransactionAsync(txHash *btcwire.ShaHash) FutureGetRawTransactionResult { +func (c *Client) GetRawTransactionAsync(txHash *wire.ShaHash) FutureGetRawTransactionResult { hash := "" if txHash != nil { hash = txHash.String() @@ -114,7 +114,7 @@ func (c *Client) GetRawTransactionAsync(txHash *btcwire.ShaHash) FutureGetRawTra // // See GetRawTransactionVerbose to obtain additional information about the // transaction. -func (c *Client) GetRawTransaction(txHash *btcwire.ShaHash) (*btcutil.Tx, error) { +func (c *Client) GetRawTransaction(txHash *wire.ShaHash) (*btcutil.Tx, error) { return c.GetRawTransactionAsync(txHash).Receive() } @@ -146,7 +146,7 @@ func (r FutureGetRawTransactionVerboseResult) Receive() (*btcjson.TxRawResult, e // function on the returned instance. // // See GetRawTransactionVerbose for the blocking version and more details. -func (c *Client) GetRawTransactionVerboseAsync(txHash *btcwire.ShaHash) FutureGetRawTransactionVerboseResult { +func (c *Client) GetRawTransactionVerboseAsync(txHash *wire.ShaHash) FutureGetRawTransactionVerboseResult { hash := "" if txHash != nil { hash = txHash.String() @@ -165,7 +165,7 @@ func (c *Client) GetRawTransactionVerboseAsync(txHash *btcwire.ShaHash) FutureGe // its hash. // // See GetRawTransaction to obtain only the transaction already deserialized. -func (c *Client) GetRawTransactionVerbose(txHash *btcwire.ShaHash) (*btcjson.TxRawResult, error) { +func (c *Client) GetRawTransactionVerbose(txHash *wire.ShaHash) (*btcjson.TxRawResult, error) { return c.GetRawTransactionVerboseAsync(txHash).Receive() } @@ -220,7 +220,7 @@ type FutureCreateRawTransactionResult chan *response // 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) { +func (r FutureCreateRawTransactionResult) Receive() (*wire.MsgTx, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -240,7 +240,7 @@ func (r FutureCreateRawTransactionResult) Receive() (*btcwire.MsgTx, error) { } // Deserialize the transaction and return it. - var msgTx btcwire.MsgTx + var msgTx wire.MsgTx if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil { return nil, err } @@ -271,7 +271,7 @@ func (c *Client) CreateRawTransactionAsync(inputs []btcjson.TransactionInput, // CreateRawTransaction returns a new transaction spending the provided inputs // and sending to the provided addresses. func (c *Client) CreateRawTransaction(inputs []btcjson.TransactionInput, - amounts map[btcutil.Address]btcutil.Amount) (*btcwire.MsgTx, error) { + amounts map[btcutil.Address]btcutil.Amount) (*wire.MsgTx, error) { return c.CreateRawTransactionAsync(inputs, amounts).Receive() } @@ -283,7 +283,7 @@ type FutureSendRawTransactionResult chan *response // 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) { +func (r FutureSendRawTransactionResult) Receive() (*wire.ShaHash, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -296,7 +296,7 @@ func (r FutureSendRawTransactionResult) Receive() (*btcwire.ShaHash, error) { return nil, err } - return btcwire.NewShaHashFromStr(txHashStr) + return wire.NewShaHashFromStr(txHashStr) } // SendRawTransactionAsync returns an instance of a type that can be used to get @@ -304,7 +304,7 @@ func (r FutureSendRawTransactionResult) Receive() (*btcwire.ShaHash, error) { // the returned instance. // // See SendRawTransaction for the blocking version and more details. -func (c *Client) SendRawTransactionAsync(tx *btcwire.MsgTx, allowHighFees bool) FutureSendRawTransactionResult { +func (c *Client) SendRawTransactionAsync(tx *wire.MsgTx, allowHighFees bool) FutureSendRawTransactionResult { txHex := "" if tx != nil { // Serialize the transaction and convert to hex string. @@ -326,7 +326,7 @@ func (c *Client) SendRawTransactionAsync(tx *btcwire.MsgTx, allowHighFees bool) // 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) { +func (c *Client) SendRawTransaction(tx *wire.MsgTx, allowHighFees bool) (*wire.ShaHash, error) { return c.SendRawTransactionAsync(tx, allowHighFees).Receive() } @@ -337,7 +337,7 @@ type FutureSignRawTransactionResult chan *response // 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) { +func (r FutureSignRawTransactionResult) Receive() (*wire.MsgTx, bool, error) { res, err := receiveFuture(r) if err != nil { return nil, false, err @@ -357,7 +357,7 @@ func (r FutureSignRawTransactionResult) Receive() (*btcwire.MsgTx, bool, error) } // Deserialize the transaction and return it. - var msgTx btcwire.MsgTx + var msgTx wire.MsgTx if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil { return nil, false, err } @@ -370,7 +370,7 @@ func (r FutureSignRawTransactionResult) Receive() (*btcwire.MsgTx, bool, error) // the returned instance. // // See SignRawTransaction for the blocking version and more details. -func (c *Client) SignRawTransactionAsync(tx *btcwire.MsgTx) FutureSignRawTransactionResult { +func (c *Client) SignRawTransactionAsync(tx *wire.MsgTx) FutureSignRawTransactionResult { txHex := "" if tx != nil { // Serialize the transaction and convert to hex string. @@ -397,7 +397,7 @@ func (c *Client) SignRawTransactionAsync(tx *btcwire.MsgTx) FutureSignRawTransac // 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) { +func (c *Client) SignRawTransaction(tx *wire.MsgTx) (*wire.MsgTx, bool, error) { return c.SignRawTransactionAsync(tx).Receive() } @@ -406,7 +406,7 @@ func (c *Client) SignRawTransaction(tx *btcwire.MsgTx) (*btcwire.MsgTx, bool, er // 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 { +func (c *Client) SignRawTransaction2Async(tx *wire.MsgTx, inputs []btcjson.RawTxInput) FutureSignRawTransactionResult { txHex := "" if tx != nil { // Serialize the transaction and convert to hex string. @@ -436,7 +436,7 @@ func (c *Client) SignRawTransaction2Async(tx *btcwire.MsgTx, inputs []btcjson.Ra // // 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) { +func (c *Client) SignRawTransaction2(tx *wire.MsgTx, inputs []btcjson.RawTxInput) (*wire.MsgTx, bool, error) { return c.SignRawTransaction2Async(tx, inputs).Receive() } @@ -445,7 +445,7 @@ func (c *Client) SignRawTransaction2(tx *btcwire.MsgTx, inputs []btcjson.RawTxIn // function on the returned instance. // // See SignRawTransaction3 for the blocking version and more details. -func (c *Client) SignRawTransaction3Async(tx *btcwire.MsgTx, +func (c *Client) SignRawTransaction3Async(tx *wire.MsgTx, inputs []btcjson.RawTxInput, privKeysWIF []string) FutureSignRawTransactionResult { @@ -486,9 +486,9 @@ func (c *Client) SignRawTransaction3Async(tx *btcwire.MsgTx, // 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, +func (c *Client) SignRawTransaction3(tx *wire.MsgTx, inputs []btcjson.RawTxInput, - privKeysWIF []string) (*btcwire.MsgTx, bool, error) { + privKeysWIF []string) (*wire.MsgTx, bool, error) { return c.SignRawTransaction3Async(tx, inputs, privKeysWIF).Receive() } @@ -498,7 +498,7 @@ func (c *Client) SignRawTransaction3(tx *btcwire.MsgTx, // function on the returned instance. // // See SignRawTransaction4 for the blocking version and more details. -func (c *Client) SignRawTransaction4Async(tx *btcwire.MsgTx, +func (c *Client) SignRawTransaction4Async(tx *wire.MsgTx, inputs []btcjson.RawTxInput, privKeysWIF []string, hashType SigHashType) FutureSignRawTransactionResult { @@ -541,9 +541,9 @@ func (c *Client) SignRawTransaction4Async(tx *btcwire.MsgTx, // 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, +func (c *Client) SignRawTransaction4(tx *wire.MsgTx, inputs []btcjson.RawTxInput, privKeysWIF []string, - hashType SigHashType) (*btcwire.MsgTx, bool, error) { + hashType SigHashType) (*wire.MsgTx, bool, error) { return c.SignRawTransaction4Async(tx, inputs, privKeysWIF, hashType).Receive() diff --git a/wallet.go b/wallet.go index fc23a428..2f753f11 100644 --- a/wallet.go +++ b/wallet.go @@ -8,10 +8,10 @@ import ( "encoding/json" "strconv" + "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcjson" "github.com/btcsuite/btcnet" "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcwire" "github.com/btcsuite/btcws" ) @@ -46,7 +46,7 @@ func (r FutureGetTransactionResult) Receive() (*btcjson.GetTransactionResult, er // the returned instance. // // See GetTransaction for the blocking version and more details. -func (c *Client) GetTransactionAsync(txHash *btcwire.ShaHash) FutureGetTransactionResult { +func (c *Client) GetTransactionAsync(txHash *wire.ShaHash) FutureGetTransactionResult { hash := "" if txHash != nil { hash = txHash.String() @@ -64,7 +64,7 @@ func (c *Client) GetTransactionAsync(txHash *btcwire.ShaHash) FutureGetTransacti // 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) { +func (c *Client) GetTransaction(txHash *wire.ShaHash) (*btcjson.GetTransactionResult, error) { return c.GetTransactionAsync(txHash).Receive() } @@ -309,7 +309,7 @@ func (r FutureListSinceBlockResult) Receive() (*btcjson.ListSinceBlockResult, er // the returned instance. // // See ListSinceBlock for the blocking version and more details. -func (c *Client) ListSinceBlockAsync(blockHash *btcwire.ShaHash) FutureListSinceBlockResult { +func (c *Client) ListSinceBlockAsync(blockHash *wire.ShaHash) FutureListSinceBlockResult { hash := "" if blockHash != nil { hash = blockHash.String() @@ -329,7 +329,7 @@ func (c *Client) ListSinceBlockAsync(blockHash *btcwire.ShaHash) FutureListSince // minimum confirmations as a filter. // // See ListSinceBlockMinConf to override the minimum number of confirmations. -func (c *Client) ListSinceBlock(blockHash *btcwire.ShaHash) (*btcjson.ListSinceBlockResult, error) { +func (c *Client) ListSinceBlock(blockHash *wire.ShaHash) (*btcjson.ListSinceBlockResult, error) { return c.ListSinceBlockAsync(blockHash).Receive() } @@ -338,7 +338,7 @@ func (c *Client) ListSinceBlock(blockHash *btcwire.ShaHash) (*btcjson.ListSinceB // function on the returned instance. // // See ListSinceBlockMinConf for the blocking version and more details. -func (c *Client) ListSinceBlockMinConfAsync(blockHash *btcwire.ShaHash, minConfirms int) FutureListSinceBlockResult { +func (c *Client) ListSinceBlockMinConfAsync(blockHash *wire.ShaHash, minConfirms int) FutureListSinceBlockResult { hash := "" if blockHash != nil { hash = blockHash.String() @@ -358,7 +358,7 @@ func (c *Client) ListSinceBlockMinConfAsync(blockHash *btcwire.ShaHash, minConfi // 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) { +func (c *Client) ListSinceBlockMinConf(blockHash *wire.ShaHash, minConfirms int) (*btcjson.ListSinceBlockResult, error) { return c.ListSinceBlockMinConfAsync(blockHash, minConfirms).Receive() } @@ -382,7 +382,7 @@ func (r FutureLockUnspentResult) Receive() error { // returned instance. // // See LockUnspent for the blocking version and more details. -func (c *Client) LockUnspentAsync(unlock bool, ops []*btcwire.OutPoint) FutureLockUnspentResult { +func (c *Client) LockUnspentAsync(unlock bool, ops []*wire.OutPoint) FutureLockUnspentResult { id := c.NextID() outputs := make([]btcjson.TransactionInput, len(ops)) for i, op := range ops { @@ -416,7 +416,7 @@ func (c *Client) LockUnspentAsync(unlock bool, ops []*btcwire.OutPoint) FutureLo // reversed (that is, LockUnspent(true, ...) locked the outputs), it has been // left as unlock to keep compatibility with the reference client API and to // avoid confusion for those who are already familiar with the lockunspent RPC. -func (c *Client) LockUnspent(unlock bool, ops []*btcwire.OutPoint) error { +func (c *Client) LockUnspent(unlock bool, ops []*wire.OutPoint) error { return c.LockUnspentAsync(unlock, ops).Receive() } @@ -426,7 +426,7 @@ type FutureListLockUnspentResult chan *response // Receive waits for the response promised by the future and returns the result // of all currently locked unspent outputs. -func (r FutureListLockUnspentResult) Receive() ([]*btcwire.OutPoint, error) { +func (r FutureListLockUnspentResult) Receive() ([]*wire.OutPoint, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -440,13 +440,13 @@ func (r FutureListLockUnspentResult) Receive() ([]*btcwire.OutPoint, error) { } // Create a slice of outpoints from the transaction input structs. - ops := make([]*btcwire.OutPoint, len(inputs)) + ops := make([]*wire.OutPoint, len(inputs)) for i, input := range inputs { - sha, err := btcwire.NewShaHashFromStr(input.Txid) + sha, err := wire.NewShaHashFromStr(input.Txid) if err != nil { return nil, err } - ops[i] = btcwire.NewOutPoint(sha, input.Vout) + ops[i] = wire.NewOutPoint(sha, input.Vout) } return ops, nil @@ -470,7 +470,7 @@ func (c *Client) ListLockUnspentAsync() FutureListLockUnspentResult { // ListLockUnspent returns a slice of outpoints for all unspent outputs marked // as locked by a wallet. Unspent outputs may be marked locked using // LockOutput. -func (c *Client) ListLockUnspent() ([]*btcwire.OutPoint, error) { +func (c *Client) ListLockUnspent() ([]*wire.OutPoint, error) { return c.ListLockUnspentAsync().Receive() } @@ -517,7 +517,7 @@ type FutureSendToAddressResult chan *response // 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) { +func (r FutureSendToAddressResult) Receive() (*wire.ShaHash, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -530,7 +530,7 @@ func (r FutureSendToAddressResult) Receive() (*btcwire.ShaHash, error) { return nil, err } - return btcwire.NewShaHashFromStr(txHash) + return wire.NewShaHashFromStr(txHash) } // SendToAddressAsync returns an instance of a type that can be used to get the @@ -557,7 +557,7 @@ func (c *Client) SendToAddressAsync(address btcutil.Address, amount btcutil.Amou // // 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) { +func (c *Client) SendToAddress(address btcutil.Address, amount btcutil.Amount) (*wire.ShaHash, error) { return c.SendToAddressAsync(address, amount).Receive() } @@ -593,7 +593,7 @@ func (c *Client) SendToAddressCommentAsync(address btcutil.Address, // // 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) { +func (c *Client) SendToAddressComment(address btcutil.Address, amount btcutil.Amount, comment, commentTo string) (*wire.ShaHash, error) { return c.SendToAddressCommentAsync(address, amount, comment, commentTo).Receive() } @@ -606,7 +606,7 @@ type FutureSendFromResult chan *response // 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) { +func (r FutureSendFromResult) Receive() (*wire.ShaHash, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -619,7 +619,7 @@ func (r FutureSendFromResult) Receive() (*btcwire.ShaHash, error) { return nil, err } - return btcwire.NewShaHashFromStr(txHash) + return wire.NewShaHashFromStr(txHash) } // SendFromAsync returns an instance of a type that can be used to get the @@ -646,7 +646,7 @@ func (c *Client) SendFromAsync(fromAccount string, toAddress btcutil.Address, am // // 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) { +func (c *Client) SendFrom(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount) (*wire.ShaHash, error) { return c.SendFromAsync(fromAccount, toAddress, amount).Receive() } @@ -676,7 +676,7 @@ func (c *Client) SendFromMinConfAsync(fromAccount string, toAddress btcutil.Addr // // 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) { +func (c *Client) SendFromMinConf(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount, minConfirms int) (*wire.ShaHash, error) { return c.SendFromMinConfAsync(fromAccount, toAddress, amount, minConfirms).Receive() } @@ -714,7 +714,7 @@ func (c *Client) SendFromCommentAsync(fromAccount string, // 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) { + comment, commentTo string) (*wire.ShaHash, error) { return c.SendFromCommentAsync(fromAccount, toAddress, amount, minConfirms, comment, commentTo).Receive() @@ -728,7 +728,7 @@ type FutureSendManyResult chan *response // 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) { +func (r FutureSendManyResult) Receive() (*wire.ShaHash, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -741,7 +741,7 @@ func (r FutureSendManyResult) Receive() (*btcwire.ShaHash, error) { return nil, err } - return btcwire.NewShaHashFromStr(txHash) + return wire.NewShaHashFromStr(txHash) } // SendManyAsync returns an instance of a type that can be used to get the @@ -771,7 +771,7 @@ func (c *Client) SendManyAsync(fromAccount string, amounts map[btcutil.Address]b // // 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) { +func (c *Client) SendMany(fromAccount string, amounts map[btcutil.Address]btcutil.Amount) (*wire.ShaHash, error) { return c.SendManyAsync(fromAccount, amounts).Receive() } @@ -809,7 +809,7 @@ func (c *Client) SendManyMinConfAsync(fromAccount string, // WalletPassphrase function for more details. func (c *Client) SendManyMinConf(fromAccount string, amounts map[btcutil.Address]btcutil.Amount, - minConfirms int) (*btcwire.ShaHash, error) { + minConfirms int) (*wire.ShaHash, error) { return c.SendManyMinConfAsync(fromAccount, amounts, minConfirms).Receive() } @@ -849,7 +849,7 @@ func (c *Client) SendManyCommentAsync(fromAccount string, // WalletPassphrase function for more details. func (c *Client) SendManyComment(fromAccount string, amounts map[btcutil.Address]btcutil.Amount, minConfirms int, - comment string) (*btcwire.ShaHash, error) { + comment string) (*wire.ShaHash, error) { return c.SendManyCommentAsync(fromAccount, amounts, minConfirms, comment).Receive() From 87ef953a6344a6308994585600dc17fae3b230de Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Thu, 5 Feb 2015 17:24:42 -0500 Subject: [PATCH 091/153] Update Go versions for Travis. --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index eb5922f2..e23b4030 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ language: go -go: release +go: + - 1.3.3 + - 1.4.1 sudo: false before_install: - gotools=golang.org/x/tools From 7d4e1e17f07043951697b50f3c6627d3da5737dd Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 5 Feb 2015 23:36:19 -0600 Subject: [PATCH 092/153] Update btcnet path import paths to new location. --- wallet.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/wallet.go b/wallet.go index 2f753f11..fead4211 100644 --- a/wallet.go +++ b/wallet.go @@ -8,9 +8,9 @@ import ( "encoding/json" "strconv" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcjson" - "github.com/btcsuite/btcnet" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcws" ) @@ -879,7 +879,7 @@ func (r FutureAddMultisigAddressResult) Receive() (btcutil.Address, error) { return nil, err } - return btcutil.DecodeAddress(addr, &btcnet.MainNetParams) + return btcutil.DecodeAddress(addr, &chaincfg.MainNetParams) } // AddMultisigAddressAsync returns an instance of a type that can be used to get @@ -1010,7 +1010,7 @@ func (r FutureGetNewAddressResult) Receive() (btcutil.Address, error) { return nil, err } - return btcutil.DecodeAddress(addr, &btcnet.MainNetParams) + return btcutil.DecodeAddress(addr, &chaincfg.MainNetParams) } // GetNewAddressAsync returns an instance of a type that can be used to get the @@ -1053,7 +1053,7 @@ func (r FutureGetRawChangeAddressResult) Receive() (btcutil.Address, error) { return nil, err } - return btcutil.DecodeAddress(addr, &btcnet.MainNetParams) + return btcutil.DecodeAddress(addr, &chaincfg.MainNetParams) } // GetRawChangeAddressAsync returns an instance of a type that can be used to @@ -1097,7 +1097,7 @@ func (r FutureGetAccountAddressResult) Receive() (btcutil.Address, error) { return nil, err } - return btcutil.DecodeAddress(addr, &btcnet.MainNetParams) + return btcutil.DecodeAddress(addr, &chaincfg.MainNetParams) } // GetAccountAddressAsync returns an instance of a type that can be used to get @@ -1221,7 +1221,8 @@ func (r FutureGetAddressesByAccountResult) Receive() ([]btcutil.Address, error) addrs := make([]btcutil.Address, 0, len(addrStrings)) for _, addrStr := range addrStrings { - addr, err := btcutil.DecodeAddress(addrStr, &btcnet.MainNetParams) + addr, err := btcutil.DecodeAddress(addrStr, + &chaincfg.MainNetParams) if err != nil { return nil, err } From 23ab2bf329f9e012b96178bcf620d06259872014 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 19 Feb 2015 12:00:51 -0600 Subject: [PATCH 093/153] Update btcjson path import paths to new location. --- chain.go | 2 +- extensions.go | 2 +- infrastructure.go | 2 +- mining.go | 2 +- net.go | 2 +- notify.go | 2 +- rawrequest.go | 2 +- rawtransactions.go | 2 +- wallet.go | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/chain.go b/chain.go index 0638c6e7..95c0586f 100644 --- a/chain.go +++ b/chain.go @@ -9,8 +9,8 @@ import ( "encoding/hex" "encoding/json" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcjson" "github.com/btcsuite/btcutil" ) diff --git a/extensions.go b/extensions.go index 033ec914..4a65d27b 100644 --- a/extensions.go +++ b/extensions.go @@ -9,8 +9,8 @@ import ( "encoding/json" "fmt" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcjson" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcws" ) diff --git a/infrastructure.go b/infrastructure.go index d14b4dc2..1af40a0b 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -20,7 +20,7 @@ import ( "sync/atomic" "time" - "github.com/btcsuite/btcjson" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcws" "github.com/btcsuite/go-socks/socks" "github.com/btcsuite/websocket" diff --git a/mining.go b/mining.go index c201e2ab..ecba727b 100644 --- a/mining.go +++ b/mining.go @@ -9,7 +9,7 @@ import ( "encoding/json" "errors" - "github.com/btcsuite/btcjson" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcutil" ) diff --git a/net.go b/net.go index ace15c88..ecf69613 100644 --- a/net.go +++ b/net.go @@ -7,7 +7,7 @@ package btcrpcclient import ( "encoding/json" - "github.com/btcsuite/btcjson" + "github.com/btcsuite/btcd/btcjson" ) // AddNodeCommand enumerates the available commands that the AddNode function diff --git a/notify.go b/notify.go index 557cb76b..65608e5c 100644 --- a/notify.go +++ b/notify.go @@ -13,8 +13,8 @@ import ( "sync" "time" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcjson" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcws" ) diff --git a/rawrequest.go b/rawrequest.go index d18f10b4..44a557e4 100644 --- a/rawrequest.go +++ b/rawrequest.go @@ -8,7 +8,7 @@ import ( "encoding/json" "errors" - "github.com/btcsuite/btcjson" + "github.com/btcsuite/btcd/btcjson" ) // rawRequest satisifies the btcjson.Cmd interface for btcjson raw commands. diff --git a/rawtransactions.go b/rawtransactions.go index 5dec6299..e75f0bb4 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -9,8 +9,8 @@ import ( "encoding/hex" "encoding/json" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcjson" "github.com/btcsuite/btcutil" ) diff --git a/wallet.go b/wallet.go index fead4211..08bd0f12 100644 --- a/wallet.go +++ b/wallet.go @@ -8,9 +8,9 @@ import ( "encoding/json" "strconv" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcjson" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcws" ) From d45f4c47a212ecdcb5b9b6b40d3be3d5f5caccf4 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 19 Feb 2015 13:08:29 -0600 Subject: [PATCH 094/153] Update btcws path import paths to new location. --- extensions.go | 2 +- infrastructure.go | 2 +- notify.go | 2 +- wallet.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions.go b/extensions.go index 4a65d27b..a823a0df 100644 --- a/extensions.go +++ b/extensions.go @@ -10,9 +10,9 @@ import ( "fmt" "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/btcjson/btcws" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcws" ) // FutureDebugLevelResult is a future promise to deliver the result of a diff --git a/infrastructure.go b/infrastructure.go index 1af40a0b..383143c3 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -21,7 +21,7 @@ import ( "time" "github.com/btcsuite/btcd/btcjson" - "github.com/btcsuite/btcws" + "github.com/btcsuite/btcd/btcjson/btcws" "github.com/btcsuite/go-socks/socks" "github.com/btcsuite/websocket" ) diff --git a/notify.go b/notify.go index 65608e5c..44c556e6 100644 --- a/notify.go +++ b/notify.go @@ -14,9 +14,9 @@ import ( "time" "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/btcjson/btcws" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcws" ) var ( diff --git a/wallet.go b/wallet.go index 08bd0f12..4542819a 100644 --- a/wallet.go +++ b/wallet.go @@ -9,10 +9,10 @@ import ( "strconv" "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/btcjson/btcws" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcws" ) // ***************************** From 9ca93b30ad11ec34348d2d788c58019571bf9524 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 1 Jan 2015 16:34:07 -0600 Subject: [PATCH 095/153] Update to make use of latest version of btcjson. This commit contains several changes needed to update the client to use the latest version of btcjson. In addition, it contains a couple of other minor changes along the way. While the underlying changes are quite large, the public API of this package is still the same, so caller should generally not have to update their code due to that. However, the underlying btcjson package API has changed significantly. Since this package hides the vast majority of that from callers, it should not afffect them very much. However, one area in particular to watch out for is that the old btcjson.Error is now btcjson.RPCError, so any callers doing any type assertions there will need to update. The following is a summary of the changes: - The underlying btcjson significantly changed how commands work, so the internals of this package have been reworked to be based off of requests instead of the now non-existant btcjson.Cmd interface - Update all call sites of btcjson.NewCmd since they can no longer error or take varargs - The ids for each request are part of the request now instead of the command to match the new btcjson model and are strict uint64s so type assertions are no longer needed (slightly improved efficiency) - Remove the old temporary workaround for the getbalance command with an account of "*" since btcwallet has since been fixed - Change all instances of JSONToAmount to btcutil.NewAmount since that function was removed in favor of btcutil.Amount - Change all btcws invocations to btcjson since they have been combined --- chain.go | 97 ++------- doc.go | 6 +- extensions.go | 37 +--- infrastructure.go | 231 ++++++++++---------- mining.go | 72 +------ net.go | 51 +---- notify.go | 100 ++++----- rawrequest.go | 67 +++--- rawtransactions.go | 73 ++----- wallet.go | 515 +++++++++------------------------------------ 10 files changed, 353 insertions(+), 896 deletions(-) diff --git a/chain.go b/chain.go index 95c0586f..2d8a725e 100644 --- a/chain.go +++ b/chain.go @@ -9,7 +9,7 @@ import ( "encoding/hex" "encoding/json" - "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/btcjson/v2/btcjson" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) @@ -41,12 +41,7 @@ func (r FutureGetBestBlockHashResult) Receive() (*wire.ShaHash, error) { // // 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) - } - + cmd := btcjson.NewGetBestBlockHashCmd() return c.sendCmd(cmd) } @@ -101,12 +96,7 @@ func (c *Client) GetBlockAsync(blockHash *wire.ShaHash) FutureGetBlockResult { hash = blockHash.String() } - id := c.NextID() - cmd, err := btcjson.NewGetBlockCmd(id, hash, false) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(false), nil) return c.sendCmd(cmd) } @@ -124,14 +114,14 @@ type FutureGetBlockVerboseResult chan *response // 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) { +func (r FutureGetBlockVerboseResult) Receive() (*btcjson.GetBlockVerboseResult, error) { res, err := receiveFuture(r) if err != nil { return nil, err } // Unmarshal the raw result into a BlockResult. - var blockResult btcjson.BlockResult + var blockResult btcjson.GetBlockVerboseResult err = json.Unmarshal(res, &blockResult) if err != nil { return nil, err @@ -150,12 +140,7 @@ func (c *Client) GetBlockVerboseAsync(blockHash *wire.ShaHash, verboseTx bool) F hash = blockHash.String() } - id := c.NextID() - cmd, err := btcjson.NewGetBlockCmd(id, hash, true, verboseTx) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(true), &verboseTx) return c.sendCmd(cmd) } @@ -163,7 +148,7 @@ func (c *Client) GetBlockVerboseAsync(blockHash *wire.ShaHash, verboseTx bool) F // about a block given its hash. // // See GetBlock to retrieve a raw block instead. -func (c *Client) GetBlockVerbose(blockHash *wire.ShaHash, verboseTx bool) (*btcjson.BlockResult, error) { +func (c *Client) GetBlockVerbose(blockHash *wire.ShaHash, verboseTx bool) (*btcjson.GetBlockVerboseResult, error) { return c.GetBlockVerboseAsync(blockHash, verboseTx).Receive() } @@ -194,12 +179,7 @@ func (r FutureGetBlockCountResult) Receive() (int64, error) { // // 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) - } - + cmd := btcjson.NewGetBlockCountCmd() return c.sendCmd(cmd) } @@ -235,12 +215,7 @@ func (r FutureGetDifficultyResult) Receive() (float64, error) { // // 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) - } - + cmd := btcjson.NewGetDifficultyCmd() return c.sendCmd(cmd) } @@ -277,12 +252,7 @@ func (r FutureGetBlockHashResult) Receive() (*wire.ShaHash, error) { // // 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) - } - + cmd := btcjson.NewGetBlockHashCmd(blockHeight) return c.sendCmd(cmd) } @@ -330,12 +300,7 @@ func (r FutureGetRawMempoolResult) Receive() ([]*wire.ShaHash, error) { // // 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) - } - + cmd := btcjson.NewGetRawMempoolCmd(btcjson.Bool(false)) return c.sendCmd(cmd) } @@ -354,7 +319,7 @@ type FutureGetRawMempoolVerboseResult chan *response // 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) { +func (r FutureGetRawMempoolVerboseResult) Receive() (map[string]btcjson.GetRawMempoolVerboseResult, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -362,7 +327,7 @@ func (r FutureGetRawMempoolVerboseResult) Receive() (map[string]btcjson.GetRawMe // Unmarshal the result as a map of strings (tx shas) to their detailed // results. - var mempoolItems map[string]btcjson.GetRawMempoolResult + var mempoolItems map[string]btcjson.GetRawMempoolVerboseResult err = json.Unmarshal(res, &mempoolItems) if err != nil { return nil, err @@ -376,12 +341,7 @@ func (r FutureGetRawMempoolVerboseResult) Receive() (map[string]btcjson.GetRawMe // // 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) - } - + cmd := btcjson.NewGetRawMempoolCmd(btcjson.Bool(true)) return c.sendCmd(cmd) } @@ -390,7 +350,7 @@ func (c *Client) GetRawMempoolVerboseAsync() FutureGetRawMempoolVerboseResult { // the memory pool. // // See GetRawMempool to retrieve only the transaction hashes instead. -func (c *Client) GetRawMempoolVerbose() (map[string]btcjson.GetRawMempoolResult, error) { +func (c *Client) GetRawMempoolVerbose() (map[string]btcjson.GetRawMempoolVerboseResult, error) { return c.GetRawMempoolVerboseAsync().Receive() } @@ -423,12 +383,7 @@ func (r FutureVerifyChainResult) Receive() (bool, error) { // // 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) - } - + cmd := btcjson.NewVerifyChainCmd(nil, nil) return c.sendCmd(cmd) } @@ -446,12 +401,7 @@ func (c *Client) VerifyChain() (bool, error) { // // 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) - } - + cmd := btcjson.NewVerifyChainCmd(&checkLevel, nil) return c.sendCmd(cmd) } @@ -474,12 +424,7 @@ func (c *Client) VerifyChainLevel(checkLevel int32) (bool, error) { // // 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) - } - + cmd := btcjson.NewVerifyChainCmd(&checkLevel, &numBlocks) return c.sendCmd(cmd) } @@ -537,11 +482,7 @@ func (c *Client) GetTxOutAsync(txHash *wire.ShaHash, index int, mempool bool) Fu hash = txHash.String() } - id := c.NextID() - cmd, err := btcjson.NewGetTxOutCmd(id, hash, index, mempool) - if err != nil { - return newFutureError(err) - } + cmd := btcjson.NewGetTxOutCmd(hash, index, &mempool) return c.sendCmd(cmd) } diff --git a/doc.go b/doc.go index 8f7a0589..ee5be686 100644 --- a/doc.go +++ b/doc.go @@ -141,14 +141,14 @@ the type can vary, but usually will be best handled by simply showing/logging it. The third category of errors, that is errors returned by the server, can be -detected by type asserting the error in a *btcjson.Error. For example, to +detected by type asserting the error in a *btcjson.RPCError. For example, to detect if a command is unimplemented by the remote RPC server: amount, err := client.GetBalance("") if err != nil { - if jerr, ok := err.(*btcjson.Error); ok { + if jerr, ok := err.(*btcjson.RPCError); ok { switch jerr.Code { - case btcjson.ErrUnimplemented.Code: + case btcjson.ErrRPCUnimplemented: // Handle not implemented error // Handle other specific errors you care about diff --git a/extensions.go b/extensions.go index a823a0df..d30f370d 100644 --- a/extensions.go +++ b/extensions.go @@ -9,8 +9,7 @@ import ( "encoding/json" "fmt" - "github.com/btcsuite/btcd/btcjson" - "github.com/btcsuite/btcd/btcjson/btcws" + "github.com/btcsuite/btcd/btcjson/v2/btcjson" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) @@ -45,12 +44,7 @@ func (r FutureDebugLevelResult) Receive() (string, error) { // // 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) - } - + cmd := btcjson.NewDebugLevelCmd(levelSpec) return c.sendCmd(cmd) } @@ -86,8 +80,7 @@ func (r FutureCreateEncryptedWalletResult) Receive() error { // // NOTE: This is a btcwallet extension. func (c *Client) CreateEncryptedWalletAsync(passphrase string) FutureCreateEncryptedWalletResult { - id := c.NextID() - cmd := btcws.NewCreateEncryptedWalletCmd(id, passphrase) + cmd := btcjson.NewCreateEncryptedWalletCmd(passphrase) return c.sendCmd(cmd) } @@ -137,12 +130,7 @@ func (c *Client) ListAddressTransactionsAsync(addresses []btcutil.Address, accou 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) - } - + cmd := btcjson.NewListAddressTransactionsCmd(addrs, &account) return c.sendCmd(cmd) } @@ -167,7 +155,7 @@ func (r FutureGetBestBlockResult) Receive() (*wire.ShaHash, int32, error) { } // Unmarsal result as a getbestblock result object. - var bestBlock btcws.GetBestBlockResult + var bestBlock btcjson.GetBestBlockResult err = json.Unmarshal(res, &bestBlock) if err != nil { return nil, 0, err @@ -190,9 +178,7 @@ func (r FutureGetBestBlockResult) Receive() (*wire.ShaHash, int32, error) { // // NOTE: This is a btcd extension. func (c *Client) GetBestBlockAsync() FutureGetBestBlockResult { - id := c.NextID() - cmd := btcws.NewGetBestBlockCmd(id) - + cmd := btcjson.NewGetBestBlockCmd() return c.sendCmd(cmd) } @@ -234,9 +220,7 @@ func (r FutureGetCurrentNetResult) Receive() (wire.BitcoinNet, error) { // // NOTE: This is a btcd extension. func (c *Client) GetCurrentNetAsync() FutureGetCurrentNetResult { - id := c.NextID() - cmd := btcws.NewGetCurrentNetCmd(id) - + cmd := btcjson.NewGetCurrentNetCmd() return c.sendCmd(cmd) } @@ -302,12 +286,7 @@ func (r FutureExportWatchingWalletResult) Receive() ([]byte, []byte, error) { // // 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) - } - + cmd := btcjson.NewExportWatchingWalletCmd(&account, btcjson.Bool(true)) return c.sendCmd(cmd) } diff --git a/infrastructure.go b/infrastructure.go index 383143c3..4e63f4a0 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -13,6 +13,7 @@ import ( "encoding/json" "errors" "fmt" + "io/ioutil" "net" "net/http" "net/url" @@ -20,8 +21,7 @@ import ( "sync/atomic" "time" - "github.com/btcsuite/btcd/btcjson" - "github.com/btcsuite/btcd/btcjson/btcws" + "github.com/btcsuite/btcd/btcjson/v2/btcjson" "github.com/btcsuite/go-socks/socks" "github.com/btcsuite/websocket" ) @@ -87,16 +87,18 @@ const ( // 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 *response + httpRequest *http.Request + jsonRequest *jsonRequest } // 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 *response + id uint64 + method string + cmd interface{} + marshalledJSON []byte + responseChan chan *response } // Client represents a Bitcoin RPC client which allows easy access to the @@ -163,15 +165,15 @@ func (c *Client) NextID() uint64 { return atomic.AddUint64(&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. +// addRequest associates the passed jsonRequest with its 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. // // If the client has already begun shutting down, ErrClientShutdown is returned // and the request is not added. // // This function is safe for concurrent access. -func (c *Client) addRequest(id uint64, request *jsonRequest) error { +func (c *Client) addRequest(jReq *jsonRequest) error { c.requestLock.Lock() defer c.requestLock.Unlock() @@ -187,9 +189,8 @@ func (c *Client) addRequest(id uint64, request *jsonRequest) error { default: } - // TODO(davec): Already there? - element := c.requestList.PushBack(request) - c.requestMap[id] = element + element := c.requestList.PushBack(jReq) + c.requestMap[jReq.id] = element return nil } @@ -224,7 +225,7 @@ func (c *Client) removeAllRequests() { // trackRegisteredNtfns examines the passed command to see if it is one of // the notification commands and updates the notification state that is used // to automatically re-establish registered notifications on reconnects. -func (c *Client) trackRegisteredNtfns(cmd btcjson.Cmd) { +func (c *Client) trackRegisteredNtfns(cmd interface{}) { // Nothing to do if the caller is not interested in notifications. if c.ntfnHandlers == nil { return @@ -234,23 +235,23 @@ func (c *Client) trackRegisteredNtfns(cmd btcjson.Cmd) { defer c.ntfnState.Unlock() switch bcmd := cmd.(type) { - case *btcws.NotifyBlocksCmd: + case *btcjson.NotifyBlocksCmd: c.ntfnState.notifyBlocks = true - case *btcws.NotifyNewTransactionsCmd: - if bcmd.Verbose { + case *btcjson.NotifyNewTransactionsCmd: + if bcmd.Verbose != nil && *bcmd.Verbose { c.ntfnState.notifyNewTxVerbose = true } else { c.ntfnState.notifyNewTx = true } - case *btcws.NotifySpentCmd: + case *btcjson.NotifySpentCmd: for _, op := range bcmd.OutPoints { c.ntfnState.notifySpent[op] = struct{}{} } - case *btcws.NotifyReceivedCmd: + case *btcjson.NotifyReceivedCmd: for _, addr := range bcmd.Addresses { c.ntfnState.notifyReceived[addr] = struct{}{} } @@ -278,8 +279,8 @@ type ( // rawResponse is a partially-unmarshaled JSON-RPC response. For this // to be valid (according to JSON-RPC 1.0 spec), ID may not be nil. rawResponse struct { - Result json.RawMessage `json:"result"` - Error *btcjson.Error `json:"error"` + Result json.RawMessage `json:"result"` + Error *btcjson.RPCError `json:"error"` } ) @@ -291,7 +292,7 @@ type response struct { } // result checks whether the unmarshaled response contains a non-nil error, -// returning an unmarshaled btcjson.Error (or an unmarshaling error) if so. +// returning an unmarshaled btcjson.RPCError (or an unmarshaling error) if so. // If the response is not an error, the raw bytes of the request are // returned for further unmashaling into specific result types. func (r rawResponse) result() (result []byte, err error) { @@ -303,7 +304,8 @@ func (r rawResponse) result() (result []byte, err error) { // handleMessage is the main handler for incoming notifications and responses. func (c *Client) handleMessage(msg []byte) { - // Attempt to unmarshal the message as either a notifiation or response. + // Attempt to unmarshal the message as either a notification or + // response. var in inMessage err := json.Unmarshal(msg, &in) if err != nil { @@ -441,7 +443,7 @@ func (c *Client) sendMessage(marshalledJSON []byte) { // reregisterNtfns creates and sends commands needed to re-establish the current // notification state associated with the client. It should only be called on -// on reconnect by the resendCmds function. +// on reconnect by the resendRequests function. func (c *Client) reregisterNtfns() error { // Nothing to do if the caller is not interested in notifications. if c.ntfnHandlers == nil { @@ -480,7 +482,7 @@ func (c *Client) reregisterNtfns() error { // outpoints in one command if needed. nslen := len(stateCopy.notifySpent) if nslen > 0 { - outpoints := make([]btcws.OutPoint, 0, nslen) + outpoints := make([]btcjson.OutPoint, 0, nslen) for op := range stateCopy.notifySpent { outpoints = append(outpoints, op) } @@ -513,10 +515,10 @@ var ignoreResends = map[string]struct{}{ "rescan": struct{}{}, } -// resendCmds resends any commands that had not completed when the client +// resendRequests resends any requests that had not completed when the client // disconnected. It is intended to be called once the client has reconnected as // a separate goroutine. -func (c *Client) resendCmds() { +func (c *Client) resendRequests() { // Set the notification state back up. If anything goes wrong, // disconnect the client. if err := c.reregisterNtfns(); err != nil { @@ -525,37 +527,39 @@ func (c *Client) resendCmds() { return } - // Since it's possible to block on send and more commands might be + // Since it's possible to block on send and more requests 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 + // requests 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()) + resendReqs := make([]*jsonRequest, 0, c.requestList.Len()) var nextElem *list.Element for e := c.requestList.Front(); e != nil; e = nextElem { nextElem = e.Next() - req := e.Value.(*jsonRequest) - if _, ok := ignoreResends[req.cmd.Method()]; ok { + jReq := e.Value.(*jsonRequest) + if _, ok := ignoreResends[jReq.method]; ok { // If a request is not sent on reconnect, remove it // from the request structures, since no reply is // expected. - delete(c.requestMap, req.cmd.Id().(uint64)) + delete(c.requestMap, jReq.id) c.requestList.Remove(e) } else { - resendCmds = append(resendCmds, req) + resendReqs = append(resendReqs, jReq) } } c.requestLock.Unlock() - for _, req := range resendCmds { + for _, jReq := range resendReqs { // 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) + log.Tracef("Sending command [%s] with id %d", jReq.method, + jReq.id) + c.sendMessage(jReq.marshalledJSON) } } @@ -624,9 +628,9 @@ out: // new connection. c.start() - // Reissue pending commands in another goroutine since + // Reissue pending requests in another goroutine since // the send can block. - go c.resendCmds() + go c.resendRequests() // Break out of the reconnect loop back to wait for // disconnect again. @@ -638,40 +642,41 @@ out: } // handleSendPostMessage handles performing the passed HTTP request, reading the -// result, unmarshalling it, and delivering the unmarhsalled result to the +// result, unmarshalling it, and delivering the unmarshalled 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) + jReq := details.jsonRequest + log.Tracef("Sending command [%s] with id %d", jReq.method, jReq.id) + httpResponse, err := c.httpClient.Do(details.httpRequest) if err != nil { - details.responseChan <- &response{err: err} + jReq.responseChan <- &response{err: err} return } // Read the raw bytes and close the response. - respBytes, err := btcjson.GetRaw(httpResponse.Body) + respBytes, err := ioutil.ReadAll(httpResponse.Body) + httpResponse.Body.Close() if err != nil { - details.responseChan <- &response{err: err} + err = fmt.Errorf("error reading json reply: %v", err) + jReq.responseChan <- &response{err: err} return } // Handle unsuccessful HTTP responses if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 { - details.responseChan <- &response{err: errors.New(string(respBytes))} + jReq.responseChan <- &response{err: errors.New(string(respBytes))} return } var resp rawResponse err = json.Unmarshal(respBytes, &resp) if err != nil { - details.responseChan <- &response{err: err} + jReq.responseChan <- &response{err: err} return } res, err := resp.result() - details.responseChan <- &response{result: res, err: err} + jReq.responseChan <- &response{result: res, err: err} } // sendPostHandler handles all outgoing messages when the client is running @@ -698,7 +703,7 @@ cleanup: for { select { case details := <-c.sendPostChan: - details.responseChan <- &response{ + details.jsonRequest.responseChan <- &response{ result: nil, err: ErrClientShutdown, } @@ -715,18 +720,17 @@ cleanup: // 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 *response) { +func (c *Client) sendPostRequest(httpReq *http.Request, jReq *jsonRequest) { // Don't send the message if shutting down. select { case <-c.shutdown: - responseChan <- &response{result: nil, err: ErrClientShutdown} + jReq.responseChan <- &response{result: nil, err: ErrClientShutdown} default: } c.sendPostChan <- &sendPostDetails{ - command: command, - request: req, - responseChan: responseChan, + jsonRequest: jReq, + httpRequest: httpReq, } } @@ -749,66 +753,45 @@ func receiveFuture(f chan *response) ([]byte, error) { return r.result, r.err } -// 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 *response) { - marshalledJSON, err := json.Marshal(cmd) - if err != nil { - responseChan <- &response{result: nil, err: err} - return - } - +// sendPost sends the passed request to the server by issuing an HTTP POST +// request using the provided response channel for the reply. 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) sendPost(jReq *jsonRequest) { // 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)) + bodyReader := bytes.NewReader(jReq.marshalledJSON) + httpReq, err := http.NewRequest("POST", url, bodyReader) if err != nil { - responseChan <- &response{result: nil, err: err} + jReq.responseChan <- &response{result: nil, err: err} return } - req.Close = true - req.Header.Set("Content-Type", "application/json") + httpReq.Close = true + httpReq.Header.Set("Content-Type", "application/json") // Configure basic access authorization. - req.SetBasicAuth(c.config.User, c.config.Pass) + httpReq.SetBasicAuth(c.config.User, c.config.Pass) - log.Tracef("Sending command [%s] with id %d", cmd.Method(), cmd.Id()) - c.sendPostRequest(req, cmd, responseChan) + log.Tracef("Sending command [%s] with id %d", jReq.method, jReq.id) + c.sendPostRequest(httpReq, jReq) } -// 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 *response) { - marshalledJSON, err := cmd.MarshalJSON() - if err != nil { - responseChan <- &response{result: 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 *response { +// sendRequest sends the passed json request to the associated server using the +// provided response channel for the reply. It handles both websocket and HTTP +// POST mode depending on the configuration of the client. +func (c *Client) sendRequest(jReq *jsonRequest) { // 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 *response, 1) if c.config.HttpPostMode { - c.marshalAndSendPost(cmd, responseChan) - return responseChan + c.sendPost(jReq) + return } // Check whether the websocket connection has never been established, @@ -816,26 +799,58 @@ func (c *Client) sendCmd(cmd btcjson.Cmd) chan *response { select { case <-c.connEstablished: default: - responseChan <- &response{err: ErrClientNotConnected} - return responseChan + jReq.responseChan <- &response{err: ErrClientNotConnected} + return } - err := c.addRequest(cmd.Id().(uint64), &jsonRequest{ - cmd: cmd, - responseChan: responseChan, - }) - if err != nil { - responseChan <- &response{err: err} - return responseChan + // Add the request to the internal tracking map so the response from the + // remote server can be properly detected and routed to the response + // channel. Then send the marshalled request via the websocket + // connection. + if err := c.addRequest(jReq); err != nil { + jReq.responseChan <- &response{err: err} + return } - c.marshalAndSend(cmd, responseChan) + log.Tracef("Sending command [%s] with id %d", jReq.method, jReq.id) + c.sendMessage(jReq.marshalledJSON) +} + +// sendCmd sends the passed command to the associated server and returns a +// response channel on which the reply will be delivered 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 interface{}) chan *response { + // Get the method associated with the command. + method, err := btcjson.CmdMethod(cmd) + if err != nil { + return newFutureError(err) + } + + // Marshal the command. + id := c.NextID() + marshalledJSON, err := btcjson.MarshalCmd(id, cmd) + if err != nil { + return newFutureError(err) + } + + // Generate the request and send it along with a channel to respond on. + responseChan := make(chan *response, 1) + jReq := &jsonRequest{ + id: id, + method: method, + cmd: cmd, + marshalledJSON: marshalledJSON, + responseChan: responseChan, + } + c.sendRequest(jReq) + 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) { +func (c *Client) sendCmdAndWait(cmd interface{}) (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)) diff --git a/mining.go b/mining.go index ecba727b..e64504d8 100644 --- a/mining.go +++ b/mining.go @@ -9,7 +9,7 @@ import ( "encoding/json" "errors" - "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/btcjson/v2/btcjson" "github.com/btcsuite/btcutil" ) @@ -41,12 +41,7 @@ func (r FutureGetGenerateResult) Receive() (bool, error) { // // 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) - } - + cmd := btcjson.NewGetGenerateCmd() return c.sendCmd(cmd) } @@ -76,12 +71,7 @@ func (r FutureSetGenerateResult) Receive() error { // // 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) - } - + cmd := btcjson.NewSetGenerateCmd(enable, &numCPUs) return c.sendCmd(cmd) } @@ -119,12 +109,7 @@ func (r FutureGetHashesPerSecResult) Receive() (int64, error) { // // 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) - } - + cmd := btcjson.NewGetHashesPerSecCmd() return c.sendCmd(cmd) } @@ -163,12 +148,7 @@ func (r FutureGetMiningInfoResult) Receive() (*btcjson.GetMiningInfoResult, erro // // 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) - } - + cmd := btcjson.NewGetMiningInfoCmd() return c.sendCmd(cmd) } @@ -206,12 +186,7 @@ func (r FutureGetNetworkHashPS) Receive() (int64, error) { // // 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) - } - + cmd := btcjson.NewGetNetworkHashPSCmd(nil, nil) return c.sendCmd(cmd) } @@ -230,12 +205,7 @@ func (c *Client) GetNetworkHashPS() (int64, error) { // // 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) - } - + cmd := btcjson.NewGetNetworkHashPSCmd(&blocks, nil) return c.sendCmd(cmd) } @@ -256,12 +226,7 @@ func (c *Client) GetNetworkHashPS2(blocks int) (int64, error) { // // 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) - } - + cmd := btcjson.NewGetNetworkHashPSCmd(&blocks, &height) return c.sendCmd(cmd) } @@ -303,12 +268,7 @@ func (r FutureGetWork) Receive() (*btcjson.GetWorkResult, error) { // // 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) - } - + cmd := btcjson.NewGetWorkCmd(nil) return c.sendCmd(cmd) } @@ -347,12 +307,7 @@ func (r FutureGetWorkSubmit) Receive() (bool, error) { // // See GetWorkSubmit for the blocking version and more details. func (c *Client) GetWorkSubmitAsync(data string) FutureGetWorkSubmit { - id := c.NextID() - cmd, err := btcjson.NewGetWorkCmd(id, data) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewGetWorkCmd(&data) return c.sendCmd(cmd) } @@ -406,12 +361,7 @@ func (c *Client) SubmitBlockAsync(block *btcutil.Block, options *btcjson.SubmitB blockHex = hex.EncodeToString(blockBytes) } - id := c.NextID() - cmd, err := btcjson.NewSubmitBlockCmd(id, blockHex, options) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewSubmitBlockCmd(blockHex, options) return c.sendCmd(cmd) } diff --git a/net.go b/net.go index ecf69613..b843c98f 100644 --- a/net.go +++ b/net.go @@ -7,7 +7,7 @@ package btcrpcclient import ( "encoding/json" - "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/btcjson/v2/btcjson" ) // AddNodeCommand enumerates the available commands that the AddNode function @@ -54,12 +54,7 @@ func (r FutureAddNodeResult) Receive() error { // // 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) - } - + cmd := btcjson.NewAddNodeCmd(host, btcjson.AddNodeSubCmd(command)) return c.sendCmd(cmd) } @@ -100,12 +95,7 @@ func (r FutureGetAddedNodeInfoResult) Receive() ([]btcjson.GetAddedNodeInfoResul // // 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) - } - + cmd := btcjson.NewGetAddedNodeInfoCmd(true, &peer) return c.sendCmd(cmd) } @@ -145,12 +135,7 @@ func (r FutureGetAddedNodeInfoNoDNSResult) Receive() ([]string, error) { // // 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) - } - + cmd := btcjson.NewGetAddedNodeInfoCmd(false, &peer) return c.sendCmd(cmd) } @@ -191,12 +176,7 @@ func (r FutureGetConnectionCountResult) Receive() (int64, error) { // // 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) - } - + cmd := btcjson.NewGetConnectionCountCmd() return c.sendCmd(cmd) } @@ -226,12 +206,7 @@ func (r FuturePingResult) Receive() error { // // 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) - } - + cmd := btcjson.NewPingCmd() return c.sendCmd(cmd) } @@ -271,12 +246,7 @@ func (r FutureGetPeerInfoResult) Receive() ([]btcjson.GetPeerInfoResult, error) // // 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) - } - + cmd := btcjson.NewGetPeerInfoCmd() return c.sendCmd(cmd) } @@ -313,12 +283,7 @@ func (r FutureGetNetTotalsResult) Receive() (*btcjson.GetNetTotalsResult, error) // // 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) - } - + cmd := btcjson.NewGetNetTotalsCmd() return c.sendCmd(cmd) } diff --git a/notify.go b/notify.go index 44c556e6..aacaa3f4 100644 --- a/notify.go +++ b/notify.go @@ -13,8 +13,7 @@ import ( "sync" "time" - "github.com/btcsuite/btcd/btcjson" - "github.com/btcsuite/btcd/btcjson/btcws" + "github.com/btcsuite/btcd/btcjson/v2/btcjson" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) @@ -37,7 +36,7 @@ type notificationState struct { notifyNewTx bool notifyNewTxVerbose bool notifyReceived map[string]struct{} - notifySpent map[btcws.OutPoint]struct{} + notifySpent map[btcjson.OutPoint]struct{} } // Copy returns a deep copy of the receiver. @@ -52,7 +51,7 @@ func (s *notificationState) Copy() *notificationState { for addr := range s.notifyReceived { stateCopy.notifyReceived[addr] = struct{}{} } - stateCopy.notifySpent = make(map[btcws.OutPoint]struct{}) + stateCopy.notifySpent = make(map[btcjson.OutPoint]struct{}) for op := range s.notifySpent { stateCopy.notifySpent[op] = struct{}{} } @@ -64,7 +63,7 @@ func (s *notificationState) Copy() *notificationState { func newNotificationState() *notificationState { return ¬ificationState{ notifyReceived: make(map[string]struct{}), - notifySpent: make(map[btcws.OutPoint]struct{}), + notifySpent: make(map[btcjson.OutPoint]struct{}), } } @@ -110,7 +109,7 @@ type NotificationHandlers struct { // 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) + OnRecvTx func(transaction *btcutil.Tx, details *btcjson.BlockDetails) // OnRedeemingTx is invoked when a transaction that spends a registered // outpoint is received into the memory pool and also connected to the @@ -122,7 +121,7 @@ type NotificationHandlers struct { // 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) + OnRedeemingTx func(transaction *btcutil.Tx, details *btcjson.BlockDetails) // OnRescanFinished is invoked after a rescan finishes due to a previous // call to Rescan or RescanEndHeight. Finished rescans should be @@ -188,7 +187,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { switch ntfn.Method { // OnBlockConnected - case btcws.BlockConnectedNtfnMethod: + case btcjson.BlockConnectedNtfnMethod: // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnBlockConnected == nil { @@ -205,7 +204,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { c.ntfnHandlers.OnBlockConnected(blockSha, blockHeight) // OnBlockDisconnected - case btcws.BlockDisconnectedNtfnMethod: + case btcjson.BlockDisconnectedNtfnMethod: // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnBlockDisconnected == nil { @@ -222,7 +221,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { c.ntfnHandlers.OnBlockDisconnected(blockSha, blockHeight) // OnRecvTx - case btcws.RecvTxNtfnMethod: + case btcjson.RecvTxNtfnMethod: // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnRecvTx == nil { @@ -239,7 +238,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { c.ntfnHandlers.OnRecvTx(tx, block) // OnRedeemingTx - case btcws.RedeemingTxNtfnMethod: + case btcjson.RedeemingTxNtfnMethod: // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnRedeemingTx == nil { @@ -256,7 +255,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { c.ntfnHandlers.OnRedeemingTx(tx, block) // OnRescanFinished - case btcws.RescanFinishedNtfnMethod: + case btcjson.RescanFinishedNtfnMethod: // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnRescanFinished == nil { @@ -273,7 +272,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { c.ntfnHandlers.OnRescanFinished(hash, height, blkTime) // OnRescanProgress - case btcws.RescanProgressNtfnMethod: + case btcjson.RescanProgressNtfnMethod: // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnRescanProgress == nil { @@ -290,7 +289,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { c.ntfnHandlers.OnRescanProgress(hash, height, blkTime) // OnTxAccepted - case btcws.TxAcceptedNtfnMethod: + case btcjson.TxAcceptedNtfnMethod: // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnTxAccepted == nil { @@ -307,7 +306,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { c.ntfnHandlers.OnTxAccepted(hash, amt) // OnTxAcceptedVerbose - case btcws.TxAcceptedVerboseNtfnMethod: + case btcjson.TxAcceptedVerboseNtfnMethod: // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnTxAcceptedVerbose == nil { @@ -324,7 +323,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { c.ntfnHandlers.OnTxAcceptedVerbose(rawTx) // OnBtcdConnected - case btcws.BtcdConnectedNtfnMethod: + case btcjson.BtcdConnectedNtfnMethod: // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnBtcdConnected == nil { @@ -341,7 +340,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { c.ntfnHandlers.OnBtcdConnected(connected) // OnAccountBalance - case btcws.AccountBalanceNtfnMethod: + case btcjson.AccountBalanceNtfnMethod: // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnAccountBalance == nil { @@ -358,7 +357,7 @@ func (c *Client) handleNotification(ntfn *rawNotification) { c.ntfnHandlers.OnAccountBalance(account, bal, conf) // OnWalletLockState - case btcws.WalletLockStateNtfnMethod: + case btcjson.WalletLockStateNtfnMethod: // Ignore the notification if the client is not interested in // it. if c.ntfnHandlers.OnWalletLockState == nil { @@ -433,7 +432,7 @@ func parseChainNtfnParams(params []json.RawMessage) (*wire.ShaHash, // the block it's mined in from the parameters of recvtx and redeemingtx // notifications. func parseChainTxNtfnParams(params []json.RawMessage) (*btcutil.Tx, - *btcws.BlockDetails, error) { + *btcjson.BlockDetails, error) { if len(params) == 0 || len(params) > 2 { return nil, nil, wrongNumParams(len(params)) @@ -448,7 +447,7 @@ func parseChainTxNtfnParams(params []json.RawMessage) (*btcutil.Tx, // If present, unmarshal second optional parameter as the block details // JSON object. - var block *btcws.BlockDetails + var block *btcjson.BlockDetails if len(params) > 1 { err = json.Unmarshal(params[1], &block) if err != nil { @@ -558,7 +557,7 @@ func parseTxAcceptedVerboseNtfnParams(params []json.RawMessage) (*btcjson.TxRawR return nil, err } - // TODO: change txacceptedverbose notifiation callbacks to use nicer + // TODO: change txacceptedverbose notification callbacks to use nicer // types for all details about the transaction (i.e. decoding hashes // from their string encoding). return &rawTx, nil @@ -611,7 +610,7 @@ func parseAccountBalanceNtfnParams(params []json.RawMessage) (account string, } // Bounds check amount. - bal, err := btcjson.JSONToAmount(fbal) + bal, err := btcutil.NewAmount(fbal) if err != nil { return "", 0, false, err } @@ -677,9 +676,7 @@ func (c *Client) NotifyBlocksAsync() FutureNotifyBlocksResult { return newNilFutureResult() } - id := c.NextID() - cmd := btcws.NewNotifyBlocksCmd(id) - + cmd := btcjson.NewNotifyBlocksCmd() return c.sendCmd(cmd) } @@ -715,7 +712,7 @@ func (r FutureNotifySpentResult) Receive() error { // notifySpentInternal is the same as notifySpentAsync except it accepts // the converted outpoints as a parameter so the client can more efficiently // recreate the previous notification state on reconnect. -func (c *Client) notifySpentInternal(outpoints []btcws.OutPoint) FutureNotifySpentResult { +func (c *Client) notifySpentInternal(outpoints []btcjson.OutPoint) FutureNotifySpentResult { // Not supported in HTTP POST mode. if c.config.HttpPostMode { return newFutureError(ErrNotificationsNotSupported) @@ -727,9 +724,7 @@ func (c *Client) notifySpentInternal(outpoints []btcws.OutPoint) FutureNotifySpe return newNilFutureResult() } - id := c.NextID() - cmd := btcws.NewNotifySpentCmd(id, outpoints) - + cmd := btcjson.NewNotifySpentCmd(outpoints) return c.sendCmd(cmd) } @@ -752,13 +747,11 @@ func (c *Client) NotifySpentAsync(outpoints []*wire.OutPoint) FutureNotifySpentR return newNilFutureResult() } - id := c.NextID() - ops := make([]btcws.OutPoint, 0, len(outpoints)) + ops := make([]btcjson.OutPoint, 0, len(outpoints)) for _, outpoint := range outpoints { - ops = append(ops, *btcws.NewOutPointFromWire(outpoint)) + ops = append(ops, *btcjson.NewOutPointFromWire(outpoint)) } - cmd := btcws.NewNotifySpentCmd(id, ops) - + cmd := btcjson.NewNotifySpentCmd(ops) return c.sendCmd(cmd) } @@ -810,12 +803,7 @@ func (c *Client) NotifyNewTransactionsAsync(verbose bool) FutureNotifyNewTransac return newNilFutureResult() } - id := c.NextID() - cmd, err := btcws.NewNotifyNewTransactionsCmd(id, verbose) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewNotifyNewTransactionsCmd(&verbose) return c.sendCmd(cmd) } @@ -865,9 +853,7 @@ func (c *Client) notifyReceivedInternal(addresses []string) FutureNotifyReceived } // Convert addresses to strings. - id := c.NextID() - cmd := btcws.NewNotifyReceivedCmd(id, addresses) - + cmd := btcjson.NewNotifyReceivedCmd(addresses) return c.sendCmd(cmd) } @@ -895,9 +881,7 @@ func (c *Client) NotifyReceivedAsync(addresses []btcutil.Address) FutureNotifyRe for _, addr := range addresses { addrs = append(addrs, addr.String()) } - id := c.NextID() - cmd := btcws.NewNotifyReceivedCmd(id, addrs) - + cmd := btcjson.NewNotifyReceivedCmd(addrs) return c.sendCmd(cmd) } @@ -978,17 +962,12 @@ func (c *Client) RescanAsync(startBlock *wire.ShaHash, } // Convert outpoints. - ops := make([]btcws.OutPoint, 0, len(outpoints)) + ops := make([]btcjson.OutPoint, 0, len(outpoints)) for _, op := range outpoints { - ops = append(ops, *btcws.NewOutPointFromWire(op)) - } - - id := c.NextID() - cmd, err := btcws.NewRescanCmd(id, startBlockShaStr, addrs, ops) - if err != nil { - return newFutureError(err) + ops = append(ops, *btcjson.NewOutPointFromWire(op)) } + cmd := btcjson.NewRescanCmd(startBlockShaStr, addrs, ops, nil) return c.sendCmd(cmd) } @@ -1063,18 +1042,13 @@ func (c *Client) RescanEndBlockAsync(startBlock *wire.ShaHash, } // Convert outpoints. - ops := make([]btcws.OutPoint, 0, len(outpoints)) + ops := make([]btcjson.OutPoint, 0, len(outpoints)) for _, op := range outpoints { - ops = append(ops, *btcws.NewOutPointFromWire(op)) - } - - id := c.NextID() - cmd, err := btcws.NewRescanCmd(id, startBlockShaStr, addrs, ops, - endBlockShaStr) - if err != nil { - return newFutureError(err) + ops = append(ops, *btcjson.NewOutPointFromWire(op)) } + cmd := btcjson.NewRescanCmd(startBlockShaStr, addrs, ops, + &endBlockShaStr) return c.sendCmd(cmd) } diff --git a/rawrequest.go b/rawrequest.go index 44a557e4..95aeeeaa 100644 --- a/rawrequest.go +++ b/rawrequest.go @@ -8,40 +8,9 @@ import ( "encoding/json" "errors" - "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/btcjson/v2/btcjson" ) -// rawRequest satisifies the btcjson.Cmd interface for btcjson raw commands. -// This type exists here rather than making btcjson.RawCmd satisify the Cmd -// interface due to conflict between the Id and Method field names vs method -// names. -type rawRequest struct { - btcjson.RawCmd -} - -// Enforce that rawRequest is a btcjson.Cmd. -var _ btcjson.Cmd = &rawRequest{} - -// Id returns the JSON-RPC id of the request. -func (r *rawRequest) Id() interface{} { - return r.RawCmd.Id -} - -// Method returns the method string of the request. -func (r *rawRequest) Method() string { - return r.RawCmd.Method -} - -// MarshalJSON marshals the raw request as a JSON-RPC 1.0 object. -func (r *rawRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(r.RawCmd) -} - -// UnmarshalJSON unmarshals a JSON-RPC 1.0 object into the raw request. -func (r *rawRequest) UnmarshalJSON(b []byte) error { - return json.Unmarshal(b, &r.RawCmd) -} - // FutureRawResult is a future promise to deliver the result of a RawRequest RPC // invocation (or an applicable error). type FutureRawResult chan *response @@ -69,16 +38,34 @@ func (c *Client) RawRequestAsync(method string, params []json.RawMessage) Future params = []json.RawMessage{} } - cmd := &rawRequest{ - RawCmd: btcjson.RawCmd{ - Jsonrpc: "1.0", - Id: c.NextID(), - Method: method, - Params: params, - }, + // Create a raw JSON-RPC request using the provided method and params + // and marshal it. This is done rather than using the sendCmd function + // since that relies on marshalling registered btcjson commands rather + // than custom commands. + id := c.NextID() + rawRequest := &btcjson.Request{ + Jsonrpc: "1.0", + ID: id, + Method: method, + Params: params, + } + marshalledJSON, err := json.Marshal(rawRequest) + if err != nil { + return newFutureError(err) } - return c.sendCmd(cmd) + // Generate the request and send it along with a channel to respond on. + responseChan := make(chan *response, 1) + jReq := &jsonRequest{ + id: id, + method: method, + cmd: nil, + marshalledJSON: marshalledJSON, + responseChan: responseChan, + } + c.sendRequest(jReq) + + return responseChan } // RawRequest allows the caller to send a raw or custom request to the server. diff --git a/rawtransactions.go b/rawtransactions.go index e75f0bb4..77013e7d 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -9,7 +9,7 @@ import ( "encoding/hex" "encoding/json" - "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/btcjson/v2/btcjson" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) @@ -101,12 +101,7 @@ func (c *Client) GetRawTransactionAsync(txHash *wire.ShaHash) FutureGetRawTransa hash = txHash.String() } - id := c.NextID() - cmd, err := btcjson.NewGetRawTransactionCmd(id, hash, 0) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewGetRawTransactionCmd(hash, btcjson.Int(0)) return c.sendCmd(cmd) } @@ -152,12 +147,7 @@ func (c *Client) GetRawTransactionVerboseAsync(txHash *wire.ShaHash) FutureGetRa hash = txHash.String() } - id := c.NextID() - cmd, err := btcjson.NewGetRawTransactionCmd(id, hash, 1) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewGetRawTransactionCmd(hash, btcjson.Int(1)) return c.sendCmd(cmd) } @@ -197,13 +187,8 @@ func (r FutureDecodeRawTransactionResult) Receive() (*btcjson.TxRawResult, error // // 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) - } - + cmd := btcjson.NewDecodeRawTransactionCmd(txHex) return c.sendCmd(cmd) } @@ -255,16 +240,11 @@ func (r FutureCreateRawTransactionResult) Receive() (*wire.MsgTx, error) { func (c *Client) CreateRawTransactionAsync(inputs []btcjson.TransactionInput, amounts map[btcutil.Address]btcutil.Amount) FutureCreateRawTransactionResult { - id := c.NextID() - convertedAmts := make(map[string]int64, len(amounts)) + convertedAmts := make(map[string]float64, len(amounts)) for addr, amount := range amounts { - convertedAmts[addr.String()] = int64(amount) + convertedAmts[addr.String()] = amount.ToBTC() } - cmd, err := btcjson.NewCreateRawTransactionCmd(id, inputs, convertedAmts) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewCreateRawTransactionCmd(inputs, convertedAmts) return c.sendCmd(cmd) } @@ -315,12 +295,7 @@ func (c *Client) SendRawTransactionAsync(tx *wire.MsgTx, allowHighFees bool) Fut txHex = hex.EncodeToString(buf.Bytes()) } - id := c.NextID() - cmd, err := btcjson.NewSendRawTransactionCmd(id, txHex, allowHighFees) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewSendRawTransactionCmd(txHex, &allowHighFees) return c.sendCmd(cmd) } @@ -381,12 +356,7 @@ func (c *Client) SignRawTransactionAsync(tx *wire.MsgTx) FutureSignRawTransactio txHex = hex.EncodeToString(buf.Bytes()) } - id := c.NextID() - cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewSignRawTransactionCmd(txHex, nil, nil, nil) return c.sendCmd(cmd) } @@ -417,12 +387,7 @@ func (c *Client) SignRawTransaction2Async(tx *wire.MsgTx, inputs []btcjson.RawTx txHex = hex.EncodeToString(buf.Bytes()) } - id := c.NextID() - cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex, inputs) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewSignRawTransactionCmd(txHex, &inputs, nil, nil) return c.sendCmd(cmd) } @@ -459,13 +424,8 @@ func (c *Client) SignRawTransaction3Async(tx *wire.MsgTx, txHex = hex.EncodeToString(buf.Bytes()) } - id := c.NextID() - cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex, inputs, - privKeysWIF) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewSignRawTransactionCmd(txHex, &inputs, &privKeysWIF, + nil) return c.sendCmd(cmd) } @@ -512,13 +472,8 @@ func (c *Client) SignRawTransaction4Async(tx *wire.MsgTx, txHex = hex.EncodeToString(buf.Bytes()) } - id := c.NextID() - cmd, err := btcjson.NewSignRawTransactionCmd(id, txHex, inputs, - privKeysWIF, string(hashType)) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewSignRawTransactionCmd(txHex, &inputs, &privKeysWIF, + btcjson.String(string(hashType))) return c.sendCmd(cmd) } diff --git a/wallet.go b/wallet.go index 4542819a..57427a93 100644 --- a/wallet.go +++ b/wallet.go @@ -8,8 +8,8 @@ import ( "encoding/json" "strconv" - "github.com/btcsuite/btcd/btcjson" - "github.com/btcsuite/btcd/btcjson/btcws" + "github.com/btcsuite/btcd/btcjson/v2/btcjson" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" @@ -52,12 +52,7 @@ func (c *Client) GetTransactionAsync(txHash *wire.ShaHash) FutureGetTransactionR hash = txHash.String() } - id := c.NextID() - cmd, err := btcjson.NewGetTransactionCmd(id, hash) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewGetTransactionCmd(hash, nil) return c.sendCmd(cmd) } @@ -97,12 +92,7 @@ func (r FutureListTransactionsResult) Receive() ([]btcjson.ListTransactionsResul // // 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) - } - + cmd := btcjson.NewListTransactionsCmd(&account, nil, nil, nil) return c.sendCmd(cmd) } @@ -120,12 +110,7 @@ func (c *Client) ListTransactions(account string) ([]btcjson.ListTransactionsRes // // 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) - } - + cmd := btcjson.NewListTransactionsCmd(&account, &count, nil, nil) return c.sendCmd(cmd) } @@ -144,12 +129,7 @@ func (c *Client) ListTransactionsCount(account string, count int) ([]btcjson.Lis // // 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) - } - + cmd := btcjson.NewListTransactionsCmd(&account, &count, &from, nil) return c.sendCmd(cmd) } @@ -193,12 +173,7 @@ func (r FutureListUnspentResult) Receive() ([]btcjson.ListUnspentResult, error) // // See ListUnspent for the blocking version and more details. func (c *Client) ListUnspentAsync() FutureListUnspentResult { - id := c.NextID() - cmd, err := btcjson.NewListUnspentCmd(id) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewListUnspentCmd(nil, nil, nil) return c.sendCmd(cmd) } @@ -208,12 +183,7 @@ func (c *Client) ListUnspentAsync() FutureListUnspentResult { // // See ListUnspentMin for the blocking version and more details. func (c *Client) ListUnspentMinAsync(minConf int) FutureListUnspentResult { - id := c.NextID() - cmd, err := btcjson.NewListUnspentCmd(id, minConf) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewListUnspentCmd(&minConf, nil, nil) return c.sendCmd(cmd) } @@ -223,12 +193,7 @@ func (c *Client) ListUnspentMinAsync(minConf int) FutureListUnspentResult { // // See ListUnspentMinMax for the blocking version and more details. func (c *Client) ListUnspentMinMaxAsync(minConf, maxConf int) FutureListUnspentResult { - id := c.NextID() - cmd, err := btcjson.NewListUnspentCmd(id, minConf, maxConf) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewListUnspentCmd(&minConf, &maxConf, nil) return c.sendCmd(cmd) } @@ -243,12 +208,7 @@ func (c *Client) ListUnspentMinMaxAddressesAsync(minConf, maxConf int, addrs []b addrStrs = append(addrStrs, a.EncodeAddress()) } - id := c.NextID() - cmd, err := btcjson.NewListUnspentCmd(id, minConf, maxConf, addrStrs) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewListUnspentCmd(&minConf, &maxConf, &addrStrs) return c.sendCmd(cmd) } @@ -315,12 +275,7 @@ func (c *Client) ListSinceBlockAsync(blockHash *wire.ShaHash) FutureListSinceBlo hash = blockHash.String() } - id := c.NextID() - cmd, err := btcjson.NewListSinceBlockCmd(id, hash) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewListSinceBlockCmd(&hash, nil, nil) return c.sendCmd(cmd) } @@ -344,12 +299,7 @@ func (c *Client) ListSinceBlockMinConfAsync(blockHash *wire.ShaHash, minConfirms hash = blockHash.String() } - id := c.NextID() - cmd, err := btcjson.NewListSinceBlockCmd(id, hash, minConfirms) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewListSinceBlockCmd(&hash, &minConfirms, nil) return c.sendCmd(cmd) } @@ -383,7 +333,6 @@ func (r FutureLockUnspentResult) Receive() error { // // See LockUnspent for the blocking version and more details. func (c *Client) LockUnspentAsync(unlock bool, ops []*wire.OutPoint) FutureLockUnspentResult { - id := c.NextID() outputs := make([]btcjson.TransactionInput, len(ops)) for i, op := range ops { outputs[i] = btcjson.TransactionInput{ @@ -391,11 +340,7 @@ func (c *Client) LockUnspentAsync(unlock bool, ops []*wire.OutPoint) FutureLockU Vout: op.Index, } } - cmd, err := btcjson.NewLockUnspentCmd(id, unlock, outputs) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewLockUnspentCmd(unlock, outputs) return c.sendCmd(cmd) } @@ -458,12 +403,7 @@ func (r FutureListLockUnspentResult) Receive() ([]*wire.OutPoint, error) { // // See ListLockUnspent for the blocking version and more details. func (c *Client) ListLockUnspentAsync() FutureListLockUnspentResult { - id := c.NextID() - cmd, err := btcjson.NewListLockUnspentCmd(id) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewListLockUnspentCmd() return c.sendCmd(cmd) } @@ -496,12 +436,7 @@ func (r FutureSetTxFeeResult) Receive() error { // // 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) - } - + cmd := btcjson.NewSetTxFeeCmd(fee.ToBTC()) return c.sendCmd(cmd) } @@ -539,13 +474,8 @@ func (r FutureSendToAddressResult) Receive() (*wire.ShaHash, error) { // // 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) - } - + cmd := btcjson.NewSendToAddressCmd(addr, amount.ToBTC(), nil, nil) return c.sendCmd(cmd) } @@ -570,14 +500,9 @@ 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) - } - + cmd := btcjson.NewSendToAddressCmd(addr, amount.ToBTC(), &comment, + &commentTo) return c.sendCmd(cmd) } @@ -628,13 +553,9 @@ func (r FutureSendFromResult) Receive() (*wire.ShaHash, error) { // // 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) - } - + cmd := btcjson.NewSendFromCmd(fromAccount, addr, amount.ToBTC(), nil, + nil, nil) return c.sendCmd(cmd) } @@ -656,14 +577,9 @@ func (c *Client) SendFrom(fromAccount string, toAddress btcutil.Address, amount // // 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) - } - + cmd := btcjson.NewSendFromCmd(fromAccount, addr, amount.ToBTC(), + &minConfirms, nil, nil) return c.sendCmd(cmd) } @@ -690,14 +606,9 @@ 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) - } - + cmd := btcjson.NewSendFromCmd(fromAccount, addr, amount.ToBTC(), + &minConfirms, &comment, &commentTo) return c.sendCmd(cmd) } @@ -750,16 +661,11 @@ func (r FutureSendManyResult) Receive() (*wire.ShaHash, error) { // // 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)) + convertedAmounts := make(map[string]float64, len(amounts)) for addr, amount := range amounts { - convertedAmounts[addr.EncodeAddress()] = int64(amount) + convertedAmounts[addr.EncodeAddress()] = amount.ToBTC() } - id := c.NextID() - cmd, err := btcjson.NewSendManyCmd(id, fromAccount, convertedAmounts) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewSendManyCmd(fromAccount, convertedAmounts, nil, nil) return c.sendCmd(cmd) } @@ -784,17 +690,12 @@ func (c *Client) SendManyMinConfAsync(fromAccount string, amounts map[btcutil.Address]btcutil.Amount, minConfirms int) FutureSendManyResult { - convertedAmounts := make(map[string]int64, len(amounts)) + convertedAmounts := make(map[string]float64, len(amounts)) for addr, amount := range amounts { - convertedAmounts[addr.EncodeAddress()] = int64(amount) + convertedAmounts[addr.EncodeAddress()] = amount.ToBTC() } - id := c.NextID() - cmd, err := btcjson.NewSendManyCmd(id, fromAccount, convertedAmounts, - minConfirms) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewSendManyCmd(fromAccount, convertedAmounts, + &minConfirms, nil) return c.sendCmd(cmd) } @@ -823,17 +724,12 @@ func (c *Client) SendManyCommentAsync(fromAccount string, amounts map[btcutil.Address]btcutil.Amount, minConfirms int, comment string) FutureSendManyResult { - convertedAmounts := make(map[string]int64, len(amounts)) + convertedAmounts := make(map[string]float64, len(amounts)) for addr, amount := range amounts { - convertedAmounts[addr.EncodeAddress()] = int64(amount) + convertedAmounts[addr.EncodeAddress()] = amount.ToBTC() } - id := c.NextID() - cmd, err := btcjson.NewSendManyCmd(id, fromAccount, convertedAmounts, - minConfirms, comment) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewSendManyCmd(fromAccount, convertedAmounts, + &minConfirms, &comment) return c.sendCmd(cmd) } @@ -888,18 +784,12 @@ func (r FutureAddMultisigAddressResult) Receive() (btcutil.Address, error) { // // 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.String()) } - cmd, err := btcjson.NewAddMultisigAddressCmd(id, requiredSigs, addrs, account) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewAddMultisigAddressCmd(requiredSigs, addrs, &account) return c.sendCmd(cmd) } @@ -938,18 +828,12 @@ func (r FutureCreateMultisigResult) Receive() (*btcjson.CreateMultiSigResult, er // // 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.String()) } - cmd, err := btcjson.NewCreateMultisigCmd(id, requiredSigs, addrs) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewCreateMultisigCmd(requiredSigs, addrs) return c.sendCmd(cmd) } @@ -981,8 +865,7 @@ func (r FutureCreateNewAccountResult) Receive() error { // // See CreateNewAccount for the blocking version and more details. func (c *Client) CreateNewAccountAsync(account string) FutureCreateNewAccountResult { - id := c.NextID() - cmd := btcws.NewCreateNewAccountCmd(id, account) + cmd := btcjson.NewCreateNewAccountCmd(account) return c.sendCmd(cmd) } @@ -1019,12 +902,7 @@ func (r FutureGetNewAddressResult) Receive() (btcutil.Address, error) { // // 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) - } - + cmd := btcjson.NewGetNewAddressCmd(nil) return c.sendCmd(cmd) } @@ -1062,12 +940,7 @@ func (r FutureGetRawChangeAddressResult) Receive() (btcutil.Address, error) { // // 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) - } - + cmd := btcjson.NewGetRawChangeAddressCmd(&account) return c.sendCmd(cmd) } @@ -1106,12 +979,7 @@ func (r FutureGetAccountAddressResult) Receive() (btcutil.Address, error) { // // 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) - } - + cmd := btcjson.NewGetAccountAddressCmd(account) return c.sendCmd(cmd) } @@ -1149,13 +1017,8 @@ func (r FutureGetAccountResult) Receive() (string, error) { // // 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) - } - + cmd := btcjson.NewGetAccountCmd(addr) return c.sendCmd(cmd) } @@ -1185,13 +1048,8 @@ func (r FutureSetAccountResult) Receive() error { // // 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) - } - + cmd := btcjson.NewSetAccountCmd(addr, account) return c.sendCmd(cmd) } @@ -1238,12 +1096,7 @@ func (r FutureGetAddressesByAccountResult) Receive() ([]btcutil.Address, error) // // 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) - } - + cmd := btcjson.NewGetAddressesByAccountCmd(account) return c.sendCmd(cmd) } @@ -1282,12 +1135,8 @@ func (r FutureMoveResult) Receive() (bool, error) { // // See Move for the blocking version and more details. func (c *Client) MoveAsync(fromAccount, toAccount string, amount btcutil.Amount) FutureMoveResult { - id := c.NextID() - cmd, err := btcjson.NewMoveCmd(id, fromAccount, toAccount, int64(amount)) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewMoveCmd(fromAccount, toAccount, amount.ToBTC(), nil, + nil) return c.sendCmd(cmd) } @@ -1307,13 +1156,8 @@ func (c *Client) Move(fromAccount, toAccount string, amount btcutil.Amount) (boo func (c *Client) MoveMinConfAsync(fromAccount, toAccount string, amount btcutil.Amount, minConfirms int) FutureMoveResult { - id := c.NextID() - cmd, err := btcjson.NewMoveCmd(id, fromAccount, toAccount, int64(amount), - minConfirms) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewMoveCmd(fromAccount, toAccount, amount.ToBTC(), + &minConfirms, nil) return c.sendCmd(cmd) } @@ -1335,13 +1179,8 @@ func (c *Client) MoveMinConf(fromAccount, toAccount string, amount btcutil.Amoun func (c *Client) MoveCommentAsync(fromAccount, toAccount string, amount btcutil.Amount, minConfirms int, comment string) FutureMoveResult { - id := c.NextID() - cmd, err := btcjson.NewMoveCmd(id, fromAccount, toAccount, int64(amount), - minConfirms, comment) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewMoveCmd(fromAccount, toAccount, amount.ToBTC(), + &minConfirms, &comment) return c.sendCmd(cmd) } @@ -1378,15 +1217,14 @@ func (r FutureRenameAccountResult) Receive() error { // returned instance. // // See RenameAccount for the blocking version and more details. -func (c *Client) RenameAccountAsync(oldaccount, newaccount string) FutureRenameAccountResult { - id := c.NextID() - cmd := btcws.NewRenameAccountCmd(id, oldaccount, newaccount) +func (c *Client) RenameAccountAsync(oldAccount, newAccount string) FutureRenameAccountResult { + cmd := btcjson.NewRenameAccountCmd(oldAccount, newAccount) return c.sendCmd(cmd) } // RenameAccount creates a new wallet account. -func (c *Client) RenameAccount(oldaccount, newaccount string) error { - return c.RenameAccountAsync(oldaccount, newaccount).Receive() +func (c *Client) RenameAccount(oldAccount, newAccount string) error { + return c.RenameAccountAsync(oldAccount, newAccount).Receive() } // FutureValidateAddressResult is a future promise to deliver the result of a @@ -1395,14 +1233,14 @@ type FutureValidateAddressResult chan *response // Receive waits for the response promised by the future and returns information // about the given bitcoin address. -func (r FutureValidateAddressResult) Receive() (*btcjson.ValidateAddressResult, error) { +func (r FutureValidateAddressResult) Receive() (*btcjson.ValidateAddressWalletResult, error) { res, err := receiveFuture(r) if err != nil { return nil, err } // Unmarshal result as a validateaddress result object. - var addrResult btcjson.ValidateAddressResult + var addrResult btcjson.ValidateAddressWalletResult err = json.Unmarshal(res, &addrResult) if err != nil { return nil, err @@ -1417,18 +1255,13 @@ func (r FutureValidateAddressResult) Receive() (*btcjson.ValidateAddressResult, // // 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) - } - + cmd := btcjson.NewValidateAddressCmd(addr) return c.sendCmd(cmd) } // ValidateAddress returns information about the given bitcoin address. -func (c *Client) ValidateAddress(address btcutil.Address) (*btcjson.ValidateAddressResult, error) { +func (c *Client) ValidateAddress(address btcutil.Address) (*btcjson.ValidateAddressWalletResult, error) { return c.ValidateAddressAsync(address).Receive() } @@ -1453,12 +1286,7 @@ func (r FutureKeyPoolRefillResult) Receive() error { // // 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) - } - + cmd := btcjson.NewKeyPoolRefillCmd(nil) return c.sendCmd(cmd) } @@ -1475,12 +1303,7 @@ func (c *Client) KeyPoolRefill() error { // // 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) - } - + cmd := btcjson.NewKeyPoolRefillCmd(&newSize) return c.sendCmd(cmd) } @@ -1516,12 +1339,12 @@ func (r FutureListAccountsResult) Receive() (map[string]btcutil.Amount, error) { accountsMap := make(map[string]btcutil.Amount) for k, v := range accounts { - satoshi, err := btcjson.JSONToAmount(v) + amount, err := btcutil.NewAmount(v) if err != nil { return nil, err } - accountsMap[k] = btcutil.Amount(satoshi) + accountsMap[k] = amount } return accountsMap, nil @@ -1533,12 +1356,7 @@ func (r FutureListAccountsResult) Receive() (map[string]btcutil.Amount, error) { // // 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) - } - + cmd := btcjson.NewListAccountsCmd(nil) return c.sendCmd(cmd) } @@ -1556,12 +1374,7 @@ func (c *Client) ListAccounts() (map[string]btcutil.Amount, error) { // // 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) - } - + cmd := btcjson.NewListAccountsCmd(&minConfirms) return c.sendCmd(cmd) } @@ -1593,12 +1406,12 @@ func (r FutureGetBalanceResult) Receive() (btcutil.Amount, error) { return 0, err } - satoshi, err := btcjson.JSONToAmount(balance) + amount, err := btcutil.NewAmount(balance) if err != nil { return 0, err } - return btcutil.Amount(satoshi), nil + return amount, nil } // FutureGetBalanceParseResult is same as FutureGetBalanceResult except @@ -1626,12 +1439,12 @@ func (r FutureGetBalanceParseResult) Receive() (btcutil.Amount, error) { if err != nil { return 0, err } - satoshi, err := btcjson.JSONToAmount(balance) + amount, err := btcutil.NewAmount(balance) if err != nil { return 0, err } - return btcutil.Amount(satoshi), nil + return amount, nil } // GetBalanceAsync returns an instance of a type that can be used to get the @@ -1640,16 +1453,7 @@ func (r FutureGetBalanceParseResult) Receive() (btcutil.Amount, error) { // // 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) - } - + cmd := btcjson.NewGetBalanceCmd(&account, nil) return c.sendCmd(cmd) } @@ -1668,16 +1472,7 @@ func (c *Client) GetBalance(account string) (btcutil.Amount, error) { // // 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) - } - + cmd := btcjson.NewGetBalanceCmd(&account, &minConfirms) return c.sendCmd(cmd) } @@ -1714,12 +1509,12 @@ func (r FutureGetReceivedByAccountResult) Receive() (btcutil.Amount, error) { return 0, err } - satoshi, err := btcjson.JSONToAmount(balance) + amount, err := btcutil.NewAmount(balance) if err != nil { return 0, err } - return btcutil.Amount(satoshi), nil + return amount, nil } // GetReceivedByAccountAsync returns an instance of a type that can be used to @@ -1728,12 +1523,7 @@ func (r FutureGetReceivedByAccountResult) Receive() (btcutil.Amount, error) { // // 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) - } - + cmd := btcjson.NewGetReceivedByAccountCmd(account, nil) return c.sendCmd(cmd) } @@ -1752,12 +1542,7 @@ func (c *Client) GetReceivedByAccount(account string) (btcutil.Amount, error) { // // 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) - } - + cmd := btcjson.NewGetReceivedByAccountCmd(account, &minConfirms) return c.sendCmd(cmd) } @@ -1789,12 +1574,12 @@ func (r FutureGetUnconfirmedBalanceResult) Receive() (btcutil.Amount, error) { return 0, err } - satoshi, err := btcjson.JSONToAmount(balance) + amount, err := btcutil.NewAmount(balance) if err != nil { return 0, err } - return btcutil.Amount(satoshi), nil + return amount, nil } // GetUnconfirmedBalanceAsync returns an instance of a type that can be used to @@ -1803,12 +1588,7 @@ func (r FutureGetUnconfirmedBalanceResult) Receive() (btcutil.Amount, error) { // // 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) - } - + cmd := btcjson.NewGetUnconfirmedBalanceCmd(&account) return c.sendCmd(cmd) } @@ -1838,12 +1618,12 @@ func (r FutureGetReceivedByAddressResult) Receive() (btcutil.Amount, error) { return 0, err } - satoshi, err := btcjson.JSONToAmount(balance) + amount, err := btcutil.NewAmount(balance) if err != nil { return 0, err } - return btcutil.Amount(satoshi), nil + return amount, nil } // GetReceivedByAddressAsync returns an instance of a type that can be used to @@ -1852,13 +1632,8 @@ func (r FutureGetReceivedByAddressResult) Receive() (btcutil.Amount, error) { // // 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) - } - + cmd := btcjson.NewGetReceivedByAddressCmd(addr, nil) return c.sendCmd(cmd) } @@ -1878,13 +1653,8 @@ func (c *Client) GetReceivedByAddress(address btcutil.Address) (btcutil.Amount, // // 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) - } - + cmd := btcjson.NewGetReceivedByAddressCmd(addr, &minConfirms) return c.sendCmd(cmd) } @@ -1926,12 +1696,7 @@ func (r FutureListReceivedByAccountResult) Receive() ([]btcjson.ListReceivedByAc // // 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) - } - + cmd := btcjson.NewListReceivedByAccountCmd(nil, nil, nil) return c.sendCmd(cmd) } @@ -1952,12 +1717,7 @@ func (c *Client) ListReceivedByAccount() ([]btcjson.ListReceivedByAccountResult, // // 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) - } - + cmd := btcjson.NewListReceivedByAccountCmd(&minConfirms, nil, nil) return c.sendCmd(cmd) } @@ -1978,12 +1738,8 @@ func (c *Client) ListReceivedByAccountMinConf(minConfirms int) ([]btcjson.ListRe // // See ListReceivedByAccountIncludeEmpty 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) - } - + cmd := btcjson.NewListReceivedByAccountCmd(&minConfirms, &includeEmpty, + nil) return c.sendCmd(cmd) } @@ -2027,12 +1783,7 @@ func (r FutureListReceivedByAddressResult) Receive() ([]btcjson.ListReceivedByAd // // See ListReceivedByAddress for the blocking version and more details. func (c *Client) ListReceivedByAddressAsync() FutureListReceivedByAddressResult { - id := c.NextID() - cmd, err := btcjson.NewListReceivedByAddressCmd(id) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewListReceivedByAddressCmd(nil, nil, nil) return c.sendCmd(cmd) } @@ -2053,12 +1804,7 @@ func (c *Client) ListReceivedByAddress() ([]btcjson.ListReceivedByAddressResult, // // See ListReceivedByAddressMinConf for the blocking version and more details. func (c *Client) ListReceivedByAddressMinConfAsync(minConfirms int) FutureListReceivedByAddressResult { - id := c.NextID() - cmd, err := btcjson.NewListReceivedByAddressCmd(id, minConfirms) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewListReceivedByAddressCmd(&minConfirms, nil, nil) return c.sendCmd(cmd) } @@ -2079,13 +1825,8 @@ func (c *Client) ListReceivedByAddressMinConf(minConfirms int) ([]btcjson.ListRe // // See ListReceivedByAccountIncludeEmpty for the blocking version and more details. func (c *Client) ListReceivedByAddressIncludeEmptyAsync(minConfirms int, includeEmpty bool) FutureListReceivedByAddressResult { - id := c.NextID() - cmd, err := btcjson.NewListReceivedByAddressCmd(id, minConfirms, - includeEmpty) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewListReceivedByAddressCmd(&minConfirms, &includeEmpty, + nil) return c.sendCmd(cmd) } @@ -2124,12 +1865,7 @@ func (r FutureWalletLockResult) Receive() error { // // 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) - } - + cmd := btcjson.NewWalletLockCmd() return c.sendCmd(cmd) } @@ -2146,13 +1882,8 @@ func (c *Client) WalletLock() error { // 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) + cmd := btcjson.NewWalletPassphraseCmd(passphrase, timeoutSecs) + _, err := c.sendCmdAndWait(cmd) if err != nil { return err } @@ -2181,12 +1912,7 @@ func (r FutureWalletPassphraseChangeResult) Receive() error { // // 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) - } - + cmd := btcjson.NewWalletPassphraseChangeCmd(old, new) return c.sendCmd(cmd) } @@ -2228,13 +1954,8 @@ func (r FutureSignMessageResult) Receive() (string, error) { // // 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) - } - + cmd := btcjson.NewSignMessageCmd(addr, message) return c.sendCmd(cmd) } @@ -2274,13 +1995,8 @@ func (r FutureVerifyMessageResult) Receive() (bool, error) { // // 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) - } - + cmd := btcjson.NewVerifyMessageCmd(addr, signature, message) return c.sendCmd(cmd) } @@ -2325,13 +2041,8 @@ func (r FutureDumpPrivKeyResult) Receive() (*btcutil.WIF, error) { // // 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) - } - + cmd := btcjson.NewDumpPrivKeyCmd(addr) return c.sendCmd(cmd) } @@ -2371,12 +2082,7 @@ func (c *Client) ImportPrivKeyAsync(privKeyWIF *btcutil.WIF) FutureImportPrivKey wif = privKeyWIF.String() } - id := c.NextID() - cmd, err := btcjson.NewImportPrivKeyCmd(id, wif) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewImportPrivKeyCmd(wif, nil, nil) return c.sendCmd(cmd) } @@ -2397,12 +2103,7 @@ func (c *Client) ImportPrivKeyLabelAsync(privKeyWIF *btcutil.WIF, label string) wif = privKeyWIF.String() } - id := c.NextID() - cmd, err := btcjson.NewImportPrivKeyCmd(id, wif, label) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewImportPrivKeyCmd(wif, &label, nil) return c.sendCmd(cmd) } @@ -2423,12 +2124,7 @@ func (c *Client) ImportPrivKeyRescanAsync(privKeyWIF *btcutil.WIF, label string, wif = privKeyWIF.String() } - id := c.NextID() - cmd, err := btcjson.NewImportPrivKeyCmd(id, wif, label, rescan) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewImportPrivKeyCmd(wif, &label, &rescan) return c.sendCmd(cmd) } @@ -2452,14 +2148,14 @@ type FutureGetInfoResult chan *response // Receive waits for the response promised by the future and returns the info // provided by the server. -func (r FutureGetInfoResult) Receive() (*btcjson.InfoResult, error) { +func (r FutureGetInfoResult) Receive() (*btcjson.InfoWalletResult, error) { res, err := receiveFuture(r) if err != nil { return nil, err } // Unmarshal result as a getinfo result object. - var infoRes btcjson.InfoResult + var infoRes btcjson.InfoWalletResult err = json.Unmarshal(res, &infoRes) if err != nil { return nil, err @@ -2474,19 +2170,14 @@ func (r FutureGetInfoResult) Receive() (*btcjson.InfoResult, error) { // // See GetInfo for the blocking version and more details. func (c *Client) GetInfoAsync() FutureGetInfoResult { - id := c.NextID() - cmd, err := btcjson.NewGetInfoCmd(id) - if err != nil { - return newFutureError(err) - } - + cmd := btcjson.NewGetInfoCmd() return c.sendCmd(cmd) } // GetInfo returns miscellaneous info regarding the RPC server. The returned // info object may be void of wallet information if the remote server does // not include wallet functionality. -func (c *Client) GetInfo() (*btcjson.InfoResult, error) { +func (c *Client) GetInfo() (*btcjson.InfoWalletResult, error) { return c.GetInfoAsync().Receive() } From 04a3ed28f561eb17831f56ae84f47f49d262985d Mon Sep 17 00:00:00 2001 From: Javed Khan Date: Wed, 18 Feb 2015 22:39:11 +0530 Subject: [PATCH 096/153] Added ImportAddress and ImportPubKey support. --- wallet.go | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/wallet.go b/wallet.go index 57427a93..73895b5c 100644 --- a/wallet.go +++ b/wallet.go @@ -2055,6 +2055,52 @@ func (c *Client) DumpPrivKey(address btcutil.Address) (*btcutil.WIF, error) { return c.DumpPrivKeyAsync(address).Receive() } +// FutureImportAddressResult is a future promise to deliver the result of an +// ImportAddressAsync RPC invocation (or an applicable error). +type FutureImportAddressResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of importing the passed public address. +func (r FutureImportAddressResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// ImportAddressAsync 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 ImportAddress for the blocking version and more details. +func (c *Client) ImportAddressAsync(address string) FutureImportAddressResult { + cmd := btcjson.NewImportAddressCmd(address, nil) + return c.sendCmd(cmd) +} + +// ImportAddress imports the passed public address. +func (c *Client) ImportAddress(address string) error { + return c.ImportAddressAsync(address).Receive() +} + +// ImportAddressRescanAsync 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 ImportAddress for the blocking version and more details. +func (c *Client) ImportAddressRescanAsync(address string, rescan bool) FutureImportAddressResult { + cmd := btcjson.NewImportAddressCmd(address, &rescan) + return c.sendCmd(cmd) +} + +// ImportAddressRescan imports the passed public address. When rescan is true, +// the block history is scanned for transactions addressed to provided address. +func (c *Client) ImportAddressRescan(address string, rescan bool) error { + return c.ImportAddressRescanAsync(address, rescan).Receive() +} + // FutureImportPrivKeyResult is a future promise to deliver the result of an // ImportPrivKeyAsync RPC invocation (or an applicable error). type FutureImportPrivKeyResult chan *response @@ -2135,6 +2181,52 @@ func (c *Client) ImportPrivKeyRescan(privKeyWIF *btcutil.WIF, label string, resc return c.ImportPrivKeyRescanAsync(privKeyWIF, label, rescan).Receive() } +// FutureImportPubKeyResult is a future promise to deliver the result of an +// ImportPubKeyAsync RPC invocation (or an applicable error). +type FutureImportPubKeyResult chan *response + +// Receive waits for the response promised by the future and returns the result +// of importing the passed public key. +func (r FutureImportPubKeyResult) Receive() error { + _, err := receiveFuture(r) + if err != nil { + return err + } + + return nil +} + +// ImportPubKeyAsync 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 ImportPubKey for the blocking version and more details. +func (c *Client) ImportPubKeyAsync(pubKey string) FutureImportPubKeyResult { + cmd := btcjson.NewImportPubKeyCmd(pubKey, nil) + return c.sendCmd(cmd) +} + +// ImportPubKey imports the passed public key. +func (c *Client) ImportPubKey(pubKey string) error { + return c.ImportPubKeyAsync(pubKey).Receive() +} + +// ImportPubKeyRescanAsync 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 ImportPubKey for the blocking version and more details. +func (c *Client) ImportPubKeyRescanAsync(pubKey string, rescan bool) FutureImportPubKeyResult { + cmd := btcjson.NewImportPubKeyCmd(pubKey, &rescan) + return c.sendCmd(cmd) +} + +// ImportPubKeyRescan imports the passed public key. When rescan is true, the +// block history is scanned for transactions addressed to provided pubkey. +func (c *Client) ImportPubKeyRescan(pubKey string, rescan bool) error { + return c.ImportPubKeyRescanAsync(pubKey, rescan).Receive() +} + // *********************** // Miscellaneous Functions // *********************** From 3daafd5617beef393a676a6ab578290b51881fba Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sat, 21 Feb 2015 21:06:06 -0600 Subject: [PATCH 097/153] Add SearchRawTransactions support. --- rawtransactions.go | 109 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/rawtransactions.go b/rawtransactions.go index 77013e7d..8736847c 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -503,3 +503,112 @@ func (c *Client) SignRawTransaction4(tx *wire.MsgTx, return c.SignRawTransaction4Async(tx, inputs, privKeysWIF, hashType).Receive() } + +// FutureSearchRawTransactionsResult is a future promise to deliver the result +// of the SearchRawTransactionsAsync RPC invocation (or an applicable error). +type FutureSearchRawTransactionsResult chan *response + +// Receive waits for the response promised by the future and returns the +// found raw transactions. +func (r FutureSearchRawTransactionsResult) Receive() ([]*wire.MsgTx, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal as an array of strings. + var searchRawTxnsResult []string + err = json.Unmarshal(res, &searchRawTxnsResult) + if err != nil { + return nil, err + } + + // Decode and deserialize each transaction. + msgTxns := make([]*wire.MsgTx, 0, len(searchRawTxnsResult)) + for _, hexTx := range searchRawTxnsResult { + // Decode the serialized transaction hex to raw bytes. + serializedTx, err := hex.DecodeString(hexTx) + if err != nil { + return nil, err + } + + // Deserialize the transaction and add it to the result slice. + var msgTx wire.MsgTx + err = msgTx.Deserialize(bytes.NewReader(serializedTx)) + if err != nil { + return nil, err + } + msgTxns = append(msgTxns, &msgTx) + } + + return msgTxns, nil +} + +// SearchRawTransactionsAsync 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 SearchRawTransactions for the blocking version and more details. +func (c *Client) SearchRawTransactionsAsync(address btcutil.Address, skip, count int) FutureSearchRawTransactionsResult { + addr := address.EncodeAddress() + verbose := btcjson.Int(0) + cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count) + return c.sendCmd(cmd) +} + +// SearchRawTransactions returns transactions that involve the passed address. +// +// NOTE: Chain servers do not typically provide this capability unless it has +// specifically been enabled. +// +// See SearchRawTransactionsVerbose to retrieve a list of data structures with +// information about the transactions instead of the transactions themselves. +func (c *Client) SearchRawTransactions(address btcutil.Address, skip, count int) ([]*wire.MsgTx, error) { + return c.SearchRawTransactionsAsync(address, skip, count).Receive() +} + +// FutureSearchRawTransactionsVerboseResult is a future promise to deliver the +// result of the SearchRawTransactionsVerboseAsync RPC invocation (or an +// applicable error). +type FutureSearchRawTransactionsVerboseResult chan *response + +// Receive waits for the response promised by the future and returns the +// found raw transactions. +func (r FutureSearchRawTransactionsVerboseResult) Receive() ([]*btcjson.TxRawResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal as an array of raw transaction results. + var result []*btcjson.TxRawResult + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + return result, nil +} + +// SearchRawTransactionsVerboseAsync 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 SearchRawTransactionsVerbose for the blocking version and more details. +func (c *Client) SearchRawTransactionsVerboseAsync(address btcutil.Address, skip, count int) FutureSearchRawTransactionsVerboseResult { + addr := address.EncodeAddress() + verbose := btcjson.Int(1) + cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count) + return c.sendCmd(cmd) +} + +// SearchRawTransactionsVerbose returns a list of data structures that describe +// transactions which involve the passed address. +// +// NOTE: Chain servers do not typically provide this capability unless it has +// specifically been enabled. +// +// See SearchRawTransactions to retrieve a list of raw transactions instead. +func (c *Client) SearchRawTransactionsVerbose(address btcutil.Address, skip, count int) ([]*btcjson.TxRawResult, error) { + return c.SearchRawTransactionsVerboseAsync(address, skip, count).Receive() +} From c4bc5220bcdd551d67ca2e01ed0f42a88d82baa0 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Tue, 24 Feb 2015 23:49:06 -0600 Subject: [PATCH 098/153] Update for recent GetTxOut argument change. --- chain.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chain.go b/chain.go index 2d8a725e..aae6f05a 100644 --- a/chain.go +++ b/chain.go @@ -476,7 +476,7 @@ func (r FutureGetTxOutResult) Receive() (*btcjson.GetTxOutResult, error) { // the returned instance. // // See GetTxOut for the blocking version and more details. -func (c *Client) GetTxOutAsync(txHash *wire.ShaHash, index int, mempool bool) FutureGetTxOutResult { +func (c *Client) GetTxOutAsync(txHash *wire.ShaHash, index uint32, mempool bool) FutureGetTxOutResult { hash := "" if txHash != nil { hash = txHash.String() @@ -488,6 +488,6 @@ func (c *Client) GetTxOutAsync(txHash *wire.ShaHash, index int, mempool bool) Fu // GetTxOut returns the transaction output info if it's unspent and // nil, otherwise. -func (c *Client) GetTxOut(txHash *wire.ShaHash, index int, mempool bool) (*btcjson.GetTxOutResult, error) { +func (c *Client) GetTxOut(txHash *wire.ShaHash, index uint32, mempool bool) (*btcjson.GetTxOutResult, error) { return c.GetTxOutAsync(txHash, index, mempool).Receive() } From 774eb787a88ce1be75d56cfc1a3e30aedd916aac Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 15 Jan 2015 18:14:45 -0600 Subject: [PATCH 099/153] Rename HttpPostMode conn param to HTTPPostMode. This rename is to make golint happy. --- examples/bitcoincorehttp/main.go | 2 +- infrastructure.go | 16 ++++++++-------- notify.go | 16 ++++++++-------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/examples/bitcoincorehttp/main.go b/examples/bitcoincorehttp/main.go index 3caddb3b..38a13699 100644 --- a/examples/bitcoincorehttp/main.go +++ b/examples/bitcoincorehttp/main.go @@ -16,7 +16,7 @@ func main() { Host: "localhost:8332", User: "yourrpcuser", Pass: "yourrpcpass", - HttpPostMode: true, // Bitcoin core only supports HTTP POST mode + HTTPPostMode: true, // Bitcoin core only supports HTTP POST mode DisableTLS: true, // Bitcoin core does not provide TLS by default } // Notice the notification parameter is nil since notifications are diff --git a/infrastructure.go b/infrastructure.go index 4e63f4a0..42ab5290 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -789,7 +789,7 @@ func (c *Client) sendRequest(jReq *jsonRequest) { // 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. - if c.config.HttpPostMode { + if c.config.HTTPPostMode { c.sendPost(jReq) return } @@ -876,7 +876,7 @@ func (c *Client) Disconnected() bool { // // This function is safe for concurrent access. func (c *Client) doDisconnect() bool { - if c.config.HttpPostMode { + if c.config.HTTPPostMode { return false } @@ -979,7 +979,7 @@ func (c *Client) start() { // 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 { + if c.config.HTTPPostMode { c.wg.Add(1) go c.sendPostHandler() } else { @@ -1055,13 +1055,13 @@ type ConnConfig struct { // called manually. DisableConnectOnNew bool - // HttpPostMode instructs the client to run using multiple independent + // 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 + HTTPPostMode bool // EnableBCInfoHacks is an option provided to enable compatiblity hacks // when connecting to blockchain.info RPC server @@ -1182,7 +1182,7 @@ func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error var httpClient *http.Client connEstablished := make(chan struct{}) var start bool - if config.HttpPostMode { + if config.HTTPPostMode { ntfnHandlers = nil start = true @@ -1222,7 +1222,7 @@ func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error if start { close(connEstablished) client.start() - if !client.config.HttpPostMode && !client.config.DisableAutoReconnect { + if !client.config.HTTPPostMode && !client.config.DisableAutoReconnect { client.wg.Add(1) go client.wsReconnectHandler() } @@ -1246,7 +1246,7 @@ func (c *Client) Connect(tries int) error { c.mtx.Lock() defer c.mtx.Unlock() - if c.config.HttpPostMode { + if c.config.HTTPPostMode { return ErrNotWebsocketClient } if c.wsConn != nil { diff --git a/notify.go b/notify.go index aacaa3f4..29be70b3 100644 --- a/notify.go +++ b/notify.go @@ -666,7 +666,7 @@ func (r FutureNotifyBlocksResult) Receive() error { // 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 { + if c.config.HTTPPostMode { return newFutureError(ErrNotificationsNotSupported) } @@ -714,7 +714,7 @@ func (r FutureNotifySpentResult) Receive() error { // recreate the previous notification state on reconnect. func (c *Client) notifySpentInternal(outpoints []btcjson.OutPoint) FutureNotifySpentResult { // Not supported in HTTP POST mode. - if c.config.HttpPostMode { + if c.config.HTTPPostMode { return newFutureError(ErrNotificationsNotSupported) } @@ -737,7 +737,7 @@ func (c *Client) notifySpentInternal(outpoints []btcjson.OutPoint) FutureNotifyS // NOTE: This is a btcd extension and requires a websocket connection. func (c *Client) NotifySpentAsync(outpoints []*wire.OutPoint) FutureNotifySpentResult { // Not supported in HTTP POST mode. - if c.config.HttpPostMode { + if c.config.HTTPPostMode { return newFutureError(ErrNotificationsNotSupported) } @@ -793,7 +793,7 @@ func (r FutureNotifyNewTransactionsResult) Receive() error { // 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 { + if c.config.HTTPPostMode { return newFutureError(ErrNotificationsNotSupported) } @@ -842,7 +842,7 @@ func (r FutureNotifyReceivedResult) Receive() error { // recreate the previous notification state on reconnect. func (c *Client) notifyReceivedInternal(addresses []string) FutureNotifyReceivedResult { // Not supported in HTTP POST mode. - if c.config.HttpPostMode { + if c.config.HTTPPostMode { return newFutureError(ErrNotificationsNotSupported) } @@ -866,7 +866,7 @@ func (c *Client) notifyReceivedInternal(addresses []string) FutureNotifyReceived // 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 { + if c.config.HTTPPostMode { return newFutureError(ErrNotificationsNotSupported) } @@ -939,7 +939,7 @@ func (c *Client) RescanAsync(startBlock *wire.ShaHash, outpoints []*wire.OutPoint) FutureRescanResult { // Not supported in HTTP POST mode. - if c.config.HttpPostMode { + if c.config.HTTPPostMode { return newFutureError(ErrNotificationsNotSupported) } @@ -1016,7 +1016,7 @@ func (c *Client) RescanEndBlockAsync(startBlock *wire.ShaHash, endBlock *wire.ShaHash) FutureRescanResult { // Not supported in HTTP POST mode. - if c.config.HttpPostMode { + if c.config.HTTPPostMode { return newFutureError(ErrNotificationsNotSupported) } From 34c87a73404c6bf1cc9648e0042f8817763f507b Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 15 Jan 2015 21:00:50 -0600 Subject: [PATCH 100/153] Add golint to TravisCI builds. --- goclean.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/goclean.sh b/goclean.sh index 47aa7956..91833320 100755 --- a/goclean.sh +++ b/goclean.sh @@ -11,7 +11,7 @@ set -e # Automatic checks test -z "$(gofmt -l -w . | tee /dev/stderr)" test -z "$(goimports -l -w . | tee /dev/stderr)" -#test -z "$(golint . | tee /dev/stderr)" +test -z "$(golint . | tee /dev/stderr)" go vet ./... env GORACE="halt_on_error=1" go test -v -race ./... From 05f1d6c89afb353ce0c6c42dfc0f74dac7e6cd52 Mon Sep 17 00:00:00 2001 From: Alex Akselrod Date: Fri, 17 Apr 2015 14:20:39 -0400 Subject: [PATCH 101/153] Change parseTxAcceptedNtfnParams to accept a float64 returned by btcd. --- notify.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/notify.go b/notify.go index 29be70b3..f40bc6d1 100644 --- a/notify.go +++ b/notify.go @@ -525,9 +525,15 @@ func parseTxAcceptedNtfnParams(params []json.RawMessage) (*wire.ShaHash, return nil, 0, err } - // Unmarshal second parameter as an integer. - var amt int64 - err = json.Unmarshal(params[1], &amt) + // Unmarshal second parameter as a floating point number. + var famt float64 + err = json.Unmarshal(params[1], &famt) + if err != nil { + return nil, 0, err + } + + // Bounds check amount. + amt, err := btcutil.NewAmount(famt) if err != nil { return nil, 0, err } From 13b872259dd68cc8d7cd0e59a9ed5bc4448d7cc6 Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Thu, 23 Apr 2015 14:55:35 -0400 Subject: [PATCH 102/153] Remove dependence on btcjson.NewOutPointFromWire. --- notify.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/notify.go b/notify.go index f40bc6d1..000125f5 100644 --- a/notify.go +++ b/notify.go @@ -734,6 +734,15 @@ func (c *Client) notifySpentInternal(outpoints []btcjson.OutPoint) FutureNotifyS return c.sendCmd(cmd) } +// newOutPointFromWire constructs the btcjson representation of a transaction +// outpoint from the wire type. +func newOutPointFromWire(op *wire.OutPoint) btcjson.OutPoint { + return btcjson.OutPoint{ + Hash: op.Hash.String(), + Index: op.Index, + } +} + // 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. @@ -755,7 +764,7 @@ func (c *Client) NotifySpentAsync(outpoints []*wire.OutPoint) FutureNotifySpentR ops := make([]btcjson.OutPoint, 0, len(outpoints)) for _, outpoint := range outpoints { - ops = append(ops, *btcjson.NewOutPointFromWire(outpoint)) + ops = append(ops, newOutPointFromWire(outpoint)) } cmd := btcjson.NewNotifySpentCmd(ops) return c.sendCmd(cmd) @@ -970,7 +979,7 @@ func (c *Client) RescanAsync(startBlock *wire.ShaHash, // Convert outpoints. ops := make([]btcjson.OutPoint, 0, len(outpoints)) for _, op := range outpoints { - ops = append(ops, *btcjson.NewOutPointFromWire(op)) + ops = append(ops, newOutPointFromWire(op)) } cmd := btcjson.NewRescanCmd(startBlockShaStr, addrs, ops, nil) @@ -1050,7 +1059,7 @@ func (c *Client) RescanEndBlockAsync(startBlock *wire.ShaHash, // Convert outpoints. ops := make([]btcjson.OutPoint, 0, len(outpoints)) for _, op := range outpoints { - ops = append(ops, *btcjson.NewOutPointFromWire(op)) + ops = append(ops, newOutPointFromWire(op)) } cmd := btcjson.NewRescanCmd(startBlockShaStr, addrs, ops, From b1d5c1b9f6d6df528fb3e21ed7bec49db163c2a2 Mon Sep 17 00:00:00 2001 From: Albert Puigsech Galicia Date: Mon, 27 Apr 2015 00:44:16 +0200 Subject: [PATCH 103/153] Add account param on GetNewAddress --- wallet.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wallet.go b/wallet.go index 73895b5c..5a646564 100644 --- a/wallet.go +++ b/wallet.go @@ -901,14 +901,14 @@ func (r FutureGetNewAddressResult) Receive() (btcutil.Address, error) { // returned instance. // // See GetNewAddress for the blocking version and more details. -func (c *Client) GetNewAddressAsync() FutureGetNewAddressResult { - cmd := btcjson.NewGetNewAddressCmd(nil) +func (c *Client) GetNewAddressAsync(account string) FutureGetNewAddressResult { + cmd := btcjson.NewGetNewAddressCmd(&account) return c.sendCmd(cmd) } // GetNewAddress returns a new address. -func (c *Client) GetNewAddress() (btcutil.Address, error) { - return c.GetNewAddressAsync().Receive() +func (c *Client) GetNewAddress(account string) (btcutil.Address, error) { + return c.GetNewAddressAsync(account).Receive() } // FutureGetRawChangeAddressResult is a future promise to deliver the result of From ce9e8aa2640c709e36df8fd51ccc6dd6400ae72a Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 1 May 2015 00:48:01 -0500 Subject: [PATCH 104/153] Update btcjson path import paths to new location. --- chain.go | 2 +- examples/btcwalletwebsockets/main.go | 1 + extensions.go | 2 +- infrastructure.go | 2 +- mining.go | 2 +- net.go | 2 +- notify.go | 2 +- rawrequest.go | 2 +- rawtransactions.go | 2 +- wallet.go | 2 +- 10 files changed, 10 insertions(+), 9 deletions(-) diff --git a/chain.go b/chain.go index aae6f05a..b59ba636 100644 --- a/chain.go +++ b/chain.go @@ -9,7 +9,7 @@ import ( "encoding/hex" "encoding/json" - "github.com/btcsuite/btcd/btcjson/v2/btcjson" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) diff --git a/examples/btcwalletwebsockets/main.go b/examples/btcwalletwebsockets/main.go index 38833931..8db235a4 100644 --- a/examples/btcwalletwebsockets/main.go +++ b/examples/btcwalletwebsockets/main.go @@ -5,6 +5,7 @@ package main import ( + "fmt" "io/ioutil" "log" "path/filepath" diff --git a/extensions.go b/extensions.go index d30f370d..af69de34 100644 --- a/extensions.go +++ b/extensions.go @@ -9,7 +9,7 @@ import ( "encoding/json" "fmt" - "github.com/btcsuite/btcd/btcjson/v2/btcjson" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) diff --git a/infrastructure.go b/infrastructure.go index 42ab5290..a97c1acb 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -21,7 +21,7 @@ import ( "sync/atomic" "time" - "github.com/btcsuite/btcd/btcjson/v2/btcjson" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/go-socks/socks" "github.com/btcsuite/websocket" ) diff --git a/mining.go b/mining.go index e64504d8..83343dee 100644 --- a/mining.go +++ b/mining.go @@ -9,7 +9,7 @@ import ( "encoding/json" "errors" - "github.com/btcsuite/btcd/btcjson/v2/btcjson" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcutil" ) diff --git a/net.go b/net.go index b843c98f..bd25d3ed 100644 --- a/net.go +++ b/net.go @@ -7,7 +7,7 @@ package btcrpcclient import ( "encoding/json" - "github.com/btcsuite/btcd/btcjson/v2/btcjson" + "github.com/btcsuite/btcd/btcjson" ) // AddNodeCommand enumerates the available commands that the AddNode function diff --git a/notify.go b/notify.go index 000125f5..1137c87e 100644 --- a/notify.go +++ b/notify.go @@ -13,7 +13,7 @@ import ( "sync" "time" - "github.com/btcsuite/btcd/btcjson/v2/btcjson" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) diff --git a/rawrequest.go b/rawrequest.go index 95aeeeaa..24e129eb 100644 --- a/rawrequest.go +++ b/rawrequest.go @@ -8,7 +8,7 @@ import ( "encoding/json" "errors" - "github.com/btcsuite/btcd/btcjson/v2/btcjson" + "github.com/btcsuite/btcd/btcjson" ) // FutureRawResult is a future promise to deliver the result of a RawRequest RPC diff --git a/rawtransactions.go b/rawtransactions.go index 8736847c..c9d8a0b5 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -9,7 +9,7 @@ import ( "encoding/hex" "encoding/json" - "github.com/btcsuite/btcd/btcjson/v2/btcjson" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) diff --git a/wallet.go b/wallet.go index 5a646564..d8938772 100644 --- a/wallet.go +++ b/wallet.go @@ -8,7 +8,7 @@ import ( "encoding/json" "strconv" - "github.com/btcsuite/btcd/btcjson/v2/btcjson" + "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/wire" From dd8dc875775c3c0e2b447725d50bb77b91b31f15 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 1 May 2015 10:54:42 -0500 Subject: [PATCH 105/153] Run goimports -w . --- examples/btcwalletwebsockets/main.go | 1 - wallet.go | 1 - 2 files changed, 2 deletions(-) diff --git a/examples/btcwalletwebsockets/main.go b/examples/btcwalletwebsockets/main.go index 8db235a4..38833931 100644 --- a/examples/btcwalletwebsockets/main.go +++ b/examples/btcwalletwebsockets/main.go @@ -5,7 +5,6 @@ package main import ( - "fmt" "io/ioutil" "log" "path/filepath" diff --git a/wallet.go b/wallet.go index d8938772..4e6938f4 100644 --- a/wallet.go +++ b/wallet.go @@ -9,7 +9,6 @@ import ( "strconv" "github.com/btcsuite/btcd/btcjson" - "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" From b046f36c723695be2d5bd0e66cb24dd0efbff49c Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 1 May 2015 12:03:51 -0500 Subject: [PATCH 106/153] Relicense to the btcsuite developers. --- chain.go | 2 +- doc.go | 2 +- examples/bitcoincorehttp/main.go | 2 +- examples/btcdwebsockets/main.go | 2 +- examples/btcwalletwebsockets/main.go | 2 +- extensions.go | 2 +- infrastructure.go | 2 +- log.go | 2 +- mining.go | 2 +- net.go | 2 +- notify.go | 2 +- rawrequest.go | 2 +- rawtransactions.go | 2 +- wallet.go | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/chain.go b/chain.go index b59ba636..184b71cc 100644 --- a/chain.go +++ b/chain.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 Conformal Systems LLC. +// Copyright (c) 2014-2015 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/doc.go b/doc.go index ee5be686..43882e01 100644 --- a/doc.go +++ b/doc.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014 Conformal Systems LLC. +// Copyright (c) 2014 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/examples/bitcoincorehttp/main.go b/examples/bitcoincorehttp/main.go index 38a13699..1f5f8b35 100644 --- a/examples/bitcoincorehttp/main.go +++ b/examples/bitcoincorehttp/main.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 Conformal Systems LLC. +// Copyright (c) 2014-2015 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/examples/btcdwebsockets/main.go b/examples/btcdwebsockets/main.go index 92978305..360f54e5 100644 --- a/examples/btcdwebsockets/main.go +++ b/examples/btcdwebsockets/main.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 Conformal Systems LLC. +// Copyright (c) 2014-2015 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/examples/btcwalletwebsockets/main.go b/examples/btcwalletwebsockets/main.go index 38833931..545fe7bb 100644 --- a/examples/btcwalletwebsockets/main.go +++ b/examples/btcwalletwebsockets/main.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 Conformal Systems LLC. +// Copyright (c) 2014-2015 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/extensions.go b/extensions.go index af69de34..197c2d1e 100644 --- a/extensions.go +++ b/extensions.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 Conformal Systems LLC. +// Copyright (c) 2014-2015 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/infrastructure.go b/infrastructure.go index a97c1acb..1ae078a0 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 Conformal Systems LLC. +// Copyright (c) 2014-2015 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/log.go b/log.go index 0b05f1ca..4d629487 100644 --- a/log.go +++ b/log.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 Conformal Systems LLC. +// Copyright (c) 2014-2015 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/mining.go b/mining.go index 83343dee..185a004a 100644 --- a/mining.go +++ b/mining.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 Conformal Systems LLC. +// Copyright (c) 2014-2015 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/net.go b/net.go index bd25d3ed..171cee63 100644 --- a/net.go +++ b/net.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 Conformal Systems LLC. +// Copyright (c) 2014-2015 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/notify.go b/notify.go index 1137c87e..2269a042 100644 --- a/notify.go +++ b/notify.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 Conformal Systems LLC. +// Copyright (c) 2014-2015 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/rawrequest.go b/rawrequest.go index 24e129eb..b4df97d0 100644 --- a/rawrequest.go +++ b/rawrequest.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 Conformal Systems LLC. +// Copyright (c) 2014-2015 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/rawtransactions.go b/rawtransactions.go index c9d8a0b5..ba43efde 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 Conformal Systems LLC. +// Copyright (c) 2014-2015 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. diff --git a/wallet.go b/wallet.go index 4e6938f4..9327ccbb 100644 --- a/wallet.go +++ b/wallet.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 Conformal Systems LLC. +// Copyright (c) 2014-2015 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. From 9874580e5b5c4fe73bbb38d8358927a4b14be46f Mon Sep 17 00:00:00 2001 From: Alex Akselrod Date: Tue, 28 Apr 2015 17:22:36 -0400 Subject: [PATCH 107/153] Add Generate support in mining.go. - Create FutureGenerateResult type with Receive() method - Create GenerateAsync method for Client which returns a FutureGenerateResult - Create Generate method for Client which calls GenerateAsync and then calls Receive() on the returned FutureGenerateResult --- mining.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/mining.go b/mining.go index 185a004a..0e9f415d 100644 --- a/mining.go +++ b/mining.go @@ -10,9 +10,56 @@ import ( "errors" "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) +// FutureGenerateResult is a future promise to deliver the result of a +// GenerateAsync RPC invocation (or an applicable error). +type FutureGenerateResult chan *response + +// Receive waits for the response promised by the future and returns a list of +// block hashes generated by the call. +func (r FutureGenerateResult) Receive() ([]*wire.ShaHash, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a list of strings. + var result []string + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + // Convert each block hash to a wire.ShaHash and store a pointer to each. + convertedResult := make([]*wire.ShaHash, len(result)) + for i, hashString := range result { + convertedResult[i], err = wire.NewShaHashFromStr(hashString) + if err != nil { + return nil, err + } + } + + return convertedResult, nil +} + +// GenerateAsync 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 Generate for the blocking version and more details. +func (c *Client) GenerateAsync(numBlocks uint32) FutureGenerateResult { + cmd := btcjson.NewGenerateCmd(numBlocks) + return c.sendCmd(cmd) +} + +// Generate generates numBlocks blocks and returns their hashes. +func (c *Client) Generate(numBlocks uint32) ([]*wire.ShaHash, error) { + return c.GenerateAsync(numBlocks).Receive() +} + // FutureGetGenerateResult is a future promise to deliver the result of a // GetGenerateAsync RPC invocation (or an applicable error). type FutureGetGenerateResult chan *response From ee3a86c44b4cd49992f2b5c830fcced60c7bcc0f Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Mon, 1 Jun 2015 13:49:15 -0400 Subject: [PATCH 108/153] Make listsinceblock block hash optional. Before, calling ListSinceBlock(Async) would create an empty string from the nil block hash. --- wallet.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/wallet.go b/wallet.go index 9327ccbb..c5e378b6 100644 --- a/wallet.go +++ b/wallet.go @@ -269,12 +269,12 @@ func (r FutureListSinceBlockResult) Receive() (*btcjson.ListSinceBlockResult, er // // See ListSinceBlock for the blocking version and more details. func (c *Client) ListSinceBlockAsync(blockHash *wire.ShaHash) FutureListSinceBlockResult { - hash := "" + var hash *string if blockHash != nil { - hash = blockHash.String() + hash = btcjson.String(blockHash.String()) } - cmd := btcjson.NewListSinceBlockCmd(&hash, nil, nil) + cmd := btcjson.NewListSinceBlockCmd(hash, nil, nil) return c.sendCmd(cmd) } @@ -293,12 +293,12 @@ func (c *Client) ListSinceBlock(blockHash *wire.ShaHash) (*btcjson.ListSinceBloc // // See ListSinceBlockMinConf for the blocking version and more details. func (c *Client) ListSinceBlockMinConfAsync(blockHash *wire.ShaHash, minConfirms int) FutureListSinceBlockResult { - hash := "" + var hash *string if blockHash != nil { - hash = blockHash.String() + hash = btcjson.String(blockHash.String()) } - cmd := btcjson.NewListSinceBlockCmd(&hash, &minConfirms, nil) + cmd := btcjson.NewListSinceBlockCmd(hash, &minConfirms, nil) return c.sendCmd(cmd) } From 77baeb8d7959628bf113a9cde2e052a5be785227 Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Mon, 15 Jun 2015 11:38:25 -0400 Subject: [PATCH 109/153] Fix data race. This synchronizes access to the (*Client).disconnect channel, which may change during reconnects. Fixes #54. --- infrastructure.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index 1ae078a0..673bd8a8 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -394,6 +394,16 @@ out: log.Tracef("RPC client input handler done for %s", c.config.Host) } +// disconnectChan returns a copy of the current disconnect channel. The channel +// is read protected by the client mutex, and is safe to call while the channel +// is being reassigned during a reconnect. +func (c *Client) disconnectChan() <-chan struct{} { + c.mtx.Lock() + ch := c.disconnect + c.mtx.Unlock() + return ch +} + // 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. @@ -410,7 +420,7 @@ out: break out } - case <-c.disconnect: + case <-c.disconnectChan(): break out } } @@ -436,7 +446,7 @@ func (c *Client) sendMessage(marshalledJSON []byte) { // Don't send the message if disconnected. select { case c.sendChan <- marshalledJSON: - case <-c.disconnect: + case <-c.disconnectChan(): return } } @@ -618,9 +628,9 @@ out: // has happened. c.wsConn = wsConn c.retryCount = 0 - c.disconnect = make(chan struct{}) c.mtx.Lock() + c.disconnect = make(chan struct{}) c.disconnected = false c.mtx.Unlock() From 183107190511616423ee60526af7ca0e31c50962 Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Thu, 18 Jun 2015 12:14:42 -0400 Subject: [PATCH 110/153] Parse/notify times from block(dis)connected notifications. --- notify.go | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/notify.go b/notify.go index 2269a042..72451f74 100644 --- a/notify.go +++ b/notify.go @@ -96,13 +96,13 @@ type NotificationHandlers struct { // (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 *wire.ShaHash, height int32) + OnBlockConnected func(hash *wire.ShaHash, height int32, t time.Time) // 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 *wire.ShaHash, height int32) + OnBlockDisconnected func(hash *wire.ShaHash, height int32, t time.Time) // OnRecvTx is invoked when a transaction that receives funds to a // registered address is received into the memory pool and also @@ -194,14 +194,14 @@ func (c *Client) handleNotification(ntfn *rawNotification) { return } - blockSha, blockHeight, err := parseChainNtfnParams(ntfn.Params) + blockSha, blockHeight, blockTime, err := parseChainNtfnParams(ntfn.Params) if err != nil { log.Warnf("Received invalid block connected "+ "notification: %v", err) return } - c.ntfnHandlers.OnBlockConnected(blockSha, blockHeight) + c.ntfnHandlers.OnBlockConnected(blockSha, blockHeight, blockTime) // OnBlockDisconnected case btcjson.BlockDisconnectedNtfnMethod: @@ -211,14 +211,14 @@ func (c *Client) handleNotification(ntfn *rawNotification) { return } - blockSha, blockHeight, err := parseChainNtfnParams(ntfn.Params) + blockSha, blockHeight, blockTime, err := parseChainNtfnParams(ntfn.Params) if err != nil { log.Warnf("Received invalid block connected "+ "notification: %v", err) return } - c.ntfnHandlers.OnBlockDisconnected(blockSha, blockHeight) + c.ntfnHandlers.OnBlockDisconnected(blockSha, blockHeight, blockTime) // OnRecvTx case btcjson.RecvTxNtfnMethod: @@ -399,33 +399,43 @@ func (e wrongNumParams) Error() string { // parseChainNtfnParams parses out the block hash and height from the parameters // of blockconnected and blockdisconnected notifications. func parseChainNtfnParams(params []json.RawMessage) (*wire.ShaHash, - int32, error) { + int32, time.Time, error) { - if len(params) != 2 { - return nil, 0, wrongNumParams(len(params)) + if len(params) != 3 { + return nil, 0, time.Time{}, wrongNumParams(len(params)) } // Unmarshal first parameter as a string. var blockShaStr string err := json.Unmarshal(params[0], &blockShaStr) if err != nil { - return nil, 0, err + return nil, 0, time.Time{}, err } // Unmarshal second parameter as an integer. var blockHeight int32 err = json.Unmarshal(params[1], &blockHeight) if err != nil { - return nil, 0, err + return nil, 0, time.Time{}, err + } + + // Unmarshal third parameter as unix time. + var blockTimeUnix int64 + err = json.Unmarshal(params[2], &blockTimeUnix) + if err != nil { + return nil, 0, time.Time{}, err } // Create ShaHash from block sha string. blockSha, err := wire.NewShaHashFromStr(blockShaStr) if err != nil { - return nil, 0, err + return nil, 0, time.Time{}, err } - return blockSha, blockHeight, nil + // Create time.Time from unix time. + blockTime := time.Unix(blockTimeUnix, 0) + + return blockSha, blockHeight, blockTime, nil } // parseChainTxNtfnParams parses out the transaction and optional details about From 7cb04ae6db6d10d1716b575485b4802423746c9f Mon Sep 17 00:00:00 2001 From: "John C. Vernaleo" Date: Thu, 18 Jun 2015 16:21:03 -0400 Subject: [PATCH 111/153] Update example code. Example code used old api for OnBlockConnected and OnBlockDisconnected --- examples/btcdwebsockets/main.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/btcdwebsockets/main.go b/examples/btcdwebsockets/main.go index 360f54e5..7e4e765f 100644 --- a/examples/btcdwebsockets/main.go +++ b/examples/btcdwebsockets/main.go @@ -21,11 +21,11 @@ func main() { // for notifications. See the documentation of the btcrpcclient // NotificationHandlers type for more details about each handler. ntfnHandlers := btcrpcclient.NotificationHandlers{ - OnBlockConnected: func(hash *wire.ShaHash, height int32) { - log.Printf("Block connected: %v (%d)", hash, height) + OnBlockConnected: func(hash *wire.ShaHash, height int32, time time.Time) { + log.Printf("Block connected: %v (%d) %v", hash, height, time) }, - OnBlockDisconnected: func(hash *wire.ShaHash, height int32) { - log.Printf("Block disconnected: %v (%d)", hash, height) + OnBlockDisconnected: func(hash *wire.ShaHash, height int32, time time.Time) { + log.Printf("Block disconnected: %v (%d) %v", hash, height, time) }, } From 34db20393074ee3d7b8f7a8900f577574e2fe53d Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Sun, 23 Aug 2015 13:30:13 -0500 Subject: [PATCH 112/153] Update SearchRawTransaction calls for latest API. This commit modifies the SearchRawTransactions and SearchRawTransactionsVerbose functions to work properly with the latest searchrawtransactions API in btcd. In particular, this involves changing the return value from []*btcjson.TxRawResult to []*btcjson.SearchRawTransactionResult and adding the additional optional parameter which specifies whether or not to include information about the previous outputs. --- rawtransactions.go | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/rawtransactions.go b/rawtransactions.go index ba43efde..50b86e34 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -552,7 +552,8 @@ func (r FutureSearchRawTransactionsResult) Receive() ([]*wire.MsgTx, error) { func (c *Client) SearchRawTransactionsAsync(address btcutil.Address, skip, count int) FutureSearchRawTransactionsResult { addr := address.EncodeAddress() verbose := btcjson.Int(0) - cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count) + cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count, + nil) return c.sendCmd(cmd) } @@ -574,14 +575,14 @@ type FutureSearchRawTransactionsVerboseResult chan *response // Receive waits for the response promised by the future and returns the // found raw transactions. -func (r FutureSearchRawTransactionsVerboseResult) Receive() ([]*btcjson.TxRawResult, error) { +func (r FutureSearchRawTransactionsVerboseResult) Receive() ([]*btcjson.SearchRawTransactionsResult, error) { res, err := receiveFuture(r) if err != nil { return nil, err } // Unmarshal as an array of raw transaction results. - var result []*btcjson.TxRawResult + var result []*btcjson.SearchRawTransactionsResult err = json.Unmarshal(res, &result) if err != nil { return nil, err @@ -595,10 +596,17 @@ func (r FutureSearchRawTransactionsVerboseResult) Receive() ([]*btcjson.TxRawRes // function on the returned instance. // // See SearchRawTransactionsVerbose for the blocking version and more details. -func (c *Client) SearchRawTransactionsVerboseAsync(address btcutil.Address, skip, count int) FutureSearchRawTransactionsVerboseResult { +func (c *Client) SearchRawTransactionsVerboseAsync(address btcutil.Address, skip, + count int, includePrevOut bool) FutureSearchRawTransactionsVerboseResult { + addr := address.EncodeAddress() verbose := btcjson.Int(1) - cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count) + var prevOut *int + if includePrevOut { + prevOut = btcjson.Int(1) + } + cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count, + prevOut) return c.sendCmd(cmd) } @@ -609,6 +617,9 @@ func (c *Client) SearchRawTransactionsVerboseAsync(address btcutil.Address, skip // specifically been enabled. // // See SearchRawTransactions to retrieve a list of raw transactions instead. -func (c *Client) SearchRawTransactionsVerbose(address btcutil.Address, skip, count int) ([]*btcjson.TxRawResult, error) { - return c.SearchRawTransactionsVerboseAsync(address, skip, count).Receive() +func (c *Client) SearchRawTransactionsVerbose(address btcutil.Address, skip, + count int, includePrevOut bool) ([]*btcjson.SearchRawTransactionsResult, error) { + + return c.SearchRawTransactionsVerboseAsync(address, skip, count, + includePrevOut).Receive() } From 6f3bc8e57cd4a7d8131e511fc7530dd5b4708191 Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Tue, 15 Sep 2015 15:11:47 -0400 Subject: [PATCH 113/153] Add support for the session extension RPC. --- extensions.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ notify.go | 28 ++++++++++++++-------------- 2 files changed, 62 insertions(+), 14 deletions(-) diff --git a/extensions.go b/extensions.go index 197c2d1e..15fc29a1 100644 --- a/extensions.go +++ b/extensions.go @@ -299,3 +299,51 @@ func (c *Client) ExportWatchingWalletAsync(account string) FutureExportWatchingW func (c *Client) ExportWatchingWallet(account string) ([]byte, []byte, error) { return c.ExportWatchingWalletAsync(account).Receive() } + +// FutureSessionResult is a future promise to deliver the result of a +// SessionAsync RPC invocation (or an applicable error). +type FutureSessionResult chan *response + +// Receive waits for the response promised by the future and returns the +// session result. +func (r FutureSessionResult) Receive() (*btcjson.SessionResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarsal result as a session result object. + var session btcjson.SessionResult + err = json.Unmarshal(res, &session) + if err != nil { + return nil, err + } + + return &session, nil +} + +// SessionAsync 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 Session for the blocking version and more details. +// +// NOTE: This is a btcsuite extension. +func (c *Client) SessionAsync() FutureSessionResult { + // Not supported in HTTP POST mode. + if c.config.HTTPPostMode { + return newFutureError(ErrWebsocketsRequired) + } + + cmd := btcjson.NewSessionCmd() + return c.sendCmd(cmd) +} + +// Session returns details regarding a websocket client's current connection. +// +// This RPC requires the client to be running in websocket mode. +// +// NOTE: This is a btcsuite extension. +func (c *Client) Session() (*btcjson.SessionResult, error) { + return c.SessionAsync().Receive() +} diff --git a/notify.go b/notify.go index 72451f74..488b36f4 100644 --- a/notify.go +++ b/notify.go @@ -19,12 +19,12 @@ import ( ) 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") + // ErrWebsocketsRequired is an error to describe the condition where the + // caller is trying to use a websocket-only feature, such as requesting + // notifications or other websocket requests when the client is + // configured to run in HTTP POST mode. + ErrWebsocketsRequired = errors.New("a websocket connection is required " + + "to use this feature") ) // notificationState is used to track the current state of successfuly @@ -683,7 +683,7 @@ func (r FutureNotifyBlocksResult) Receive() error { func (c *Client) NotifyBlocksAsync() FutureNotifyBlocksResult { // Not supported in HTTP POST mode. if c.config.HTTPPostMode { - return newFutureError(ErrNotificationsNotSupported) + return newFutureError(ErrWebsocketsRequired) } // Ignore the notification if the client is not interested in @@ -731,7 +731,7 @@ func (r FutureNotifySpentResult) Receive() error { func (c *Client) notifySpentInternal(outpoints []btcjson.OutPoint) FutureNotifySpentResult { // Not supported in HTTP POST mode. if c.config.HTTPPostMode { - return newFutureError(ErrNotificationsNotSupported) + return newFutureError(ErrWebsocketsRequired) } // Ignore the notification if the client is not interested in @@ -763,7 +763,7 @@ func newOutPointFromWire(op *wire.OutPoint) btcjson.OutPoint { func (c *Client) NotifySpentAsync(outpoints []*wire.OutPoint) FutureNotifySpentResult { // Not supported in HTTP POST mode. if c.config.HTTPPostMode { - return newFutureError(ErrNotificationsNotSupported) + return newFutureError(ErrWebsocketsRequired) } // Ignore the notification if the client is not interested in @@ -819,7 +819,7 @@ func (r FutureNotifyNewTransactionsResult) Receive() error { func (c *Client) NotifyNewTransactionsAsync(verbose bool) FutureNotifyNewTransactionsResult { // Not supported in HTTP POST mode. if c.config.HTTPPostMode { - return newFutureError(ErrNotificationsNotSupported) + return newFutureError(ErrWebsocketsRequired) } // Ignore the notification if the client is not interested in @@ -868,7 +868,7 @@ func (r FutureNotifyReceivedResult) Receive() error { func (c *Client) notifyReceivedInternal(addresses []string) FutureNotifyReceivedResult { // Not supported in HTTP POST mode. if c.config.HTTPPostMode { - return newFutureError(ErrNotificationsNotSupported) + return newFutureError(ErrWebsocketsRequired) } // Ignore the notification if the client is not interested in @@ -892,7 +892,7 @@ func (c *Client) notifyReceivedInternal(addresses []string) FutureNotifyReceived func (c *Client) NotifyReceivedAsync(addresses []btcutil.Address) FutureNotifyReceivedResult { // Not supported in HTTP POST mode. if c.config.HTTPPostMode { - return newFutureError(ErrNotificationsNotSupported) + return newFutureError(ErrWebsocketsRequired) } // Ignore the notification if the client is not interested in @@ -965,7 +965,7 @@ func (c *Client) RescanAsync(startBlock *wire.ShaHash, // Not supported in HTTP POST mode. if c.config.HTTPPostMode { - return newFutureError(ErrNotificationsNotSupported) + return newFutureError(ErrWebsocketsRequired) } // Ignore the notification if the client is not interested in @@ -1042,7 +1042,7 @@ func (c *Client) RescanEndBlockAsync(startBlock *wire.ShaHash, // Not supported in HTTP POST mode. if c.config.HTTPPostMode { - return newFutureError(ErrNotificationsNotSupported) + return newFutureError(ErrWebsocketsRequired) } // Ignore the notification if the client is not interested in From d06c232f04888e8cd74df478a0e0381ed0c866b6 Mon Sep 17 00:00:00 2001 From: Mawueli Kofi Adzoe Date: Sun, 20 Sep 2015 11:42:49 +0000 Subject: [PATCH 114/153] Review and fix. Mostly typos. --- extensions.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions.go b/extensions.go index 15fc29a1..d13a136a 100644 --- a/extensions.go +++ b/extensions.go @@ -51,7 +51,7 @@ func (c *Client) DebugLevelAsync(levelSpec string) FutureDebugLevelResult { // DebugLevel dynamically sets the debug logging level to the passed level // specification. // -// The levelspec can either a debug level or of the form: +// The levelspec can be either a debug level or of the form: // =,=,... // // Additionally, the special keyword 'show' can be used to get a list of the @@ -154,14 +154,14 @@ func (r FutureGetBestBlockResult) Receive() (*wire.ShaHash, int32, error) { return nil, 0, err } - // Unmarsal result as a getbestblock result object. + // Unmarshal result as a getbestblock result object. var bestBlock btcjson.GetBestBlockResult err = json.Unmarshal(res, &bestBlock) if err != nil { return nil, 0, err } - // Convert hash string. + // Convert to hash from string. hash, err := wire.NewShaHashFromStr(bestBlock.Hash) if err != nil { return nil, 0, err @@ -312,7 +312,7 @@ func (r FutureSessionResult) Receive() (*btcjson.SessionResult, error) { return nil, err } - // Unmarsal result as a session result object. + // Unmarshal result as a session result object. var session btcjson.SessionResult err = json.Unmarshal(res, &session) if err != nil { From 29a254488756901ef19173b19a22510081c7416c Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 7 Oct 2015 12:09:58 -0500 Subject: [PATCH 115/153] Update SearchRawTransaction calls for latest API. This commit modifies the SearchRawTransactions and SearchRawTransactionsVerbose functions to work properly with the latest searchrawtransactions API in btcd. In particular, this involves adding a new parameter for the reverse option which specifies whether or not to include the results in reverse order. --- rawtransactions.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/rawtransactions.go b/rawtransactions.go index 50b86e34..5ea45e90 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -549,11 +549,11 @@ func (r FutureSearchRawTransactionsResult) Receive() ([]*wire.MsgTx, error) { // function on the returned instance. // // See SearchRawTransactions for the blocking version and more details. -func (c *Client) SearchRawTransactionsAsync(address btcutil.Address, skip, count int) FutureSearchRawTransactionsResult { +func (c *Client) SearchRawTransactionsAsync(address btcutil.Address, skip, count int, reverse bool) FutureSearchRawTransactionsResult { addr := address.EncodeAddress() verbose := btcjson.Int(0) cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count, - nil) + nil, &reverse) return c.sendCmd(cmd) } @@ -564,8 +564,8 @@ func (c *Client) SearchRawTransactionsAsync(address btcutil.Address, skip, count // // See SearchRawTransactionsVerbose to retrieve a list of data structures with // information about the transactions instead of the transactions themselves. -func (c *Client) SearchRawTransactions(address btcutil.Address, skip, count int) ([]*wire.MsgTx, error) { - return c.SearchRawTransactionsAsync(address, skip, count).Receive() +func (c *Client) SearchRawTransactions(address btcutil.Address, skip, count int, reverse bool) ([]*wire.MsgTx, error) { + return c.SearchRawTransactionsAsync(address, skip, count, reverse).Receive() } // FutureSearchRawTransactionsVerboseResult is a future promise to deliver the @@ -597,7 +597,7 @@ func (r FutureSearchRawTransactionsVerboseResult) Receive() ([]*btcjson.SearchRa // // See SearchRawTransactionsVerbose for the blocking version and more details. func (c *Client) SearchRawTransactionsVerboseAsync(address btcutil.Address, skip, - count int, includePrevOut bool) FutureSearchRawTransactionsVerboseResult { + count int, includePrevOut, reverse bool) FutureSearchRawTransactionsVerboseResult { addr := address.EncodeAddress() verbose := btcjson.Int(1) @@ -606,7 +606,7 @@ func (c *Client) SearchRawTransactionsVerboseAsync(address btcutil.Address, skip prevOut = btcjson.Int(1) } cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count, - prevOut) + prevOut, &reverse) return c.sendCmd(cmd) } @@ -618,8 +618,8 @@ func (c *Client) SearchRawTransactionsVerboseAsync(address btcutil.Address, skip // // See SearchRawTransactions to retrieve a list of raw transactions instead. func (c *Client) SearchRawTransactionsVerbose(address btcutil.Address, skip, - count int, includePrevOut bool) ([]*btcjson.SearchRawTransactionsResult, error) { + count int, includePrevOut, reverse bool) ([]*btcjson.SearchRawTransactionsResult, error) { return c.SearchRawTransactionsVerboseAsync(address, skip, count, - includePrevOut).Receive() + includePrevOut, reverse).Receive() } From c56072017d2c4c1247ed1c2ae04c461cec284779 Mon Sep 17 00:00:00 2001 From: David Hill Date: Tue, 27 Oct 2015 12:34:11 -0400 Subject: [PATCH 116/153] Add optional locktime parameter to CreateRawTransaction APIs. --- rawtransactions.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rawtransactions.go b/rawtransactions.go index 5ea45e90..aa24f61d 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -238,22 +238,22 @@ func (r FutureCreateRawTransactionResult) Receive() (*wire.MsgTx, error) { // // See CreateRawTransaction for the blocking version and more details. func (c *Client) CreateRawTransactionAsync(inputs []btcjson.TransactionInput, - amounts map[btcutil.Address]btcutil.Amount) FutureCreateRawTransactionResult { + amounts map[btcutil.Address]btcutil.Amount, lockTime *int64) FutureCreateRawTransactionResult { convertedAmts := make(map[string]float64, len(amounts)) for addr, amount := range amounts { convertedAmts[addr.String()] = amount.ToBTC() } - cmd := btcjson.NewCreateRawTransactionCmd(inputs, convertedAmts) + cmd := btcjson.NewCreateRawTransactionCmd(inputs, convertedAmts, lockTime) 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[btcutil.Address]btcutil.Amount) (*wire.MsgTx, error) { + amounts map[btcutil.Address]btcutil.Amount, lockTime *int64) (*wire.MsgTx, error) { - return c.CreateRawTransactionAsync(inputs, amounts).Receive() + return c.CreateRawTransactionAsync(inputs, amounts, lockTime).Receive() } // FutureSendRawTransactionResult is a future promise to deliver the result From 975536f20f06492724b197ddb79f13af17ce5986 Mon Sep 17 00:00:00 2001 From: danda Date: Mon, 16 Nov 2015 22:17:19 -0800 Subject: [PATCH 117/153] add filteraddrs param to searchrawtransactions --- rawtransactions.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/rawtransactions.go b/rawtransactions.go index aa24f61d..5e4fdac1 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -549,11 +549,11 @@ func (r FutureSearchRawTransactionsResult) Receive() ([]*wire.MsgTx, error) { // function on the returned instance. // // See SearchRawTransactions for the blocking version and more details. -func (c *Client) SearchRawTransactionsAsync(address btcutil.Address, skip, count int, reverse bool) FutureSearchRawTransactionsResult { +func (c *Client) SearchRawTransactionsAsync(address btcutil.Address, skip, count int, reverse bool, filterAddrs []string) FutureSearchRawTransactionsResult { addr := address.EncodeAddress() verbose := btcjson.Int(0) cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count, - nil, &reverse) + nil, &reverse, &filterAddrs) return c.sendCmd(cmd) } @@ -564,8 +564,8 @@ func (c *Client) SearchRawTransactionsAsync(address btcutil.Address, skip, count // // See SearchRawTransactionsVerbose to retrieve a list of data structures with // information about the transactions instead of the transactions themselves. -func (c *Client) SearchRawTransactions(address btcutil.Address, skip, count int, reverse bool) ([]*wire.MsgTx, error) { - return c.SearchRawTransactionsAsync(address, skip, count, reverse).Receive() +func (c *Client) SearchRawTransactions(address btcutil.Address, skip, count int, reverse bool, filterAddrs []string) ([]*wire.MsgTx, error) { + return c.SearchRawTransactionsAsync(address, skip, count, reverse, filterAddrs).Receive() } // FutureSearchRawTransactionsVerboseResult is a future promise to deliver the @@ -597,7 +597,7 @@ func (r FutureSearchRawTransactionsVerboseResult) Receive() ([]*btcjson.SearchRa // // See SearchRawTransactionsVerbose for the blocking version and more details. func (c *Client) SearchRawTransactionsVerboseAsync(address btcutil.Address, skip, - count int, includePrevOut, reverse bool) FutureSearchRawTransactionsVerboseResult { + count int, includePrevOut, reverse bool, filterAddrs *[]string) FutureSearchRawTransactionsVerboseResult { addr := address.EncodeAddress() verbose := btcjson.Int(1) @@ -606,7 +606,7 @@ func (c *Client) SearchRawTransactionsVerboseAsync(address btcutil.Address, skip prevOut = btcjson.Int(1) } cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count, - prevOut, &reverse) + prevOut, &reverse, filterAddrs) return c.sendCmd(cmd) } @@ -618,8 +618,8 @@ func (c *Client) SearchRawTransactionsVerboseAsync(address btcutil.Address, skip // // See SearchRawTransactions to retrieve a list of raw transactions instead. func (c *Client) SearchRawTransactionsVerbose(address btcutil.Address, skip, - count int, includePrevOut, reverse bool) ([]*btcjson.SearchRawTransactionsResult, error) { + count int, includePrevOut, reverse bool, filterAddrs []string) ([]*btcjson.SearchRawTransactionsResult, error) { return c.SearchRawTransactionsVerboseAsync(address, skip, count, - includePrevOut, reverse).Receive() + includePrevOut, reverse, &filterAddrs).Receive() } From b81555beeac8eda71e8150cc9d63631aaa756965 Mon Sep 17 00:00:00 2001 From: "Rune T. Aune" Date: Sat, 21 Nov 2015 14:28:18 -0600 Subject: [PATCH 118/153] Fix "Established connection" log message. Don't log "Established connection" message on new when DisableConnectOnNew is set and no connection was actually established. Do log that same message when Connect() successfully connects. --- infrastructure.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index 673bd8a8..df3d6766 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -1211,8 +1211,6 @@ func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error start = true } } - log.Infof("Established connection to RPC server %s", - config.Host) client := &Client{ config: config, @@ -1230,6 +1228,8 @@ func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error } if start { + log.Infof("Established connection to RPC server %s", + config.Host) close(connEstablished) client.start() if !client.config.HTTPPostMode && !client.config.DisableAutoReconnect { @@ -1282,6 +1282,8 @@ func (c *Client) Connect(tries int) error { // Connection was established. Set the websocket connection // member of the client and start the goroutines necessary // to run the client. + log.Infof("Established connection to RPC server %s", + c.config.Host) c.wsConn = wsConn close(c.connEstablished) c.start() From 911a735e1fbe11f3d92841c1bc8b8fc87315d124 Mon Sep 17 00:00:00 2001 From: Mawuli Adzoe Date: Sat, 2 Jan 2016 14:06:55 -0700 Subject: [PATCH 119/153] Clarify documentation. Fix typo. --- infrastructure.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure.go b/infrastructure.go index df3d6766..85c9a83b 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -1182,7 +1182,7 @@ func dial(config *ConnConfig) (*websocket.Conn, error) { // New creates 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 +// interested in receiving notifications and will be ignored 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 From 73a9dd628d72e56ba4f514494bc9346263fa52fd Mon Sep 17 00:00:00 2001 From: Mawuli Adzoe Date: Sat, 2 Jan 2016 14:08:08 -0700 Subject: [PATCH 120/153] Bump up copyright date to reflect recent fix time. --- infrastructure.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure.go b/infrastructure.go index 85c9a83b..5edf5a3a 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 The btcsuite developers +// Copyright (c) 2014-2016 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. From e6e3b66040e809966ce190411bdd6e121c6e38f9 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 10 Mar 2016 22:21:59 -0600 Subject: [PATCH 121/153] Improve error log avoidance on client disconnect. The existing check to only log errors from websocket connections when the error is not because the connection is being shutdown only detects the single case where the remote client was shutdown. This commit modifies that logic to include when the connection is being forcibly shutdown via closing the quit channel and when it has been shutdown from a client side close. --- infrastructure.go | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/infrastructure.go b/infrastructure.go index 5edf5a3a..098edcc5 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -13,6 +13,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "io/ioutil" "net" "net/http" @@ -362,6 +363,28 @@ func (c *Client) handleMessage(msg []byte) { request.responseChan <- &response{result: result, err: err} } +// shouldLogReadError returns whether or not the passed error, which is expected +// to have come from reading from the websocket connection in wsInHandler, +// should be logged. +func (c *Client) shouldLogReadError(err error) bool { + // No logging when the connetion is being forcibly disconnected. + select { + case <-c.shutdown: + return false + default: + } + + // No logging when the connection has been disconnected. + if err == io.EOF { + return false + } + if opErr, ok := err.(*net.OpError); ok && !opErr.Temporary() { + return false + } + + return true +} + // wsInHandler handles all incoming messages for the websocket connection // associated with the client. It must be run as a goroutine. func (c *Client) wsInHandler() { @@ -379,7 +402,7 @@ out: _, msg, err := c.wsConn.ReadMessage() if err != nil { // Log the error if it's not due to disconnecting. - if _, ok := err.(*net.OpError); !ok { + if c.shouldLogReadError(err) { log.Errorf("Websocket receive error from "+ "%s: %v", c.config.Host, err) } From 64922553b5c21f333438edaed6e0ba2b5a7760ee Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 7 Apr 2016 13:26:15 -0500 Subject: [PATCH 122/153] TravisCI: Update to latest configurations. (#76) This updates the Go versions using when running the TravisCI integration tests to reflect the latest supported Go versions. Also, the vet tool moved into the Go source tree as of Go 1.5. Its previous location in the x/tools repo was deprecated at that time and has now been removed. Finally, old tool path is no longer needed, so it has been removed. --- .travis.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index e23b4030..8c24901c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,13 @@ language: go go: - - 1.3.3 - - 1.4.1 + - 1.5.3 + - 1.6 sudo: false before_install: - gotools=golang.org/x/tools - - if [ "$TRAVIS_GO_VERSION" = "release" ]; then gotools=code.google.com/p/go.tools; fi install: - go get -d -t -v ./... - go get -v $gotools/cmd/cover - - go get -v $gotools/cmd/vet - go get -v github.com/bradfitz/goimports - go get -v github.com/golang/lint/golint script: From 5f1911342274c2e2dc25d10f249f890f47a31ac3 Mon Sep 17 00:00:00 2001 From: Calvin McAnarney Date: Mon, 8 Aug 2016 05:47:07 +0200 Subject: [PATCH 123/153] Allow RPC identifiers in E notation (#80) --- infrastructure.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index 098edcc5..985441c9 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -15,6 +15,7 @@ import ( "fmt" "io" "io/ioutil" + "math" "net" "net/http" "net/url" @@ -266,7 +267,7 @@ type ( // the embedded ID (from the response) is nil. Otherwise, it is a // response. inMessage struct { - ID *uint64 `json:"id"` + ID *float64 `json:"id"` *rawNotification *rawResponse } @@ -337,12 +338,18 @@ func (c *Client) handleMessage(msg []byte) { return } + // ensure that in.ID can be converted to an integer without loss of precision + if *in.ID < 0 || *in.ID != math.Trunc(*in.ID) { + log.Warn("Malformed response: invalid identifier") + return + } + if in.rawResponse == nil { log.Warn("Malformed response: missing result and error") return } - id := *in.ID + id := uint64(*in.ID) log.Tracef("Received response for id %d (result %s)", id, in.Result) request := c.removeRequest(id) From c39e35318d5dc8d7e5e4171a34a17bc03dce06b4 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Mon, 8 Aug 2016 14:16:04 -0500 Subject: [PATCH 124/153] Update for recent chainhash-related API changes. (#81) This updates all code to make use of the new chainhash package since the old wire.ShaHash type and related functions have been removed in favor of the abstracted package. Also, while here, rename all variables that included sha in their name to include hash instead. --- chain.go | 35 ++++++++------- examples/btcdwebsockets/main.go | 6 +-- extensions.go | 7 +-- mining.go | 15 ++++--- notify.go | 79 +++++++++++++++++---------------- rawtransactions.go | 17 +++---- wallet.go | 45 ++++++++++--------- 7 files changed, 105 insertions(+), 99 deletions(-) diff --git a/chain.go b/chain.go index 184b71cc..9d6144e3 100644 --- a/chain.go +++ b/chain.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 The btcsuite developers +// Copyright (c) 2014-2016 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -10,6 +10,7 @@ import ( "encoding/json" "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) @@ -20,7 +21,7 @@ type FutureGetBestBlockHashResult chan *response // 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() (*wire.ShaHash, error) { +func (r FutureGetBestBlockHashResult) Receive() (*chainhash.Hash, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -32,7 +33,7 @@ func (r FutureGetBestBlockHashResult) Receive() (*wire.ShaHash, error) { if err != nil { return nil, err } - return wire.NewShaHashFromStr(txHashStr) + return chainhash.NewHashFromStr(txHashStr) } // GetBestBlockHashAsync returns an instance of a type that can be used to get @@ -47,7 +48,7 @@ func (c *Client) GetBestBlockHashAsync() FutureGetBestBlockHashResult { // GetBestBlockHash returns the hash of the best block in the longest block // chain. -func (c *Client) GetBestBlockHash() (*wire.ShaHash, error) { +func (c *Client) GetBestBlockHash() (*chainhash.Hash, error) { return c.GetBestBlockHashAsync().Receive() } @@ -90,7 +91,7 @@ func (r FutureGetBlockResult) Receive() (*btcutil.Block, error) { // returned instance. // // See GetBlock for the blocking version and more details. -func (c *Client) GetBlockAsync(blockHash *wire.ShaHash) FutureGetBlockResult { +func (c *Client) GetBlockAsync(blockHash *chainhash.Hash) FutureGetBlockResult { hash := "" if blockHash != nil { hash = blockHash.String() @@ -104,7 +105,7 @@ func (c *Client) GetBlockAsync(blockHash *wire.ShaHash) FutureGetBlockResult { // // See GetBlockVerbose to retrieve a data structure with information about the // block instead. -func (c *Client) GetBlock(blockHash *wire.ShaHash) (*btcutil.Block, error) { +func (c *Client) GetBlock(blockHash *chainhash.Hash) (*btcutil.Block, error) { return c.GetBlockAsync(blockHash).Receive() } @@ -134,7 +135,7 @@ func (r FutureGetBlockVerboseResult) Receive() (*btcjson.GetBlockVerboseResult, // the returned instance. // // See GetBlockVerbose for the blocking version and more details. -func (c *Client) GetBlockVerboseAsync(blockHash *wire.ShaHash, verboseTx bool) FutureGetBlockVerboseResult { +func (c *Client) GetBlockVerboseAsync(blockHash *chainhash.Hash, verboseTx bool) FutureGetBlockVerboseResult { hash := "" if blockHash != nil { hash = blockHash.String() @@ -148,7 +149,7 @@ func (c *Client) GetBlockVerboseAsync(blockHash *wire.ShaHash, verboseTx bool) F // about a block given its hash. // // See GetBlock to retrieve a raw block instead. -func (c *Client) GetBlockVerbose(blockHash *wire.ShaHash, verboseTx bool) (*btcjson.GetBlockVerboseResult, error) { +func (c *Client) GetBlockVerbose(blockHash *chainhash.Hash, verboseTx bool) (*btcjson.GetBlockVerboseResult, error) { return c.GetBlockVerboseAsync(blockHash, verboseTx).Receive() } @@ -231,7 +232,7 @@ type FutureGetBlockHashResult chan *response // 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() (*wire.ShaHash, error) { +func (r FutureGetBlockHashResult) Receive() (*chainhash.Hash, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -243,7 +244,7 @@ func (r FutureGetBlockHashResult) Receive() (*wire.ShaHash, error) { if err != nil { return nil, err } - return wire.NewShaHashFromStr(txHashStr) + return chainhash.NewHashFromStr(txHashStr) } // GetBlockHashAsync returns an instance of a type that can be used to get the @@ -258,7 +259,7 @@ func (c *Client) GetBlockHashAsync(blockHeight int64) FutureGetBlockHashResult { // GetBlockHash returns the hash of the block in the best block chain at the // given height. -func (c *Client) GetBlockHash(blockHeight int64) (*wire.ShaHash, error) { +func (c *Client) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) { return c.GetBlockHashAsync(blockHeight).Receive() } @@ -268,7 +269,7 @@ type FutureGetRawMempoolResult chan *response // Receive waits for the response promised by the future and returns the hashes // of all transactions in the memory pool. -func (r FutureGetRawMempoolResult) Receive() ([]*wire.ShaHash, error) { +func (r FutureGetRawMempoolResult) Receive() ([]*chainhash.Hash, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -282,9 +283,9 @@ func (r FutureGetRawMempoolResult) Receive() ([]*wire.ShaHash, error) { } // Create a slice of ShaHash arrays from the string slice. - txHashes := make([]*wire.ShaHash, 0, len(txHashStrs)) + txHashes := make([]*chainhash.Hash, 0, len(txHashStrs)) for _, hashStr := range txHashStrs { - txHash, err := wire.NewShaHashFromStr(hashStr) + txHash, err := chainhash.NewHashFromStr(hashStr) if err != nil { return nil, err } @@ -308,7 +309,7 @@ func (c *Client) GetRawMempoolAsync() FutureGetRawMempoolResult { // // See GetRawMempoolVerbose to retrieve data structures with information about // the transactions instead. -func (c *Client) GetRawMempool() ([]*wire.ShaHash, error) { +func (c *Client) GetRawMempool() ([]*chainhash.Hash, error) { return c.GetRawMempoolAsync().Receive() } @@ -476,7 +477,7 @@ func (r FutureGetTxOutResult) Receive() (*btcjson.GetTxOutResult, error) { // the returned instance. // // See GetTxOut for the blocking version and more details. -func (c *Client) GetTxOutAsync(txHash *wire.ShaHash, index uint32, mempool bool) FutureGetTxOutResult { +func (c *Client) GetTxOutAsync(txHash *chainhash.Hash, index uint32, mempool bool) FutureGetTxOutResult { hash := "" if txHash != nil { hash = txHash.String() @@ -488,6 +489,6 @@ func (c *Client) GetTxOutAsync(txHash *wire.ShaHash, index uint32, mempool bool) // GetTxOut returns the transaction output info if it's unspent and // nil, otherwise. -func (c *Client) GetTxOut(txHash *wire.ShaHash, index uint32, mempool bool) (*btcjson.GetTxOutResult, error) { +func (c *Client) GetTxOut(txHash *chainhash.Hash, index uint32, mempool bool) (*btcjson.GetTxOutResult, error) { return c.GetTxOutAsync(txHash, index, mempool).Receive() } diff --git a/examples/btcdwebsockets/main.go b/examples/btcdwebsockets/main.go index 7e4e765f..93809953 100644 --- a/examples/btcdwebsockets/main.go +++ b/examples/btcdwebsockets/main.go @@ -10,7 +10,7 @@ import ( "path/filepath" "time" - "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcrpcclient" "github.com/btcsuite/btcutil" ) @@ -21,10 +21,10 @@ func main() { // for notifications. See the documentation of the btcrpcclient // NotificationHandlers type for more details about each handler. ntfnHandlers := btcrpcclient.NotificationHandlers{ - OnBlockConnected: func(hash *wire.ShaHash, height int32, time time.Time) { + OnBlockConnected: func(hash *chainhash.Hash, height int32, time time.Time) { log.Printf("Block connected: %v (%d) %v", hash, height, time) }, - OnBlockDisconnected: func(hash *wire.ShaHash, height int32, time time.Time) { + OnBlockDisconnected: func(hash *chainhash.Hash, height int32, time time.Time) { log.Printf("Block disconnected: %v (%d) %v", hash, height, time) }, } diff --git a/extensions.go b/extensions.go index d13a136a..05e8e70a 100644 --- a/extensions.go +++ b/extensions.go @@ -10,6 +10,7 @@ import ( "fmt" "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) @@ -148,7 +149,7 @@ type FutureGetBestBlockResult chan *response // 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() (*wire.ShaHash, int32, error) { +func (r FutureGetBestBlockResult) Receive() (*chainhash.Hash, int32, error) { res, err := receiveFuture(r) if err != nil { return nil, 0, err @@ -162,7 +163,7 @@ func (r FutureGetBestBlockResult) Receive() (*wire.ShaHash, int32, error) { } // Convert to hash from string. - hash, err := wire.NewShaHashFromStr(bestBlock.Hash) + hash, err := chainhash.NewHashFromStr(bestBlock.Hash) if err != nil { return nil, 0, err } @@ -186,7 +187,7 @@ func (c *Client) GetBestBlockAsync() FutureGetBestBlockResult { // chain. // // NOTE: This is a btcd extension. -func (c *Client) GetBestBlock() (*wire.ShaHash, int32, error) { +func (c *Client) GetBestBlock() (*chainhash.Hash, int32, error) { return c.GetBestBlockAsync().Receive() } diff --git a/mining.go b/mining.go index 0e9f415d..4ac94f14 100644 --- a/mining.go +++ b/mining.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 The btcsuite developers +// Copyright (c) 2014-2016 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -10,7 +10,7 @@ import ( "errors" "github.com/btcsuite/btcd/btcjson" - "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcutil" ) @@ -20,7 +20,7 @@ type FutureGenerateResult chan *response // Receive waits for the response promised by the future and returns a list of // block hashes generated by the call. -func (r FutureGenerateResult) Receive() ([]*wire.ShaHash, error) { +func (r FutureGenerateResult) Receive() ([]*chainhash.Hash, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -33,10 +33,11 @@ func (r FutureGenerateResult) Receive() ([]*wire.ShaHash, error) { return nil, err } - // Convert each block hash to a wire.ShaHash and store a pointer to each. - convertedResult := make([]*wire.ShaHash, len(result)) + // Convert each block hash to a chainhash.Hash and store a pointer to + // each. + convertedResult := make([]*chainhash.Hash, len(result)) for i, hashString := range result { - convertedResult[i], err = wire.NewShaHashFromStr(hashString) + convertedResult[i], err = chainhash.NewHashFromStr(hashString) if err != nil { return nil, err } @@ -56,7 +57,7 @@ func (c *Client) GenerateAsync(numBlocks uint32) FutureGenerateResult { } // Generate generates numBlocks blocks and returns their hashes. -func (c *Client) Generate(numBlocks uint32) ([]*wire.ShaHash, error) { +func (c *Client) Generate(numBlocks uint32) ([]*chainhash.Hash, error) { return c.GenerateAsync(numBlocks).Receive() } diff --git a/notify.go b/notify.go index 488b36f4..908ec9d1 100644 --- a/notify.go +++ b/notify.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 The btcsuite developers +// Copyright (c) 2014-2016 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -14,6 +14,7 @@ import ( "time" "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) @@ -96,13 +97,13 @@ type NotificationHandlers struct { // (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 *wire.ShaHash, height int32, t time.Time) + OnBlockConnected func(hash *chainhash.Hash, height int32, t time.Time) // 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 *wire.ShaHash, height int32, t time.Time) + OnBlockDisconnected func(hash *chainhash.Hash, height int32, t time.Time) // OnRecvTx is invoked when a transaction that receives funds to a // registered address is received into the memory pool and also @@ -128,18 +129,18 @@ type NotificationHandlers struct { // signaled on this notification, rather than relying on the return // result of a rescan request, due to how btcd may send various rescan // notifications after the rescan request has already returned. - OnRescanFinished func(hash *wire.ShaHash, height int32, blkTime time.Time) + OnRescanFinished func(hash *chainhash.Hash, height int32, blkTime time.Time) // 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(hash *wire.ShaHash, height int32, blkTime time.Time) + OnRescanProgress func(hash *chainhash.Hash, height int32, blkTime time.Time) // 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 *wire.ShaHash, amount btcutil.Amount) + OnTxAccepted func(hash *chainhash.Hash, 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 @@ -194,14 +195,14 @@ func (c *Client) handleNotification(ntfn *rawNotification) { return } - blockSha, blockHeight, blockTime, err := parseChainNtfnParams(ntfn.Params) + blockHash, blockHeight, blockTime, err := parseChainNtfnParams(ntfn.Params) if err != nil { log.Warnf("Received invalid block connected "+ "notification: %v", err) return } - c.ntfnHandlers.OnBlockConnected(blockSha, blockHeight, blockTime) + c.ntfnHandlers.OnBlockConnected(blockHash, blockHeight, blockTime) // OnBlockDisconnected case btcjson.BlockDisconnectedNtfnMethod: @@ -211,14 +212,14 @@ func (c *Client) handleNotification(ntfn *rawNotification) { return } - blockSha, blockHeight, blockTime, err := parseChainNtfnParams(ntfn.Params) + blockHash, blockHeight, blockTime, err := parseChainNtfnParams(ntfn.Params) if err != nil { log.Warnf("Received invalid block connected "+ "notification: %v", err) return } - c.ntfnHandlers.OnBlockDisconnected(blockSha, blockHeight, blockTime) + c.ntfnHandlers.OnBlockDisconnected(blockHash, blockHeight, blockTime) // OnRecvTx case btcjson.RecvTxNtfnMethod: @@ -398,7 +399,7 @@ func (e wrongNumParams) Error() string { // parseChainNtfnParams parses out the block hash and height from the parameters // of blockconnected and blockdisconnected notifications. -func parseChainNtfnParams(params []json.RawMessage) (*wire.ShaHash, +func parseChainNtfnParams(params []json.RawMessage) (*chainhash.Hash, int32, time.Time, error) { if len(params) != 3 { @@ -406,8 +407,8 @@ func parseChainNtfnParams(params []json.RawMessage) (*wire.ShaHash, } // Unmarshal first parameter as a string. - var blockShaStr string - err := json.Unmarshal(params[0], &blockShaStr) + var blockHashStr string + err := json.Unmarshal(params[0], &blockHashStr) if err != nil { return nil, 0, time.Time{}, err } @@ -426,8 +427,8 @@ func parseChainNtfnParams(params []json.RawMessage) (*wire.ShaHash, return nil, 0, time.Time{}, err } - // Create ShaHash from block sha string. - blockSha, err := wire.NewShaHashFromStr(blockShaStr) + // Create hash from block hash string. + blockHash, err := chainhash.NewHashFromStr(blockHashStr) if err != nil { return nil, 0, time.Time{}, err } @@ -435,7 +436,7 @@ func parseChainNtfnParams(params []json.RawMessage) (*wire.ShaHash, // Create time.Time from unix time. blockTime := time.Unix(blockTimeUnix, 0) - return blockSha, blockHeight, blockTime, nil + return blockHash, blockHeight, blockTime, nil } // parseChainTxNtfnParams parses out the transaction and optional details about @@ -477,14 +478,14 @@ func parseChainTxNtfnParams(params []json.RawMessage) (*btcutil.Tx, } // TODO: Change recvtx and redeemingtx callback signatures to use - // nicer types for details about the block (block sha as a - // wire.ShaHash, block time as a time.Time, etc.). + // nicer types for details about the block (block hash as a + // chainhash.Hash, block time as a time.Time, etc.). return btcutil.NewTx(&msgTx), block, nil } // parseRescanProgressParams parses out the height of the last rescanned block // from the parameters of rescanfinished and rescanprogress notifications. -func parseRescanProgressParams(params []json.RawMessage) (*wire.ShaHash, int32, time.Time, error) { +func parseRescanProgressParams(params []json.RawMessage) (*chainhash.Hash, int32, time.Time, error) { if len(params) != 3 { return nil, 0, time.Time{}, wrongNumParams(len(params)) } @@ -511,7 +512,7 @@ func parseRescanProgressParams(params []json.RawMessage) (*wire.ShaHash, int32, } // Decode string encoding of block hash. - hash, err := wire.NewShaHashFromStr(hashStr) + hash, err := chainhash.NewHashFromStr(hashStr) if err != nil { return nil, 0, time.Time{}, err } @@ -521,7 +522,7 @@ func parseRescanProgressParams(params []json.RawMessage) (*wire.ShaHash, int32, // parseTxAcceptedNtfnParams parses out the transaction hash and total amount // from the parameters of a txaccepted notification. -func parseTxAcceptedNtfnParams(params []json.RawMessage) (*wire.ShaHash, +func parseTxAcceptedNtfnParams(params []json.RawMessage) (*chainhash.Hash, btcutil.Amount, error) { if len(params) != 2 { @@ -529,8 +530,8 @@ func parseTxAcceptedNtfnParams(params []json.RawMessage) (*wire.ShaHash, } // Unmarshal first parameter as a string. - var txShaStr string - err := json.Unmarshal(params[0], &txShaStr) + var txHashStr string + err := json.Unmarshal(params[0], &txHashStr) if err != nil { return nil, 0, err } @@ -549,12 +550,12 @@ func parseTxAcceptedNtfnParams(params []json.RawMessage) (*wire.ShaHash, } // Decode string encoding of transaction sha. - txSha, err := wire.NewShaHashFromStr(txShaStr) + txHash, err := chainhash.NewHashFromStr(txHashStr) if err != nil { return nil, 0, err } - return txSha, btcutil.Amount(amt), nil + return txHash, btcutil.Amount(amt), nil } // parseTxAcceptedVerboseNtfnParams parses out details about a raw transaction @@ -959,7 +960,7 @@ func (r FutureRescanResult) Receive() error { // reconnect. // // NOTE: This is a btcd extension and requires a websocket connection. -func (c *Client) RescanAsync(startBlock *wire.ShaHash, +func (c *Client) RescanAsync(startBlock *chainhash.Hash, addresses []btcutil.Address, outpoints []*wire.OutPoint) FutureRescanResult { @@ -975,9 +976,9 @@ func (c *Client) RescanAsync(startBlock *wire.ShaHash, } // Convert block hashes to strings. - var startBlockShaStr string + var startBlockHashStr string if startBlock != nil { - startBlockShaStr = startBlock.String() + startBlockHashStr = startBlock.String() } // Convert addresses to strings. @@ -992,7 +993,7 @@ func (c *Client) RescanAsync(startBlock *wire.ShaHash, ops = append(ops, newOutPointFromWire(op)) } - cmd := btcjson.NewRescanCmd(startBlockShaStr, addrs, ops, nil) + cmd := btcjson.NewRescanCmd(startBlockHashStr, addrs, ops, nil) return c.sendCmd(cmd) } @@ -1022,7 +1023,7 @@ func (c *Client) RescanAsync(startBlock *wire.ShaHash, // reconnect. // // NOTE: This is a btcd extension and requires a websocket connection. -func (c *Client) Rescan(startBlock *wire.ShaHash, +func (c *Client) Rescan(startBlock *chainhash.Hash, addresses []btcutil.Address, outpoints []*wire.OutPoint) error { @@ -1036,9 +1037,9 @@ func (c *Client) Rescan(startBlock *wire.ShaHash, // See RescanEndBlock for the blocking version and more details. // // NOTE: This is a btcd extension and requires a websocket connection. -func (c *Client) RescanEndBlockAsync(startBlock *wire.ShaHash, +func (c *Client) RescanEndBlockAsync(startBlock *chainhash.Hash, addresses []btcutil.Address, outpoints []*wire.OutPoint, - endBlock *wire.ShaHash) FutureRescanResult { + endBlock *chainhash.Hash) FutureRescanResult { // Not supported in HTTP POST mode. if c.config.HTTPPostMode { @@ -1052,12 +1053,12 @@ func (c *Client) RescanEndBlockAsync(startBlock *wire.ShaHash, } // Convert block hashes to strings. - var startBlockShaStr, endBlockShaStr string + var startBlockHashStr, endBlockHashStr string if startBlock != nil { - startBlockShaStr = startBlock.String() + startBlockHashStr = startBlock.String() } if endBlock != nil { - endBlockShaStr = endBlock.String() + endBlockHashStr = endBlock.String() } // Convert addresses to strings. @@ -1072,8 +1073,8 @@ func (c *Client) RescanEndBlockAsync(startBlock *wire.ShaHash, ops = append(ops, newOutPointFromWire(op)) } - cmd := btcjson.NewRescanCmd(startBlockShaStr, addrs, ops, - &endBlockShaStr) + cmd := btcjson.NewRescanCmd(startBlockHashStr, addrs, ops, + &endBlockHashStr) return c.sendCmd(cmd) } @@ -1096,9 +1097,9 @@ func (c *Client) RescanEndBlockAsync(startBlock *wire.ShaHash, // 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(startBlock *wire.ShaHash, +func (c *Client) RescanEndHeight(startBlock *chainhash.Hash, addresses []btcutil.Address, outpoints []*wire.OutPoint, - endBlock *wire.ShaHash) error { + endBlock *chainhash.Hash) error { return c.RescanEndBlockAsync(startBlock, addresses, outpoints, endBlock).Receive() diff --git a/rawtransactions.go b/rawtransactions.go index 5e4fdac1..0c934554 100644 --- a/rawtransactions.go +++ b/rawtransactions.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 The btcsuite developers +// Copyright (c) 2014-2016 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -10,6 +10,7 @@ import ( "encoding/json" "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) @@ -95,7 +96,7 @@ func (r FutureGetRawTransactionResult) Receive() (*btcutil.Tx, error) { // the returned instance. // // See GetRawTransaction for the blocking version and more details. -func (c *Client) GetRawTransactionAsync(txHash *wire.ShaHash) FutureGetRawTransactionResult { +func (c *Client) GetRawTransactionAsync(txHash *chainhash.Hash) FutureGetRawTransactionResult { hash := "" if txHash != nil { hash = txHash.String() @@ -109,7 +110,7 @@ func (c *Client) GetRawTransactionAsync(txHash *wire.ShaHash) FutureGetRawTransa // // See GetRawTransactionVerbose to obtain additional information about the // transaction. -func (c *Client) GetRawTransaction(txHash *wire.ShaHash) (*btcutil.Tx, error) { +func (c *Client) GetRawTransaction(txHash *chainhash.Hash) (*btcutil.Tx, error) { return c.GetRawTransactionAsync(txHash).Receive() } @@ -141,7 +142,7 @@ func (r FutureGetRawTransactionVerboseResult) Receive() (*btcjson.TxRawResult, e // function on the returned instance. // // See GetRawTransactionVerbose for the blocking version and more details. -func (c *Client) GetRawTransactionVerboseAsync(txHash *wire.ShaHash) FutureGetRawTransactionVerboseResult { +func (c *Client) GetRawTransactionVerboseAsync(txHash *chainhash.Hash) FutureGetRawTransactionVerboseResult { hash := "" if txHash != nil { hash = txHash.String() @@ -155,7 +156,7 @@ func (c *Client) GetRawTransactionVerboseAsync(txHash *wire.ShaHash) FutureGetRa // its hash. // // See GetRawTransaction to obtain only the transaction already deserialized. -func (c *Client) GetRawTransactionVerbose(txHash *wire.ShaHash) (*btcjson.TxRawResult, error) { +func (c *Client) GetRawTransactionVerbose(txHash *chainhash.Hash) (*btcjson.TxRawResult, error) { return c.GetRawTransactionVerboseAsync(txHash).Receive() } @@ -263,7 +264,7 @@ type FutureSendRawTransactionResult chan *response // 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() (*wire.ShaHash, error) { +func (r FutureSendRawTransactionResult) Receive() (*chainhash.Hash, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -276,7 +277,7 @@ func (r FutureSendRawTransactionResult) Receive() (*wire.ShaHash, error) { return nil, err } - return wire.NewShaHashFromStr(txHashStr) + return chainhash.NewHashFromStr(txHashStr) } // SendRawTransactionAsync returns an instance of a type that can be used to get @@ -301,7 +302,7 @@ func (c *Client) SendRawTransactionAsync(tx *wire.MsgTx, allowHighFees bool) Fut // SendRawTransaction submits the encoded transaction to the server which will // then relay it to the network. -func (c *Client) SendRawTransaction(tx *wire.MsgTx, allowHighFees bool) (*wire.ShaHash, error) { +func (c *Client) SendRawTransaction(tx *wire.MsgTx, allowHighFees bool) (*chainhash.Hash, error) { return c.SendRawTransactionAsync(tx, allowHighFees).Receive() } diff --git a/wallet.go b/wallet.go index c5e378b6..0eb58061 100644 --- a/wallet.go +++ b/wallet.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 The btcsuite developers +// Copyright (c) 2014-2016 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) @@ -45,7 +46,7 @@ func (r FutureGetTransactionResult) Receive() (*btcjson.GetTransactionResult, er // the returned instance. // // See GetTransaction for the blocking version and more details. -func (c *Client) GetTransactionAsync(txHash *wire.ShaHash) FutureGetTransactionResult { +func (c *Client) GetTransactionAsync(txHash *chainhash.Hash) FutureGetTransactionResult { hash := "" if txHash != nil { hash = txHash.String() @@ -58,7 +59,7 @@ func (c *Client) GetTransactionAsync(txHash *wire.ShaHash) FutureGetTransactionR // GetTransaction returns detailed information about a wallet transaction. // // See GetRawTransaction to return the raw transaction instead. -func (c *Client) GetTransaction(txHash *wire.ShaHash) (*btcjson.GetTransactionResult, error) { +func (c *Client) GetTransaction(txHash *chainhash.Hash) (*btcjson.GetTransactionResult, error) { return c.GetTransactionAsync(txHash).Receive() } @@ -268,7 +269,7 @@ func (r FutureListSinceBlockResult) Receive() (*btcjson.ListSinceBlockResult, er // the returned instance. // // See ListSinceBlock for the blocking version and more details. -func (c *Client) ListSinceBlockAsync(blockHash *wire.ShaHash) FutureListSinceBlockResult { +func (c *Client) ListSinceBlockAsync(blockHash *chainhash.Hash) FutureListSinceBlockResult { var hash *string if blockHash != nil { hash = btcjson.String(blockHash.String()) @@ -283,7 +284,7 @@ func (c *Client) ListSinceBlockAsync(blockHash *wire.ShaHash) FutureListSinceBlo // minimum confirmations as a filter. // // See ListSinceBlockMinConf to override the minimum number of confirmations. -func (c *Client) ListSinceBlock(blockHash *wire.ShaHash) (*btcjson.ListSinceBlockResult, error) { +func (c *Client) ListSinceBlock(blockHash *chainhash.Hash) (*btcjson.ListSinceBlockResult, error) { return c.ListSinceBlockAsync(blockHash).Receive() } @@ -292,7 +293,7 @@ func (c *Client) ListSinceBlock(blockHash *wire.ShaHash) (*btcjson.ListSinceBloc // function on the returned instance. // // See ListSinceBlockMinConf for the blocking version and more details. -func (c *Client) ListSinceBlockMinConfAsync(blockHash *wire.ShaHash, minConfirms int) FutureListSinceBlockResult { +func (c *Client) ListSinceBlockMinConfAsync(blockHash *chainhash.Hash, minConfirms int) FutureListSinceBlockResult { var hash *string if blockHash != nil { hash = btcjson.String(blockHash.String()) @@ -307,7 +308,7 @@ func (c *Client) ListSinceBlockMinConfAsync(blockHash *wire.ShaHash, minConfirms // number of minimum confirmations as a filter. // // See ListSinceBlock to use the default minimum number of confirmations. -func (c *Client) ListSinceBlockMinConf(blockHash *wire.ShaHash, minConfirms int) (*btcjson.ListSinceBlockResult, error) { +func (c *Client) ListSinceBlockMinConf(blockHash *chainhash.Hash, minConfirms int) (*btcjson.ListSinceBlockResult, error) { return c.ListSinceBlockMinConfAsync(blockHash, minConfirms).Receive() } @@ -386,7 +387,7 @@ func (r FutureListLockUnspentResult) Receive() ([]*wire.OutPoint, error) { // Create a slice of outpoints from the transaction input structs. ops := make([]*wire.OutPoint, len(inputs)) for i, input := range inputs { - sha, err := wire.NewShaHashFromStr(input.Txid) + sha, err := chainhash.NewHashFromStr(input.Txid) if err != nil { return nil, err } @@ -451,7 +452,7 @@ type FutureSendToAddressResult chan *response // 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() (*wire.ShaHash, error) { +func (r FutureSendToAddressResult) Receive() (*chainhash.Hash, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -464,7 +465,7 @@ func (r FutureSendToAddressResult) Receive() (*wire.ShaHash, error) { return nil, err } - return wire.NewShaHashFromStr(txHash) + return chainhash.NewHashFromStr(txHash) } // SendToAddressAsync returns an instance of a type that can be used to get the @@ -486,7 +487,7 @@ func (c *Client) SendToAddressAsync(address btcutil.Address, amount btcutil.Amou // // 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) (*wire.ShaHash, error) { +func (c *Client) SendToAddress(address btcutil.Address, amount btcutil.Amount) (*chainhash.Hash, error) { return c.SendToAddressAsync(address, amount).Receive() } @@ -517,7 +518,7 @@ func (c *Client) SendToAddressCommentAsync(address btcutil.Address, // // 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) (*wire.ShaHash, error) { +func (c *Client) SendToAddressComment(address btcutil.Address, amount btcutil.Amount, comment, commentTo string) (*chainhash.Hash, error) { return c.SendToAddressCommentAsync(address, amount, comment, commentTo).Receive() } @@ -530,7 +531,7 @@ type FutureSendFromResult chan *response // 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() (*wire.ShaHash, error) { +func (r FutureSendFromResult) Receive() (*chainhash.Hash, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -543,7 +544,7 @@ func (r FutureSendFromResult) Receive() (*wire.ShaHash, error) { return nil, err } - return wire.NewShaHashFromStr(txHash) + return chainhash.NewHashFromStr(txHash) } // SendFromAsync returns an instance of a type that can be used to get the @@ -566,7 +567,7 @@ func (c *Client) SendFromAsync(fromAccount string, toAddress btcutil.Address, am // // 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) (*wire.ShaHash, error) { +func (c *Client) SendFrom(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount) (*chainhash.Hash, error) { return c.SendFromAsync(fromAccount, toAddress, amount).Receive() } @@ -591,7 +592,7 @@ func (c *Client) SendFromMinConfAsync(fromAccount string, toAddress btcutil.Addr // // 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) (*wire.ShaHash, error) { +func (c *Client) SendFromMinConf(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount, minConfirms int) (*chainhash.Hash, error) { return c.SendFromMinConfAsync(fromAccount, toAddress, amount, minConfirms).Receive() } @@ -624,7 +625,7 @@ func (c *Client) SendFromCommentAsync(fromAccount string, // WalletPassphrase function for more details. func (c *Client) SendFromComment(fromAccount string, toAddress btcutil.Address, amount btcutil.Amount, minConfirms int, - comment, commentTo string) (*wire.ShaHash, error) { + comment, commentTo string) (*chainhash.Hash, error) { return c.SendFromCommentAsync(fromAccount, toAddress, amount, minConfirms, comment, commentTo).Receive() @@ -638,7 +639,7 @@ type FutureSendManyResult chan *response // 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() (*wire.ShaHash, error) { +func (r FutureSendManyResult) Receive() (*chainhash.Hash, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -651,7 +652,7 @@ func (r FutureSendManyResult) Receive() (*wire.ShaHash, error) { return nil, err } - return wire.NewShaHashFromStr(txHash) + return chainhash.NewHashFromStr(txHash) } // SendManyAsync returns an instance of a type that can be used to get the @@ -676,7 +677,7 @@ func (c *Client) SendManyAsync(fromAccount string, amounts map[btcutil.Address]b // // 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) (*wire.ShaHash, error) { +func (c *Client) SendMany(fromAccount string, amounts map[btcutil.Address]btcutil.Amount) (*chainhash.Hash, error) { return c.SendManyAsync(fromAccount, amounts).Receive() } @@ -709,7 +710,7 @@ func (c *Client) SendManyMinConfAsync(fromAccount string, // WalletPassphrase function for more details. func (c *Client) SendManyMinConf(fromAccount string, amounts map[btcutil.Address]btcutil.Amount, - minConfirms int) (*wire.ShaHash, error) { + minConfirms int) (*chainhash.Hash, error) { return c.SendManyMinConfAsync(fromAccount, amounts, minConfirms).Receive() } @@ -744,7 +745,7 @@ func (c *Client) SendManyCommentAsync(fromAccount string, // WalletPassphrase function for more details. func (c *Client) SendManyComment(fromAccount string, amounts map[btcutil.Address]btcutil.Amount, minConfirms int, - comment string) (*wire.ShaHash, error) { + comment string) (*chainhash.Hash, error) { return c.SendManyCommentAsync(fromAccount, amounts, minConfirms, comment).Receive() From f584dbd2e595fcce72fe88440886d4935371ae6b Mon Sep 17 00:00:00 2001 From: Waldir Pimenta Date: Fri, 12 Aug 2016 02:44:30 +0100 Subject: [PATCH 125/153] add license title (#82) background: https://github.com/btcsuite/btcd/pull/717 --- LICENSE | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 809b8fe7..0e7e628b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,5 @@ +ISC License + Copyright (c) 2014-2015 Conformal Systems LLC. Permission to use, copy, modify, and distribute this software for any @@ -10,4 +12,4 @@ 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 +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. From 2de61b2d9a35293fd392d6b243a7f3d5844be02d Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 17 Aug 2016 12:39:34 -0500 Subject: [PATCH 126/153] Manually copy relevant fields in ntfn state copy. (#84) This makes vet happy by manually copying the notification state fields during the deep copy instead of copying the entire struct which contains an embedded mutex. --- notify.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/notify.go b/notify.go index 908ec9d1..45c0b893 100644 --- a/notify.go +++ b/notify.go @@ -47,7 +47,10 @@ func (s *notificationState) Copy() *notificationState { s.Lock() defer s.Unlock() - stateCopy := *s + var stateCopy notificationState + stateCopy.notifyBlocks = s.notifyBlocks + stateCopy.notifyNewTx = s.notifyNewTx + stateCopy.notifyNewTxVerbose = s.notifyNewTxVerbose stateCopy.notifyReceived = make(map[string]struct{}) for addr := range s.notifyReceived { stateCopy.notifyReceived[addr] = struct{}{} From 8c8d4453ad9d8544c0cb1707d5dda20166c3766c Mon Sep 17 00:00:00 2001 From: David Hill Date: Wed, 17 Aug 2016 14:09:36 -0400 Subject: [PATCH 127/153] travis: Add golang 1.7 and drop golang 1.5 support. (#83) --- .travis.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8c24901c..662bf9af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,11 @@ language: go go: - - 1.5.3 - - 1.6 + - 1.6.3 + - 1.7 sudo: false -before_install: - - gotools=golang.org/x/tools install: - go get -d -t -v ./... - - go get -v $gotools/cmd/cover + - go get -v golang.org/x/tools/cmd/cover - go get -v github.com/bradfitz/goimports - go get -v github.com/golang/lint/golint script: From b89c91b9d6d11a1890ebe408b00849c88e827c8c Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 17 Aug 2016 13:12:27 -0500 Subject: [PATCH 128/153] Refactor ntfnstate mutex. (#85) This refactors the notification state mutex out of the state itself to the client. This is being done since the code makes a copy of the notification state and accesses that copy immutably, and therefore there is no need for it to have its own mutex. --- infrastructure.go | 11 +++++++---- notify.go | 7 ------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index 985441c9..fe6cbc90 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -145,8 +145,9 @@ type Client struct { requestList *list.List // Notifications. - ntfnHandlers *NotificationHandlers - ntfnState *notificationState + ntfnHandlers *NotificationHandlers + ntfnStateLock sync.Mutex + ntfnState *notificationState // Networking infrastructure. sendChan chan []byte @@ -233,8 +234,8 @@ func (c *Client) trackRegisteredNtfns(cmd interface{}) { return } - c.ntfnState.Lock() - defer c.ntfnState.Unlock() + c.ntfnStateLock.Lock() + defer c.ntfnStateLock.Unlock() switch bcmd := cmd.(type) { case *btcjson.NotifyBlocksCmd: @@ -498,7 +499,9 @@ func (c *Client) reregisterNtfns() error { // the notification state (while not under the lock of course) which // also register it with the remote RPC server, so this prevents double // registrations. + c.ntfnStateLock.Lock() stateCopy := c.ntfnState.Copy() + c.ntfnStateLock.Unlock() // Reregister notifyblocks if needed. if stateCopy.notifyBlocks { diff --git a/notify.go b/notify.go index 45c0b893..0e4ca144 100644 --- a/notify.go +++ b/notify.go @@ -10,7 +10,6 @@ import ( "encoding/json" "errors" "fmt" - "sync" "time" "github.com/btcsuite/btcd/btcjson" @@ -32,7 +31,6 @@ var ( // registered notification so the state can be automatically re-established on // reconnect. type notificationState struct { - sync.Mutex notifyBlocks bool notifyNewTx bool notifyNewTxVerbose bool @@ -41,12 +39,7 @@ type notificationState struct { } // Copy returns a deep copy of the receiver. -// -// This function is safe for concurrent access. func (s *notificationState) Copy() *notificationState { - s.Lock() - defer s.Unlock() - var stateCopy notificationState stateCopy.notifyBlocks = s.notifyBlocks stateCopy.notifyNewTx = s.notifyNewTx From 8ff9623a8ce8e55d317e33be24eb2815e15c773c Mon Sep 17 00:00:00 2001 From: David Hill Date: Thu, 15 Sep 2016 12:38:52 -0400 Subject: [PATCH 129/153] Add API for getblockheader GetBlockHeader returns the blockheader of the specified blockhash. GetBlockHeaderVerbose returns the data structure with information about the blockheader of the specified blockhash. Fixes #89 --- chain.go | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/chain.go b/chain.go index 9d6144e3..3150953e 100644 --- a/chain.go +++ b/chain.go @@ -263,6 +263,108 @@ func (c *Client) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) { return c.GetBlockHashAsync(blockHeight).Receive() } +// FutureGetBlockHeaderResult is a future promise to deliver the result of a +// GetBlockHeaderAsync RPC invocation (or an applicable error). +type FutureGetBlockHeaderResult chan *response + +// Receive waits for the response promised by the future and returns the +// blockheader requested from the server given its hash. +func (r FutureGetBlockHeaderResult) Receive() (*wire.BlockHeader, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a string. + var bhHex string + err = json.Unmarshal(res, &bhHex) + if err != nil { + return nil, err + } + + serializedBH, err := hex.DecodeString(bhHex) + if err != nil { + return nil, err + } + + // Deserialize the blockheader and return it. + var bh wire.BlockHeader + err = bh.Deserialize(bytes.NewReader(serializedBH)) + if err != nil { + return nil, err + } + + return &bh, err +} + +// GetBlockHeaderAsync 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 GetBlockHeader for the blocking version and more details. +func (c *Client) GetBlockHeaderAsync(blockHash *chainhash.Hash) FutureGetBlockHeaderResult { + hash := "" + if blockHash != nil { + hash = blockHash.String() + } + + cmd := btcjson.NewGetBlockHeaderCmd(hash, btcjson.Bool(false)) + return c.sendCmd(cmd) +} + +// GetBlockHeader returns the blockheader from the server given its hash. +// +// See GetBlockHeaderVerbose to retrieve a data structure with information about the +// block instead. +func (c *Client) GetBlockHeader(blockHash *chainhash.Hash) (*wire.BlockHeader, error) { + return c.GetBlockHeaderAsync(blockHash).Receive() +} + +// FutureGetBlockHeaderVerboseResult is a future promise to deliver the result of a +// GetBlockAsync RPC invocation (or an applicable error). +type FutureGetBlockHeaderVerboseResult chan *response + +// Receive waits for the response promised by the future and returns the +// data structure of the blockheader requested from the server given its hash. +func (r FutureGetBlockHeaderVerboseResult) Receive() (*btcjson.GetBlockHeaderVerboseResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a string. + var bh btcjson.GetBlockHeaderVerboseResult + err = json.Unmarshal(res, &bh) + if err != nil { + return nil, err + } + + return &bh, nil +} + +// GetBlockHeaderVerboseAsync 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 GetBlockHeader for the blocking version and more details. +func (c *Client) GetBlockHeaderVerboseAsync(blockHash *chainhash.Hash) FutureGetBlockHeaderVerboseResult { + hash := "" + if blockHash != nil { + hash = blockHash.String() + } + + cmd := btcjson.NewGetBlockHeaderCmd(hash, btcjson.Bool(true)) + return c.sendCmd(cmd) +} + +// GetBlockHeaderVerbose returns a data structure with information about the +// blockheader from the server given its hash. +// +// See GetBlockHeader to retrieve a blockheader instead. +func (c *Client) GetBlockHeaderVerbose(blockHash *chainhash.Hash) (*btcjson.GetBlockHeaderVerboseResult, error) { + return c.GetBlockHeaderVerboseAsync(blockHash).Receive() +} + // FutureGetRawMempoolResult is a future promise to deliver the result of a // GetRawMempoolAsync RPC invocation (or an applicable error). type FutureGetRawMempoolResult chan *response From 96ed4cb5b033fb84ad73691a22c6e31d1f50799b Mon Sep 17 00:00:00 2001 From: David Hill Date: Thu, 15 Sep 2016 14:25:23 -0400 Subject: [PATCH 130/153] Update installation section in README.md Have users use -u along with go get to pull the latest dependencies. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6ee8e377..3f1c52e2 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ implement and the API is not stable yet. ## Installation ```bash -$ go get github.com/btcsuite/btcrpcclient +$ go get -u github.com/btcsuite/btcrpcclient ``` ## License From 982229439aa78a43c7d68d6b68f7ac98722502de Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 15 Sep 2016 14:06:36 -0500 Subject: [PATCH 131/153] Return wire.MsgBlock from GetBlock(Async). This modifies the return value of the GetBlock and GetBlockAsync functions to a raw *wire.MsgBlock instead of *btcutil.Block. This is being done because a btcutil.Block assumes there is a height associated with the block and the block height is not part of a raw serialized block as returned by the underlying non-verbose RPC API getblock call and thus is not available to populate a btcutil.Block correctly. A raw wire.Block correctly matches what the underlying RPC call is actually returning and thus avoids the confusion that could occur. --- chain.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/chain.go b/chain.go index 3150953e..369632df 100644 --- a/chain.go +++ b/chain.go @@ -12,7 +12,6 @@ import ( "github.com/btcsuite/btcd/btcjson" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" ) // FutureGetBestBlockHashResult is a future promise to deliver the result of a @@ -58,7 +57,7 @@ type FutureGetBlockResult chan *response // 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) { +func (r FutureGetBlockResult) Receive() (*wire.MsgBlock, error) { res, err := receiveFuture(r) if err != nil { return nil, err @@ -83,7 +82,7 @@ func (r FutureGetBlockResult) Receive() (*btcutil.Block, error) { if err != nil { return nil, err } - return btcutil.NewBlock(&msgBlock), nil + return &msgBlock, nil } // GetBlockAsync returns an instance of a type that can be used to get the @@ -105,7 +104,7 @@ func (c *Client) GetBlockAsync(blockHash *chainhash.Hash) FutureGetBlockResult { // // See GetBlockVerbose to retrieve a data structure with information about the // block instead. -func (c *Client) GetBlock(blockHash *chainhash.Hash) (*btcutil.Block, error) { +func (c *Client) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) { return c.GetBlockAsync(blockHash).Receive() } From 6e5c0a79044d3d80a22d94049eeb509ea040efac Mon Sep 17 00:00:00 2001 From: David Hill Date: Thu, 15 Sep 2016 16:01:54 -0400 Subject: [PATCH 132/153] Fix GetBlockVerbose API. This changes the GetBlockVersion API to not send a third parameter. The third parameter is a boolean that expands the transaction data structures as well. However, Bitcore Core does not recognize this feature. GetBlockVerbose now only takes a hash value. Users of the GetBlockVerbose(hash, true) must now use GetBlockVerboseTx(hash). --- chain.go | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/chain.go b/chain.go index 369632df..30543b0d 100644 --- a/chain.go +++ b/chain.go @@ -134,22 +134,47 @@ func (r FutureGetBlockVerboseResult) Receive() (*btcjson.GetBlockVerboseResult, // the returned instance. // // See GetBlockVerbose for the blocking version and more details. -func (c *Client) GetBlockVerboseAsync(blockHash *chainhash.Hash, verboseTx bool) FutureGetBlockVerboseResult { +func (c *Client) GetBlockVerboseAsync(blockHash *chainhash.Hash) FutureGetBlockVerboseResult { hash := "" if blockHash != nil { hash = blockHash.String() } - cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(true), &verboseTx) + cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(true), nil) return c.sendCmd(cmd) } // GetBlockVerbose returns a data structure from the server with information // about a block given its hash. // +// See GetBlockVerboseTx to retrieve transaction data structures as well. // See GetBlock to retrieve a raw block instead. -func (c *Client) GetBlockVerbose(blockHash *chainhash.Hash, verboseTx bool) (*btcjson.GetBlockVerboseResult, error) { - return c.GetBlockVerboseAsync(blockHash, verboseTx).Receive() +func (c *Client) GetBlockVerbose(blockHash *chainhash.Hash) (*btcjson.GetBlockVerboseResult, error) { + return c.GetBlockVerboseAsync(blockHash).Receive() +} + +// GetBlockVerboseTxAsync 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 GetBlockVerboseTx or the blocking version and more details. +func (c *Client) GetBlockVerboseTxAsync(blockHash *chainhash.Hash) FutureGetBlockVerboseResult { + hash := "" + if blockHash != nil { + hash = blockHash.String() + } + + cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(true), btcjson.Bool(true)) + return c.sendCmd(cmd) +} + +// GetBlockVerboseTx returns a data structure from the server with information +// about a block and its transactions given its hash. +// +// See GetBlockVerbose if only transaction hashes are preferred. +// See GetBlock to retrieve a raw block instead. +func (c *Client) GetBlockVerboseTx(blockHash *chainhash.Hash) (*btcjson.GetBlockVerboseResult, error) { + return c.GetBlockVerboseTxAsync(blockHash).Receive() } // FutureGetBlockCountResult is a future promise to deliver the result of a From 2b780d16b042054d07aa322146194118fd7f7b81 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Fri, 16 Sep 2016 14:40:27 -0500 Subject: [PATCH 133/153] Improve HTTP error handling. This modifies the code which handles failed server responses to attempt to unmarshal the response bytes as a regular JSON-RPC response regardless of the HTTP status code and, if that fails, return an error that includes the status code as well as the raw response bytes. This is being done because some JSON-RPC servers, such as Bitcoin Core, don't follow the intended HTTP status code meanings. For example, if you request a block that doesn't exist, it returns a status code of 500 which is supposed to mean internal server error instead of a standard 200 status code (since the HTTP request itself was successful) along with the JSON-RPC error response. The result is that errors from Core will now show up as an actual RPCError instead of an error with the raw JSON bytes. This also has the benefit of returning the HTTP status code in the error for real HTTP failure cases such as 401 authentication failures, which previously would just be an empty error when used against Core since it doesn't return the actual response along with the status code as it should. --- infrastructure.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/infrastructure.go b/infrastructure.go index fe6cbc90..66ca79d6 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -705,15 +705,15 @@ func (c *Client) handleSendPostMessage(details *sendPostDetails) { return } - // Handle unsuccessful HTTP responses - if httpResponse.StatusCode < 200 || httpResponse.StatusCode >= 300 { - jReq.responseChan <- &response{err: errors.New(string(respBytes))} - return - } - + // Try to unmarshal the response as a regular JSON-RPC response. var resp rawResponse err = json.Unmarshal(respBytes, &resp) if err != nil { + // When the response itself isn't a valid JSON-RPC response + // return an error which includes the HTTP status code and raw + // response bytes. + err = fmt.Errorf("status code: %d, response: %q", + httpResponse.StatusCode, string(respBytes)) jReq.responseChan <- &response{err: err} return } From 01050a2f821f37448f1404197d77ddb485804adf Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Sun, 6 Nov 2016 16:03:08 -0800 Subject: [PATCH 134/153] add support for getblockchaininfo --- chain.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/chain.go b/chain.go index 30543b0d..b3753b4e 100644 --- a/chain.go +++ b/chain.go @@ -250,6 +250,42 @@ func (c *Client) GetDifficulty() (float64, error) { return c.GetDifficultyAsync().Receive() } +// FutureGetBlockChainInfoResult is a promised to deliver the result of a +// GetBlockChainInfoAsync RPC invocation (or an applicable error). +type FutureGetBlockChainInfoResult chan *response + +// Receive waits for the response promised by the future and returns chain info +// result provided by the server. +func (r FutureGetBlockChainInfoResult) Receive() (*btcjson.GetBlockChainInfoResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + var chainInfo btcjson.GetBlockChainInfoResult + if err := json.Unmarshal(res, &chainInfo); err != nil { + return nil, err + } + return &chainInfo, nil +} + +// GetBlockChainInfoAsync 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 GetBlockChainInfo for the blocking version and more details. +func (c *Client) GetBlockChainInfoAsync() FutureGetBlockChainInfoResult { + cmd := btcjson.NewGetBlockChainInfoCmd() + return c.sendCmd(cmd) +} + +// GetBlockChainInfo returns information related to the processing staate of +// various chain-specific details such as the current difficulty from the tip +// of the main-chain. +func (c *Client) GetBlockChainInfo() (*btcjson.GetBlockChainInfoResult, error) { + return c.GetBlockChainInfoAsync().Receive() +} + // FutureGetBlockHashResult is a future promise to deliver the result of a // GetBlockHashAsync RPC invocation (or an applicable error). type FutureGetBlockHashResult chan *response From c6e1d711da0db56803309479a5deabb02b816e88 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 9 Nov 2016 11:13:10 -0800 Subject: [PATCH 135/153] review: fix typos --- chain.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chain.go b/chain.go index b3753b4e..8cb54fb4 100644 --- a/chain.go +++ b/chain.go @@ -250,7 +250,7 @@ func (c *Client) GetDifficulty() (float64, error) { return c.GetDifficultyAsync().Receive() } -// FutureGetBlockChainInfoResult is a promised to deliver the result of a +// FutureGetBlockChainInfoResult is a promise to deliver the result of a // GetBlockChainInfoAsync RPC invocation (or an applicable error). type FutureGetBlockChainInfoResult chan *response @@ -279,9 +279,9 @@ func (c *Client) GetBlockChainInfoAsync() FutureGetBlockChainInfoResult { return c.sendCmd(cmd) } -// GetBlockChainInfo returns information related to the processing staate of +// GetBlockChainInfo returns information related to the processing state of // various chain-specific details such as the current difficulty from the tip -// of the main-chain. +// of the main chain. func (c *Client) GetBlockChainInfo() (*btcjson.GetBlockChainInfoResult, error) { return c.GetBlockChainInfoAsync().Receive() } From 441bb918ecc2b97de90e44f27b2e5a0d932e6ebc Mon Sep 17 00:00:00 2001 From: "John C. Vernaleo" Date: Wed, 16 Nov 2016 16:00:41 -0500 Subject: [PATCH 136/153] Modify goclean to use gometalint like btcd --- .travis.yml | 4 ++-- goclean.sh | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 662bf9af..e3128e11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,8 @@ sudo: false install: - go get -d -t -v ./... - go get -v golang.org/x/tools/cmd/cover - - go get -v github.com/bradfitz/goimports - - go get -v github.com/golang/lint/golint + - go get -v github.com/alecthomas/gometalinter + - gometalinter --install script: - export PATH=$PATH:$HOME/gopath/bin - ./goclean.sh diff --git a/goclean.sh b/goclean.sh index 91833320..91ae7d79 100755 --- a/goclean.sh +++ b/goclean.sh @@ -5,14 +5,19 @@ # 3. golint (https://github.com/golang/lint) # 4. go vet (http://golang.org/cmd/vet) # 5. test coverage (http://blog.golang.org/cover) +# +# gometalinter (github.com/alecthomas/gometalinter) is used to run each each +# static checker. set -e # Automatic checks test -z "$(gofmt -l -w . | tee /dev/stderr)" -test -z "$(goimports -l -w . | tee /dev/stderr)" -test -z "$(golint . | tee /dev/stderr)" -go vet ./... +test -z "$(gometalinter --disable-all \ +--enable=golint \ +--enable=vet \ +--enable=goimports \ +--deadline=20s ./... | tee /dev/stderr)" env GORACE="halt_on_error=1" go test -v -race ./... # Run test coverage on each subdirectories and merge the coverage profile. From 5c3e647618850ab3b00c7434fc185906ba219af8 Mon Sep 17 00:00:00 2001 From: "John C. Vernaleo" Date: Thu, 3 Nov 2016 09:20:38 -0400 Subject: [PATCH 137/153] Add gofmt to metalinter. Fix one issue it caught. This depends on PR#97 --- goclean.sh | 2 +- infrastructure.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/goclean.sh b/goclean.sh index 91ae7d79..0e779e60 100755 --- a/goclean.sh +++ b/goclean.sh @@ -12,8 +12,8 @@ set -e # Automatic checks -test -z "$(gofmt -l -w . | tee /dev/stderr)" test -z "$(gometalinter --disable-all \ +--enable=gofmt \ --enable=golint \ --enable=vet \ --enable=goimports \ diff --git a/infrastructure.go b/infrastructure.go index 66ca79d6..8f6e2423 100644 --- a/infrastructure.go +++ b/infrastructure.go @@ -555,7 +555,7 @@ func (c *Client) reregisterNtfns() error { // ignoreResends is a set of all methods for requests that are "long running" // are not be reissued by the client on reconnect. var ignoreResends = map[string]struct{}{ - "rescan": struct{}{}, + "rescan": {}, } // resendRequests resends any requests that had not completed when the client From b83f837ad27e37e4a871d00c2ea0a83fb2756a27 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 16 Nov 2016 14:52:02 -0600 Subject: [PATCH 138/153] Simplify code per gosimple linter. This simplifies the code based on the recommendations of the gosimple lint tool. --- mining.go | 6 +---- net.go | 12 ++-------- notify.go | 30 +++++-------------------- wallet.go | 66 ++++++++++--------------------------------------------- 4 files changed, 19 insertions(+), 95 deletions(-) diff --git a/mining.go b/mining.go index 4ac94f14..aaf4b7f3 100644 --- a/mining.go +++ b/mining.go @@ -106,11 +106,7 @@ type FutureSetGenerateResult chan *response // 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 + return err } // SetGenerateAsync returns an instance of a type that can be used to get the diff --git a/net.go b/net.go index 171cee63..d4e91a81 100644 --- a/net.go +++ b/net.go @@ -41,11 +41,7 @@ type FutureAddNodeResult chan *response // any occurred when performing the specified command. func (r FutureAddNodeResult) Receive() error { _, err := receiveFuture(r) - if err != nil { - return err - } - - return nil + return err } // AddNodeAsync returns an instance of a type that can be used to get the result @@ -193,11 +189,7 @@ type FuturePingResult chan *response // 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 + return err } // PingAsync returns an instance of a type that can be used to get the result of diff --git a/notify.go b/notify.go index 0e4ca144..bb572e50 100644 --- a/notify.go +++ b/notify.go @@ -663,11 +663,7 @@ type FutureNotifyBlocksResult chan *response // if the registration was not successful. func (r FutureNotifyBlocksResult) Receive() error { _, err := receiveFuture(r) - if err != nil { - return err - } - - return nil + return err } // NotifyBlocksAsync returns an instance of a type that can be used to get the @@ -715,11 +711,7 @@ type FutureNotifySpentResult chan *response // if the registration was not successful. func (r FutureNotifySpentResult) Receive() error { _, err := receiveFuture(r) - if err != nil { - return err - } - - return nil + return err } // notifySpentInternal is the same as notifySpentAsync except it accepts @@ -799,11 +791,7 @@ type FutureNotifyNewTransactionsResult chan *response // if the registration was not successful. func (r FutureNotifyNewTransactionsResult) Receive() error { _, err := receiveFuture(r) - if err != nil { - return err - } - - return nil + return err } // NotifyNewTransactionsAsync returns an instance of a type that can be used to @@ -852,11 +840,7 @@ type FutureNotifyReceivedResult chan *response // if the registration was not successful. func (r FutureNotifyReceivedResult) Receive() error { _, err := receiveFuture(r) - if err != nil { - return err - } - - return nil + return err } // notifyReceivedInternal is the same as notifyReceivedAsync except it accepts @@ -936,11 +920,7 @@ type FutureRescanResult chan *response // if the rescan was not successful. func (r FutureRescanResult) Receive() error { _, err := receiveFuture(r) - if err != nil { - return err - } - - return nil + return err } // RescanAsync returns an instance of a type that can be used to get the result diff --git a/wallet.go b/wallet.go index 0eb58061..3459d012 100644 --- a/wallet.go +++ b/wallet.go @@ -423,11 +423,7 @@ type FutureSetTxFeeResult chan *response // are processed quickly. Most transaction are 1KB. func (r FutureSetTxFeeResult) Receive() error { _, err := receiveFuture(r) - if err != nil { - return err - } - - return nil + return err } // SetTxFeeAsync returns an instance of a type that can be used to get the @@ -852,11 +848,7 @@ type FutureCreateNewAccountResult chan *response // result of creating new account. func (r FutureCreateNewAccountResult) Receive() error { _, err := receiveFuture(r) - if err != nil { - return err - } - - return nil + return err } // CreateNewAccountAsync returns an instance of a type that can be used to get the @@ -1035,11 +1027,7 @@ type FutureSetAccountResult chan *response // 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 + return err } // SetAccountAsync returns an instance of a type that can be used to get the @@ -1205,11 +1193,7 @@ type FutureRenameAccountResult chan *response // result of creating new account. func (r FutureRenameAccountResult) Receive() error { _, err := receiveFuture(r) - if err != nil { - return err - } - - return nil + return err } // RenameAccountAsync returns an instance of a type that can be used to get the @@ -1273,11 +1257,7 @@ type FutureKeyPoolRefillResult chan *response // of refilling the key pool. func (r FutureKeyPoolRefillResult) Receive() error { _, err := receiveFuture(r) - if err != nil { - return err - } - - return nil + return err } // KeyPoolRefillAsync returns an instance of a type that can be used to get the @@ -1852,11 +1832,7 @@ type FutureWalletLockResult chan *response // of locking the wallet. func (r FutureWalletLockResult) Receive() error { _, err := receiveFuture(r) - if err != nil { - return err - } - - return nil + return err } // WalletLockAsync returns an instance of a type that can be used to get the @@ -1884,11 +1860,7 @@ func (c *Client) WalletLock() error { func (c *Client) WalletPassphrase(passphrase string, timeoutSecs int64) error { cmd := btcjson.NewWalletPassphraseCmd(passphrase, timeoutSecs) _, err := c.sendCmdAndWait(cmd) - if err != nil { - return err - } - - return nil + return err } // FutureWalletPassphraseChangeResult is a future promise to deliver the result @@ -1899,11 +1871,7 @@ type FutureWalletPassphraseChangeResult chan *response // of changing the wallet passphrase. func (r FutureWalletPassphraseChangeResult) Receive() error { _, err := receiveFuture(r) - if err != nil { - return err - } - - return nil + return err } // WalletPassphraseChangeAsync returns an instance of a type that can be used to @@ -2063,11 +2031,7 @@ type FutureImportAddressResult chan *response // of importing the passed public address. func (r FutureImportAddressResult) Receive() error { _, err := receiveFuture(r) - if err != nil { - return err - } - - return nil + return err } // ImportAddressAsync returns an instance of a type that can be used to get the @@ -2110,11 +2074,7 @@ type FutureImportPrivKeyResult chan *response // (WIF). func (r FutureImportPrivKeyResult) Receive() error { _, err := receiveFuture(r) - if err != nil { - return err - } - - return nil + return err } // ImportPrivKeyAsync returns an instance of a type that can be used to get the @@ -2189,11 +2149,7 @@ type FutureImportPubKeyResult chan *response // of importing the passed public key. func (r FutureImportPubKeyResult) Receive() error { _, err := receiveFuture(r) - if err != nil { - return err - } - - return nil + return err } // ImportPubKeyAsync returns an instance of a type that can be used to get the From f9ac1a778654fa3d66495a327f050d68e40ba157 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 16 Nov 2016 15:07:40 -0600 Subject: [PATCH 139/153] build: Add gosimple linter to goclean.sh. This modifies the goclean.sh script to include the gosimple lint tool to the gometalinter configuration. --- goclean.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/goclean.sh b/goclean.sh index 0e779e60..a173ebfa 100755 --- a/goclean.sh +++ b/goclean.sh @@ -4,7 +4,8 @@ # 2. goimports (https://github.com/bradfitz/goimports) # 3. golint (https://github.com/golang/lint) # 4. go vet (http://golang.org/cmd/vet) -# 5. test coverage (http://blog.golang.org/cover) +# 5. gosimple (https://github.com/dominikh/go-simple) +# 6. test coverage (http://blog.golang.org/cover) # # gometalinter (github.com/alecthomas/gometalinter) is used to run each each # static checker. @@ -16,8 +17,9 @@ test -z "$(gometalinter --disable-all \ --enable=gofmt \ --enable=golint \ --enable=vet \ +--enable=gosimple \ --enable=goimports \ ---deadline=20s ./... | tee /dev/stderr)" +--deadline=45s ./... | tee /dev/stderr)" env GORACE="halt_on_error=1" go test -v -race ./... # Run test coverage on each subdirectories and merge the coverage profile. From 36af96160d83868caa152027803af9dbf4b25e23 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 16 Nov 2016 15:11:20 -0600 Subject: [PATCH 140/153] Remove unnecessary convs found by unconvert. This removes all unnecessary typecast conversions as found by the unconvert linter. --- notify.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notify.go b/notify.go index bb572e50..2e486a1a 100644 --- a/notify.go +++ b/notify.go @@ -551,7 +551,7 @@ func parseTxAcceptedNtfnParams(params []json.RawMessage) (*chainhash.Hash, return nil, 0, err } - return txHash, btcutil.Amount(amt), nil + return txHash, amt, nil } // parseTxAcceptedVerboseNtfnParams parses out details about a raw transaction @@ -628,7 +628,7 @@ func parseAccountBalanceNtfnParams(params []json.RawMessage) (account string, return "", 0, false, err } - return account, btcutil.Amount(bal), confirmed, nil + return account, bal, confirmed, nil } // parseWalletLockStateNtfnParams parses out the account name and locked From 1b800b69cf99d2c2ab4c35be26db7ffe1c8657e0 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 16 Nov 2016 15:15:12 -0600 Subject: [PATCH 141/153] build: Add unconvert linter to goclean.sh. This modifies the goclean.sh script to include the unconvert lint tool in the gometalinter configuration. --- goclean.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/goclean.sh b/goclean.sh index a173ebfa..fde79a0e 100755 --- a/goclean.sh +++ b/goclean.sh @@ -5,7 +5,8 @@ # 3. golint (https://github.com/golang/lint) # 4. go vet (http://golang.org/cmd/vet) # 5. gosimple (https://github.com/dominikh/go-simple) -# 6. test coverage (http://blog.golang.org/cover) +# 6. unconvert (https://github.com/mdempsky/unconvert) +# 7. test coverage (http://blog.golang.org/cover) # # gometalinter (github.com/alecthomas/gometalinter) is used to run each each # static checker. @@ -18,6 +19,7 @@ test -z "$(gometalinter --disable-all \ --enable=golint \ --enable=vet \ --enable=gosimple \ +--enable=unconvert \ --enable=goimports \ --deadline=45s ./... | tee /dev/stderr)" env GORACE="halt_on_error=1" go test -v -race ./... From 7903290fd9956021ed7b65463f8667df345b365e Mon Sep 17 00:00:00 2001 From: "John C. Vernaleo" Date: Wed, 7 Dec 2016 10:14:06 -0500 Subject: [PATCH 142/153] Remove -v from go test. This caused unhelpful clutter in travis output. Closes #106 --- goclean.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/goclean.sh b/goclean.sh index fde79a0e..56268e09 100755 --- a/goclean.sh +++ b/goclean.sh @@ -22,7 +22,7 @@ test -z "$(gometalinter --disable-all \ --enable=unconvert \ --enable=goimports \ --deadline=45s ./... | tee /dev/stderr)" -env GORACE="halt_on_error=1" go test -v -race ./... +env GORACE="halt_on_error=1" go test -race ./... # Run test coverage on each subdirectories and merge the coverage profile. From 0519b86a9d0b4f9a2609ebe5bc6c9964be4bbf0c Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 15 Dec 2016 14:03:58 -0700 Subject: [PATCH 143/153] Add `version` command to JSON-RPC API for API versioning --- LICENSE | 3 ++- extensions.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 0e7e628b..f627807e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ ISC License -Copyright (c) 2014-2015 Conformal Systems LLC. +Copyright (c) 2014-2016 The btcsuite developers +Copyright (c) 2015-2016 The Decred developers Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/extensions.go b/extensions.go index 05e8e70a..c0c0c8e8 100644 --- a/extensions.go +++ b/extensions.go @@ -1,4 +1,5 @@ -// Copyright (c) 2014-2015 The btcsuite developers +// Copyright (c) 2014-2016 The btcsuite developers +// Copyright (c) 2015-2016 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -348,3 +349,53 @@ func (c *Client) SessionAsync() FutureSessionResult { func (c *Client) Session() (*btcjson.SessionResult, error) { return c.SessionAsync().Receive() } + +// FutureVersionResult is a future promise to delivere the result of a version +// RPC invocation (or an applicable error). +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +type FutureVersionResult chan *response + +// Receive waits for the response promised by the future and returns the version +// result. +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +func (r FutureVersionResult) Receive() (map[string]btcjson.VersionResult, + error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a version result object. + var vr map[string]btcjson.VersionResult + err = json.Unmarshal(res, &vr) + if err != nil { + return nil, err + } + + return vr, nil +} + +// VersionAsync 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 Version for the blocking version and more details. +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +func (c *Client) VersionAsync() FutureVersionResult { + cmd := btcjson.NewVersionCmd() + return c.sendCmd(cmd) +} + +// Version returns information about the server's JSON-RPC API versions. +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +func (c *Client) Version() (map[string]btcjson.VersionResult, error) { + return c.VersionAsync().Receive() +} From 5ce0ed600997eafaed25ad4936c1d84ec6ad2b5a Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 16 Dec 2016 16:59:31 -0700 Subject: [PATCH 144/153] Port `getheaders` JSON-RPC command from dcrd --- extensions.go | 76 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/extensions.go b/extensions.go index c0c0c8e8..e5315b9a 100644 --- a/extensions.go +++ b/extensions.go @@ -1,12 +1,14 @@ -// Copyright (c) 2014-2016 The btcsuite developers -// Copyright (c) 2015-2016 The Decred developers +// Copyright (c) 2014-2017 The btcsuite developers +// Copyright (c) 2015-2017 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package btcrpcclient import ( + "bytes" "encoding/base64" + "encoding/hex" "encoding/json" "fmt" @@ -233,6 +235,76 @@ func (c *Client) GetCurrentNet() (wire.BitcoinNet, error) { return c.GetCurrentNetAsync().Receive() } +// FutureGetHeadersResult is a future promise to deliver the result of a +// getheaders RPC invocation (or an applicable error). +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +type FutureGetHeadersResult chan *response + +// Receive waits for the response promised by the future and returns the +// getheaders result. +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +func (r FutureGetHeadersResult) Receive() ([]wire.BlockHeader, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal result as a slice of strings. + var result []string + err = json.Unmarshal(res, &result) + if err != nil { + return nil, err + } + + // Deserialize the []string into []wire.BlockHeader. + headers := make([]wire.BlockHeader, len(result)) + for i, headerHex := range result { + serialized, err := hex.DecodeString(headerHex) + if err != nil { + return nil, err + } + err = headers[i].Deserialize(bytes.NewReader(serialized)) + if err != nil { + return nil, err + } + } + return headers, nil +} + +// GetHeadersAsync 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 GetHeaders for the blocking version and more details. +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +func (c *Client) GetHeadersAsync(blockLocators []chainhash.Hash, hashStop *chainhash.Hash) FutureGetHeadersResult { + locators := make([]string, len(blockLocators)) + for i := range blockLocators { + locators[i] = blockLocators[i].String() + } + hash := "" + if hashStop != nil { + hash = hashStop.String() + } + cmd := btcjson.NewGetHeadersCmd(locators, hash) + return c.sendCmd(cmd) +} + +// GetHeaders mimics the wire protocol getheaders and headers messages by +// returning all headers on the main chain after the first known block in the +// locators, up until a block hash matches hashStop. +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +func (c *Client) GetHeaders(blockLocators []chainhash.Hash, hashStop *chainhash.Hash) ([]wire.BlockHeader, error) { + return c.GetHeadersAsync(blockLocators, hashStop).Receive() +} + // FutureExportWatchingWalletResult is a future promise to deliver the result of // an ExportWatchingWalletAsync RPC invocation (or an applicable error). type FutureExportWatchingWalletResult chan *response From e15ac5024a9aaaa03e1c896ed49eca31fba1082d Mon Sep 17 00:00:00 2001 From: David Hill Date: Sun, 15 Jan 2017 08:38:08 -0500 Subject: [PATCH 145/153] travis: test against latest releases. While here, clean up goclean.sh. --- .travis.yml | 5 ++--- goclean.sh | 22 ++++++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index e3128e11..65aa0a9d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,10 @@ language: go go: - - 1.6.3 - - 1.7 + - 1.6.x + - 1.7.x sudo: false install: - go get -d -t -v ./... - - go get -v golang.org/x/tools/cmd/cover - go get -v github.com/alecthomas/gometalinter - gometalinter --install script: diff --git a/goclean.sh b/goclean.sh index 56268e09..3c1f084f 100755 --- a/goclean.sh +++ b/goclean.sh @@ -6,30 +6,40 @@ # 4. go vet (http://golang.org/cmd/vet) # 5. gosimple (https://github.com/dominikh/go-simple) # 6. unconvert (https://github.com/mdempsky/unconvert) -# 7. test coverage (http://blog.golang.org/cover) +# 7. race detector (http://blog.golang.org/race-detector) +# 8. test coverage (http://blog.golang.org/cover) # -# gometalinter (github.com/alecthomas/gometalinter) is used to run each each -# static checker. +# gometalinter (github.com/alecthomas/gometalinter) is used to run each static +# checker. -set -e +set -ex + +# Make sure gometalinter is installed and $GOPATH/bin is in your path. +# $ go get -v github.com/alecthomas/gometalinter" +# $ gometalinter --install" +if [ ! -x "$(type -p gometalinter)" ]; then + exit 1 +fi # Automatic checks test -z "$(gometalinter --disable-all \ --enable=gofmt \ +--enable=goimports \ --enable=golint \ --enable=vet \ --enable=gosimple \ --enable=unconvert \ ---enable=goimports \ --deadline=45s ./... | tee /dev/stderr)" env GORACE="halt_on_error=1" go test -race ./... # Run test coverage on each subdirectories and merge the coverage profile. +set +x echo "mode: count" > profile.cov # Standard go tooling behavior is to ignore dirs with leading underscores. -for dir in $(find . -maxdepth 10 -not -path './.git*' -not -path '*/_*' -type d); +for dir in $(find . -maxdepth 10 -not -path '.' -not -path './.git*' \ + -not -path '*/_*' -type d); do if ls $dir/*.go &> /dev/null; then go test -covermode=count -coverprofile=$dir/profile.tmp $dir From a835a9ca8bf1bec3a27b621dcea2ebe6b863d7a8 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 24 Jan 2017 19:39:03 -0700 Subject: [PATCH 146/153] Port `loadtxfilter` JSON-RPC command from dcrd --- notify.go | 263 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 262 insertions(+), 1 deletion(-) diff --git a/notify.go b/notify.go index 2e486a1a..3391708c 100644 --- a/notify.go +++ b/notify.go @@ -1,4 +1,5 @@ -// Copyright (c) 2014-2016 The btcsuite developers +// Copyright (c) 2014-2017 The btcsuite developers +// Copyright (c) 2015-2017 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -93,19 +94,40 @@ type NotificationHandlers struct { // (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. + // + // NOTE: Deprecated. Use OnFilteredBlockConnected instead. OnBlockConnected func(hash *chainhash.Hash, height int32, t time.Time) + // OnFilteredBlockConnected 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. Its parameters differ from OnBlockConnected: it + // receives the block's height, header, and relevant transactions. + OnFilteredBlockConnected func(height int32, header *wire.BlockHeader, + txs []*btcutil.Tx) + // 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. + // + // NOTE: Deprecated. Use OnFilteredBlockDisconnected instead. OnBlockDisconnected func(hash *chainhash.Hash, height int32, t time.Time) + // OnFilteredBlockDisconnected is invoked when a block is disconnected + // from the longest (best) chain. It will only be invoked if a + // preceding NotifyBlocks has been made to register for the notification + // and the call to function is non-nil. Its parameters differ from + // OnBlockDisconnected: it receives the block's height and header. + OnFilteredBlockDisconnected func(height int32, header *wire.BlockHeader) + // 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. + // + // NOTE: Deprecated. Use OnRelevantTxAccepted instead. OnRecvTx func(transaction *btcutil.Tx, details *btcjson.BlockDetails) // OnRedeemingTx is invoked when a transaction that spends a registered @@ -118,8 +140,17 @@ type NotificationHandlers struct { // 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. + // + // NOTE: Deprecated. Use OnRelevantTxAccepted instead. OnRedeemingTx func(transaction *btcutil.Tx, details *btcjson.BlockDetails) + // OnRelevantTxAccepted is invoked when an unmined transaction passes + // the client's transaction filter. + // + // NOTE: This is a btcsuite extension ported from + // github.com/decred/dcrrpcclient. + OnRelevantTxAccepted func(transaction []byte) + // OnRescanFinished is invoked after a rescan finishes due to a previous // call to Rescan or RescanEndHeight. Finished rescans should be // signaled on this notification, rather than relying on the return @@ -200,6 +231,25 @@ func (c *Client) handleNotification(ntfn *rawNotification) { c.ntfnHandlers.OnBlockConnected(blockHash, blockHeight, blockTime) + // OnFilteredBlockConnected + case btcjson.FilteredBlockConnectedNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnFilteredBlockConnected == nil { + return + } + + blockHeight, blockHeader, transactions, err := + parseFilteredBlockConnectedParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid filtered block "+ + "connected notification: %v", err) + return + } + + c.ntfnHandlers.OnFilteredBlockConnected(blockHeight, + blockHeader, transactions) + // OnBlockDisconnected case btcjson.BlockDisconnectedNtfnMethod: // Ignore the notification if the client is not interested in @@ -217,6 +267,25 @@ func (c *Client) handleNotification(ntfn *rawNotification) { c.ntfnHandlers.OnBlockDisconnected(blockHash, blockHeight, blockTime) + // OnFilteredBlockDisconnected + case btcjson.FilteredBlockDisconnectedNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnFilteredBlockDisconnected == nil { + return + } + + blockHeight, blockHeader, err := + parseFilteredBlockDisconnectedParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid filtered block "+ + "disconnected notification: %v", err) + return + } + + c.ntfnHandlers.OnFilteredBlockDisconnected(blockHeight, + blockHeader) + // OnRecvTx case btcjson.RecvTxNtfnMethod: // Ignore the notification if the client is not interested in @@ -251,6 +320,23 @@ func (c *Client) handleNotification(ntfn *rawNotification) { c.ntfnHandlers.OnRedeemingTx(tx, block) + // OnRelevantTxAccepted + case btcjson.RelevantTxAcceptedNtfnMethod: + // Ignore the notification if the client is not interested in + // it. + if c.ntfnHandlers.OnRelevantTxAccepted == nil { + return + } + + transaction, err := parseRelevantTxAcceptedParams(ntfn.Params) + if err != nil { + log.Warnf("Received invalid relevanttxaccepted "+ + "notification: %v", err) + return + } + + c.ntfnHandlers.OnRelevantTxAccepted(transaction) + // OnRescanFinished case btcjson.RescanFinishedNtfnMethod: // Ignore the notification if the client is not interested in @@ -435,6 +521,115 @@ func parseChainNtfnParams(params []json.RawMessage) (*chainhash.Hash, return blockHash, blockHeight, blockTime, nil } +// parseFilteredBlockConnectedParams parses out the parameters included in a +// filteredblockconnected notification. +// +// NOTE: This is a btcd extension ported from github.com/decred/dcrrpcclient +// and requires a websocket connection. +func parseFilteredBlockConnectedParams(params []json.RawMessage) (int32, + *wire.BlockHeader, []*btcutil.Tx, error) { + + if len(params) < 3 { + return 0, nil, nil, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as an integer. + var blockHeight int32 + err := json.Unmarshal(params[0], &blockHeight) + if err != nil { + return 0, nil, nil, err + } + + // Unmarshal second parameter as a slice of bytes. + blockHeaderBytes, err := parseHexParam(params[1]) + if err != nil { + return 0, nil, nil, err + } + + // Deserialize block header from slice of bytes. + var blockHeader wire.BlockHeader + err = blockHeader.Deserialize(bytes.NewReader(blockHeaderBytes)) + if err != nil { + return 0, nil, nil, err + } + + // Unmarshal third parameter as a slice of hex-encoded strings. + var hexTransactions []string + err = json.Unmarshal(params[2], &hexTransactions) + if err != nil { + return 0, nil, nil, err + } + + // Create slice of transactions from slice of strings by hex-decoding. + transactions := make([]*btcutil.Tx, len(hexTransactions)) + for i, hexTx := range hexTransactions { + transaction, err := hex.DecodeString(hexTx) + if err != nil { + return 0, nil, nil, err + } + + transactions[i], err = btcutil.NewTxFromBytes(transaction) + if err != nil { + return 0, nil, nil, err + } + } + + return blockHeight, &blockHeader, transactions, nil +} + +// parseFilteredBlockDisconnectedParams parses out the parameters included in a +// filteredblockdisconnected notification. +// +// NOTE: This is a btcd extension ported from github.com/decred/dcrrpcclient +// and requires a websocket connection. +func parseFilteredBlockDisconnectedParams(params []json.RawMessage) (int32, + *wire.BlockHeader, error) { + if len(params) < 2 { + return 0, nil, wrongNumParams(len(params)) + } + + // Unmarshal first parameter as an integer. + var blockHeight int32 + err := json.Unmarshal(params[0], &blockHeight) + if err != nil { + return 0, nil, err + } + + // Unmarshal second parmeter as a slice of bytes. + blockHeaderBytes, err := parseHexParam(params[1]) + if err != nil { + return 0, nil, err + } + + // Deserialize block header from slice of bytes. + var blockHeader wire.BlockHeader + err = blockHeader.Deserialize(bytes.NewReader(blockHeaderBytes)) + if err != nil { + return 0, nil, err + } + + return blockHeight, &blockHeader, nil +} + +func parseHexParam(param json.RawMessage) ([]byte, error) { + var s string + err := json.Unmarshal(param, &s) + if err != nil { + return nil, err + } + return hex.DecodeString(s) +} + +// parseRelevantTxAcceptedParams parses out the parameter included in a +// relevanttxaccepted notification. +func parseRelevantTxAcceptedParams(params []json.RawMessage) (transaction []byte, err error) { + if len(params) < 1 { + return nil, wrongNumParams(len(params)) + } + + return parseHexParam(params[0]) +} + // parseChainTxNtfnParams parses out the transaction and optional details about // the block it's mined in from the parameters of recvtx and redeemingtx // notifications. @@ -705,6 +900,8 @@ func (c *Client) NotifyBlocks() error { // FutureNotifySpentResult is a future promise to deliver the result of a // NotifySpentAsync RPC invocation (or an applicable error). +// +// NOTE: Deprecated. Use FutureLoadTxFilterResult instead. type FutureNotifySpentResult chan *response // Receive waits for the response promised by the future and returns an error @@ -749,6 +946,8 @@ func newOutPointFromWire(op *wire.OutPoint) btcjson.OutPoint { // See NotifySpent for the blocking version and more details. // // NOTE: This is a btcd extension and requires a websocket connection. +// +// NOTE: Deprecated. Use LoadTxFilterAsync instead. func (c *Client) NotifySpentAsync(outpoints []*wire.OutPoint) FutureNotifySpentResult { // Not supported in HTTP POST mode. if c.config.HTTPPostMode { @@ -779,6 +978,8 @@ func (c *Client) NotifySpentAsync(outpoints []*wire.OutPoint) FutureNotifySpentR // OnRedeemingTx. // // NOTE: This is a btcd extension and requires a websocket connection. +// +// NOTE: Deprecated. Use LoadTxFilter instead. func (c *Client) NotifySpent(outpoints []*wire.OutPoint) error { return c.NotifySpentAsync(outpoints).Receive() } @@ -834,6 +1035,8 @@ func (c *Client) NotifyNewTransactions(verbose bool) error { // FutureNotifyReceivedResult is a future promise to deliver the result of a // NotifyReceivedAsync RPC invocation (or an applicable error). +// +// NOTE: Deprecated. Use FutureLoadTxFilterResult instead. type FutureNotifyReceivedResult chan *response // Receive waits for the response promised by the future and returns an error @@ -870,6 +1073,8 @@ func (c *Client) notifyReceivedInternal(addresses []string) FutureNotifyReceived // See NotifyReceived for the blocking version and more details. // // NOTE: This is a btcd extension and requires a websocket connection. +// +// NOTE: Deprecated. Use LoadTxFilterAsync instead. func (c *Client) NotifyReceivedAsync(addresses []btcutil.Address) FutureNotifyReceivedResult { // Not supported in HTTP POST mode. if c.config.HTTPPostMode { @@ -908,6 +1113,8 @@ func (c *Client) NotifyReceivedAsync(addresses []btcutil.Address) FutureNotifyRe // the address). // // NOTE: This is a btcd extension and requires a websocket connection. +// +// NOTE: Deprecated. Use LoadTxFilter instead. func (c *Client) NotifyReceived(addresses []btcutil.Address) error { return c.NotifyReceivedAsync(addresses).Receive() } @@ -1080,3 +1287,57 @@ func (c *Client) RescanEndHeight(startBlock *chainhash.Hash, return c.RescanEndBlockAsync(startBlock, addresses, outpoints, endBlock).Receive() } + +// FutureLoadTxFilterResult is a future promise to deliver the result +// of a LoadTxFilterAsync RPC invocation (or an applicable error). +// +// NOTE: This is a btcd extension ported from github.com/decred/dcrrpcclient +// and requires a websocket connection. +type FutureLoadTxFilterResult chan *response + +// Receive waits for the response promised by the future and returns an error +// if the registration was not successful. +// +// NOTE: This is a btcd extension ported from github.com/decred/dcrrpcclient +// and requires a websocket connection. +func (r FutureLoadTxFilterResult) Receive() error { + _, err := receiveFuture(r) + return err +} + +// LoadTxFilterAsync 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 LoadTxFilter for the blocking version and more details. +// +// NOTE: This is a btcd extension ported from github.com/decred/dcrrpcclient +// and requires a websocket connection. +func (c *Client) LoadTxFilterAsync(reload bool, addresses []btcutil.Address, + outPoints []wire.OutPoint) FutureLoadTxFilterResult { + + addrStrs := make([]string, len(addresses)) + for i, a := range addresses { + addrStrs[i] = a.EncodeAddress() + } + outPointObjects := make([]btcjson.OutPoint, len(outPoints)) + for i := range outPoints { + outPointObjects[i] = btcjson.OutPoint{ + Hash: outPoints[i].Hash.String(), + Index: outPoints[i].Index, + } + } + + cmd := btcjson.NewLoadTxFilterCmd(reload, addrStrs, outPointObjects) + return c.sendCmd(cmd) +} + +// LoadTxFilter loads, reloads, or adds data to a websocket client's transaction +// filter. The filter is consistently updated based on inspected transactions +// during mempool acceptance, block acceptance, and for all rescanned blocks. +// +// NOTE: This is a btcd extension ported from github.com/decred/dcrrpcclient +// and requires a websocket connection. +func (c *Client) LoadTxFilter(reload bool, addresses []btcutil.Address, outPoints []wire.OutPoint) error { + return c.LoadTxFilterAsync(reload, addresses, outPoints).Receive() +} From abcdfb702a7ca67e4a32c10be380c63216bd69fe Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 24 Jan 2017 19:44:20 -0700 Subject: [PATCH 147/153] Port `rescanblocks` JSON-RPC command from dcrd --- chain.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- notify.go | 14 ++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/chain.go b/chain.go index 8cb54fb4..48fb600e 100644 --- a/chain.go +++ b/chain.go @@ -1,4 +1,5 @@ -// Copyright (c) 2014-2016 The btcsuite developers +// Copyright (c) 2014-2017 The btcsuite developers +// Copyright (c) 2015-2017 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -654,3 +655,58 @@ func (c *Client) GetTxOutAsync(txHash *chainhash.Hash, index uint32, mempool boo func (c *Client) GetTxOut(txHash *chainhash.Hash, index uint32, mempool bool) (*btcjson.GetTxOutResult, error) { return c.GetTxOutAsync(txHash, index, mempool).Receive() } + +// FutureRescanBlocksResult is a future promise to deliver the result of a +// RescanBlocksAsync RPC invocation (or an applicable error). +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +type FutureRescanBlocksResult chan *response + +// Receive waits for the response promised by the future and returns the +// discovered rescanblocks data. +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +func (r FutureRescanBlocksResult) Receive() ([]btcjson.RescannedBlock, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + var rescanBlocksResult []btcjson.RescannedBlock + err = json.Unmarshal(res, &rescanBlocksResult) + if err != nil { + return nil, err + } + + return rescanBlocksResult, nil +} + +// RescanBlocksAsync 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 RescanBlocks for the blocking version and more details. +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +func (c *Client) RescanBlocksAsync(blockHashes []chainhash.Hash) FutureRescanBlocksResult { + strBlockHashes := make([]string, len(blockHashes)) + for i := range blockHashes { + strBlockHashes[i] = blockHashes[i].String() + } + + cmd := btcjson.NewRescanBlocksCmd(strBlockHashes) + return c.sendCmd(cmd) +} + +// RescanBlocks rescans the blocks identified by blockHashes, in order, using +// the client's loaded transaction filter. The blocks do not need to be on the +// main chain, but they do need to be adjacent to each other. +// +// NOTE: This is a btcsuite extension ported from +// github.com/decred/dcrrpcclient. +func (c *Client) RescanBlocks(blockHashes []chainhash.Hash) ([]btcjson.RescannedBlock, error) { + return c.RescanBlocksAsync(blockHashes).Receive() +} diff --git a/notify.go b/notify.go index 3391708c..600ae14c 100644 --- a/notify.go +++ b/notify.go @@ -156,11 +156,15 @@ type NotificationHandlers struct { // signaled on this notification, rather than relying on the return // result of a rescan request, due to how btcd may send various rescan // notifications after the rescan request has already returned. + // + // NOTE: Deprecated. Not used with RescanBlocks. OnRescanFinished func(hash *chainhash.Hash, height int32, blkTime time.Time) // 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. + // + // NOTE: Deprecated. Not used with RescanBlocks. OnRescanProgress func(hash *chainhash.Hash, height int32, blkTime time.Time) // OnTxAccepted is invoked when a transaction is accepted into the @@ -1121,6 +1125,8 @@ func (c *Client) NotifyReceived(addresses []btcutil.Address) error { // FutureRescanResult is a future promise to deliver the result of a RescanAsync // or RescanEndHeightAsync RPC invocation (or an applicable error). +// +// NOTE: Deprecated. Use FutureRescanBlocksResult instead. type FutureRescanResult chan *response // Receive waits for the response promised by the future and returns an error @@ -1143,6 +1149,8 @@ func (r FutureRescanResult) Receive() error { // reconnect. // // NOTE: This is a btcd extension and requires a websocket connection. +// +// NOTE: Deprecated. Use RescanBlocksAsync instead. func (c *Client) RescanAsync(startBlock *chainhash.Hash, addresses []btcutil.Address, outpoints []*wire.OutPoint) FutureRescanResult { @@ -1206,6 +1214,8 @@ func (c *Client) RescanAsync(startBlock *chainhash.Hash, // reconnect. // // NOTE: This is a btcd extension and requires a websocket connection. +// +// NOTE: Deprecated. Use RescanBlocks instead. func (c *Client) Rescan(startBlock *chainhash.Hash, addresses []btcutil.Address, outpoints []*wire.OutPoint) error { @@ -1220,6 +1230,8 @@ func (c *Client) Rescan(startBlock *chainhash.Hash, // See RescanEndBlock for the blocking version and more details. // // NOTE: This is a btcd extension and requires a websocket connection. +// +// NOTE: Deprecated. Use RescanBlocksAsync instead. func (c *Client) RescanEndBlockAsync(startBlock *chainhash.Hash, addresses []btcutil.Address, outpoints []*wire.OutPoint, endBlock *chainhash.Hash) FutureRescanResult { @@ -1280,6 +1292,8 @@ func (c *Client) RescanEndBlockAsync(startBlock *chainhash.Hash, // 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. +// +// NOTE: Deprecated. Use RescanBlocks instead. func (c *Client) RescanEndHeight(startBlock *chainhash.Hash, addresses []btcutil.Address, outpoints []*wire.OutPoint, endBlock *chainhash.Hash) error { From eef39394b7eb639be140973f8f2dcc2020784f5f Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Wed, 25 Jan 2017 23:48:44 -0600 Subject: [PATCH 148/153] Update ws example to use new filtered blocks ntfns. The old notifications are now deprecated, so the examples shouldn't use them. --- examples/btcdwebsockets/main.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/examples/btcdwebsockets/main.go b/examples/btcdwebsockets/main.go index 93809953..96c50a19 100644 --- a/examples/btcdwebsockets/main.go +++ b/examples/btcdwebsockets/main.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 The btcsuite developers +// Copyright (c) 2014-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -10,7 +10,7 @@ import ( "path/filepath" "time" - "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcrpcclient" "github.com/btcsuite/btcutil" ) @@ -21,11 +21,13 @@ func main() { // for notifications. See the documentation of the btcrpcclient // NotificationHandlers type for more details about each handler. ntfnHandlers := btcrpcclient.NotificationHandlers{ - OnBlockConnected: func(hash *chainhash.Hash, height int32, time time.Time) { - log.Printf("Block connected: %v (%d) %v", hash, height, time) + OnFilteredBlockConnected: func(height int32, header *wire.BlockHeader, txns []*btcutil.Tx) { + log.Printf("Block connected: %v (%d) %v", + header.BlockHash(), height, header.Timestamp) }, - OnBlockDisconnected: func(hash *chainhash.Hash, height int32, time time.Time) { - log.Printf("Block disconnected: %v (%d) %v", hash, height, time) + OnFilteredBlockDisconnected: func(height int32, header *wire.BlockHeader) { + log.Printf("Block disconnected: %v (%d) %v", + header.BlockHash(), height, header.Timestamp) }, } From e3e4c726af933ac58e84935fd26b2aa9939fefaf Mon Sep 17 00:00:00 2001 From: David Hill Date: Fri, 3 Feb 2017 14:27:05 -0500 Subject: [PATCH 149/153] Add GetMempoolEntry API --- chain.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/chain.go b/chain.go index 48fb600e..a0968588 100644 --- a/chain.go +++ b/chain.go @@ -426,6 +426,45 @@ func (c *Client) GetBlockHeaderVerbose(blockHash *chainhash.Hash) (*btcjson.GetB return c.GetBlockHeaderVerboseAsync(blockHash).Receive() } +// FutureGetMempoolEntryResult is a future promise to deliver the result of a +// GetMempoolEntryAsync RPC invocation (or an applicable error). +type FutureGetMempoolEntryResult chan *response + +// Receive waits for the response promised by the future and returns a data +// structure with information about the transaction in the memory pool given +// its hash. +func (r FutureGetMempoolEntryResult) Receive() (*btcjson.GetMempoolEntryResult, error) { + res, err := receiveFuture(r) + if err != nil { + return nil, err + } + + // Unmarshal the result as an array of strings. + var mempoolEntryResult btcjson.GetMempoolEntryResult + err = json.Unmarshal(res, &mempoolEntryResult) + if err != nil { + return nil, err + } + + return &mempoolEntryResult, nil +} + +// GetMempoolEntryAsync 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 GetMempoolEntry for the blocking version and more details. +func (c *Client) GetMempoolEntryAsync(txHash string) FutureGetMempoolEntryResult { + cmd := btcjson.NewGetMempoolEntryCmd(txHash) + return c.sendCmd(cmd) +} + +// GetMempoolEntry returns a data structure with information about the +// transaction in the memory pool given its hash. +func (c *Client) GetMempoolEntry(txHash string) (*btcjson.GetMempoolEntryResult, error) { + return c.GetMempoolEntryAsync(txHash).Receive() +} + // FutureGetRawMempoolResult is a future promise to deliver the result of a // GetRawMempoolAsync RPC invocation (or an applicable error). type FutureGetRawMempoolResult chan *response From b14827fd646e93d77e54edb524eb70e8f5914716 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 25 May 2017 12:42:31 -0500 Subject: [PATCH 150/153] Update markdown files for GFM changes. The github markdown interpreter has been changed such that it no longer allows spaces in between the brackets and parenthesis of links. This updates the markdown files accordingly. While here, it also corrects a couple of inconsistencies in regards to other README.md files in the project. --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3f1c52e2..9c3b329e 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ btcrpcclient ============ -[![Build Status](https://travis-ci.org/btcsuite/btcrpcclient.png?branch=master)] -(https://travis-ci.org/btcsuite/btcrpcclient) -[![GoDoc](https://godoc.org/github.com/btcsuite/btcrpcclient?status.png)] -(http://godoc.org/github.com/btcsuite/btcrpcclient) +[![Build Status](http://img.shields.io/travis/btcsuite/btcrpcclient.svg)](https://travis-ci.org/btcsuite/btcrpcclient) +[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcrpcclient) btcrpcclient implements a Websocket-enabled Bitcoin JSON-RPC client package written in [Go](http://golang.org/). It provides a robust and easy to use From 45b9cb481d2aead4e80aab32d7aa86db386430ab Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Thu, 25 May 2017 09:28:33 -0400 Subject: [PATCH 151/153] Track btclog API updates. --- log.go | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/log.go b/log.go index 4d629487..1886a189 100644 --- a/log.go +++ b/log.go @@ -5,9 +5,6 @@ package btcrpcclient import ( - "errors" - "io" - "github.com/btcsuite/btclog" ) @@ -22,41 +19,16 @@ func init() { } // DisableLog disables all library log output. Logging output is disabled -// by default until either UseLogger or SetLogWriter are called. +// by default until UseLogger is 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. From c72658166ae09457e6beb14e9112241e352ebd35 Mon Sep 17 00:00:00 2001 From: Ruben de Vries Date: Thu, 29 Jun 2017 16:02:09 +0200 Subject: [PATCH 152/153] add InvalidateBlock RPC command --- CONTRIBUTORS | 1 + chain.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 1b09bce3..86c2b993 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -10,3 +10,4 @@ Dave Collins Geert-Johan Riemer Josh Rickmar Michalis Kargakis +Ruben de Vries Date: Fri, 21 Jul 2017 21:53:54 -0500 Subject: [PATCH 153/153] Import btcrpcclient repo into rpcclient directory. This commit contains the entire btcrpcclient repository along with several changes needed to move all of the files into the rpcclient directory in order to prepare it for merging. This does NOT update btcd or any of the other packages to use the new location as that will be done separately. - All import paths in the old btcrpcclient files have been changed to the new location - All references to btcrpcclient as the package name have been changed to rpcclient --- .gitignore | 28 --------- .travis.yml | 12 ---- LICENSE | 16 ------ goclean.sh | 57 ------------------- CONTRIBUTORS => rpcclient/CONTRIBUTORS | 0 README.md => rpcclient/README.md | 28 ++++----- chain.go => rpcclient/chain.go | 2 +- doc.go => rpcclient/doc.go | 6 +- .../examples}/bitcoincorehttp/README.md | 9 ++- .../examples}/bitcoincorehttp/main.go | 8 +-- .../examples}/btcdwebsockets/README.md | 11 ++-- .../examples}/btcdwebsockets/main.go | 10 ++-- .../examples}/btcwalletwebsockets/README.md | 9 ++- .../examples}/btcwalletwebsockets/main.go | 12 ++-- extensions.go => rpcclient/extensions.go | 2 +- .../infrastructure.go | 4 +- log.go => rpcclient/log.go | 4 +- mining.go => rpcclient/mining.go | 4 +- net.go => rpcclient/net.go | 4 +- notify.go => rpcclient/notify.go | 2 +- rawrequest.go => rpcclient/rawrequest.go | 4 +- .../rawtransactions.go | 4 +- wallet.go => rpcclient/wallet.go | 4 +- 23 files changed, 62 insertions(+), 178 deletions(-) delete mode 100644 .gitignore delete mode 100644 .travis.yml delete mode 100644 LICENSE delete mode 100755 goclean.sh rename CONTRIBUTORS => rpcclient/CONTRIBUTORS (100%) rename README.md => rpcclient/README.md (65%) rename chain.go => rpcclient/chain.go (99%) rename doc.go => rpcclient/doc.go (98%) rename {examples => rpcclient/examples}/bitcoincorehttp/README.md (65%) rename {examples => rpcclient/examples}/bitcoincorehttp/main.go (81%) rename {examples => rpcclient/examples}/btcdwebsockets/README.md (62%) rename {examples => rpcclient/examples}/btcdwebsockets/main.go (89%) rename {examples => rpcclient/examples}/btcwalletwebsockets/README.md (71%) rename {examples => rpcclient/examples}/btcwalletwebsockets/main.go (86%) rename extensions.go => rpcclient/extensions.go (99%) rename infrastructure.go => rpcclient/infrastructure.go (99%) rename log.go => rpcclient/log.go (94%) rename mining.go => rpcclient/mining.go (99%) rename net.go => rpcclient/net.go (99%) rename notify.go => rpcclient/notify.go (99%) rename rawrequest.go => rpcclient/rawrequest.go (97%) rename rawtransactions.go => rpcclient/rawtransactions.go (99%) rename wallet.go => rpcclient/wallet.go (99%) diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 612e2e96..00000000 --- a/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# 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/.travis.yml b/.travis.yml deleted file mode 100644 index 65aa0a9d..00000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: go -go: - - 1.6.x - - 1.7.x -sudo: false -install: - - go get -d -t -v ./... - - go get -v github.com/alecthomas/gometalinter - - gometalinter --install -script: - - export PATH=$PATH:$HOME/gopath/bin - - ./goclean.sh diff --git a/LICENSE b/LICENSE deleted file mode 100644 index f627807e..00000000 --- a/LICENSE +++ /dev/null @@ -1,16 +0,0 @@ -ISC License - -Copyright (c) 2014-2016 The btcsuite developers -Copyright (c) 2015-2016 The Decred developers - -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. diff --git a/goclean.sh b/goclean.sh deleted file mode 100755 index 3c1f084f..00000000 --- a/goclean.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash -# The script does automatic checking on a Go package and its sub-packages, including: -# 1. gofmt (http://golang.org/cmd/gofmt/) -# 2. goimports (https://github.com/bradfitz/goimports) -# 3. golint (https://github.com/golang/lint) -# 4. go vet (http://golang.org/cmd/vet) -# 5. gosimple (https://github.com/dominikh/go-simple) -# 6. unconvert (https://github.com/mdempsky/unconvert) -# 7. race detector (http://blog.golang.org/race-detector) -# 8. test coverage (http://blog.golang.org/cover) -# -# gometalinter (github.com/alecthomas/gometalinter) is used to run each static -# checker. - -set -ex - -# Make sure gometalinter is installed and $GOPATH/bin is in your path. -# $ go get -v github.com/alecthomas/gometalinter" -# $ gometalinter --install" -if [ ! -x "$(type -p gometalinter)" ]; then - exit 1 -fi - -# Automatic checks -test -z "$(gometalinter --disable-all \ ---enable=gofmt \ ---enable=goimports \ ---enable=golint \ ---enable=vet \ ---enable=gosimple \ ---enable=unconvert \ ---deadline=45s ./... | tee /dev/stderr)" -env GORACE="halt_on_error=1" go test -race ./... - -# Run test coverage on each subdirectories and merge the coverage profile. - -set +x -echo "mode: count" > profile.cov - -# Standard go tooling behavior is to ignore dirs with leading underscores. -for dir in $(find . -maxdepth 10 -not -path '.' -not -path './.git*' \ - -not -path '*/_*' -type d); -do -if ls $dir/*.go &> /dev/null; then - go test -covermode=count -coverprofile=$dir/profile.tmp $dir - if [ -f $dir/profile.tmp ]; then - cat $dir/profile.tmp | tail -n +2 >> profile.cov - rm $dir/profile.tmp - fi -fi -done - -go tool cover -func profile.cov - -# To submit the test coverage result to coveralls.io, -# use goveralls (https://github.com/mattn/goveralls) -# goveralls -coverprofile=profile.cov -service=travis-ci diff --git a/CONTRIBUTORS b/rpcclient/CONTRIBUTORS similarity index 100% rename from CONTRIBUTORS rename to rpcclient/CONTRIBUTORS diff --git a/README.md b/rpcclient/README.md similarity index 65% rename from README.md rename to rpcclient/README.md index 9c3b329e..9c3ef18f 100644 --- a/README.md +++ b/rpcclient/README.md @@ -1,14 +1,14 @@ -btcrpcclient -============ +rpcclient +========= -[![Build Status](http://img.shields.io/travis/btcsuite/btcrpcclient.svg)](https://travis-ci.org/btcsuite/btcrpcclient) +[![Build Status](http://img.shields.io/travis/btcsuite/btcd.svg)](https://travis-ci.org/btcsuite/btcd) [![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) -[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcrpcclient) +[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcd/rpcclient) -btcrpcclient implements a Websocket-enabled Bitcoin JSON-RPC client package -written in [Go](http://golang.org/). It provides a robust and easy to use -client for interfacing with a Bitcoin RPC server that uses a btcd/bitcoin core -compatible Bitcoin JSON-RPC API. +rpcclient implements a Websocket-enabled Bitcoin JSON-RPC client package written +in [Go](http://golang.org/). It provides a robust and easy to use client for +interfacing with a Bitcoin RPC server that uses a btcd/bitcoin core compatible +Bitcoin JSON-RPC API. ## Status @@ -18,16 +18,16 @@ implement and the API is not stable yet. ## Documentation -* [API Reference](http://godoc.org/github.com/btcsuite/btcrpcclient) -* [btcd Websockets Example](https://github.com/btcsuite/btcrpcclient/blob/master/examples/btcdwebsockets) +* [API Reference](http://godoc.org/github.com/btcsuite/rpcclient) +* [btcd Websockets Example](https://github.com/btcsuite/btcd/rpcclient/blob/master/examples/btcdwebsockets) Connects to a btcd RPC server using TLS-secured websockets, registers for block connected and block disconnected notifications, and gets the current block count -* [btcwallet Websockets Example](https://github.com/btcsuite/btcrpcclient/blob/master/examples/btcwalletwebsockets) +* [btcwallet Websockets Example](https://github.com/btcsuite/btcd/rpcclient/blob/master/examples/btcwalletwebsockets) Connects to a btcwallet RPC server using TLS-secured websockets, registers for notifications about changes to account balances, and gets a list of unspent transaction outputs (utxos) the wallet can sign -* [Bitcoin Core HTTP POST Example](https://github.com/btcsuite/btcrpcclient/blob/master/examples/bitcoincorehttp) +* [Bitcoin Core HTTP POST Example](https://github.com/btcsuite/btcd/rpcclient/blob/master/examples/bitcoincorehttp) Connects to a bitcoin core RPC server using HTTP POST mode with TLS disabled and gets the current block count @@ -47,10 +47,10 @@ implement and the API is not stable yet. ## Installation ```bash -$ go get -u github.com/btcsuite/btcrpcclient +$ go get -u github.com/btcsuite/btcd/rpcclient ``` ## License -Package btcrpcclient is licensed under the [copyfree](http://copyfree.org) ISC +Package rpcclient is licensed under the [copyfree](http://copyfree.org) ISC License. diff --git a/chain.go b/rpcclient/chain.go similarity index 99% rename from chain.go rename to rpcclient/chain.go index 592e1887..2539bc80 100644 --- a/chain.go +++ b/rpcclient/chain.go @@ -3,7 +3,7 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package btcrpcclient +package rpcclient import ( "bytes" diff --git a/doc.go b/rpcclient/doc.go similarity index 98% rename from doc.go rename to rpcclient/doc.go index 43882e01..d4bbb64d 100644 --- a/doc.go +++ b/rpcclient/doc.go @@ -1,9 +1,9 @@ -// Copyright (c) 2014 The btcsuite developers +// Copyright (c) 2014-2017 The btcsuite developers // 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. +Package rpcclient implements a websocket-enabled Bitcoin JSON-RPC client. Overview @@ -175,4 +175,4 @@ The following full-blown client examples are in the examples directory: for notifications about changes to account balances, and gets a list of unspent transaction outputs (utxos) the wallet can sign */ -package btcrpcclient +package rpcclient diff --git a/examples/bitcoincorehttp/README.md b/rpcclient/examples/bitcoincorehttp/README.md similarity index 65% rename from examples/bitcoincorehttp/README.md rename to rpcclient/examples/bitcoincorehttp/README.md index cc1dfa30..4d1f0adf 100644 --- a/examples/bitcoincorehttp/README.md +++ b/rpcclient/examples/bitcoincorehttp/README.md @@ -1,17 +1,16 @@ Bitcoin Core HTTP POST Example ============================== -This example shows how to use the btcrpcclient package to connect to a Bitcoin +This example shows how to use the rpcclient package to connect to a Bitcoin Core RPC server using HTTP POST mode with TLS disabled and gets the current block count. ## Running the Example -The first step is to use `go get` to download and install the btcrpcclient -package: +The first step is to use `go get` to download and install the rpcclient package: ```bash -$ go get github.com/btcsuite/btcrpcclient +$ go get github.com/btcsuite/btcd/rpcclient ``` Next, modify the `main.go` source to specify the correct RPC username and @@ -25,7 +24,7 @@ password for the RPC server: Finally, navigate to the example's directory and run it with: ```bash -$ cd $GOPATH/src/github.com/btcsuite/btcrpcclient/examples/bitcoincorehttp +$ cd $GOPATH/src/github.com/btcsuite/btcd/rpcclient/examples/bitcoincorehttp $ go run *.go ``` diff --git a/examples/bitcoincorehttp/main.go b/rpcclient/examples/bitcoincorehttp/main.go similarity index 81% rename from examples/bitcoincorehttp/main.go rename to rpcclient/examples/bitcoincorehttp/main.go index 1f5f8b35..489770a2 100644 --- a/examples/bitcoincorehttp/main.go +++ b/rpcclient/examples/bitcoincorehttp/main.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 The btcsuite developers +// Copyright (c) 2014-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -7,12 +7,12 @@ package main import ( "log" - "github.com/btcsuite/btcrpcclient" + "github.com/btcsuite/btcd/rpcclient" ) func main() { // Connect to local bitcoin core RPC server using HTTP POST mode. - connCfg := &btcrpcclient.ConnConfig{ + connCfg := &rpcclient.ConnConfig{ Host: "localhost:8332", User: "yourrpcuser", Pass: "yourrpcpass", @@ -21,7 +21,7 @@ func main() { } // Notice the notification parameter is nil since notifications are // not supported in HTTP POST mode. - client, err := btcrpcclient.New(connCfg, nil) + client, err := rpcclient.New(connCfg, nil) if err != nil { log.Fatal(err) } diff --git a/examples/btcdwebsockets/README.md b/rpcclient/examples/btcdwebsockets/README.md similarity index 62% rename from examples/btcdwebsockets/README.md rename to rpcclient/examples/btcdwebsockets/README.md index b6050fef..a1686484 100644 --- a/examples/btcdwebsockets/README.md +++ b/rpcclient/examples/btcdwebsockets/README.md @@ -1,8 +1,8 @@ btcd Websockets Example ======================= -This example shows how to use the btcrpcclient package to connect to a btcd -RPC server using TLS-secured websockets, register for block connected and block +This example shows how to use the rpcclient package to connect to a btcd RPC +server using TLS-secured websockets, register for block connected and block disconnected notifications, and get the current block count. This example also sets a timer to shutdown the client after 10 seconds to @@ -10,11 +10,10 @@ demonstrate clean shutdown. ## Running the Example -The first step is to use `go get` to download and install the btcrpcclient -package: +The first step is to use `go get` to download and install the rpcclient package: ```bash -$ go get github.com/btcsuite/btcrpcclient +$ go get github.com/btcsuite/btcd/rpcclient ``` Next, modify the `main.go` source to specify the correct RPC username and @@ -28,7 +27,7 @@ password for the RPC server: Finally, navigate to the example's directory and run it with: ```bash -$ cd $GOPATH/src/github.com/btcsuite/btcrpcclient/examples/btcdwebsockets +$ cd $GOPATH/src/github.com/btcsuite/btcd/rpcclient/examples/btcdwebsockets $ go run *.go ``` diff --git a/examples/btcdwebsockets/main.go b/rpcclient/examples/btcdwebsockets/main.go similarity index 89% rename from examples/btcdwebsockets/main.go rename to rpcclient/examples/btcdwebsockets/main.go index 96c50a19..56d12d82 100644 --- a/examples/btcdwebsockets/main.go +++ b/rpcclient/examples/btcdwebsockets/main.go @@ -10,17 +10,17 @@ import ( "path/filepath" "time" + "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcrpcclient" "github.com/btcsuite/btcutil" ) func main() { // Only override the handlers for notifications you care about. // Also note most of these handlers will only be called if you register - // for notifications. See the documentation of the btcrpcclient + // for notifications. See the documentation of the rpcclient // NotificationHandlers type for more details about each handler. - ntfnHandlers := btcrpcclient.NotificationHandlers{ + ntfnHandlers := rpcclient.NotificationHandlers{ OnFilteredBlockConnected: func(height int32, header *wire.BlockHeader, txns []*btcutil.Tx) { log.Printf("Block connected: %v (%d) %v", header.BlockHash(), height, header.Timestamp) @@ -37,14 +37,14 @@ func main() { if err != nil { log.Fatal(err) } - connCfg := &btcrpcclient.ConnConfig{ + connCfg := &rpcclient.ConnConfig{ Host: "localhost:8334", Endpoint: "ws", User: "yourrpcuser", Pass: "yourrpcpass", Certificates: certs, } - client, err := btcrpcclient.New(connCfg, &ntfnHandlers) + client, err := rpcclient.New(connCfg, &ntfnHandlers) if err != nil { log.Fatal(err) } diff --git a/examples/btcwalletwebsockets/README.md b/rpcclient/examples/btcwalletwebsockets/README.md similarity index 71% rename from examples/btcwalletwebsockets/README.md rename to rpcclient/examples/btcwalletwebsockets/README.md index 3d41e944..e495dff8 100644 --- a/examples/btcwalletwebsockets/README.md +++ b/rpcclient/examples/btcwalletwebsockets/README.md @@ -1,7 +1,7 @@ btcwallet Websockets Example ============================ -This example shows how to use the btcrpcclient package to connect to a btcwallet +This example shows how to use the rpcclient package to connect to a btcwallet RPC server using TLS-secured websockets, register for notifications about changes to account balances, and get a list of unspent transaction outputs (utxos) the wallet can sign. @@ -11,11 +11,10 @@ demonstrate clean shutdown. ## Running the Example -The first step is to use `go get` to download and install the btcrpcclient -package: +The first step is to use `go get` to download and install the rpcclient package: ```bash -$ go get github.com/btcsuite/btcrpcclient +$ go get github.com/btcsuite/btcd/rpcclient ``` Next, modify the `main.go` source to specify the correct RPC username and @@ -29,7 +28,7 @@ password for the RPC server: Finally, navigate to the example's directory and run it with: ```bash -$ cd $GOPATH/src/github.com/btcsuite/btcrpcclient/examples/btcwalletwebsockets +$ cd $GOPATH/src/github.com/btcsuite/btcd/rpcclient/examples/btcwalletwebsockets $ go run *.go ``` diff --git a/examples/btcwalletwebsockets/main.go b/rpcclient/examples/btcwalletwebsockets/main.go similarity index 86% rename from examples/btcwalletwebsockets/main.go rename to rpcclient/examples/btcwalletwebsockets/main.go index 545fe7bb..e803138d 100644 --- a/examples/btcwalletwebsockets/main.go +++ b/rpcclient/examples/btcwalletwebsockets/main.go @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2015 The btcsuite developers +// Copyright (c) 2014-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -10,7 +10,7 @@ import ( "path/filepath" "time" - "github.com/btcsuite/btcrpcclient" + "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcutil" "github.com/davecgh/go-spew/spew" ) @@ -18,9 +18,9 @@ import ( func main() { // Only override the handlers for notifications you care about. // Also note most of the handlers will only be called if you register - // for notifications. See the documentation of the btcrpcclient + // for notifications. See the documentation of the rpcclient // NotificationHandlers type for more details about each handler. - ntfnHandlers := btcrpcclient.NotificationHandlers{ + ntfnHandlers := rpcclient.NotificationHandlers{ OnAccountBalance: func(account string, balance btcutil.Amount, confirmed bool) { log.Printf("New balance for account %s: %v", account, balance) @@ -33,14 +33,14 @@ func main() { if err != nil { log.Fatal(err) } - connCfg := &btcrpcclient.ConnConfig{ + connCfg := &rpcclient.ConnConfig{ Host: "localhost:18332", Endpoint: "ws", User: "yourrpcuser", Pass: "yourrpcpass", Certificates: certs, } - client, err := btcrpcclient.New(connCfg, &ntfnHandlers) + client, err := rpcclient.New(connCfg, &ntfnHandlers) if err != nil { log.Fatal(err) } diff --git a/extensions.go b/rpcclient/extensions.go similarity index 99% rename from extensions.go rename to rpcclient/extensions.go index e5315b9a..916ddb5f 100644 --- a/extensions.go +++ b/rpcclient/extensions.go @@ -3,7 +3,7 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package btcrpcclient +package rpcclient import ( "bytes" diff --git a/infrastructure.go b/rpcclient/infrastructure.go similarity index 99% rename from infrastructure.go rename to rpcclient/infrastructure.go index 8f6e2423..24f0ae99 100644 --- a/infrastructure.go +++ b/rpcclient/infrastructure.go @@ -1,8 +1,8 @@ -// Copyright (c) 2014-2016 The btcsuite developers +// Copyright (c) 2014-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package btcrpcclient +package rpcclient import ( "bytes" diff --git a/log.go b/rpcclient/log.go similarity index 94% rename from log.go rename to rpcclient/log.go index 1886a189..30226187 100644 --- a/log.go +++ b/rpcclient/log.go @@ -1,8 +1,8 @@ -// Copyright (c) 2014-2015 The btcsuite developers +// Copyright (c) 2014-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package btcrpcclient +package rpcclient import ( "github.com/btcsuite/btclog" diff --git a/mining.go b/rpcclient/mining.go similarity index 99% rename from mining.go rename to rpcclient/mining.go index aaf4b7f3..76a9d305 100644 --- a/mining.go +++ b/rpcclient/mining.go @@ -1,8 +1,8 @@ -// Copyright (c) 2014-2016 The btcsuite developers +// Copyright (c) 2014-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package btcrpcclient +package rpcclient import ( "encoding/hex" diff --git a/net.go b/rpcclient/net.go similarity index 99% rename from net.go rename to rpcclient/net.go index d4e91a81..aeda8b3c 100644 --- a/net.go +++ b/rpcclient/net.go @@ -1,8 +1,8 @@ -// Copyright (c) 2014-2015 The btcsuite developers +// Copyright (c) 2014-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package btcrpcclient +package rpcclient import ( "encoding/json" diff --git a/notify.go b/rpcclient/notify.go similarity index 99% rename from notify.go rename to rpcclient/notify.go index 600ae14c..9a0a3568 100644 --- a/notify.go +++ b/rpcclient/notify.go @@ -3,7 +3,7 @@ // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package btcrpcclient +package rpcclient import ( "bytes" diff --git a/rawrequest.go b/rpcclient/rawrequest.go similarity index 97% rename from rawrequest.go rename to rpcclient/rawrequest.go index b4df97d0..dd16fd04 100644 --- a/rawrequest.go +++ b/rpcclient/rawrequest.go @@ -1,8 +1,8 @@ -// Copyright (c) 2014-2015 The btcsuite developers +// Copyright (c) 2014-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package btcrpcclient +package rpcclient import ( "encoding/json" diff --git a/rawtransactions.go b/rpcclient/rawtransactions.go similarity index 99% rename from rawtransactions.go rename to rpcclient/rawtransactions.go index 0c934554..3bd6e5d3 100644 --- a/rawtransactions.go +++ b/rpcclient/rawtransactions.go @@ -1,8 +1,8 @@ -// Copyright (c) 2014-2016 The btcsuite developers +// Copyright (c) 2014-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package btcrpcclient +package rpcclient import ( "bytes" diff --git a/wallet.go b/rpcclient/wallet.go similarity index 99% rename from wallet.go rename to rpcclient/wallet.go index 3459d012..caea70b8 100644 --- a/wallet.go +++ b/rpcclient/wallet.go @@ -1,8 +1,8 @@ -// Copyright (c) 2014-2016 The btcsuite developers +// Copyright (c) 2014-2017 The btcsuite developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. -package btcrpcclient +package rpcclient import ( "encoding/json"