Add custom/raw request/response support.

This commit is contained in:
Josh Rickmar 2014-06-09 19:49:41 -05:00
parent 793e66c785
commit 1ec6dde39c
9 changed files with 948 additions and 638 deletions

141
chain.go
View file

@ -7,7 +7,7 @@ package btcrpcclient
import ( import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"fmt" "encoding/json"
"github.com/conformal/btcjson" "github.com/conformal/btcjson"
"github.com/conformal/btcutil" "github.com/conformal/btcutil"
"github.com/conformal/btcwire" "github.com/conformal/btcwire"
@ -15,23 +15,22 @@ import (
// FutureGetBestBlockHashResult is a future promise to deliver the result of a // FutureGetBestBlockHashResult is a future promise to deliver the result of a
// GetBestBlockAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the hash of
// the best block in the longest block chain. // the best block in the longest block chain.
func (r FutureGetBestBlockHashResult) Receive() (*btcwire.ShaHash, error) { func (r FutureGetBestBlockHashResult) Receive() (*btcwire.ShaHash, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a string.
txHashStr, ok := reply.(string) var txHashStr string
if !ok { err = json.Unmarshal(res, &txHashStr)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getbestblockhash: %T\n", reply) return nil, err
} }
return btcwire.NewShaHashFromStr(txHashStr) 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 // FutureGetBlockResult is a future promise to deliver the result of a
// GetBlockAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the raw
// block requested from the server given its hash. // block requested from the server given its hash.
func (r FutureGetBlockResult) Receive() (*btcutil.Block, error) { func (r FutureGetBlockResult) Receive() (*btcutil.Block, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a string.
blockHex, ok := reply.(string) var blockHex string
if !ok { err = json.Unmarshal(res, &blockHex)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getblock (verbose=0): %T\n", reply) return nil, err
} }
// Decode the serialized block hex to raw bytes. // 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 // FutureGetBlockVerboseResult is a future promise to deliver the result of a
// GetBlockVerboseAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the data
// structure from the server with information about the requested block. // structure from the server with information about the requested block.
func (r FutureGetBlockVerboseResult) Receive() (*btcjson.BlockResult, error) { func (r FutureGetBlockVerboseResult) Receive() (*btcjson.BlockResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal the raw result into a BlockResult.
blockResult, ok := reply.(*btcjson.BlockResult) var blockResult btcjson.BlockResult
if !ok { err = json.Unmarshal(res, &blockResult)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getblock (verbose=1): %T\n", reply) return nil, err
} }
return &blockResult, nil
return blockResult, nil
} }
// GetBlockVerboseAsync returns an instance of a type that can be used to get // 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 // FutureGetBlockCountResult is a future promise to deliver the result of a
// GetBlockCountAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the number
// of blocks in the longest block chain. // of blocks in the longest block chain.
func (r FutureGetBlockCountResult) Receive() (int64, error) { func (r FutureGetBlockCountResult) Receive() (int64, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return 0, err return 0, err
} }
// Ensure the returned data is the expected type. // Unmarshal the result as an int64.
count, ok := reply.(float64) var count int64
if !ok { err = json.Unmarshal(res, &count)
return 0, fmt.Errorf("unexpected response type for "+ if err != nil {
"getblockcount: %T\n", reply) return 0, err
} }
return count, nil
return int64(count), nil
} }
// GetBlockCountAsync returns an instance of a type that can be used to get the // 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 // FutureGetDifficultyResult is a future promise to deliver the result of a
// GetDifficultyAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the
// proof-of-work difficulty as a multiple of the minimum difficulty. // proof-of-work difficulty as a multiple of the minimum difficulty.
func (r FutureGetDifficultyResult) Receive() (float64, error) { func (r FutureGetDifficultyResult) Receive() (float64, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return 0, err return 0, err
} }
// Ensure the returned data is the expected type. // Unmarshal the result as a float64.
difficulty, ok := reply.(float64) var difficulty float64
if !ok { err = json.Unmarshal(res, &difficulty)
return 0, fmt.Errorf("unexpected response type for "+ if err != nil {
"getdifficulty: %T\n", reply) return 0, err
} }
return difficulty, nil return difficulty, nil
} }
@ -255,23 +251,22 @@ func (c *Client) GetDifficulty() (float64, error) {
// FutureGetBlockHashResult is a future promise to deliver the result of a // FutureGetBlockHashResult is a future promise to deliver the result of a
// GetBlockHashAsync RPC invocation (or an applicable error). // 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 // 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. // the block in the best block chain at the given height.
func (r FutureGetBlockHashResult) Receive() (*btcwire.ShaHash, error) { func (r FutureGetBlockHashResult) Receive() (*btcwire.ShaHash, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal the result as a string-encoded sha.
txHashStr, ok := reply.(string) var txHashStr string
if !ok { err = json.Unmarshal(res, &txHashStr)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getblockhash: %T\n", reply) return nil, err
} }
return btcwire.NewShaHashFromStr(txHashStr) 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 // FutureGetRawMempoolResult is a future promise to deliver the result of a
// GetRawMempoolAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the hashes
// of all transactions in the memory pool. // of all transactions in the memory pool.
func (r FutureGetRawMempoolResult) Receive() ([]*btcwire.ShaHash, error) { func (r FutureGetRawMempoolResult) Receive() ([]*btcwire.ShaHash, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal the result as an array of strings.
txHashStrs, ok := reply.([]string) var txHashStrs []string
if !ok { err = json.Unmarshal(res, &txHashStrs)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getrawmempool (verbose=false): %T\n", reply) return nil, err
} }
// Create a slice of ShaHash arrays from the string slice.
txHashes := make([]*btcwire.ShaHash, 0, len(txHashStrs)) txHashes := make([]*btcwire.ShaHash, 0, len(txHashStrs))
for _, hashStr := range txHashStrs { for _, hashStr := range txHashStrs {
txHash, err := btcwire.NewShaHashFromStr(hashStr) 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 // FutureGetRawMempoolVerboseResult is a future promise to deliver the result of
// a GetRawMempoolVerboseAsync RPC invocation (or an applicable error). // 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 // 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 hashes to an associated data structure with information about the
// transaction for all transactions in the memory pool. // transaction for all transactions in the memory pool.
func (r FutureGetRawMempoolVerboseResult) Receive() (map[string]btcjson.GetRawMempoolResult, error) { func (r FutureGetRawMempoolVerboseResult) Receive() (map[string]btcjson.GetRawMempoolResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal the result as a map of strings (tx shas) to their detailed
mempoolItems, ok := reply.(map[string]btcjson.GetRawMempoolResult) // results.
if !ok { var mempoolItems map[string]btcjson.GetRawMempoolResult
return nil, fmt.Errorf("unexpected response type for "+ err = json.Unmarshal(res, &mempoolItems)
"getrawmempool (verbose=true): %T\n", reply) if err != nil {
return nil, err
} }
return mempoolItems, nil 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 // FutureVerifyChainResult is a future promise to deliver the result of a
// VerifyChainAsync, VerifyChainLevelAsyncRPC, or VerifyChainBlocksAsync // VerifyChainAsync, VerifyChainLevelAsyncRPC, or VerifyChainBlocksAsync
// invocation (or an applicable error). // 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 // 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 // or not the chain verified based on the check level and number of blocks
// to verify specified in the original call. // to verify specified in the original call.
func (r FutureVerifyChainResult) Receive() (bool, error) { func (r FutureVerifyChainResult) Receive() (bool, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return false, err return false, err
} }
// Ensure the returned data is the expected type. // Unmarshal the result as a boolean.
verified, ok := reply.(bool) var verified bool
if !ok { err = json.Unmarshal(res, &verified)
return false, fmt.Errorf("unexpected response type for "+ if err != nil {
"verifychain: %T\n", reply) return false, err
} }
return verified, nil return verified, nil
} }

View file

@ -6,6 +6,7 @@ package btcrpcclient
import ( import (
"encoding/base64" "encoding/base64"
"encoding/json"
"fmt" "fmt"
"github.com/conformal/btcjson" "github.com/conformal/btcjson"
"github.com/conformal/btcutil" "github.com/conformal/btcutil"
@ -15,24 +16,23 @@ import (
// FutureDebugLevelResult is a future promise to deliver the result of a // FutureDebugLevelResult is a future promise to deliver the result of a
// DebugLevelAsync RPC invocation (or an applicable error). // 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 // 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 // of setting the debug logging level to the passed level specification or the
// list of of the available subsystems for the special keyword 'show'. // list of of the available subsystems for the special keyword 'show'.
func (r FutureDebugLevelResult) Receive() (string, error) { func (r FutureDebugLevelResult) Receive() (string, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return "", err return "", err
} }
// Ensure the returned data is the expected type. // Unmashal the result as a string.
result, ok := reply.(string) var result string
if !ok { err = json.Unmarshal(res, &result)
return "", fmt.Errorf("unexpected response type for "+ if err != nil {
"debuglevel: %T\n", reply) return "", err
} }
return result, nil return result, nil
} }
@ -69,7 +69,7 @@ func (c *Client) DebugLevel(levelSpec string) (string, error) {
// FutureCreateEncryptedWalletResult is a future promise to deliver the error // FutureCreateEncryptedWalletResult is a future promise to deliver the error
// result of a CreateEncryptedWalletAsync RPC invocation. // 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. // Receive waits for and returns the error response promised by the future.
func (r FutureCreateEncryptedWalletResult) Receive() error { 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 // FutureListAddressTransactionsResult is a future promise to deliver the result
// of a ListAddressTransactionsAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns information
// about all transactions associated with the provided addresses. // about all transactions associated with the provided addresses.
func (r FutureListAddressTransactionsResult) Receive() ([]btcjson.ListTransactionsResult, error) { func (r FutureListAddressTransactionsResult) Receive() ([]btcjson.ListTransactionsResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// No transactions. // Unmarshal the result as an array of listtransactions objects.
if reply == nil { var transactions []btcjson.ListTransactionsResult
return nil, nil 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 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 // FutureGetBestBlockResult is a future promise to deliver the result of a
// GetBestBlockAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the hash
// and height of the block in the longest (best) chain. // and height of the block in the longest (best) chain.
func (r FutureGetBestBlockResult) Receive() (*btcwire.ShaHash, int32, error) { func (r FutureGetBestBlockResult) Receive() (*btcwire.ShaHash, int32, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
// Ensure the returned data is the expected type. // Unmarsal result as a getbestblock result object.
result, ok := reply.(*btcws.GetBestBlockResult) var bestBlock btcws.GetBestBlockResult
if !ok { err = json.Unmarshal(res, &bestBlock)
return nil, 0, fmt.Errorf("unexpected response type for "+ if err != nil {
"getbestblock: %T\n", reply) return nil, 0, err
} }
// Convert hash string. // Convert hash string.
hash, err := btcwire.NewShaHashFromStr(result.Hash) hash, err := btcwire.NewShaHashFromStr(bestBlock.Hash)
if err != nil { if err != nil {
return nil, 0, err 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 // 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 // FutureGetCurrentNetResult is a future promise to deliver the result of a
// GetCurrentNetAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the network
// the server is running on. // the server is running on.
func (r FutureGetCurrentNetResult) Receive() (btcwire.BitcoinNet, error) { func (r FutureGetCurrentNetResult) Receive() (btcwire.BitcoinNet, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return 0, err return 0, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as an int64.
fnet, ok := reply.(float64) var net int64
if !ok { err = json.Unmarshal(res, &net)
return 0, fmt.Errorf("unexpected response type for "+ if err != nil {
"getcurrentnet: %T\n", reply) 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 // 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 // FutureExportWatchingWalletResult is a future promise to deliver the result of
// an ExportWatchingWalletAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the
// exported wallet. // exported wallet.
func (r FutureExportWatchingWalletResult) Receive() ([]byte, []byte, error) { func (r FutureExportWatchingWalletResult) Receive() ([]byte, []byte, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a JSON object.
result, ok := reply.(map[string]interface{}) var obj map[string]interface{}
if !ok { err = json.Unmarshal(res, &obj)
return nil, nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"exportwatchingwallet: %T\n", reply) 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 { if !ok {
return nil, nil, fmt.Errorf("unexpected response type for "+ return nil, nil, fmt.Errorf("unexpected response type for "+
"exportwatchingwallet 'wallet' field: %T\n", "exportwatchingwallet 'wallet' field: %T\n",
result["wallet"]) obj["wallet"])
} }
base64TxStore, ok := result["tx"].(string) base64TxStore, ok := obj["tx"].(string)
if !ok { if !ok {
return nil, nil, fmt.Errorf("unexpected response type for "+ return nil, nil, fmt.Errorf("unexpected response type for "+
"exportwatchingwallet 'tx' field: %T\n", "exportwatchingwallet 'tx' field: %T\n",
result["tx"]) obj["tx"])
} }
walletBytes, err := base64.StdEncoding.DecodeString(base64Wallet) walletBytes, err := base64.StdEncoding.DecodeString(base64Wallet)

View file

@ -64,27 +64,20 @@ const (
connectionRetryInterval = time.Second * 5 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 // 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 // as the original JSON-RPC command and a channel to reply on when the server
// responds with the result. // responds with the result.
type sendPostDetails struct { type sendPostDetails struct {
command btcjson.Cmd command btcjson.Cmd
request *http.Request request *http.Request
responseChan chan *futureResult responseChan chan *response
} }
// jsonRequest holds information about a json request that is used to properly // jsonRequest holds information about a json request that is used to properly
// detect, interpret, and deliver a reply to it. // detect, interpret, and deliver a reply to it.
type jsonRequest struct { type jsonRequest struct {
cmd btcjson.Cmd cmd btcjson.Cmd
responseChan chan *futureResult responseChan chan *response
} }
// Client represents a Bitcoin RPC client which allows easy access to the // 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 type (
// authentication, parses the incoming json, looks up and executes handlers // inMessage is the first type that an incoming message is unmarshaled
// (including pass through for standard RPC commands), sends the appropriate // into. It supports both requests (for notification support) and
// response. It also detects commands which are marked as long-running and // responses. The partially-unmarshaled message is a notification if
// sends them off to the asyncHander for processing. // 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) { func (c *Client) handleMessage(msg []byte) {
// Attempt to unmarshal the message as a known JSON-RPC command. // Attempt to unmarshal the message as either a notifiation or response.
if cmd, err := btcjson.ParseMarshaledCmd(msg); err == nil { in := inMessage{}
// Commands that have an ID associated with them are not err := json.Unmarshal(msg, &in)
// notifications. Since this is a client, it should not if err != nil {
// be receiving non-notifications. log.Warnf("Remote server sent invalid message: %v", err)
if cmd.Id() != nil { return
// Invalid response }
log.Warnf("Remote server sent a non-notification "+
"JSON-RPC Request (Id: %v)", cmd.Id()) // 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 return
} }
// Deliver the notification. // Deliver the notification.
log.Tracef("Received notification [%s]", cmd.Method()) log.Tracef("Received notification [%s]", in.Method)
c.handleNotification(cmd) c.handleNotification(in.rawNotification)
return return
} }
// The message was not a command/notification, so it should be a reply if in.rawResponse == nil {
// to a previous request. log.Warn("Malformed response: missing result and error")
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 return
} }
// Ensure the reply has an id. id := *in.ID
if r.Id == nil { log.Tracef("Received response for id %d (result %s)", id, in.Result)
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)
request := c.removeRequest(id) request := c.removeRequest(id)
// Nothing more to do if there is no request associated with this reply. // Nothing more to do if there is no request associated with this reply.
if request == nil || request.responseChan == nil { if request == nil || request.responseChan == nil {
log.Warnf("Received unexpected reply: %s (id %d)", r.Result, id) log.Warnf("Received unexpected reply: %s (id %d)", in.Result,
return id)
}
// 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 return
} }
@ -303,8 +322,9 @@ func (c *Client) handleMessage(msg []byte) {
// can automatically be re-established on reconnect. // can automatically be re-established on reconnect.
c.trackRegisteredNtfns(request.cmd) c.trackRegisteredNtfns(request.cmd)
// Deliver the reply. // Deliver the response.
request.responseChan <- &futureResult{reply: &reply, err: nil} result, err := in.rawResponse.result()
request.responseChan <- &response{result: result, err: err}
} }
// wsInHandler handles all incoming messages for the websocket connection // 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()) log.Tracef("Sending command [%s] with id %d", cmd.Method(), cmd.Id())
httpResponse, err := c.httpClient.Do(details.request) httpResponse, err := c.httpClient.Do(details.request)
if err != nil { if err != nil {
details.responseChan <- &futureResult{reply: nil, err: err} details.responseChan <- &response{result: nil, err: err}
return return
} }
// Read the raw bytes and close the response. // Read the raw bytes and close the response.
respBytes, err := btcjson.GetRaw(httpResponse.Body) resp, err := btcjson.GetRaw(httpResponse.Body)
if err != nil { if err != nil {
details.responseChan <- &futureResult{reply: nil, err: err} details.responseChan <- &response{result: nil, err: err}
return return
} }
details.responseChan <- &response{result: resp, err: nil}
// 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 // sendPostHandler handles all outgoing messages when the client is running
@ -620,9 +633,9 @@ cleanup:
for { for {
select { select {
case details := <-c.sendPostChan: case details := <-c.sendPostChan:
details.responseChan <- &futureResult{ details.responseChan <- &response{
reply: nil, result: nil,
err: ErrClientShutdown, err: ErrClientShutdown,
} }
default: default:
@ -637,17 +650,17 @@ cleanup:
// sendPostRequest sends the passed HTTP request to the RPC server using the // 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, // HTTP client associated with the client. It is backed by a buffered channel,
// so it will not block until the send channel is full. // 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. // Don't send the message if shutting down.
select { select {
case <-c.shutdown: case <-c.shutdown:
responseChan <- &futureResult{reply: nil, err: ErrClientShutdown} responseChan <- &response{result: nil, err: ErrClientShutdown}
default: default:
} }
c.sendPostChan <- &sendPostDetails{ c.sendPostChan <- &sendPostDetails{
request: req,
command: command, command: command,
request: req,
responseChan: responseChan, 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 // 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 // passed error waitin on the channel with the reply set to nil. This is useful
// to easily return errors from the various Async functions. // to easily return errors from the various Async functions.
func newFutureError(err error) chan *futureResult { func newFutureError(err error) chan *response {
responseChan := make(chan *futureResult, 1) responseChan := make(chan *response, 1)
responseChan <- &futureResult{err: err} responseChan <- &response{err: err}
return responseChan return responseChan
} }
@ -665,27 +678,10 @@ func newFutureError(err error) chan *futureResult {
// reply or any errors. The examined errors include an error in the // 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 // futureResult and the error in the reply from the server. This will block
// until the result is available on the passed channel. // 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. // Wait for a response on the returned channel.
response := <-responseChan r := <-f
if response.err != nil { return r.result, r.err
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 // 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 // and closed for each command when using this method, however, the underlying
// HTTP client might coalesce multiple commands depending on several factors // HTTP client might coalesce multiple commands depending on several factors
// including the remote server configuration. // 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) marshalledJSON, err := json.Marshal(cmd)
if err != nil { if err != nil {
responseChan <- &futureResult{reply: nil, err: err} responseChan <- &response{result: nil, err: err}
return return
} }
@ -709,7 +705,7 @@ func (c *Client) marshalAndSendPost(cmd btcjson.Cmd, responseChan chan *futureRe
url := protocol + "://" + c.config.Host url := protocol + "://" + c.config.Host
req, err := http.NewRequest("POST", url, bytes.NewReader(marshalledJSON)) req, err := http.NewRequest("POST", url, bytes.NewReader(marshalledJSON))
if err != nil { if err != nil {
responseChan <- &futureResult{reply: nil, err: err} responseChan <- &response{result: nil, err: err}
return return
} }
req.Close = true 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 // 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. // server. It returns a response channel on which the reply will be delivered.
func (c *Client) marshalAndSend(cmd btcjson.Cmd, responseChan chan *futureResult) { func (c *Client) marshalAndSend(cmd btcjson.Cmd, responseChan chan *response) {
marshalledJSON, err := json.Marshal(cmd) marshalledJSON, err := cmd.MarshalJSON()
if err != nil { if err != nil {
responseChan <- &futureResult{reply: nil, err: err} responseChan <- &response{result: nil, err: err}
return 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 // 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 // future. It handles both websocket and HTTP POST mode depending on the
// configuration of the client. // 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 // Choose which marshal and send function to use depending on whether
// the client running in HTTP POST mode or not. When running in HTTP // the client running in HTTP POST mode or not. When running in HTTP
// POST mode, the command is issued via an HTTP client. Otherwise, // POST mode, the command is issued via an HTTP client. Otherwise,
// the command is issued via the asynchronous websocket channels. // the command is issued via the asynchronous websocket channels.
responseChan := make(chan *futureResult, 1) responseChan := make(chan *response, 1)
if c.config.HttpPostMode { if c.config.HttpPostMode {
c.marshalAndSendPost(cmd, responseChan) c.marshalAndSendPost(cmd, responseChan)
return responseChan return responseChan
@ -804,9 +800,9 @@ func (c *Client) Disconnect() {
c.requestLock.Lock() c.requestLock.Lock()
for e := c.requestList.Front(); e != nil; e = e.Next() { for e := c.requestList.Front(); e != nil; e = e.Next() {
req := e.Value.(*jsonRequest) req := e.Value.(*jsonRequest)
req.responseChan <- &futureResult{ req.responseChan <- &response{
reply: nil, result: nil,
err: ErrClientDisconnect, err: ErrClientDisconnect,
} }
} }
c.requestLock.Unlock() c.requestLock.Unlock()
@ -834,9 +830,9 @@ func (c *Client) Shutdown() {
c.requestLock.Lock() c.requestLock.Lock()
for e := c.requestList.Front(); e != nil; e = e.Next() { for e := c.requestList.Front(); e != nil; e = e.Next() {
req := e.Value.(*jsonRequest) req := e.Value.(*jsonRequest)
req.responseChan <- &futureResult{ req.responseChan <- &response{
reply: nil, result: nil,
err: ErrClientShutdown, err: ErrClientShutdown,
} }
} }
c.requestLock.Unlock() c.requestLock.Unlock()

View file

@ -6,28 +6,28 @@ package btcrpcclient
import ( import (
"encoding/hex" "encoding/hex"
"fmt" "encoding/json"
"github.com/conformal/btcjson" "github.com/conformal/btcjson"
"github.com/conformal/btcutil" "github.com/conformal/btcutil"
) )
// FutureGetGenerateResult is a future promise to deliver the result of a // FutureGetGenerateResult is a future promise to deliver the result of a
// GetGenerateAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns true if the
// server is set to mine, otherwise false. // server is set to mine, otherwise false.
func (r FutureGetGenerateResult) Receive() (bool, error) { func (r FutureGetGenerateResult) Receive() (bool, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return false, err return false, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a boolean.
result, ok := reply.(bool) var result bool
if !ok { err = json.Unmarshal(res, &result)
return false, fmt.Errorf("unexpected response type for "+ if err != nil {
"getgenerate: %T\n", reply) return false, err
} }
return result, nil return result, nil
@ -55,7 +55,7 @@ func (c *Client) GetGenerate() (bool, error) {
// FutureSetGenerateResult is a future promise to deliver the result of a // FutureSetGenerateResult is a future promise to deliver the result of a
// SetGenerateAsync RPC invocation (or an applicable error). // 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 // 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. // 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 // FutureGetHashesPerSecResult is a future promise to deliver the result of a
// GetHashesPerSecAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns a recent
// hashes per second performance measurement while generating coins (mining). // hashes per second performance measurement while generating coins (mining).
// Zero is returned if the server is not mining. // Zero is returned if the server is not mining.
func (r FutureGetHashesPerSecResult) Receive() (int64, error) { func (r FutureGetHashesPerSecResult) Receive() (int64, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return -1, err return -1, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as an int64.
result, ok := reply.(int64) var result int64
if !ok { err = json.Unmarshal(res, &result)
return -1, fmt.Errorf("unexpected response type for "+ if err != nil {
"getnetworkhashps: %T\n", reply) return 0, err
} }
return result, nil return result, nil
@ -135,24 +135,24 @@ func (c *Client) GetHashesPerSec() (int64, error) {
// FutureGetMiningInfoResult is a future promise to deliver the result of a // FutureGetMiningInfoResult is a future promise to deliver the result of a
// GetMiningInfoAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the mining
// information. // information.
func (r FutureGetMiningInfoResult) Receive() (*btcjson.GetMiningInfoResult, error) { func (r FutureGetMiningInfoResult) Receive() (*btcjson.GetMiningInfoResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a getmininginfo result object.
result, ok := reply.(*btcjson.GetMiningInfoResult) var infoResult btcjson.GetMiningInfoResult
if !ok { err = json.Unmarshal(res, &infoResult)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getmininginfo: %T\n", reply) return nil, err
} }
return result, nil return &infoResult, nil
} }
// GetMiningInfoAsync returns an instance of a type that can be used to get // 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 // FutureGetNetworkHashPS is a future promise to deliver the result of a
// GetNetworkHashPSAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the
// estimated network hashes per second for the block heights provided by the // estimated network hashes per second for the block heights provided by the
// parameters. // parameters.
func (r FutureGetNetworkHashPS) Receive() (int64, error) { func (r FutureGetNetworkHashPS) Receive() (int64, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return -1, err return -1, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as an int64.
result, ok := reply.(int64) var result int64
if !ok { err = json.Unmarshal(res, &result)
return -1, fmt.Errorf("unexpected response type for "+ if err != nil {
"getnetworkhashps: %T\n", reply) return 0, err
} }
return result, nil 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 // FutureGetWork is a future promise to deliver the result of a
// GetWorkAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the hash
// data to work on. // data to work on.
func (r FutureGetWork) Receive() (*btcjson.GetWorkResult, error) { func (r FutureGetWork) Receive() (*btcjson.GetWorkResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a getwork result object.
result, ok := reply.(*btcjson.GetWorkResult) var result btcjson.GetWorkResult
if !ok { err = json.Unmarshal(res, &result)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getwork (request data): %T\n", reply) return nil, err
} }
return result, nil return &result, nil
} }
// GetWorkAsync returns an instance of a type that can be used to get the result // 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 // FutureGetWorkSubmit is a future promise to deliver the result of a
// GetWorkSubmitAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns whether
// or not the submitted block header was accepted. // or not the submitted block header was accepted.
func (r FutureGetWorkSubmit) Receive() (bool, error) { func (r FutureGetWorkSubmit) Receive() (bool, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return false, err return false, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a boolean.
accepted, ok := reply.(bool) var accepted bool
if !ok { err = json.Unmarshal(res, &accepted)
return false, fmt.Errorf("unexpected response type for "+ if err != nil {
"getwork (submit data): %T\n", reply) return false, err
} }
return accepted, nil 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 // FutureSubmitBlockResult is a future promise to deliver the result of a
// SubmitBlockAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns an error if
// any occurred when submitting the block. // any occurred when submitting the block.

80
net.go
View file

@ -5,7 +5,7 @@
package btcrpcclient package btcrpcclient
import ( import (
"fmt" "encoding/json"
"github.com/conformal/btcjson" "github.com/conformal/btcjson"
) )
@ -34,7 +34,7 @@ func (cmd AddNodeCommand) String() string {
// FutureAddNodeResult is a future promise to deliver the result of an // FutureAddNodeResult is a future promise to deliver the result of an
// AddNodeAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns an error if
// any occurred when performing the specified command. // 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 // FutureGetAddedNodeInfoResult is a future promise to deliver the result of a
// GetAddedNodeInfoAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns information
// about manually added (persistent) peers. // about manually added (persistent) peers.
func (r FutureGetAddedNodeInfoResult) Receive() ([]btcjson.GetAddedNodeInfoResult, error) { func (r FutureGetAddedNodeInfoResult) Receive() ([]btcjson.GetAddedNodeInfoResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal as an array of getaddednodeinfo result objects.
nodeInfo, ok := reply.([]btcjson.GetAddedNodeInfoResult) var nodeInfo []btcjson.GetAddedNodeInfoResult
if !ok { err = json.Unmarshal(res, &nodeInfo)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getaddednodeinfo (dns=true): %T\n", reply) return nil, err
} }
return nodeInfo, nil return nodeInfo, nil
@ -118,21 +118,21 @@ func (c *Client) GetAddedNodeInfo(peer string) ([]btcjson.GetAddedNodeInfoResult
// FutureGetAddedNodeInfoNoDNSResult is a future promise to deliver the result // FutureGetAddedNodeInfoNoDNSResult is a future promise to deliver the result
// of a GetAddedNodeInfoNoDNSAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns a list of
// manually added (persistent) peers. // manually added (persistent) peers.
func (r FutureGetAddedNodeInfoNoDNSResult) Receive() ([]string, error) { func (r FutureGetAddedNodeInfoNoDNSResult) Receive() ([]string, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as an array of strings.
nodes, ok := reply.([]string) var nodes []string
if !ok { err = json.Unmarshal(res, &nodes)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getaddednodeinfo (dns=false): %T\n", reply) return nil, err
} }
return nodes, nil return nodes, nil
@ -164,24 +164,24 @@ func (c *Client) GetAddedNodeInfoNoDNS(peer string) ([]string, error) {
// FutureGetConnectionCountResult is a future promise to deliver the result // FutureGetConnectionCountResult is a future promise to deliver the result
// of a GetConnectionCountAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the number
// of active connections to other peers. // of active connections to other peers.
func (r FutureGetConnectionCountResult) Receive() (int64, error) { func (r FutureGetConnectionCountResult) Receive() (int64, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return 0, err return 0, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as an int64.
fcount, ok := reply.(float64) var count int64
if !ok { err = json.Unmarshal(res, &count)
return 0, fmt.Errorf("unexpected response type for "+ if err != nil {
"getconnectioncount: %T\n", reply) return 0, err
} }
return int64(fcount), nil return count, nil
} }
// GetConnectionCountAsync returns an instance of a type that can be used to get // 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 // FuturePingResult is a future promise to deliver the result of a PingAsync RPC
// invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the result
// of queueing a ping to be sent to each connected peer. // 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 // FutureGetPeerInfoResult is a future promise to deliver the result of a
// GetPeerInfoAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns data about
// each connected network peer. // each connected network peer.
func (r FutureGetPeerInfoResult) Receive() ([]btcjson.GetPeerInfoResult, error) { func (r FutureGetPeerInfoResult) Receive() ([]btcjson.GetPeerInfoResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as an array of getpeerinfo result objects.
peerInfo, ok := reply.([]btcjson.GetPeerInfoResult) var peerInfo []btcjson.GetPeerInfoResult
if !ok { err = json.Unmarshal(res, &peerInfo)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getpeerinfo: %T\n", reply) return nil, err
} }
return peerInfo, nil 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 // FutureGetNetTotalsResult is a future promise to deliver the result of a
// GetNetTotalsAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns network
// traffic statistics. // traffic statistics.
func (r FutureGetNetTotalsResult) Receive() (*btcjson.GetNetTotalsResult, error) { func (r FutureGetNetTotalsResult) Receive() (*btcjson.GetNetTotalsResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a getnettotals result object.
totals, ok := reply.(*btcjson.GetNetTotalsResult) var totals btcjson.GetNetTotalsResult
if !ok { err = json.Unmarshal(res, &totals)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getnettotals: %T\n", reply) return nil, err
} }
return totals, nil return &totals, nil
} }
// GetNetTotalsAsync returns an instance of a type that can be used to get the // GetNetTotalsAsync returns an instance of a type that can be used to get the

392
notify.go
View file

@ -7,7 +7,9 @@ package btcrpcclient
import ( import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"encoding/json"
"errors" "errors"
"fmt"
"github.com/conformal/btcjson" "github.com/conformal/btcjson"
"github.com/conformal/btcutil" "github.com/conformal/btcutil"
"github.com/conformal/btcwire" "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 // 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 // to ignore things such as notifications when the caller didn't specify any
// notification handlers. // notification handlers.
func newNilFutureResult() chan *futureResult { func newNilFutureResult() chan *response {
responseChan := make(chan *futureResult, 1) responseChan := make(chan *response, 1)
responseChan <- &futureResult{reply: nil} responseChan <- &response{result: nil, err: nil}
return responseChan return responseChan
} }
@ -161,183 +163,192 @@ type NotificationHandlers struct {
// for this package needs to be updated for a new notification type or // 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 // the caller is using a custom notification this package does not know
// about. // about.
OnUnknownNotification func(ntfn interface{}) OnUnknownNotification func(method string, params []json.RawMessage)
} }
// handleNotification examines the passed notification type, performs // handleNotification examines the passed notification type, performs
// conversions to get the raw notification types into higher level types and // conversions to get the raw notification types into higher level types and
// delivers the notification to the appropriate On<X> handler registered with // delivers the notification to the appropriate On<X> handler registered with
// the client. // 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 // Ignore the notification if the client is not interested in any
// notifications. // notifications.
if c.ntfnHandlers == nil { if c.ntfnHandlers == nil {
return return
} }
switch ntfn := cmd.(type) { switch ntfn.Method {
// OnBlockConnected // OnBlockConnected
case *btcws.BlockConnectedNtfn: case btcws.BlockConnectedNtfnMethod:
// Ignore the notification is the client is not interested in // Ignore the notification is the client is not interested in
// it. // it.
if c.ntfnHandlers.OnBlockConnected == nil { if c.ntfnHandlers.OnBlockConnected == nil {
return return
} }
hash, err := btcwire.NewShaHashFromStr(ntfn.Hash) blockSha, blockHeight, err := parseChainNtfnParams(ntfn.Params)
if err != nil { if err != nil {
log.Warnf("Received block connected notification with "+ log.Warnf("Received invalid block connected "+
"invalid hash string: %q", ntfn.Hash) "notification: %v", err)
return return
} }
c.ntfnHandlers.OnBlockConnected(hash, ntfn.Height) c.ntfnHandlers.OnBlockConnected(blockSha, blockHeight)
// OnBlockDisconnected // OnBlockDisconnected
case *btcws.BlockDisconnectedNtfn: case btcws.BlockDisconnectedNtfnMethod:
// Ignore the notification is the client is not interested in // Ignore the notification is the client is not interested in
// it. // it.
if c.ntfnHandlers.OnBlockDisconnected == nil { if c.ntfnHandlers.OnBlockDisconnected == nil {
return return
} }
hash, err := btcwire.NewShaHashFromStr(ntfn.Hash) blockSha, blockHeight, err := parseChainNtfnParams(ntfn.Params)
if err != nil { if err != nil {
log.Warnf("Received block disconnected notification "+ log.Warnf("Received invalid block connected "+
"with invalid hash string: %q", ntfn.Hash) "notification: %v", err)
return return
} }
c.ntfnHandlers.OnBlockDisconnected(hash, ntfn.Height) c.ntfnHandlers.OnBlockDisconnected(blockSha, blockHeight)
// OnRecvTx // OnRecvTx
case *btcws.RecvTxNtfn: case btcws.RecvTxNtfnMethod:
// Ignore the notification is the client is not interested in // Ignore the notification is the client is not interested in
// it. // it.
if c.ntfnHandlers.OnRecvTx == nil { if c.ntfnHandlers.OnRecvTx == nil {
return return
} }
// Decode the serialized transaction hex to raw bytes. tx, block, err := parseChainTxNtfnParams(ntfn.Params)
serializedTx, err := hex.DecodeString(ntfn.HexTx)
if err != nil { if err != nil {
log.Warnf("Received recvtx notification with invalid "+ log.Warnf("Received invalid recvtx notification: %v",
"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) err)
return
} }
c.ntfnHandlers.OnRecvTx(btcutil.NewTx(&msgTx), ntfn.Block) c.ntfnHandlers.OnRecvTx(tx, block)
// OnRedeemingTx // OnRedeemingTx
case *btcws.RedeemingTxNtfn: case btcws.RedeemingTxNtfnMethod:
// Ignore the notification is the client is not interested in // Ignore the notification is the client is not interested in
// it. // it.
if c.ntfnHandlers.OnRedeemingTx == nil { if c.ntfnHandlers.OnRedeemingTx == nil {
return return
} }
// Decode the serialized transaction hex to raw bytes. tx, block, err := parseChainTxNtfnParams(ntfn.Params)
serializedTx, err := hex.DecodeString(ntfn.HexTx)
if err != nil { if err != nil {
log.Warnf("Received redeemingtx notification with "+ log.Warnf("Received invalid redeemingtx "+
"invalid transaction hex '%q': %v", ntfn.HexTx, "notification: %v", err)
err) return
} }
// Deserialize the transaction. c.ntfnHandlers.OnRedeemingTx(tx, block)
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 // OnRescanProgress
case *btcws.RescanProgressNtfn: case btcws.RescanProgressNtfnMethod:
// Ignore the notification is the client is not interested in // Ignore the notification is the client is not interested in
// it. // it.
if c.ntfnHandlers.OnRescanProgress == nil { if c.ntfnHandlers.OnRescanProgress == nil {
return 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 // OnTxAccepted
case *btcws.TxAcceptedNtfn: case btcws.TxAcceptedNtfnMethod:
// Ignore the notification is the client is not interested in // Ignore the notification is the client is not interested in
// it. // it.
if c.ntfnHandlers.OnTxAccepted == nil { if c.ntfnHandlers.OnTxAccepted == nil {
return return
} }
hash, err := btcwire.NewShaHashFromStr(ntfn.TxID) hash, amt, err := parseTxAcceptedNtfnParams(ntfn.Params)
if err != nil { if err != nil {
log.Warnf("Received tx accepted notification with "+ log.Warnf("Received invalid tx accepted "+
"invalid hash string: %q", ntfn.TxID) "notification: %v", err)
return return
} }
c.ntfnHandlers.OnTxAccepted(hash, btcutil.Amount(ntfn.Amount)) c.ntfnHandlers.OnTxAccepted(hash, amt)
// OnTxAcceptedVerbose // OnTxAcceptedVerbose
case *btcws.TxAcceptedVerboseNtfn: case btcws.TxAcceptedVerboseNtfnMethod:
// Ignore the notification is the client is not interested in // Ignore the notification is the client is not interested in
// it. // it.
if c.ntfnHandlers.OnTxAcceptedVerbose == nil { if c.ntfnHandlers.OnTxAcceptedVerbose == nil {
return 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 // OnBtcdConnected
case *btcws.BtcdConnectedNtfn: case btcws.BtcdConnectedNtfnMethod:
// Ignore the notification is the client is not interested in // Ignore the notification is the client is not interested in
// it. // it.
if c.ntfnHandlers.OnBtcdConnected == nil { if c.ntfnHandlers.OnBtcdConnected == nil {
return 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 // OnAccountBalance
case *btcws.AccountBalanceNtfn: case btcws.AccountBalanceNtfnMethod:
// Ignore the notification is the client is not interested in // Ignore the notification is the client is not interested in
// it. // it.
if c.ntfnHandlers.OnAccountBalance == nil { if c.ntfnHandlers.OnAccountBalance == nil {
return return
} }
balance, err := btcjson.JSONToAmount(ntfn.Balance) account, bal, conf, err := parseAccountBalanceNtfnParams(ntfn.Params)
if err != nil { if err != nil {
log.Warnf("Received account balance notification with "+ log.Warnf("Received invalid account balance "+
"an amount that does not parse: %v", "notification: %v", err)
ntfn.Balance)
return return
} }
c.ntfnHandlers.OnAccountBalance(ntfn.Account, c.ntfnHandlers.OnAccountBalance(account, bal, conf)
btcutil.Amount(balance), ntfn.Confirmed)
// OnWalletLockState // OnWalletLockState
case *btcws.WalletLockStateNtfn: case btcws.WalletLockStateNtfnMethod:
// Ignore the notification is the client is not interested in // Ignore the notification is the client is not interested in
// it. // it.
if c.ntfnHandlers.OnWalletLockState == nil { if c.ntfnHandlers.OnWalletLockState == nil {
return 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 // OnUnknownNotification
default: default:
@ -345,13 +356,250 @@ func (c *Client) handleNotification(cmd btcjson.Cmd) {
return 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 // FutureNotifyBlocksResult is a future promise to deliver the result of a
// NotifyBlocksAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns an error
// if the registration was not successful. // 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 // FutureNotifySpentResult is a future promise to deliver the result of a
// NotifySpentAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns an error
// if the registration was not successful. // 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 // FutureNotifyNewTransactionsResult is a future promise to deliver the result
// of a NotifyNewTransactionsAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns an error
// if the registration was not successful. // 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 // FutureNotifyReceivedResult is a future promise to deliver the result of a
// NotifyReceivedAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns an error
// if the registration was not successful. // 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 // FutureRescanResult is a future promise to deliver the result of a RescanAsync
// or RescanEndHeightAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns an error
// if the rescan was not successful. // if the rescan was not successful.

90
rawrequest.go Normal file
View file

@ -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()
}

View file

@ -7,7 +7,7 @@ package btcrpcclient
import ( import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"fmt" "encoding/json"
"github.com/conformal/btcjson" "github.com/conformal/btcjson"
"github.com/conformal/btcutil" "github.com/conformal/btcutil"
"github.com/conformal/btcwire" "github.com/conformal/btcwire"
@ -58,21 +58,21 @@ func (s SigHashType) String() string {
// FutureGetRawTransactionResult is a future promise to deliver the result of a // FutureGetRawTransactionResult is a future promise to deliver the result of a
// GetRawTransactionAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns a
// transaction given its hash. // transaction given its hash.
func (r FutureGetRawTransactionResult) Receive() (*btcutil.Tx, error) { func (r FutureGetRawTransactionResult) Receive() (*btcutil.Tx, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a string.
txHex, ok := reply.(string) var txHex string
if !ok { err = json.Unmarshal(res, &txHex)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getrawtransaction (verbose=0): %T\n", reply) return nil, err
} }
// Decode the serialized transaction hex to raw bytes. // 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 // FutureGetRawTransactionVerboseResult is a future promise to deliver the
// result of a GetRawTransactionVerboseAsync RPC invocation (or an applicable // result of a GetRawTransactionVerboseAsync RPC invocation (or an applicable
// error). // error).
type FutureGetRawTransactionVerboseResult chan *futureResult type FutureGetRawTransactionVerboseResult chan *response
// Receive waits for the response promised by the future and returns information // Receive waits for the response promised by the future and returns information
// about a transaction given its hash. // about a transaction given its hash.
func (r FutureGetRawTransactionVerboseResult) Receive() (*btcjson.TxRawResult, error) { func (r FutureGetRawTransactionVerboseResult) Receive() (*btcjson.TxRawResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a gettrawtransaction result object.
result, ok := reply.(*btcjson.TxRawResult) var rawTxResult btcjson.TxRawResult
if !ok { err = json.Unmarshal(res, &rawTxResult)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getrawtransaction (verbose=1): %T\n", reply) return nil, err
} }
return result, nil return &rawTxResult, nil
} }
// GetRawTransactionVerboseAsync returns an instance of a type that can be used // 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 // FutureDecodeRawTransactionResult is a future promise to deliver the result
// of a DecodeRawTransactionAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns information
// about a transaction given its serialized bytes. // about a transaction given its serialized bytes.
func (r FutureDecodeRawTransactionResult) Receive() (*btcjson.TxRawResult, error) { func (r FutureDecodeRawTransactionResult) Receive() (*btcjson.TxRawResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a decoderawtransaction result object.
result, ok := reply.(*btcjson.TxRawResult) var rawTxResult btcjson.TxRawResult
if !ok { err = json.Unmarshal(res, &rawTxResult)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"decoderawtransaction: %T\n", reply) return nil, err
} }
return result, nil return &rawTxResult, nil
} }
// DecodeRawTransactionAsync returns an instance of a type that can be used to // 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 // FutureCreateRawTransactionResult is a future promise to deliver the result
// of a CreateRawTransactionAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns a new
// transaction spending the provided inputs and sending to the provided // transaction spending the provided inputs and sending to the provided
// addresses. // addresses.
func (r FutureCreateRawTransactionResult) Receive() (*btcwire.MsgTx, error) { func (r FutureCreateRawTransactionResult) Receive() (*btcwire.MsgTx, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a string.
txHex, ok := reply.(string) var txHex string
if !ok { err = json.Unmarshal(res, &txHex)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"createrawtransaction: %T\n", reply) return nil, err
} }
// Decode the serialized transaction hex to raw bytes. // 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 // FutureSendRawTransactionResult is a future promise to deliver the result
// of a SendRawTransactionAsync RPC invocation (or an applicable error). // 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 // 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 // of submitting the encoded transaction to the server which then relays it to
// the network. // the network.
func (r FutureSendRawTransactionResult) Receive() (*btcwire.ShaHash, error) { func (r FutureSendRawTransactionResult) Receive() (*btcwire.ShaHash, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a string.
txHashStr, ok := reply.(string) var txHashStr string
if !ok { err = json.Unmarshal(res, &txHashStr)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"decoderawtransaction: %T\n", reply) return nil, err
} }
return btcwire.NewShaHashFromStr(txHashStr) 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 // FutureSignRawTransactionResult is a future promise to deliver the result
// of one of the SignRawTransactionAsync family of RPC invocations (or an // of one of the SignRawTransactionAsync family of RPC invocations (or an
// applicable error). // applicable error).
type FutureSignRawTransactionResult chan *futureResult type FutureSignRawTransactionResult chan *response
// Receive waits for the response promised by the future and returns the // 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. // 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() (*btcwire.MsgTx, bool, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
// Ensure the returned data is the expected type. // Unmarshal as a signrawtransaction result.
result, ok := reply.(*btcjson.SignRawTransactionResult) var signRawTxResult btcjson.SignRawTransactionResult
if !ok { err = json.Unmarshal(res, &signRawTxResult)
return nil, false, fmt.Errorf("unexpected response type for "+ if err != nil {
"signrawtransaction: %T\n", reply) return nil, false, err
} }
// Decode the serialized transaction hex to raw bytes. // Decode the serialized transaction hex to raw bytes.
serializedTx, err := hex.DecodeString(result.Hex) serializedTx, err := hex.DecodeString(signRawTxResult.Hex)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
@ -361,7 +361,7 @@ func (r FutureSignRawTransactionResult) Receive() (*btcwire.MsgTx, bool, error)
return nil, false, err 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 // SignRawTransactionAsync returns an instance of a type that can be used to get

374
wallet.go
View file

@ -5,7 +5,7 @@
package btcrpcclient package btcrpcclient
import ( import (
"fmt" "encoding/json"
"github.com/conformal/btcjson" "github.com/conformal/btcjson"
"github.com/conformal/btcnet" "github.com/conformal/btcnet"
"github.com/conformal/btcutil" "github.com/conformal/btcutil"
@ -19,24 +19,24 @@ import (
// FutureGetTransactionResult is a future promise to deliver the result // FutureGetTransactionResult is a future promise to deliver the result
// of a GetTransactionAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns detailed
// information about a wallet transaction. // information about a wallet transaction.
func (r FutureGetTransactionResult) Receive() (*btcjson.GetTransactionResult, error) { func (r FutureGetTransactionResult) Receive() (*btcjson.GetTransactionResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a gettransaction result object
result, ok := reply.(*btcjson.GetTransactionResult) var getTx btcjson.GetTransactionResult
if !ok { err = json.Unmarshal(res, &getTx)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"gettransaction: %T\n", reply) return nil, err
} }
return result, nil return &getTx, nil
} }
// GetTransactionAsync returns an instance of a type that can be used to get the // 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 // FutureListTransactionsResult is a future promise to deliver the result of a
// ListTransactionsAsync, ListTransactionsCountAsync, or // ListTransactionsAsync, ListTransactionsCountAsync, or
// ListTransactionsCountFromAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns a list of
// the most recent transactions. // the most recent transactions.
func (r FutureListTransactionsResult) Receive() ([]btcjson.ListTransactionsResult, error) { func (r FutureListTransactionsResult) Receive() ([]btcjson.ListTransactionsResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// No transactions. // Unmarshal result as an array of listtransaction result objects.
if reply == nil { var transactions []btcjson.ListTransactionsResult
return nil, nil 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 "+
"listtransactions: %T\n", reply)
} }
return transactions, nil 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 // FutureListUnspentResult is a future promise to deliver the result of a
// ListUnspentAsync, ListUnspentMinAsync, ListUnspentMinMaxAsync, or // ListUnspentAsync, ListUnspentMinAsync, ListUnspentMinMaxAsync, or
// ListUnspentMinMaxAddressesAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns all
// unspent wallet transaction outputs returned by the RPC call. If the // 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 // or ListUnspentMinMaxAddressesAsync, the range may be limited by the
// parameters of the RPC invocation. // parameters of the RPC invocation.
func (r FutureListUnspentResult) Receive() ([]btcjson.ListUnspentResult, error) { func (r FutureListUnspentResult) Receive() ([]btcjson.ListUnspentResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// No unspent transaction outputs. // Unmarshal result as an array of listunspent results.
if reply == nil { var unspent []btcjson.ListUnspentResult
return nil, nil err = json.Unmarshal(res, &unspent)
} if err != nil {
return nil, err
// 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 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 // FutureListSinceBlockResult is a future promise to deliver the result of a
// ListSinceBlockAsync or ListSinceBlockMinConfAsync RPC invocation (or an // ListSinceBlockAsync or ListSinceBlockMinConfAsync RPC invocation (or an
// applicable error). // applicable error).
type FutureListSinceBlockResult chan *futureResult type FutureListSinceBlockResult chan *response
// Receive waits for the response promised by the future and returns all // Receive waits for the response promised by the future and returns all
// transactions added in blocks since the specified block hash, or all // transactions added in blocks since the specified block hash, or all
// transactions if it is nil. // transactions if it is nil.
func (r FutureListSinceBlockResult) Receive() (*btcjson.ListSinceBlockResult, error) { func (r FutureListSinceBlockResult) Receive() (*btcjson.ListSinceBlockResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a listsinceblock result object.
listResult, ok := reply.(*btcjson.ListSinceBlockResult) var listResult btcjson.ListSinceBlockResult
if !ok { err = json.Unmarshal(res, &listResult)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"listsinceblock: %T\n", reply) return nil, err
} }
return listResult, nil return &listResult, nil
} }
// ListSinceBlockAsync returns an instance of a type that can be used to get // 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 // FutureSetTxFeeResult is a future promise to deliver the result of a
// SetTxFeeAsync RPC invocation (or an applicable error). // 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 // 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 // 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 // FutureSendToAddressResult is a future promise to deliver the result of a
// SendToAddressAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the hash
// of the transaction sending the passed amount to the given address. // of the transaction sending the passed amount to the given address.
func (r FutureSendToAddressResult) Receive() (*btcwire.ShaHash, error) { func (r FutureSendToAddressResult) Receive() (*btcwire.ShaHash, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
txHash, ok := reply.(string) // Unmarshal result as a string.
if !ok { var txHash string
return nil, fmt.Errorf("unexpected response type for "+ err = json.Unmarshal(res, &txHash)
"sendtoaddress: %T\n", reply) if err != nil {
return nil, err
} }
return btcwire.NewShaHashFromStr(txHash) 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 // FutureSendFromResult is a future promise to deliver the result of a
// SendFromAsync, SendFromMinConfAsync, or SendFromCommentAsync RPC invocation // SendFromAsync, SendFromMinConfAsync, or SendFromCommentAsync RPC invocation
// (or an applicable error). // (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 // 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 // of the transaction sending amount to the given address using the provided
// account as a source of funds. // account as a source of funds.
func (r FutureSendFromResult) Receive() (*btcwire.ShaHash, error) { func (r FutureSendFromResult) Receive() (*btcwire.ShaHash, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a string.
txHash, ok := reply.(string) var txHash string
if !ok { err = json.Unmarshal(res, &txHash)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"sendfrom: %T\n", reply) return nil, err
} }
return btcwire.NewShaHashFromStr(txHash) 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 // FutureSendManyResult is a future promise to deliver the result of a
// SendManyAsync, SendManyMinConfAsync, or SendManyCommentAsync RPC invocation // SendManyAsync, SendManyMinConfAsync, or SendManyCommentAsync RPC invocation
// (or an applicable error). // (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 // Receive waits for the response promised by the future and returns the hash
// of the transaction sending multiple amounts to multiple addresses using the // of the transaction sending multiple amounts to multiple addresses using the
// provided account as a source of funds. // provided account as a source of funds.
func (r FutureSendManyResult) Receive() (*btcwire.ShaHash, error) { func (r FutureSendManyResult) Receive() (*btcwire.ShaHash, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmashal result as a string.
txHash, ok := reply.(string) var txHash string
if !ok { err = json.Unmarshal(res, &txHash)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"sendmany: %T\n", reply) return nil, err
} }
return btcwire.NewShaHashFromStr(txHash) 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 // FutureAddMultisigAddressResult is a future promise to deliver the result of a
// AddMultisigAddressAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the
// multisignature address that requires the specified number of signatures for // multisignature address that requires the specified number of signatures for
// the provided addresses. // the provided addresses.
func (r FutureAddMultisigAddressResult) Receive() (btcutil.Address, error) { func (r FutureAddMultisigAddressResult) Receive() (btcutil.Address, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a string.
addr, ok := reply.(string) var addr string
if !ok { err = json.Unmarshal(res, &addr)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"addmultisigaddress: %T\n", reply) return nil, err
} }
return btcutil.DecodeAddress(addr, &btcnet.MainNetParams) 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 // FutureCreateMultisigResult is a future promise to deliver the result of a
// CreateMultisigAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the
// multisignature address and script needed to redeem it. // multisignature address and script needed to redeem it.
func (r FutureCreateMultisigResult) Receive() (*btcjson.CreateMultiSigResult, error) { func (r FutureCreateMultisigResult) Receive() (*btcjson.CreateMultiSigResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a createmultisig result object.
result, ok := reply.(*btcjson.CreateMultiSigResult) var multisigRes btcjson.CreateMultiSigResult
if !ok { err = json.Unmarshal(res, &multisigRes)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"createmultisig: %T\n", reply) return nil, err
} }
return result, nil return &multisigRes, nil
} }
// CreateMultisigAsync returns an instance of a type that can be used to get // 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 // FutureGetNewAddressResult is a future promise to deliver the result of a
// GetNewAddressAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns a new
// address. // address.
func (r FutureGetNewAddressResult) Receive() (btcutil.Address, error) { func (r FutureGetNewAddressResult) Receive() (btcutil.Address, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a string.
addr, ok := reply.(string) var addr string
if !ok { err = json.Unmarshal(res, &addr)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getnewaddress: %T\n", reply) return nil, err
} }
return btcutil.DecodeAddress(addr, &btcnet.MainNetParams) 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 // FutureGetRawChangeAddressResult is a future promise to deliver the result of
// a GetRawChangeAddressAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns a new
// address for receiving change that will be associated with the provided // 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. // account. Note that this is only for raw transactions and NOT for normal use.
func (r FutureGetRawChangeAddressResult) Receive() (btcutil.Address, error) { func (r FutureGetRawChangeAddressResult) Receive() (btcutil.Address, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a string.
addr, ok := reply.(string) var addr string
if !ok { err = json.Unmarshal(res, &addr)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getrawchangeaddress: %T\n", reply) return nil, err
} }
return btcutil.DecodeAddress(addr, &btcnet.MainNetParams) 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 // FutureGetAccountAddressResult is a future promise to deliver the result of a
// GetAccountAddressAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the current
// Bitcoin address for receiving payments to the specified account. // Bitcoin address for receiving payments to the specified account.
func (r FutureGetAccountAddressResult) Receive() (btcutil.Address, error) { func (r FutureGetAccountAddressResult) Receive() (btcutil.Address, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a string.
addr, ok := reply.(string) var addr string
if !ok { err = json.Unmarshal(res, &addr)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getaccountaddress: %T\n", reply) return nil, err
} }
return btcutil.DecodeAddress(addr, &btcnet.MainNetParams) 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 // FutureGetAccountResult is a future promise to deliver the result of a
// GetAccountAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the account
// associated with the passed address. // associated with the passed address.
func (r FutureGetAccountResult) Receive() (string, error) { func (r FutureGetAccountResult) Receive() (string, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return "", err return "", err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a string.
account, ok := reply.(string) var account string
if !ok { err = json.Unmarshal(res, &account)
return "", fmt.Errorf("unexpected response type for "+ if err != nil {
"getaccount: %T\n", reply) return "", err
} }
return account, nil 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 // FutureSetAccountResult is a future promise to deliver the result of a
// SetAccountAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the result
// of setting the account to be associated with the passed address. // 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 // FutureGetAddressesByAccountResult is a future promise to deliver the result
// of a GetAddressesByAccountAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the list of
// addresses associated with the passed account. // addresses associated with the passed account.
func (r FutureGetAddressesByAccountResult) Receive() ([]btcutil.Address, error) { func (r FutureGetAddressesByAccountResult) Receive() ([]btcutil.Address, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmashal result as an array of string.
addrStrings, ok := reply.([]string) var addrStrings []string
if !ok { err = json.Unmarshal(res, &addrStrings)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"getaddressesbyaccount: %T\n", reply) return nil, err
} }
addrs := make([]btcutil.Address, 0, len(addrStrings)) 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 // FutureValidateAddressResult is a future promise to deliver the result of a
// ValidateAddressAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns information
// about the given bitcoin address. // about the given bitcoin address.
func (r FutureValidateAddressResult) Receive() (*btcjson.ValidateAddressResult, error) { func (r FutureValidateAddressResult) Receive() (*btcjson.ValidateAddressResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a validateaddress result object.
result, ok := reply.(*btcjson.ValidateAddressResult) var addrResult btcjson.ValidateAddressResult
if !ok { err = json.Unmarshal(res, &addrResult)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"validateaddress: %T\n", reply) return nil, err
} }
return result, nil return &addrResult, nil
} }
// ValidateAddressAsync returns an instance of a type that can be used to get // 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 // FutureKeyPoolRefillResult is a future promise to deliver the result of a
// KeyPoolRefillAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the result
// of refilling the key pool. // 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 // FutureListAccountsResult is a future promise to deliver the result of a
// ListAccountsAsync or ListAccountsMinConfAsync RPC invocation (or an // ListAccountsAsync or ListAccountsMinConfAsync RPC invocation (or an
// applicable error). // applicable error).
type FutureListAccountsResult chan *futureResult type FutureListAccountsResult chan *response
// Receive waits for the response promised by the future and returns returns a // Receive waits for the response promised by the future and returns returns a
// map of account names and their associated balances. // map of account names and their associated balances.
func (r FutureListAccountsResult) Receive() (map[string]btcutil.Amount, error) { func (r FutureListAccountsResult) Receive() (map[string]btcutil.Amount, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a json object.
accounts, ok := reply.(map[string]float64) var accounts map[string]float64
if !ok { err = json.Unmarshal(res, &accounts)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"listaccounts: %T\n", reply) return nil, err
} }
accountsMap := make(map[string]btcutil.Amount) 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 // FutureGetBalanceResult is a future promise to deliver the result of a
// GetBalanceAsync or GetBalanceMinConfAsync RPC invocation (or an applicable // GetBalanceAsync or GetBalanceMinConfAsync RPC invocation (or an applicable
// error). // error).
type FutureGetBalanceResult chan *futureResult type FutureGetBalanceResult chan *response
// Receive waits for the response promised by the future and returns the // Receive waits for the response promised by the future and returns the
// available balance from the server for the specified account. // available balance from the server for the specified account.
func (r FutureGetBalanceResult) Receive() (btcutil.Amount, error) { func (r FutureGetBalanceResult) Receive() (btcutil.Amount, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return 0, err return 0, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a floating point number.
balance, ok := reply.(float64) var balance float64
if !ok { err = json.Unmarshal(res, &balance)
return 0, fmt.Errorf("unexpected response type for "+ if err != nil {
"getbalance: %T\n", reply) return 0, err
} }
satoshi, err := btcjson.JSONToAmount(balance) 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 // FutureGetReceivedByAccountResult is a future promise to deliver the result of
// a GetReceivedByAccountAsync or GetReceivedByAccountMinConfAsync RPC // a GetReceivedByAccountAsync or GetReceivedByAccountMinConfAsync RPC
// invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the total
// amount received with the specified account. // amount received with the specified account.
func (r FutureGetReceivedByAccountResult) Receive() (btcutil.Amount, error) { func (r FutureGetReceivedByAccountResult) Receive() (btcutil.Amount, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return 0, err return 0, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a floating point number.
balance, ok := reply.(float64) var balance float64
if !ok { err = json.Unmarshal(res, &balance)
return 0, fmt.Errorf("unexpected response type for "+ if err != nil {
"getreceivedbyaccount: %T\n", reply) return 0, err
} }
satoshi, err := btcjson.JSONToAmount(balance) 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 // FutureGetUnconfirmedBalanceResult is a future promise to deliver the result
// of a GetUnconfirmedBalanceAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns returns the
// unconfirmed balance from the server for the specified account. // unconfirmed balance from the server for the specified account.
func (r FutureGetUnconfirmedBalanceResult) Receive() (btcutil.Amount, error) { func (r FutureGetUnconfirmedBalanceResult) Receive() (btcutil.Amount, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return 0, err return 0, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a floating point number.
balance, ok := reply.(float64) var balance float64
if !ok { err = json.Unmarshal(res, &balance)
return 0, fmt.Errorf("unexpected response type for "+ if err != nil {
"getunconfirmedbalance: %T\n", reply) return 0, err
} }
satoshi, err := btcjson.JSONToAmount(balance) 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 // FutureGetReceivedByAddressResult is a future promise to deliver the result of
// a GetReceivedByAddressAsync or GetReceivedByAddressMinConfAsync RPC // a GetReceivedByAddressAsync or GetReceivedByAddressMinConfAsync RPC
// invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the total
// amount received by the specified address. // amount received by the specified address.
func (r FutureGetReceivedByAddressResult) Receive() (btcutil.Amount, error) { func (r FutureGetReceivedByAddressResult) Receive() (btcutil.Amount, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return 0, err return 0, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a floating point number.
balance, ok := reply.(float64) var balance float64
if !ok { err = json.Unmarshal(res, &balance)
return 0, fmt.Errorf("unexpected response type for "+ if err != nil {
"getreceivedbyaddress: %T\n", reply) return 0, err
} }
satoshi, err := btcjson.JSONToAmount(balance) satoshi, err := btcjson.JSONToAmount(balance)
@ -1594,29 +1585,24 @@ func (c *Client) GetReceivedByAddressMinConf(address btcutil.Address, minConfirm
// of a ListReceivedByAccountAsync, ListReceivedByAccountMinConfAsync, or // of a ListReceivedByAccountAsync, ListReceivedByAccountMinConfAsync, or
// ListReceivedByAccountIncludeEmptyAsync RPC invocation (or an applicable // ListReceivedByAccountIncludeEmptyAsync RPC invocation (or an applicable
// error). // error).
type FutureListReceivedByAccountResult chan *futureResult type FutureListReceivedByAccountResult chan *response
// Receive waits for the response promised by the future and returns a list of // Receive waits for the response promised by the future and returns a list of
// balances by account. // balances by account.
func (r FutureListReceivedByAccountResult) Receive() ([]btcjson.ListReceivedByAccountResult, error) { func (r FutureListReceivedByAccountResult) Receive() ([]btcjson.ListReceivedByAccountResult, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// No results. // Unmarshal as an array of listreceivedbyaccount result objects.
if reply == nil { var received []btcjson.ListReceivedByAccountResult
return nil, nil err = json.Unmarshal(res, &received)
if err != nil {
return nil, err
} }
// Ensure the returned data is the expected type. return received, nil
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 // 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 // FutureWalletLockResult is a future promise to deliver the result of a
// WalletLockAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the result
// of locking the wallet. // 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 // FutureWalletPassphraseChangeResult is a future promise to deliver the result
// of a WalletPassphraseChangeAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the result
// of changing the wallet passphrase. // 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 // FutureSignMessageResult is a future promise to deliver the result of a
// SignMessageAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns the message
// signed with the private key of the specified address. // signed with the private key of the specified address.
func (r FutureSignMessageResult) Receive() (string, error) { func (r FutureSignMessageResult) Receive() (string, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return "", err return "", err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a string.
b64, ok := reply.(string) var b64 string
if !ok { err = json.Unmarshal(res, &b64)
return "", fmt.Errorf("unexpected response type for "+ if err != nil {
"signmessage: %T\n", reply) return "", err
} }
return b64, nil 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 // FutureVerifyMessageResult is a future promise to deliver the result of a
// VerifyMessageAsync RPC invocation (or an applicable error). // 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 // Receive waits for the response promised by the future and returns whether or
// not the message was successfully verified. // not the message was successfully verified.
func (r FutureVerifyMessageResult) Receive() (bool, error) { func (r FutureVerifyMessageResult) Receive() (bool, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return false, err return false, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a boolean.
verified, ok := reply.(bool) var verified bool
if !ok { err = json.Unmarshal(res, &verified)
return false, fmt.Errorf("unexpected response type for "+ if err != nil {
"verifymessage: %T\n", reply) return false, err
} }
return verified, nil 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 // FutureDumpPrivKeyResult is a future promise to deliver the result of a
// DumpPrivKeyAsync RPC invocation (or an applicable error). // 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 // 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 // key corresponding to the passed address encoded in the wallet import format
// (WIF) // (WIF)
func (r FutureDumpPrivKeyResult) Receive() (*btcutil.WIF, error) { func (r FutureDumpPrivKeyResult) Receive() (*btcutil.WIF, error) {
reply, err := receiveFuture(r) res, err := receiveFuture(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Ensure the returned data is the expected type. // Unmarshal result as a string.
privKeyWIF, ok := reply.(string) var privKeyWIF string
if !ok { err = json.Unmarshal(res, &privKeyWIF)
return nil, fmt.Errorf("unexpected response type for "+ if err != nil {
"dumpprivkey: %T\n", reply) return nil, err
} }
return btcutil.DecodeWIF(privKeyWIF) 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 // FutureImportPrivKeyResult is a future promise to deliver the result of an
// ImportPrivKeyAsync RPC invocation (or an applicable error). // 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 // 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 // of importing the passed private key which must be the wallet import format